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/lib/AppInfo/Application.php b/lib/AppInfo/Application.php
index 31b6b1645..4e5762a7c 100644
--- a/lib/AppInfo/Application.php
+++ b/lib/AppInfo/Application.php
@@ -33,11 +33,13 @@
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;
@@ -83,6 +85,7 @@ public function register()
$this->registerFilesPlugin();
$this->registerHooks();
$this->registerDavHooks();
+ $this->registerGroupsBackendHooks();
}
/**
@@ -207,7 +210,48 @@ public function registerDavHooks() {
$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']);
+ }
+
+ 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/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/Service/ConfigService.php b/lib/Service/ConfigService.php
index 80230a107..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,6 +38,9 @@ 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';
@@ -56,22 +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 */
@@ -113,6 +120,12 @@ class ConfigService {
/** @var int */
private $localNonSSL = -1;
+ /** @var string */
+ private $groupBackendNamePrefix = null;
+
+ /** @var string */
+ private $groupBackendNameSuffix = null;
+
/**
* ConfigService constructor.
*
@@ -440,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/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 a8c52f6c0..673241d68 100644
--- a/lib/Service/GroupsService.php
+++ b/lib/Service/GroupsService.php
@@ -280,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 d4e48b43a..400aa3212 100644
--- a/lib/Service/MembersService.php
+++ b/lib/Service/MembersService.php
@@ -178,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();
@@ -286,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.
*
@@ -668,4 +696,3 @@ public function onUserRemoved($userId) {
}
-