diff --git a/README.md b/README.md index acb4602fc..aa6a7abeb 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,26 @@ In non-SSL environments (like on development setups) it is necessary to set two `./occ config:app:set circles --value 1 local_is_non_ssl` +## Allow mirroring circles as groups + +```bash +./occ maintenance:mode --on + +./occ config:app:set circles --value 1 group_backend # Mirroring circles as groups +./occ config:app:set circles --value 0 allow_listed_circles # Hide circles in shared list, useful with the 'group_backend' option + +# ./occ config:app:set circles --value "🌀 " group_backend_name_prefix # You can customize group name prefix +# ./occ config:app:set circles --value " " group_backend_name_suffix # Remove default group name suffix with a `space` character + +./occ config:app:set circles --value 12 allow_circles # Only show 'public' and 'closed' circles +./occ config:app:set circles --value 1 skip_invitation_to_closed_circles + +./occ config:app:set circles --value 0 allow_files_filtered_by_circles # Disable files list filtering by circles in the 'files' application +./occ config:app:set circles --value 0 allow_adding_any_group_members # Adding group members only for groups where the current user is a member or global administrators + +./occ maintenance:mode --off +``` + # Credits App Icon by [Madebyoliver](http://www.flaticon.com/authors/madebyoliver) under Creative Commons BY 3.0 diff --git a/appinfo/app.php b/appinfo/app.php index a2dfb5939..709bd1e79 100644 --- a/appinfo/app.php +++ b/appinfo/app.php @@ -34,7 +34,4 @@ $app = \OC::$server->query(Application::class); -$app->registerNavigation(); -$app->registerFilesNavigation(); -$app->registerFilesPlugin(); - +$app->register(); diff --git a/lib/Activity/ProviderParser.php b/lib/Activity/ProviderParser.php index 6254d40b7..99af607b1 100644 --- a/lib/Activity/ProviderParser.php +++ b/lib/Activity/ProviderParser.php @@ -32,6 +32,7 @@ use OCA\Circles\Model\FederatedLink; use OCA\Circles\Model\Member; use OCA\Circles\Service\MiscService; +use OCA\Circles\Service\ConfigService; use OCP\Activity\IEvent; use OCP\Activity\IManager; use OCP\IL10N; @@ -39,7 +40,6 @@ class ProviderParser { - /** @var MiscService */ protected $miscService; @@ -52,13 +52,28 @@ class ProviderParser { /** @var IManager */ protected $activityManager; + /** @var ConfigService */ + protected $configService; + + /** + * @param IURLGenerator $url + * @param IManager $activityManager + * @param IL10N $l10n + * @param MiscService $miscService + * @param ConfigService $configService + */ public function __construct( - IURLGenerator $url, IManager $activityManager, IL10N $l10n, MiscService $miscService + IURLGenerator $url, + IManager $activityManager, + IL10N $l10n, + MiscService $miscService, + ConfigService $configService ) { $this->url = $url; $this->activityManager = $activityManager; $this->l10n = $l10n; $this->miscService = $miscService; + $this->configService = $configService; } @@ -299,7 +314,7 @@ protected function generateViewerParameter(Circle $circle) { */ protected function generateExternalMemberParameter(Member $member) { return [ - 'type' => 'user', + 'type' => $member->getTypeName(), 'id' => $member->getUserId(), 'name' => $member->getDisplayName() . ' (' . $member->getTypeString() . ')', '_parsed' => $member->getDisplayName() diff --git a/lib/Activity/ProviderSubjectMember.php b/lib/Activity/ProviderSubjectMember.php index 486cb0543..37cf1ba06 100644 --- a/lib/Activity/ProviderSubjectMember.php +++ b/lib/Activity/ProviderSubjectMember.php @@ -68,7 +68,9 @@ public function parseSubjectMemberJoin(IEvent $event, Circle $circle, Member $me * @throws FakeException */ private function parseSubjectMemberJoinClosedCircle(IEvent $event, Circle $circle, Member $member) { - if ($circle->getType() !== Circle::CIRCLES_CLOSED) { + if ($circle->getType() !== Circle::CIRCLES_CLOSED || + $this->configService->isInvitationSkipped() + ) { return; } @@ -141,7 +143,9 @@ private function parseSubjectMemberAddNotLocalMember(IEvent $event, Circle $circ * @throws FakeException */ private function parseSubjectMemberAddClosedCircle(IEvent $event, Circle $circle, Member $member) { - if ($circle->getType() !== Circle::CIRCLES_CLOSED) { + if ($circle->getType() !== Circle::CIRCLES_CLOSED || + $this->configService->isInvitationSkipped() + ) { return; } diff --git a/lib/Api/Sharees.php b/lib/Api/Sharees.php index 2755f93ce..7c80409de 100644 --- a/lib/Api/Sharees.php +++ b/lib/Api/Sharees.php @@ -30,6 +30,7 @@ use OCA\Circles\Model\Circle; use OCA\Circles\Model\Member; use OCA\Circles\Service\CirclesService; +use OCA\Circles\Service\ConfigService; use OCA\Circles\Service\MiscService; use OCP\Share; @@ -61,12 +62,20 @@ protected static function getContainer() { // public static function search($search, $limit, $offset) { public static function search($search) { $c = self::getContainer(); + + $type = Circle::CIRCLES_ALL; + $circlesAreVisible = $c->query(ConfigService::class) + ->isListedCirclesAllowed(); + if (!$circlesAreVisible) { + $type = $type - Circle::CIRCLES_CLOSED - Circle::CIRCLES_PUBLIC; + } + $userId = \OC::$server->getUserSession() ->getUser() ->getUID(); $data = $c->query(CirclesService::class) - ->listCircles($userId, Circle::CIRCLES_ALL, $search, Member::LEVEL_MEMBER); + ->listCircles($userId, $type, $search, Member::LEVEL_MEMBER); $result = array( 'exact' => ['circles'], 'circles' => [] @@ -111,4 +120,4 @@ private static function addResultEntry(&$result, $entry, $exact = false) { } -} \ No newline at end of file +} diff --git a/lib/Api/v1/Circles.php b/lib/Api/v1/Circles.php index 294e9433e..03f494a9f 100644 --- a/lib/Api/v1/Circles.php +++ b/lib/Api/v1/Circles.php @@ -37,6 +37,7 @@ use OCA\Circles\Model\Member; use OCA\Circles\Model\SharingFrame; use OCA\Circles\Service\CirclesService; +use OCA\Circles\Service\ConfigService; use OCA\Circles\Service\FederatedLinkService; use OCA\Circles\Service\MembersService; use OCA\Circles\Service\MiscService; @@ -181,6 +182,43 @@ public static function leaveCircle($circleUniqueId) { public static function listCircles($type, $name = '', $level = 0, $userId = '', $forceAll = false) { $c = self::getContainer(); + $configService = $c->query(ConfigService::class); + $circlesAreVisible = $configService->isListedCirclesAllowed(); + $circlesAllowed = (int)$configService->getAppValue(ConfigService::CIRCLES_ALLOW_CIRCLES); + + if (!$circlesAreVisible) { + switch (true) { + case ($type === Circle::CIRCLES_CLOSED): + case ($type === Circle::CIRCLES_PUBLIC): + case (Circle::CIRCLES_CLOSED + Circle::CIRCLES_PUBLIC === $circlesAllowed): + return []; + break; + case ($type === Circle::CIRCLES_ALL && + $circlesAllowed === Circle::CIRCLES_ALL + ): + $type = $type - Circle::CIRCLES_CLOSED - Circle::CIRCLES_PUBLIC; + break; + case ($type > Circle::CIRCLES_CLOSED && + $type < Circle::CIRCLES_PUBLIC && + $circlesAllowed === Circle::CIRCLES_ALL + ): + $type = $type - Circle::CIRCLES_CLOSED; + break; + case ($type > Circle::CIRCLES_PUBLIC && + $type < (Circle::CIRCLES_PUBLIC + Circle::CIRCLES_CLOSED) && + $circlesAllowed === Circle::CIRCLES_ALL + ): + $type = $type - Circle::CIRCLES_PUBLIC; + break; + case ($type > (Circle::CIRCLES_PUBLIC + Circle::CIRCLES_CLOSED) && + $type < Circle::CIRCLES_ALL && + $circlesAllowed === Circle::CIRCLES_ALL + ): + $type = $type - Circle::CIRCLES_PUBLIC - Circle::CIRCLES_CLOSED; + break; + } + } + if ($userId === '') { $userId = \OC::$server->getUserSession() ->getUser() diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 1b3c247dc..4e5762a7c 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -29,16 +29,18 @@ namespace OCA\Circles\AppInfo; -use OC; use OCA\Circles\Api\v1\Circles; use OCA\Circles\Notification\Notifier; use OCA\Circles\Service\ConfigService; use OCA\Circles\Service\DavService; +use OCA\Circles\Service\GroupsBackendService; use OCA\Files\App as FilesApp; use OCP\App\ManagerEvent; use OCP\AppFramework\App; use OCP\AppFramework\IAppContainer; use OCP\AppFramework\QueryException; +use OCP\IGroup; +use OCP\EventDispatcher\IEventDispatcher; use OCP\Util; require_once __DIR__ . '/../../appinfo/autoload.php'; @@ -56,22 +58,36 @@ class Application extends App { /** @var IAppContainer */ private $container; + /** @var \OC\ServerServer */ + private $server; + + /** @var IEventDispatcher */ + private $dispatcher; + /** * @param array $params */ public function __construct(array $params = array()) { parent::__construct(self::APP_NAME, $params); + } + public function register() + { $this->container = $this->getContainer(); + $this->server = $this->container->getServer(); + $this->dispatcher = $this->server->query(IEventDispatcher::class); - $manager = OC::$server->getNotificationManager(); + $manager = $this->server->getNotificationManager(); $manager->registerNotifierService(Notifier::class); + $this->registerNavigation(); + $this->registerFilesNavigation(); + $this->registerFilesPlugin(); $this->registerHooks(); $this->registerDavHooks(); + $this->registerGroupsBackendHooks(); } - /** * Register Hooks */ @@ -91,7 +107,7 @@ public function registerHooks() { public function registerNavigation() { /** @var ConfigService $configService */ try { - $configService = OC::$server->query(ConfigService::class); + $configService = $this->server->query(ConfigService::class); } catch (QueryException $e) { return; } @@ -104,8 +120,8 @@ public function registerNavigation() { ->getNavigationManager(); $appManager->add( function() { - $urlGen = OC::$server->getURLGenerator(); - $navName = OC::$server->getL10N(self::APP_NAME) + $urlGen = $this->server->getURLGenerator(); + $navName = $this->server->getL10N(self::APP_NAME) ->t('Circles'); return [ @@ -121,8 +137,17 @@ function() { } public function registerFilesPlugin() { - $eventDispatcher = OC::$server->getEventDispatcher(); - $eventDispatcher->addListener( + try { + /** @var ConfigService $configService */ + $configService = $this->server->query(ConfigService::class); + if (!$configService->isFilesFilteredCirclesAllowed()) { + return; + } + } catch (QueryException $e) { + return; + } + + $this->dispatcher->addListener( 'OCA\Files::loadAdditionalScripts', function() { Circles::addJavascriptAPI(); @@ -140,10 +165,20 @@ function() { * */ public function registerFilesNavigation() { + try { + /** @var ConfigService $configService */ + $configService = $this->server->query(ConfigService::class); + if (!$configService->isFilesFilteredCirclesAllowed()) { + return; + } + } catch (QueryException $e) { + return; + } + $appManager = FilesApp::getNavigationManager(); $appManager->add( function() { - $l = OC::$server->getL10N('circles'); + $l = $this->server->getL10N('circles'); return [ 'id' => 'circlesfilter', @@ -160,24 +195,63 @@ function() { public function registerDavHooks() { try { /** @var ConfigService $configService */ - $configService = OC::$server->query(ConfigService::class); + $configService = $this->server->query(ConfigService::class); if (!$configService->isContactsBackend()) { return; } /** @var DavService $davService */ - $davService = OC::$server->query(DavService::class); + $davService = $this->server->query(DavService::class); } catch (QueryException $e) { return; } - $event = OC::$server->getEventDispatcher(); + $this->dispatcher->addListener(ManagerEvent::EVENT_APP_ENABLE, [$davService, 'onAppEnabled']); + $this->dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::createCard', [$davService, 'onCreateCard']); + $this->dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::updateCard', [$davService, 'onUpdateCard']); + $this->dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::deleteCard', [$davService, 'onDeleteCard']); + } - $event->addListener(ManagerEvent::EVENT_APP_ENABLE, [$davService, 'onAppEnabled']); - $event->addListener('\OCA\DAV\CardDAV\CardDavBackend::createCard', [$davService, 'onCreateCard']); - $event->addListener('\OCA\DAV\CardDAV\CardDavBackend::updateCard', [$davService, 'onUpdateCard']); - $event->addListener('\OCA\DAV\CardDAV\CardDavBackend::deleteCard', [$davService, 'onDeleteCard']); + public function registerGroupsBackendHooks() { + try { + /** @var ConfigService $configService */ + $configService = $this->server->query(ConfigService::class); + if (!$configService->isGroupsBackend()) { + return; + } + + /** @var GroupsBackendService $groupsBackendService */ + $groupsBackendService = $this->server->query(GroupsBackendService::class); + } catch (QueryException $e) { + return; + } + + $this->dispatcher->addListener(ManagerEvent::EVENT_APP_ENABLE, [$groupsBackendService, 'onAppEnabled']); + $this->dispatcher->addListener('\OCA\Circles::onCircleCreation', [$groupsBackendService, 'onCircleCreation']); + $this->dispatcher->addListener('\OCA\Circles::onCircleDestruction', [$groupsBackendService, 'onCircleDestruction']); + $this->dispatcher->addListener('\OCA\Circles::onMemberNew', [$groupsBackendService, 'onMemberNew']); + $this->dispatcher->addListener('\OCA\Circles::onMemberInvited', [$groupsBackendService, 'onMemberInvited']); + $this->dispatcher->addListener('\OCA\Circles::onMemberRequesting', [$groupsBackendService, 'onMemberRequesting']); + $this->dispatcher->addListener('\OCA\Circles::onMemberLeaving', [$groupsBackendService, 'onMemberLeaving']); + $this->dispatcher->addListener('\OCA\Circles::onMemberLevel', [$groupsBackendService, 'onMemberLevel']); + $this->dispatcher->addListener('\OCA\Circles::onMemberOwner', [$groupsBackendService, 'onMemberOwner']); + $this->dispatcher->addListener('\OCA\Circles::onGroupLink', [$groupsBackendService, 'onGroupLink']); + $this->dispatcher->addListener('\OCA\Circles::onGroupUnlink', [$groupsBackendService, 'onGroupUnlink']); + $this->dispatcher->addListener('\OCA\Circles::onGroupLevel', [$groupsBackendService, 'onGroupLevel']); + $this->dispatcher->addListener('\OCA\Circles::onLinkRequestSent', [$groupsBackendService, 'onLinkRequestSent']); + $this->dispatcher->addListener('\OCA\Circles::onLinkRequestReceived', [$groupsBackendService, 'onLinkRequestReceived']); + $this->dispatcher->addListener('\OCA\Circles::onLinkRequestRejected', [$groupsBackendService, 'onLinkRequestRejected']); + $this->dispatcher->addListener('\OCA\Circles::onLinkRequestCanceled', [$groupsBackendService, 'onLinkRequestCanceled']); + $this->dispatcher->addListener('\OCA\Circles::onLinkRequestAccepted', [$groupsBackendService, 'onLinkRequestAccepted']); + $this->dispatcher->addListener('\OCA\Circles::onLinkRequestAccepting', [$groupsBackendService, 'onLinkRequestAccepting']); + $this->dispatcher->addListener('\OCA\Circles::onLinkUp', [$groupsBackendService, 'onLinkUp']); + $this->dispatcher->addListener('\OCA\Circles::onLinkDown', [$groupsBackendService, 'onLinkDown']); + $this->dispatcher->addListener('\OCA\Circles::onLinkRemove', [$groupsBackendService, 'onLinkRemove']); + $this->dispatcher->addListener('\OCA\Circles::onSettingsChange', [$groupsBackendService, 'onSettingsChange']); + + $this->dispatcher->addListener(IGroup::class . '::postAddUser', [$groupsBackendService, 'onGroupPostAddUser']); + $this->dispatcher->addListener(IGroup::class . '::postRemoveUser', [$groupsBackendService, 'onGroupPostRemoveUser']); + $this->dispatcher->addListener(IGroup::class . '::postDelete', [$groupsBackendService, 'onGroupPostDelete']); } } - diff --git a/lib/Db/CirclesRequest.php b/lib/Db/CirclesRequest.php index aca9d7fec..d49304634 100644 --- a/lib/Db/CirclesRequest.php +++ b/lib/Db/CirclesRequest.php @@ -130,6 +130,36 @@ public function forceGetCircleByName($name) { } + /** + * forceGetCircleByGroupId(); + * + * returns data of a circle from its Group ID. + * + * WARNING: This function does not filters data regarding the current user/viewer. + * In case of interaction with users, do not use this method. + * + * @param $groupId + * + * @return null|Circle + */ + public function forceGetCircleByGroupId($groupId) { + $qb = $this->getCirclesSelectSql(); + + $this->limitToGroupId($qb, $groupId); + + $cursor = $qb->execute(); + $data = $cursor->fetch(); + $cursor->closeCursor(); + + if ($data === false) { + return null; + } + + $entry = $this->parseCirclesSelectSql($data); + + return $entry; + } + /** * @param string $userId * @param int $type @@ -331,7 +361,8 @@ public function updateCircle(Circle $circle, $userId) { $qb = $this->getCirclesUpdateSql($circle->getUniqueId(true)); $qb->set('name', $qb->createNamedParameter($circle->getName())) ->set('description', $qb->createNamedParameter($circle->getDescription())) - ->set('settings', $qb->createNamedParameter($circle->getSettings(true))); + ->set('settings', $qb->createNamedParameter($circle->getSettings(true))) + ->set('group_id', $qb->createNamedParameter($circle->getGroupId())); $qb->execute(); } diff --git a/lib/Db/CirclesRequestBuilder.php b/lib/Db/CirclesRequestBuilder.php index 9f773c8fe..9226aa2c8 100644 --- a/lib/Db/CirclesRequestBuilder.php +++ b/lib/Db/CirclesRequestBuilder.php @@ -392,7 +392,7 @@ protected function getCirclesSelectSql() { $qb->selectDistinct('c.unique_id') ->addSelect( 'c.id', 'c.name', 'c.description', 'c.settings', 'c.type', 'contact_addressbook', - 'contact_groupname', 'c.creation' + 'contact_groupname', 'c.creation', 'c.group_id' ) ->from(CoreRequestBuilder::TABLE_CIRCLES, 'c'); $this->default_select_alias = 'c'; @@ -419,6 +419,7 @@ protected function parseCirclesSelectSql($data) { if ($data['contact_groupname'] !== null) { $circle->setContactGroupName($data['contact_groupname']); } + $circle->setGroupId($data['group_id']); $circle->setSettings($data['settings']); $circle->setType($data['type']); $circle->setCreation($data['creation']); diff --git a/lib/Db/MembersRequest.php b/lib/Db/MembersRequest.php index 54bd6961e..e60c8f048 100644 --- a/lib/Db/MembersRequest.php +++ b/lib/Db/MembersRequest.php @@ -637,6 +637,14 @@ public function unlinkAllFromGroup($groupId) { } + public function unlinkFromGroup($circleId, $groupId) { + $qb = $this->getGroupsDeleteSql($groupId); + $this->limitToCircleId($qb, $circleId); + + $qb->execute(); + } + + /** * @param string $contactId * @@ -731,4 +739,3 @@ public function removeMembersByContactId(string $contactId, int $type = 0) { } - diff --git a/lib/Migration/Version0017Date20200221173726.php b/lib/Migration/Version0017Date20200221173726.php new file mode 100644 index 000000000..e27d834a1 --- /dev/null +++ b/lib/Migration/Version0017Date20200221173726.php @@ -0,0 +1,81 @@ + + * @copyright 2019 + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +declare(strict_types=1); + +namespace OCA\Circles\Migration; + +use Closure; +use Doctrine\DBAL\Schema\SchemaException; +use Doctrine\DBAL\Types\Type; +use OCP\DB\ISchemaWrapper; +use OCP\IDBConnection; +use OCP\Migration\IOutput; +use OCP\Migration\SimpleMigrationStep; + +/** + * Auto-generated migration step: Please modify to your needs! + */ +class Version0017Date20200221173726 extends SimpleMigrationStep { + + + /** @var IDBConnection */ + private $connection; + + + /** + * @param IDBConnection $connection + */ + public function __construct(IDBConnection $connection) { + $this->connection = $connection; + } + + + /** + * @param IOutput $output + * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + * @param array $options + * + * @return null|ISchemaWrapper + * @throws SchemaException + */ + public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) { + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + + $table = $schema->getTable('circles_circles'); + $table->addColumn( + 'group_id', 'string', [ + 'notnull' => false, + 'default' => '', + 'length' => 64, + ] + ); + + return $schema; + } + +} diff --git a/lib/Model/BaseCircle.php b/lib/Model/BaseCircle.php index 523662cff..3bd78c0a4 100644 --- a/lib/Model/BaseCircle.php +++ b/lib/Model/BaseCircle.php @@ -82,6 +82,8 @@ class BaseCircle { /** @var int */ private $contactAddressBook = 0; + /** @var string */ + private $groupId = ''; /** @var string */ private $creation; @@ -307,6 +309,23 @@ public function getContactGroupName() { return $this->contactGroupName; } + /** + * @param string $groupId + * + * @return BaseCircle + */ + public function setGroupId($groupId) { + $this->groupId = (string) $groupId; + + return $this; + } + + /** + * @return string + */ + public function getGroupId() { + return (string) $this->groupId; + } /** * @param string|array $settings diff --git a/lib/Model/BaseMember.php b/lib/Model/BaseMember.php index 60c177b71..d37596eec 100644 --- a/lib/Model/BaseMember.php +++ b/lib/Model/BaseMember.php @@ -439,4 +439,17 @@ public function getTypeString() { return 'none'; } + + public function getTypeName() { + switch ($this->getType()) { + case self::TYPE_USER: + case self::TYPE_MAIL: + case self::TYPE_CONTACT: + return 'user'; + case self::TYPE_GROUP: + return 'user-group'; + } + + return 'none'; + } } diff --git a/lib/Model/Circle.php b/lib/Model/Circle.php index 506ee313c..bdad2c37c 100644 --- a/lib/Model/Circle.php +++ b/lib/Model/Circle.php @@ -75,6 +75,7 @@ public function jsonSerialize() { 'viewer' => $this->getHigherViewer(), 'description' => $this->getDescription(), 'settings' => $this->getSettings(), + 'group_id' => $this->getGroupId(), 'type' => $this->getType(), 'creation' => $this->getCreation(), 'type_string' => $this->getTypeString(), @@ -253,5 +254,3 @@ public static function typeLongString($type) { } - - diff --git a/lib/Model/Member.php b/lib/Model/Member.php index af6f8c1f9..03c2612b3 100644 --- a/lib/Model/Member.php +++ b/lib/Model/Member.php @@ -63,6 +63,7 @@ public function inviteToCircle($circleType) { public function joinCircle($circleType) { switch ($circleType) { + case Circle::CIRCLES_PERSONAL: case Circle::CIRCLES_SECRET: case Circle::CIRCLES_PUBLIC: return $this->addMemberToCircle(); @@ -264,5 +265,3 @@ public function hasToBeInviteAble() { } } - - diff --git a/lib/Search/LocalGroups.php b/lib/Search/LocalGroups.php index 7c2e3f202..594490ac8 100644 --- a/lib/Search/LocalGroups.php +++ b/lib/Search/LocalGroups.php @@ -26,26 +26,48 @@ namespace OCA\Circles\Search; +use OC; use OCA\Circles\ISearch; use OCA\Circles\Model\Member; use OCA\Circles\Model\SearchResult; +use OCP\IUser; +use OCA\Circles\Service\ConfigService; class LocalGroups implements ISearch { + /** @var ConfigService */ + private $configService; + + /** + * @param ConfigService $configService + */ + public function __construct(ConfigService $configService) + { + $this->configService = $configService; + } + /** * {@inheritdoc} */ public function search($search) { $result = []; - $groupManager = \OC::$server->getGroupManager(); + $groupManager = OC::$server->getGroupManager(); $groups = $groupManager->search($search); + $user = OC::$server->getUserSession()->getUser(); foreach ($groups as $group) { - $result[] = new SearchResult($group->getGID(), Member::TYPE_GROUP); + if ($this->configService->isAddingAnyGroupMembersAllowed() || + ( + $user instanceof IUser && + ($group->inGroup($user) || $groupManager->isAdmin($user->getUID())) + ) + ) { + $result[] = new SearchResult($group->getGID(), Member::TYPE_GROUP); + } } return $result; } -} \ No newline at end of file +} diff --git a/lib/Service/CirclesService.php b/lib/Service/CirclesService.php index 92f3c85b2..0cd6aaaac 100644 --- a/lib/Service/CirclesService.php +++ b/lib/Service/CirclesService.php @@ -318,6 +318,14 @@ public function settingsCircle($circleUniqueId, $settings) { $circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId); $this->hasToBeOwner($circle->getHigherViewer()); + $oldSettings = array_merge( + $circle->getSettings(), + [ + 'circle_name' => $circle->getName(), + 'circle_desc' => $circle->getDescription(), + ] + ); + if (!$this->viewerIsAdmin()) { $settings['members_limit'] = $circle->getSetting('members_limit'); } @@ -329,7 +337,7 @@ public function settingsCircle($circleUniqueId, $settings) { $this->circlesRequest->updateCircle($circle, $this->userId); - $this->eventsService->onSettingsChange($circle); + $this->eventsService->onSettingsChange($circle, $oldSettings); } catch (\Exception $e) { throw $e; } @@ -577,11 +585,11 @@ public function checkThatCircleIsNotFull(Circle $circle) { $circle->getUniqueId(), Member::LEVEL_MEMBER, true ); - $limit = $circle->getSetting('members_limit'); + $limit = (int) $circle->getSetting('members_limit'); if ($limit === -1) { return; } - if ($limit === 0 || $limit === '' || $limit === null) { + if ($limit === 0) { $limit = $this->configService->getAppValue(ConfigService::CIRCLES_MEMBERS_LIMIT); } diff --git a/lib/Service/ConfigService.php b/lib/Service/ConfigService.php index c1497c7ac..02a873c41 100644 --- a/lib/Service/ConfigService.php +++ b/lib/Service/ConfigService.php @@ -26,6 +26,7 @@ namespace OCA\Circles\Service; +use OC; use OCA\Circles\Model\Circle; use OCP\IConfig; use OCP\IRequest; @@ -37,10 +38,16 @@ class ConfigService { const CIRCLES_ALLOW_CIRCLES = 'allow_circles'; const CIRCLES_CONTACT_BACKEND = 'contact_backend'; const CIRCLES_STILL_FRONTEND = 'still_frontend'; + const CIRCLES_GROUP_BACKEND = 'group_backend'; + const CIRCLES_GROUP_BACKEND_NAME_PREFIX = 'group_backend_name_prefix'; + const CIRCLES_GROUP_BACKEND_NAME_SUFFIX = 'group_backend_name_suffix'; const CIRCLES_SWAP_TO_TEAMS = 'swap_to_teams'; const CIRCLES_ALLOW_FEDERATED_CIRCLES = 'allow_federated'; const CIRCLES_MEMBERS_LIMIT = 'members_limit'; const CIRCLES_ACCOUNTS_ONLY = 'accounts_only'; + const CIRCLES_ALLOW_FILES_CIRCLES_FILTER = 'allow_files_filtered_by_circles'; + const CIRCLES_ALLOW_LISTED_CIRCLES = 'allow_listed_circles'; + const CIRCLES_ALLOW_ANY_GROUP_MEMBERS = 'allow_adding_any_group_members'; const CIRCLES_ALLOW_LINKED_GROUPS = 'allow_linked_groups'; const CIRCLES_ALLOW_NON_SSL_LINKS = 'allow_non_ssl_links'; const CIRCLES_NON_SSL_LOCAL = 'local_is_non_ssl'; @@ -53,19 +60,25 @@ class ConfigService { const CIRCLES_TEST_ASYNC_COUNT = 'test_async_count'; private $defaults = [ - self::CIRCLES_ALLOW_CIRCLES => Circle::CIRCLES_ALL, - self::CIRCLES_CONTACT_BACKEND => '0', - self::CIRCLES_STILL_FRONTEND => '0', - self::CIRCLES_TEST_ASYNC_INIT => '0', - self::CIRCLES_SWAP_TO_TEAMS => '0', - self::CIRCLES_ACCOUNTS_ONLY => '0', - self::CIRCLES_MEMBERS_LIMIT => '50', - self::CIRCLES_ALLOW_LINKED_GROUPS => '0', - self::CIRCLES_ALLOW_FEDERATED_CIRCLES => '0', - self::CIRCLES_ALLOW_NON_SSL_LINKS => '0', - self::CIRCLES_NON_SSL_LOCAL => '0', - self::CIRCLES_ACTIVITY_ON_CREATION => '1', - self::CIRCLES_SKIP_INVITATION_STEP => '0' + self::CIRCLES_ALLOW_CIRCLES => Circle::CIRCLES_ALL, + self::CIRCLES_CONTACT_BACKEND => '0', + self::CIRCLES_STILL_FRONTEND => '0', + self::CIRCLES_GROUP_BACKEND => '0', + self::CIRCLES_GROUP_BACKEND_NAME_PREFIX => '', + self::CIRCLES_GROUP_BACKEND_NAME_SUFFIX => '', + self::CIRCLES_TEST_ASYNC_INIT => '0', + self::CIRCLES_SWAP_TO_TEAMS => '0', + self::CIRCLES_ACCOUNTS_ONLY => '0', + self::CIRCLES_MEMBERS_LIMIT => '50', + self::CIRCLES_ALLOW_FILES_CIRCLES_FILTER => '1', + self::CIRCLES_ALLOW_LISTED_CIRCLES => '1', + self::CIRCLES_ALLOW_ANY_GROUP_MEMBERS => '1', + self::CIRCLES_ALLOW_LINKED_GROUPS => '0', + self::CIRCLES_ALLOW_FEDERATED_CIRCLES => '0', + self::CIRCLES_ALLOW_NON_SSL_LINKS => '0', + self::CIRCLES_NON_SSL_LOCAL => '0', + self::CIRCLES_ACTIVITY_ON_CREATION => '1', + self::CIRCLES_SKIP_INVITATION_STEP => '0' ]; /** @var string */ @@ -86,6 +99,15 @@ class ConfigService { /** @var int */ private $allowedCircle = -1; + /** @var int */ + private $allowFilesFilteredByCircles = -1; + + /** @var int */ + private $allowedListedCircles = -1; + + /** @var int */ + private $allowAddingAnyGroupMembers = -1; + /** @var int */ private $allowedLinkedGroups = -1; @@ -98,6 +120,12 @@ class ConfigService { /** @var int */ private $localNonSSL = -1; + /** @var string */ + private $groupBackendNamePrefix = null; + + /** @var string */ + private $groupBackendNameSuffix = null; + /** * ConfigService constructor. * @@ -139,6 +167,48 @@ public function isCircleAllowed($type) { return ((int)$type & (int)$this->allowedCircle); } + /** + * returns if the files list could be filtered by circles + * + * @return bool + */ + public function isFilesFilteredCirclesAllowed() { + if ($this->allowFilesFilteredByCircles === -1) { + $this->allowFilesFilteredByCircles = + (int)$this->getAppValue(self::CIRCLES_ALLOW_FILES_CIRCLES_FILTER); + } + + return ($this->allowFilesFilteredByCircles === 1); + } + + /** + * returns if the circles are allowed to be listed outside the Circles application. + * + * @return bool + */ + public function isListedCirclesAllowed() { + if ($this->allowedListedCircles === -1) { + $this->allowedListedCircles = + (int)$this->getAppValue(self::CIRCLES_ALLOW_LISTED_CIRCLES); + } + + return ($this->allowedListedCircles === 1); + } + + /** + * returns if the current user is allowed to add any group members. + * even if he isn't a member of these groups + * + * @return bool + */ + public function isAddingAnyGroupMembersAllowed() { + if ($this->allowAddingAnyGroupMembers === -1) { + $this->allowAddingAnyGroupMembers = + (int)$this->getAppValue(self::CIRCLES_ALLOW_ANY_GROUP_MEMBERS); + } + + return ($this->allowAddingAnyGroupMembers === 1); + } /** * @return bool @@ -383,6 +453,41 @@ public function contactsBackendType(): int { return (int)$this->getAppValue(ConfigService::CIRCLES_CONTACT_BACKEND); } + /** + * @return bool + */ + public function isGroupsBackend(): bool { + return ($this->getAppValue(ConfigService::CIRCLES_GROUP_BACKEND) !== '0'); + } + + /** + * returns the prefix of the group name + * + * @return string|null + */ + public function getGroupBackendNamePrefix() { + if ($this->groupBackendNamePrefix === null && $this->isGroupsBackend()) { + $this->groupBackendNamePrefix = ltrim((string) $this->getAppValue(self::CIRCLES_GROUP_BACKEND_NAME_PREFIX)); + } + + return $this->groupBackendNamePrefix; + } + + /** + * returns the suffix of the group name + * + * @return string|null + */ + public function getGroupBackendNameSuffix() { + if ($this->groupBackendNameSuffix === null && $this->isGroupsBackend()) { + $l = OC::$server->getL10N('circles'); + $defaultSuffix = ' '.$l->t('Circle'); + $customSuffix = (string) $this->getAppValue(self::CIRCLES_GROUP_BACKEND_NAME_SUFFIX); + $this->groupBackendNameSuffix = rtrim($customSuffix ?: $defaultSuffix); + } + + return $this->groupBackendNameSuffix; + } /** * @return bool diff --git a/lib/Service/DavService.php b/lib/Service/DavService.php index 448bb2213..baa508c9a 100644 --- a/lib/Service/DavService.php +++ b/lib/Service/DavService.php @@ -46,7 +46,7 @@ use OCP\App\ManagerEvent; use OCP\Federation\ICloudIdManager; use OCP\IUserManager; -use Symfony\Component\EventDispatcher\GenericEvent; +use OCP\EventDispatcher\GenericEvent; /** diff --git a/lib/Service/EventsService.php b/lib/Service/EventsService.php index 6495c2279..b3fb183d9 100644 --- a/lib/Service/EventsService.php +++ b/lib/Service/EventsService.php @@ -39,10 +39,10 @@ use OCP\IURLGenerator; use OCP\IUser; use OCP\IUserManager; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\EventDispatcher\GenericEvent; use OCP\Notification\IManager as INotificationManager; use OCP\Notification\INotification; -use Symfony\Component\EventDispatcher\EventDispatcher; -use Symfony\Component\EventDispatcher\GenericEvent; class EventsService { @@ -65,7 +65,7 @@ class EventsService { /** @var IURLGenerator */ private $urlGenerator; - /** @var EventDispatcher */ + /** @var IEventDispatcher */ private $eventDispatcher; /** @var CirclesRequest */ @@ -90,7 +90,7 @@ class EventsService { * @param INotificationManager $notificationManager * @param IUserManager $userManager * @param IURLGenerator $urlGenerator - * @param EventDispatcher $eventDispatcher + * @param IEventDispatcher $eventDispatcher * @param CirclesRequest $circlesRequest * @param MembersRequest $membersRequest * @param ConfigService $configService @@ -99,7 +99,7 @@ class EventsService { public function __construct( $userId, ITimeFactory $time, IActivityManager $activityManager, INotificationManager $notificationManager, IUserManager $userManager, IURLGenerator $urlGenerator, - EventDispatcher $eventDispatcher, CirclesRequest $circlesRequest, MembersRequest $membersRequest, + IEventDispatcher $eventDispatcher, CirclesRequest $circlesRequest, MembersRequest $membersRequest, ConfigService $configService, MiscService $miscService ) { $this->userId = $userId; @@ -126,21 +126,22 @@ public function __construct( * @param Circle $circle */ public function onCircleCreation(Circle $circle) { - if ($this->configService->getAppValue(ConfigService::CIRCLES_ACTIVITY_ON_CREATION) !== '1' - || ($circle->getType() !== Circle::CIRCLES_PUBLIC - && $circle->getType() !== Circle::CIRCLES_CLOSED)) { + if ($circle->getType() !== Circle::CIRCLES_PUBLIC + && $circle->getType() !== Circle::CIRCLES_CLOSED) { return; } - $event = $this->generateEvent('circles_as_non_member'); - $event->setSubject('circle_create', ['circle' => json_encode($circle)]); + if ($this->configService->getAppValue(ConfigService::CIRCLES_ACTIVITY_ON_CREATION) === '1') { + $event = $this->generateEvent('circles_as_non_member'); + $event->setSubject('circle_create', ['circle' => json_encode($circle)]); - $this->userManager->callForSeenUsers( - function($user) use ($event) { - /** @var IUser $user */ - $this->publishEvent($event, [$user]); - } - ); + $this->userManager->callForSeenUsers( + function($user) use ($event) { + /** @var IUser $user */ + $this->publishEvent($event, [$user]); + } + ); + } $this->dispatch('\OCA\Circles::onCircleCreation', ['circle' => $circle]); } @@ -213,6 +214,7 @@ public function onMemberNew(Circle $circle, Member $member) { ) ) ); + $this->dispatch('\OCA\Circles::onMemberNew', ['circle' => $circle, 'member' => $member]); $this->notificationOnMemberNew($circle, $member); @@ -340,6 +342,7 @@ public function onMemberLeaving(Circle $circle, Member $member) { ) ) ); + $this->dispatch('\OCA\Circles::onMemberLeaving', ['circle' => $circle, 'member' => $member]); $this->deleteNotification('membership', $member->getMemberId()); @@ -747,9 +750,10 @@ public function onLinkRemove(Circle $circle, FederatedLink $link) { * Called when the circle's settings are changed * * @param Circle $circle + * @param array $oldSettings */ - public function onSettingsChange(Circle $circle) { - $this->dispatch('\OCA\Circles::onSettingsChange', ['circle' => $circle]); + public function onSettingsChange(Circle $circle, array $oldSettings = []) { + $this->dispatch('\OCA\Circles::onSettingsChange', ['circle' => $circle, 'oldSettings' => $oldSettings]); } diff --git a/lib/Service/GroupsBackendService.php b/lib/Service/GroupsBackendService.php new file mode 100644 index 000000000..98f91ff85 --- /dev/null +++ b/lib/Service/GroupsBackendService.php @@ -0,0 +1,442 @@ + + * @copyright 2017 + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Circles\Service; + +// use Exception; +use OCP\App\ManagerEvent; +use OCA\Circles\Db\CirclesRequest; +use OCA\Circles\Db\MembersRequest; +use OCA\Circles\Model\Circle; +use OCA\Circles\Model\Member; +use OCP\EventDispatcher\GenericEvent; +use Symfony\Component\EventDispatcher\GenericEvent as SymfonyGenericEvent; +use OCP\IGroup; +use OCP\IUser; +use OCP\IGroupManager; +use OCP\IUserManager; + +/** + * Class GroupsBackendService + * + * @package OCA\Circles\Service + */ +class GroupsBackendService { + + /** @var string */ + protected $userId; + + /** @var Circle */ + protected $circle; + + /** @var Member */ + protected $member; + + /** @var IGroup */ + protected $group; + + /** @var IUser */ + protected $user; + + /** @var ConfigService */ + protected $configService; + + /** @var MiscService */ + protected $miscService; + + /** @var CirclesRequest */ + protected $circlesRequest; + + /** @var MembersRequest */ + protected $membersRequest; + + /** @var IGroupManager */ + protected $groupManager; + + /** @var IUserManager */ + protected $userManager; + + /** + * GroupsBackendService constructor. + * + * @param string $userId + * @param CirclesRequest $circlesRequest + * @param MembersRequest $membersRequest + * @param IGroupManager $groupManager + * @param IUserManager $userManager + * @param ConfigService $configService + * @param MiscService $miscService + */ + public function __construct( + $userId, + CirclesRequest $circlesRequest, + MembersRequest $membersRequest, + IGroupManager $groupManager, + IUserManager $userManager, + ConfigService $configService, + MiscService $miscService + ) { + $this->userId = $userId; + $this->circlesRequest = $circlesRequest; + $this->membersRequest = $membersRequest; + $this->groupManager = $groupManager; + $this->userManager = $userManager; + $this->configService = $configService; + $this->miscService = $miscService; + } + + /** + * @param ManagerEvent $event + */ + public function onAppEnabled(ManagerEvent $event) { + if ($event->getAppID() !== 'circles') { + return; + } + } + + /** + * @param GenericEvent $event + */ + public function onCircleCreation(GenericEvent $event) { + $this->circle = $event->getArgument('circle'); + // '\OC\Group', 'postDelete' + // '\OC\Group', 'postAddUser' + // '\OC\Group', 'postRemoveUser' + // $eventName ='\OC\Group::postCreate'; + + // $listeners = $this->eventDispatcher->getSymfonyDispatcher()->getListeners($eventName); + // $this->miscService->log('number of listeners: '. count($listeners), 1); + + // foreach ($listeners as $listener) { + // $this->miscService->log('remove listener: '. json_encode($listener), 1); + // $this->eventDispatcher->getSymfonyDispatcher()->removeListener($eventName, $listener); + // } + + $this->group = $this->groupManager->createGroup($this->getCircleGroupName()); + + // foreach ($listeners as $listener) { + // $this->miscService->log('add listener: '. json_encode($listener), 1); + // $this->eventDispatcher->getSymfonyDispatcher()->addListener($eventName, $listener); + // } + + if ($this->group) { + $this->member = $this->circle->getOwner(); + + $this->circle->setGroupId($this->group->getGID()); + $this->circlesRequest->updateCircle($this->circle, $this->member->getUserId()); + + if ($this->member->getType() === Member::TYPE_USER) { + $this->user = $this->userManager->get($this->member->getUserId()); + if ($this->user) { + $this->group->addUser($this->user); + } + } + } + + $this->miscService->log('onCircleCreation: '. json_encode($this->circle), 1); + } + + /** + * @param GenericEvent $event + */ + public function onCircleDestruction(GenericEvent $event) { + $this->circle = $event->getArgument('circle'); + $gid = $this->circle->getGroupId(); + $this->group = $this->groupManager->get($gid); + + if ($this->group) { + $this->group->delete(); + } + + $this->miscService->log('onCircleDestruction: '. json_encode($this->circle), 1); + } + + /** + * @param GenericEvent $event + */ + public function onMemberNew(GenericEvent $event) { + $this->circle = $event->getArgument('circle'); + $this->member = $event->getArgument('member'); + + if ($this->member->getType() === Member::TYPE_USER) { + $gid = $this->circle->getGroupId(); + $this->group = $this->groupManager->get($gid); + $this->user = $this->userManager->get($this->member->getUserId()); + + if ($this->group && $this->user) { + $this->group->addUser($this->user); + } + } + + $this->miscService->log('onMemberNew: '. json_encode($this->circle).json_encode($this->member), 1); + } + + /** + * @param GenericEvent $event + */ + public function onMemberInvited(GenericEvent $event) { + $this->miscService->log('onMemberInvited: '. json_encode($event), 1); + } + + /** + * @param GenericEvent $event + */ + public function onMemberRequesting(GenericEvent $event) { + $this->miscService->log('onMemberRequesting: '. json_encode($event), 1); + } + + /** + * @param GenericEvent $event + */ + public function onMemberLeaving(GenericEvent $event) { + $this->circle = $event->getArgument('circle'); + $this->member = $event->getArgument('member'); + + if ($this->member->getType() === Member::TYPE_USER) { + $gid = $this->circle->getGroupId(); + $this->group = $this->groupManager->get($gid); + $this->user = $this->userManager->get($this->member->getUserId()); + + if ($this->group && $this->user) { + $this->group->removeUser($this->user); + } + } + + $this->miscService->log('onMemberLeaving: '. json_encode($this->circle).json_encode($this->member), 1); + } + + /** + * @param GenericEvent $event + */ + public function onMemberLevel(GenericEvent $event) { + $this->miscService->log('onMemberLevel: '. json_encode($event), 1); + } + + /** + * @param GenericEvent $event + */ + public function onMemberOwner(GenericEvent $event) { + $this->miscService->log('onMemberOwner: '. json_encode($event), 1); + } + + /** + * @param GenericEvent $event + */ + public function onGroupLink(GenericEvent $event) { + $this->miscService->log('onGroupLink: '. json_encode($event), 1); + } + + /** + * @param GenericEvent $event + */ + public function onGroupUnlink(GenericEvent $event) { + $this->miscService->log('onGroupUnlink: '. json_encode($event), 1); + } + + /** + * @param GenericEvent $event + */ + public function onGroupLevel(GenericEvent $event) { + $this->miscService->log('onGroupLevel: '. json_encode($event), 1); + } + + /** + * @param GenericEvent $event + */ + public function onLinkRequestSent(GenericEvent $event) { + $this->miscService->log('onLinkRequestSent: '. json_encode($event), 1); + } + + /** + * @param GenericEvent $event + */ + public function onLinkRequestReceived(GenericEvent $event) { + $this->miscService->log('onLinkRequestReceived: '. json_encode($event), 1); + } + + /** + * @param GenericEvent $event + */ + public function onLinkRequestRejected(GenericEvent $event) { + $this->miscService->log('onLinkRequestRejected: '. json_encode($event), 1); + } + + /** + * @param GenericEvent $event + */ + public function onLinkRequestCanceled(GenericEvent $event) { + $this->miscService->log('onLinkRequestCanceled: '. json_encode($event), 1); + } + + /** + * @param GenericEvent $event + */ + public function onLinkRequestAccepted(GenericEvent $event) { + $this->miscService->log('onLinkRequestAccepted: '. json_encode($event), 1); + } + + /** + * @param GenericEvent $event + */ + public function onLinkRequestAccepting(GenericEvent $event) { + $this->miscService->log('onLinkRequestAccepting: '. json_encode($event), 1); + } + + /** + * @param GenericEvent $event + */ + public function onLinkUp(GenericEvent $event) { + $this->miscService->log('onLinkUp: '. json_encode($event), 1); + } + + /** + * @param GenericEvent $event + */ + public function onLinkDown(GenericEvent $event) { + $this->miscService->log('onLinkDown: '. json_encode($event), 1); + } + + /** + * @param GenericEvent $event + */ + public function onLinkRemove(GenericEvent $event) { + $this->miscService->log('onLinkRemove: '. json_encode($event), 1); + } + + /** + * @param GenericEvent $event + */ + public function onSettingsChange(GenericEvent $event) { + $this->circle = $event->getArgument('circle'); + $oldSettings = $event->getArgument('oldSettings'); + + $this->circle = $event->getArgument('circle'); + $gid = $this->circle->getGroupId(); + $this->group = $this->groupManager->get($gid); + + $this->setCircleGroupName($this->getCircleGroupName()); + + $this->miscService->log('onSettingsChange: '. json_encode($this->circle).json_encode($oldSettings), 1); + } + + /** + * When a group add a user, add it as a member of the associate Circle + * + * @param SymfonyGenericEvent $event + */ + public function onGroupPostAddUser(SymfonyGenericEvent $event) { + $this->group = $event->getSubject(); + $this->user = $event->getArgument('user'); + + $this->miscService->log('onGroupPostAddUser: '.json_encode($event).json_encode($this->group).json_encode($this->user), 1); + if ($this->group instanceof IGroup && $this->group->getGID()) { + $this->circle = $this->circlesRequest->forceGetCircleByGroupId($this->group->getGID()); + if ($this->circle) { + $this->member = $this->membersRequest->getFreshNewMember( + $this->circle->getUniqueId(), $this->user->getUID(), Member::TYPE_USER + ); + $this->member->addMemberToCircle(); + $this->membersRequest->updateMember($this->member); + } + } + } + + /** + * When a group remove a user, remove it as a member of the associate Circle + * + * @param SymfonyGenericEvent $event + */ + public function onGroupPostRemoveUser(SymfonyGenericEvent $event) { + $this->group = $event->getSubject(); + $this->user = $event->getArgument('user'); + + $this->miscService->log('onGroupPostRemoveUser: '.json_encode($event).json_encode($this->group).json_encode($this->user), 1); + if ($this->group instanceof IGroup && $this->group->getGID()) { + $this->circle = $this->circlesRequest->forceGetCircleByGroupId($this->group->getGID()); + if ($this->circle) { + try { + $this->member = $this->membersRequest->forceGetMember( + $this->circle->getUniqueId(), $this->user->getUID(), Member::TYPE_USER + ); + $this->member->hasToBeMember(); + $this->member->cantBeOwner(); + } catch (MemberDoesNotExistException $e) { + $this->member = null; + } catch (MemberIsOwnerException $e) { + $this->member = null; + } + if ($this->member) { + $this->membersRequest->removeMember($this->member); + } + } + } + } + + /** + * When a group is removed, remove its associated Circle, if any + * + * @param SymfonyGenericEvent $event + */ + public function onGroupPostDelete(SymfonyGenericEvent $event) { + $this->group = $event->getSubject(); + $this->miscService->log('onGroupPostDelete: '.json_encode($event).json_encode($this->group), 1); + if ($this->group instanceof IGroup && $this->group->getGID()) { + $circle = $this->circlesRequest->forceGetCircleByGroupId($this->group->getGID()); + if ($circle) { + $this->circlesRequest->destroyCircle($circle->getUniqueId()); + } + } + } + + /** + * @return string|null + */ + protected function getCircleGroupName() + { + if ($this->circle instanceof Circle) { + return $this->configService->getGroupBackendNamePrefix(). + $this->circle->getName(). + $this->configService->getGroupBackendNameSuffix(); + } + + return; + } + + /** + * @param string $displayName + * @return bool + */ + protected function setCircleGroupName($displayName) + { + if ($this->group && method_exists($this->group, 'setDisplayName')) { + $this->miscService->log('setCircleGroupName: '. json_encode($displayName), 1); + return $this->group->setDisplayName($displayName); + } + + return false; + } +} diff --git a/lib/Service/GroupsService.php b/lib/Service/GroupsService.php index f0cbd7878..673241d68 100644 --- a/lib/Service/GroupsService.php +++ b/lib/Service/GroupsService.php @@ -54,6 +54,9 @@ class GroupsService { /** @var IUserManager */ private $userManager; + /** @var ConfigService */ + private $configService; + /** @var CirclesRequest */ private $circlesRequest; @@ -76,6 +79,7 @@ class GroupsService { * @param IL10N $l10n * @param IGroupManager $groupManager * @param IUserManager $userManager + * @param ConfigService $configService * @param CirclesRequest $circlesRequest * @param MembersRequest $membersRequest * @param CirclesService $circlesService @@ -84,7 +88,7 @@ class GroupsService { */ public function __construct( $userId, IL10N $l10n, IGroupManager $groupManager, IUserManager $userManager, - CirclesRequest $circlesRequest, + ConfigService $configService, CirclesRequest $circlesRequest, MembersRequest $membersRequest, CirclesService $circlesService, EventsService $eventsService, MiscService $miscService ) { @@ -92,6 +96,7 @@ public function __construct( $this->l10n = $l10n; $this->groupManager = $groupManager; $this->userManager = $userManager; + $this->configService = $configService; $this->circlesRequest = $circlesRequest; $this->membersRequest = $membersRequest; $this->circlesService = $circlesService; @@ -132,8 +137,13 @@ public function linkGroup($circleUniqueId, $groupId) { $count++; } - if ($count > $circle->getSetting('members_limit')) { - throw new \Exception('Group contains too many members'); + $limit = (int) $circle->getSetting('members_limit'); + if ($limit === 0) { + $limit = $this->configService->getAppValue(ConfigService::CIRCLES_MEMBERS_LIMIT); + } + + if ($limit !== -1 && $count > $limit) { + throw new \Exception($this->l10n->t('Group contains too many members')); } $group = $this->getFreshNewMember($circleUniqueId, $groupId); @@ -270,7 +280,7 @@ public function unlinkGroup($circleUniqueId, $groupId) { $group->cantBeOwner(); $higherViewer->hasToBeHigherLevel($group->getLevel()); - $this->membersRequest->unlinkAllFromGroup($groupId); + $this->membersRequest->unlinkFromGroup($circleUniqueId, $groupId); $this->eventsService->onGroupUnlink($circle, $group); diff --git a/lib/Service/MembersService.php b/lib/Service/MembersService.php index c025187e4..400aa3212 100644 --- a/lib/Service/MembersService.php +++ b/lib/Service/MembersService.php @@ -48,7 +48,9 @@ use OCA\Circles\Exceptions\ModeratorIsNotHighEnoughException; use OCA\Circles\Model\Circle; use OCA\Circles\Model\Member; +use OCP\IGroup; use OCP\IL10N; +use OCP\IUser; use OCP\IUserManager; @@ -176,6 +178,7 @@ public function addMember($circleUniqueId, $ident, $type, bool $force = false) { */ private function addSingleMember(Circle $circle, $ident, $type) { $this->verifyIdentBasedOnItsType($ident, $type); + $this->verifyIdentWithGroupBackend($circle, $ident, $type); $member = $this->membersRequest->getFreshNewMember($circle->getUniqueId(), $ident, $type); $member->hasToBeInviteAble(); @@ -284,6 +287,33 @@ private function addContact(Member $member) { } + /** + * Verify the availability of an ident when Group Backend is enabled + * + * @param Circle $circle + * @param string $ident + * @param int $type + * + * @throws Exception + */ + private function verifyIdentWithGroupBackend(Circle $circle, $ident, $type) { + if ($this->configService->isGroupsBackend() && + in_array($type, [Member::TYPE_MAIL, Member::TYPE_CONTACT], true) && + in_array($circle->getType(), [Circle::CIRCLES_CLOSED, Circle::CIRCLES_PUBLIC], true) + ) { + if ($type === Member::TYPE_MAIL) { + $errorMessage = 'You cannot add a mail address as member of your Circle'; + } + if ($type === Member::TYPE_CONTACT) { + $errorMessage = 'You cannot add a contact as member of your Circle'; + } + throw new EmailAccountInvalidFormatException( + $this->l10n->t($errorMessage) + ); + } + } + + /** * Verify the availability of an ident, based on its type. * @@ -387,8 +417,18 @@ private function verifyIdentContact(&$ident, $type) { */ private function addGroupMembers(Circle $circle, $groupId) { - $group = OC::$server->getGroupManager() - ->get($groupId); + $groupManager = OC::$server->getGroupManager(); + $group = $groupManager->get($groupId); + + $user = OC::$server->getUserSession()->getUser(); + + if (!$this->configService->isAddingAnyGroupMembersAllowed() && + $group instanceof IGroup && $user instanceof IUser && + !$group->inGroup($user) && !$groupManager->isAdmin($user->getUID()) + ) { + $group = null; + } + if ($group === null) { throw new GroupDoesNotExistException($this->l10n->t('This group does not exist')); } @@ -656,4 +696,3 @@ public function onUserRemoved($userId) { } - diff --git a/templates/navigate.php b/templates/navigate.php index 84aac63d8..43031a079 100644 --- a/templates/navigate.php +++ b/templates/navigate.php @@ -359,12 +359,12 @@ - + - +
Name of the Circlet('Name of the Circle')); ?>
Descriptiont('Description')); ?>