diff --git a/css/icons.scss b/css/icons.scss index 0b2aa0ff6..cea81462a 100644 --- a/css/icons.scss +++ b/css/icons.scss @@ -56,6 +56,7 @@ @if mixin-exists('icon-black-white') { @include icon-black-white('deck', 'deck', 1); @include icon-black-white('archive', 'deck', 1); + @include icon-black-white('circles', 'deck', 1); .icon-toggle-compact-collapsed { @include icon-color('toggle-view-expand', 'deck', $color-black); @@ -68,3 +69,12 @@ @include icon-color('activity-dark', 'activity', $color-black); } } + +.avatardiv.circles { + background: var(--color-primary); +} +.icon-circles { + opacity: 1; + background-size: 20px; + background-position: center center; +} \ No newline at end of file diff --git a/css/style.scss b/css/style.scss index 876b4a849..4e26a034d 100644 --- a/css/style.scss +++ b/css/style.scss @@ -1292,7 +1292,7 @@ input.input-inline { width: 32px; height: 32px; - .icon-group { + .icon-group, .icon { padding: 16px; opacity: 0.5; } diff --git a/img/circles.svg b/img/circles.svg new file mode 100644 index 000000000..30035d9dc --- /dev/null +++ b/img/circles.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/js/controller/BoardController.js b/js/controller/BoardController.js index 41729c8aa..2a6330398 100644 --- a/js/controller/BoardController.js +++ b/js/controller/BoardController.js @@ -403,6 +403,8 @@ app.controller('BoardController', function ($rootScope, $scope, $stateParams, St return 'user'; case OC.Share.SHARE_TYPE_GROUP: return 'group'; + case OC.Share.SHARE_TYPE_CIRCLE: + return 'circles'; default: return ''; } diff --git a/js/service/BoardService.js b/js/service/BoardService.js index 93ee24a23..2857b6d0f 100644 --- a/js/service/BoardService.js +++ b/js/service/BoardService.js @@ -20,7 +20,7 @@ * */ import app from '../app/App.js'; -/* global app OC */ +/* global app OC angular */ app.factory('BoardService', function (ApiService, $http, $q) { var BoardService = function ($http, ep, $q) { ApiService.call(this, $http, ep, $q); @@ -59,7 +59,7 @@ app.factory('BoardService', function (ApiService, $http, $q) { var searchData = { format: 'json', perPage: 4, - itemType: [0, 1] + itemType: [0, 1, 7] }; if (search !== "") { searchData.search = search; @@ -79,6 +79,7 @@ app.factory('BoardService', function (ApiService, $http, $q) { var users = response.ocs.data.exact.users.concat(response.ocs.data.users.slice(0, 4)); var groups = response.ocs.data.exact.groups.concat(response.ocs.data.groups.slice(0, 4)); + var circles = response.ocs.data.exact.groups.concat(response.ocs.data.circles.slice(0, 4)); // filter out everyone who is already in the share list angular.forEach(users, function (item) { @@ -105,6 +106,18 @@ app.factory('BoardService', function (ApiService, $http, $q) { self.sharees.push(acl); } }); + angular.forEach(circles, function (item) { + var acl = self.generateAcl(OC.Share.SHARE_TYPE_CIRCLE, item); + var exists = false; + angular.forEach(self.getCurrent().acl, function (acl) { + if (acl.participant.primaryKey === item.value.shareWith) { + exists = true; + } + }); + if (!exists) { + self.sharees.push(acl); + } + }); deferred.resolve(self.sharees); }, function () { diff --git a/lib/Db/Acl.php b/lib/Db/Acl.php index b0b8cfc07..1aeea0e92 100644 --- a/lib/Db/Acl.php +++ b/lib/Db/Acl.php @@ -32,6 +32,7 @@ class Acl extends RelationalEntity { const PERMISSION_TYPE_USER = 0; const PERMISSION_TYPE_GROUP = 1; + const PERMISSION_TYPE_CIRCLE = 7; protected $participant; protected $type; diff --git a/lib/Db/BoardMapper.php b/lib/Db/BoardMapper.php index fddff1ac6..31a8cf43a 100644 --- a/lib/Db/BoardMapper.php +++ b/lib/Db/BoardMapper.php @@ -24,6 +24,7 @@ namespace OCA\Deck\Db; use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\QueryException; use OCP\IDBConnection; use OCP\IUserManager; use OCP\IGroupManager; @@ -36,6 +37,8 @@ class BoardMapper extends DeckMapper implements IPermissionMapper { private $userManager; private $groupManager; + private $circlesEnabled; + public function __construct( IDBConnection $db, LabelMapper $labelMapper, @@ -50,6 +53,8 @@ class BoardMapper extends DeckMapper implements IPermissionMapper { $this->stackMapper = $stackMapper; $this->userManager = $userManager; $this->groupManager = $groupManager; + + $this->circlesEnabled = \OC::$server->getAppManager()->isEnabledForUser('circles'); } @@ -136,6 +141,35 @@ class BoardMapper extends DeckMapper implements IPermissionMapper { return $entries; } + public function findAllByCircles($userId, $limit = null, $offset = null) { + if (!$this->circlesEnabled) { + return []; + } + $circles = array_map(function($circle) { + return $circle->getUniqueId(); + }, \OCA\Circles\Api\v1\Circles::joinedCircles('', true)); + if (count($circles) === 0) { + return []; + } + + $sql = 'SELECT boards.id, title, owner, color, archived, deleted_at, 2 as shared, last_modified FROM `*PREFIX*deck_boards` as boards ' . + 'INNER JOIN `*PREFIX*deck_board_acl` as acl ON boards.id=acl.board_id WHERE owner != ? AND type=? AND ('; + for ($i = 0, $iMax = count($circles); $i < $iMax; $i++) { + $sql .= 'acl.participant = ? '; + if (count($circles) > 1 && $i < count($circles) - 1) { + $sql .= ' OR '; + } + } + $sql .= ');'; + $entries = $this->findEntities($sql, array_merge([$userId, Acl::PERMISSION_TYPE_CIRCLE], $circles), $limit, $offset); + /* @var Board $entry */ + foreach ($entries as $entry) { + $acl = $this->aclMapper->findAll($entry->id); + $entry->setAcl($acl); + } + return $entries; + } + public function findAll() { $sql = 'SELECT id from *PREFIX*deck_boards;'; return $this->findEntities($sql); @@ -200,6 +234,20 @@ class BoardMapper extends DeckMapper implements IPermissionMapper { \OC::$server->getLogger()->debug('Group ' . $acl->getId() . ' not found when mapping acl ' . $acl->getParticipant()); return null; } + // TODO: get circles list + if ($acl->getType() === Acl::PERMISSION_TYPE_CIRCLE) { + try { + $circle = \OCA\Circles\Api\v1\Circles::detailsCircle($acl->getParticipant()); + if ($circle) { + return new Circle($circle); + } + } catch (QueryException $e) { + } catch (\Exception $e) { + // TODO catch not found + } + return null; + } + // TODO: get circles list throw new \Exception('Unknown permission type for mapping Acl'); }); } diff --git a/lib/Db/Circle.php b/lib/Db/Circle.php new file mode 100644 index 000000000..6ea1e39eb --- /dev/null +++ b/lib/Db/Circle.php @@ -0,0 +1,45 @@ + + * + * @author Julius Härtl + * + * @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\Deck\Db; + + +class Circle extends RelationalObject { + + /** @var \OCA\Circles\Model\Circle */ + protected $object; + + public function __construct(\OCA\Circles\Model\Circle $circle) { + $primaryKey = $circle->getUniqueId(); + parent::__construct($primaryKey, $circle); + } + + public function getObjectSerialization() { + return [ + 'uid' => $this->object->getUniqueId(), + 'displayname' => $this->object->getName(), + 'typeString' => $this->object->getTypeString(), + 'circleOwner' => $this->object->getOwner() + ]; + } +} \ No newline at end of file diff --git a/lib/Service/BoardService.php b/lib/Service/BoardService.php index d39ba969b..71d4408d7 100644 --- a/lib/Service/BoardService.php +++ b/lib/Service/BoardService.php @@ -97,7 +97,8 @@ class BoardService { $userInfo = $this->getBoardPrerequisites(); $userBoards = $this->boardMapper->findAllByUser($userInfo['user'], null, null, $since); $groupBoards = $this->boardMapper->findAllByGroups($userInfo['user'], $userInfo['groups'],null, null, $since); - $complete = array_merge($userBoards, $groupBoards); + $circleBoards = $this->boardMapper->findAllByCircles($userInfo['user'], null, null, $since); + $complete = array_merge($userBoards, $groupBoards, $circleBoards); $result = []; /** @var Board $item */ foreach ($complete as &$item) { diff --git a/lib/Service/PermissionService.php b/lib/Service/PermissionService.php index e6f8db541..3ea6530bf 100644 --- a/lib/Service/PermissionService.php +++ b/lib/Service/PermissionService.php @@ -33,6 +33,7 @@ use OCA\Deck\NoPermissionException; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\Entity; use OCP\AppFramework\Db\MultipleObjectsReturnedException; +use OCP\AppFramework\QueryException; use OCP\IConfig; use OCP\IGroupManager; use OCP\ILogger; @@ -61,6 +62,8 @@ class PermissionService { /** @var array */ private $users = []; + private $circlesEnabled = false; + public function __construct( ILogger $logger, AclMapper $aclMapper, @@ -79,6 +82,8 @@ class PermissionService { $this->shareManager = $shareManager; $this->config = $config; $this->userId = $userId; + + $this->circlesEnabled = \OC::$server->getAppManager()->isEnabledForUser('circles'); } /** @@ -182,6 +187,16 @@ class PermissionService { if ($acl->getType() === Acl::PERMISSION_TYPE_USER && $acl->getParticipant() === $this->userId) { return $acl->getPermission($permission); } + + if ($this->circlesEnabled && $acl->getType() === Acl::PERMISSION_TYPE_CIRCLE) { + try { + \OCA\Circles\Api\v1\Circles::getMember($acl->getParticipant(), $this->userId, 1); + return $acl->getPermission($permission); + } catch (\Exception $e) { + // TODO: getMember doesn't work for personal circles + $this->logger->info('Member not found in circle that was accessed. This should not happen.'); + } + } } // check for groups $hasGroupPermission = false; diff --git a/templates/part.board.sidebarView.php b/templates/part.board.sidebarView.php index 928510b01..da2066ca1 100644 --- a/templates/part.board.sidebarView.php +++ b/templates/part.board.sidebarView.php @@ -33,6 +33,10 @@ {{ sharee.participant.displayname }} (t('Group')); ?>) +
+ + {{ sharee.participant.displayname }} (t('Circle')); ?>) + {{ sharee.participant.displayname }} @@ -53,13 +57,17 @@
  • -
    +
    + + {{ acl.participant.displayname }} + {{ acl.participant.displayname }} (t('Group')); ?>) - - {{ acl.participant.displayname }} + + {{ acl.participant.displayname }} (t('Circle')); ?> {{ acl.participant.typeString }}) +
    {{ acl.participant.circleOwner.display_name }}
    diff --git a/tests/unit/Service/BoardServiceTest.php b/tests/unit/Service/BoardServiceTest.php index 512c663f7..b88e47505 100644 --- a/tests/unit/Service/BoardServiceTest.php +++ b/tests/unit/Service/BoardServiceTest.php @@ -123,6 +123,10 @@ class BoardServiceTest extends TestCase { ->method('findAllByGroups') ->with('admin', ['a', 'b', 'c']) ->willReturn([$b2, $b3]); + $this->boardMapper->expects($this->once()) + ->method('findAllByCircles') + ->with('admin') + ->willReturn([]); $user = $this->createMock(IUser::class); $this->groupManager->method('getUserGroupIds') ->willReturn(['a', 'b', 'c']);