Skip to content

Commit

Permalink
Add group_backend option to mirroring the circle as a group
Browse files Browse the repository at this point in the history
The main goal is to have better integrations with Nextcloud applications
and clients.

The group name is automatically suffixed by ` Circle`
And you can customize the prefix and/or suffix group name with the
`group_backend_name_prefix` and `group_backend_name_suffix` options

You should also hide these mirrored circles in Nextcloud applications
and clients, because their equivalent group will be used instead, via
the `allow_listed_circles` option set to `0`.

Fix nextcloud#363

Signed-off-by: Tortue Torche <tortuetorche@users.noreply.github.com>
  • Loading branch information
tortuetorche committed Apr 9, 2020
1 parent 5a0e5bb commit 109a7d6
Show file tree
Hide file tree
Showing 12 changed files with 789 additions and 37 deletions.
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
108 changes: 91 additions & 17 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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
*/
Expand All @@ -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;
}
Expand All @@ -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 [
Expand All @@ -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();
Expand All @@ -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',
Expand All @@ -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']);
}

}

33 changes: 32 additions & 1 deletion lib/Db/CirclesRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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();
}
Expand Down
3 changes: 2 additions & 1 deletion lib/Db/CirclesRequestBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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']);
Expand Down
9 changes: 8 additions & 1 deletion lib/Db/MembersRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down Expand Up @@ -731,4 +739,3 @@ public function removeMembersByContactId(string $contactId, int $type = 0) {


}

81 changes: 81 additions & 0 deletions lib/Migration/Version0017Date20200221173726.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/

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;
}

}
Loading

0 comments on commit 109a7d6

Please sign in to comment.