diff --git a/js/service/BoardService.js b/js/service/BoardService.js index a7ea42352..7e60c88a9 100644 --- a/js/service/BoardService.js +++ b/js/service/BoardService.js @@ -55,6 +55,7 @@ app.factory('BoardService', function(ApiService, $http, $q){ // filter out everyone who is already in the share list angular.forEach(users, function (item) { + var acl = self.generateAcl('user', item); var exists = false; angular.forEach(self.getCurrent().acl, function (acl) { if (acl.participant.primaryKey === item.value.shareWith || OC.getCurrentUser() === item.value.shareWith) { @@ -62,19 +63,11 @@ app.factory('BoardService', function(ApiService, $http, $q){ } }); if (!exists) { - self.sharees.push({ - boardId: null, - id: null, - owner: false, - participant: item.value.shareWith, - permissionEdit: true, - permissionManage: true, - permissionShare: true, - type: 'user' - }); + self.sharees.push(acl); } }); angular.forEach(groups, function (item) { + var acl = self.generateAcl('group', item); var exists = false; angular.forEach(self.getCurrent().acl, function (acl) { if (acl.participant.primaryKey === item.value.shareWith) { @@ -82,16 +75,7 @@ app.factory('BoardService', function(ApiService, $http, $q){ } }); if (!exists) { - self.sharees.push({ - boardId: null, - id: null, - owner: false, - participant: item.value.shareWith, - permissionEdit: true, - permissionManage: true, - permissionShare: true, - type: 'group' - }); + self.sharees.push(acl); } }); @@ -103,13 +87,30 @@ app.factory('BoardService', function(ApiService, $http, $q){ return deferred.promise; }; + BoardService.prototype.generateAcl = function(type, ocsItem) { + return { + boardId: null, + id: null, + owner: false, + participant: { + primaryKey: ocsItem.value.shareWith, + uid: ocsItem.value.shareWith, + displayname: ocsItem.label + }, + permissionEdit: true, + permissionManage: true, + permissionShare: true, + type: type + } + }; + BoardService.prototype.addAcl = function(acl) { var board = this.getCurrent(); var deferred = $q.defer(); var self = this; var _acl = acl; $http.post(this.baseUrl + '/' + acl.boardId + '/acl', _acl).then(function (response) { - if(!board.acl) { + if(!board.acl || board.acl.length === 0) { board.acl = {}; } board.acl[response.data.id] = response.data; diff --git a/lib/Db/Acl.php b/lib/Db/Acl.php index b9ad83abf..f96a97b68 100644 --- a/lib/Db/Acl.php +++ b/lib/Db/Acl.php @@ -48,12 +48,10 @@ class Acl extends RelationalEntity implements \JsonSerializable { $this->addType('permissionEdit', 'boolean'); $this->addType('permissionShare', 'boolean'); $this->addType('permissionManage', 'boolean'); - $this->addType('owner', 'boolean'); $this->addType('type', 'integer'); + $this->addType('owner', 'boolean'); $this->addRelation('owner'); - $this->setPermissionEdit(false); - $this->setPermissionShare(false); - $this->setPermissionManage(false); + $this->addResolvable('participant'); } public function getPermission($permission) { @@ -93,4 +91,5 @@ class Acl extends RelationalEntity implements \JsonSerializable { $this->markFieldUpdated('type'); $this->type = $typeInt; } + } \ No newline at end of file diff --git a/lib/Db/AclMapper.php b/lib/Db/AclMapper.php index ff1cade1c..bd1ba187c 100644 --- a/lib/Db/AclMapper.php +++ b/lib/Db/AclMapper.php @@ -25,7 +25,6 @@ namespace OCA\Deck\Db; use OCP\IDBConnection; - class AclMapper extends DeckMapper implements IPermissionMapper { public function __construct(IDBConnection $db) { diff --git a/lib/Db/Board.php b/lib/Db/Board.php index b24a74102..4d3b5e42d 100644 --- a/lib/Db/Board.php +++ b/lib/Db/Board.php @@ -32,8 +32,8 @@ class Board extends RelationalEntity implements JsonSerializable { protected $owner; protected $color; protected $archived = false; - protected $labels; - protected $acl; + protected $labels = []; + protected $acl = []; protected $shared; public function __construct() { @@ -43,6 +43,7 @@ class Board extends RelationalEntity implements JsonSerializable { $this->addRelation('labels'); $this->addRelation('acl'); $this->addRelation('shared'); + $this->addResolvable('owner'); $this->shared = -1; } diff --git a/lib/Db/RelationalEntity.php b/lib/Db/RelationalEntity.php index 130e6fe09..498a22703 100644 --- a/lib/Db/RelationalEntity.php +++ b/lib/Db/RelationalEntity.php @@ -26,7 +26,9 @@ namespace OCA\Deck\Db; class RelationalEntity extends \OCP\AppFramework\Db\Entity implements \JsonSerializable { + private $primaryKey; private $_relations = array(); + private $_resolvedProperties = []; /** * Mark a property as relation so it will not get updated using Mapper::update @@ -37,6 +39,11 @@ class RelationalEntity extends \OCP\AppFramework\Db\Entity implements \JsonSeria $this->_relations[] = $property; } } + + public function addResolvable($property) { + $this->_resolvedProperties[$property] = null; + } + /** * Mark am attribute as updated * overwritten from \OCP\AppFramework\Db\Entity to avoid writing relational attributes @@ -65,5 +72,40 @@ class RelationalEntity extends \OCP\AppFramework\Db\Entity implements \JsonSeria } } return $json; + + public function resolveRelation($property, $resolver) { + if($property !== null && $this->$property !== null) { + $result = $resolver($this->$property); + } else { + $result = null; + } + + if($result instanceof RelationalObject || $result === null) { + $this->_resolvedProperties[$property] = $result; + } else { + throw new \Exception('resolver must return an instance of RelationalObject'); + } } + + public function __call($methodName, $args){ + $attr = lcfirst( substr($methodName, 7) ); + if(strpos($methodName, 'resolve') === 0 && array_key_exists($attr, $this->_resolvedProperties)) { + if($this->_resolvedProperties[$attr] !== null) { + return $this->_resolvedProperties[$attr]; + } else { + return $this->getter($attr, $args); + } + } + + $attr = lcfirst( substr($methodName, 3) ); + if(strpos($methodName, 'set') === 0 && array_key_exists($attr, $this->_resolvedProperties)) { + if(!is_scalar($args[0])) { + $args[0] = $args[0]['primaryKey']; + parent::setter($attr, $args); + } + parent::setter($attr, $args); + } + return parent::__call($methodName, $args); + } + } \ No newline at end of file diff --git a/lib/Db/RelationalObject.php b/lib/Db/RelationalObject.php new file mode 100644 index 000000000..8a3346c78 --- /dev/null +++ b/lib/Db/RelationalObject.php @@ -0,0 +1,61 @@ + + * + * @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 . + * + */ + +/** + * Created by PhpStorm. + * User: jus + * Date: 27.02.17 + * Time: 14:05 + */ + +namespace OCA\Deck\Db; + + +abstract class RelationalObject implements \JsonSerializable { + + /** + * RelationalObject constructor. + * + * @param $primaryKey string + * @param $object + */ + public function __construct($primaryKey, $object) { + $this->primaryKey = $primaryKey; + $this->object = $object; + } + + public function jsonSerialize() { + return array_merge( + ['primaryKey' => $this->primaryKey], + $this->getObjectSerialization() + ); + } + + public function getObjectSerialization() { + $this->object->jsonSerialize(); + } + + public function getPrimaryKey() { + return $this->getPrimaryKey(); + } +} \ No newline at end of file diff --git a/lib/Db/User.php b/lib/Db/User.php new file mode 100644 index 000000000..436e5547f --- /dev/null +++ b/lib/Db/User.php @@ -0,0 +1,42 @@ + + * + * @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; + +use OCP\IGroup; +use OCP\IUser; + +class User extends RelationalObject { + + public function __construct(IUser $user = null) { + $primaryKey = $user->getUID(); + parent::__construct($primaryKey, $user); + } + + public function getObjectSerialization() { + return [ + 'uid' => $this->object->getUID(), + 'displayname' => $this->object->getDisplayName() + ]; + } +} \ No newline at end of file diff --git a/lib/Service/BoardService.php b/lib/Service/BoardService.php index 579ac2393..664699445 100644 --- a/lib/Service/BoardService.php +++ b/lib/Service/BoardService.php @@ -25,14 +25,18 @@ namespace OCA\Deck\Service; use OCA\Deck\Db\Acl; use OCA\Deck\Db\AclMapper; +use OCA\Deck\Db\Group; use OCA\Deck\Db\Label; +use OCA\Deck\Db\User; +use OCP\IGroupManager; use OCP\IL10N; use OCA\Deck\Db\Board; use OCA\Deck\Db\BoardMapper; use OCA\Deck\Db\LabelMapper; +use OCP\IUserManager; class BoardService { @@ -43,24 +47,67 @@ class BoardService { private $l10n; private $permissionService; - public function __construct(BoardMapper $boardMapper, IL10N $l10n, LabelMapper $labelMapper, AclMapper $aclMapper, PermissionService $permissionService) { + public function __construct(BoardMapper $boardMapper, IL10N $l10n, LabelMapper $labelMapper, AclMapper $aclMapper, PermissionService $permissionService, IUserManager $userManager, IGroupManager $groupManager) { $this->boardMapper = $boardMapper; $this->labelMapper = $labelMapper; $this->aclMapper = $aclMapper; $this->l10n = $l10n; $this->permissionService = $permissionService; + $this->userManager = $userManager; + $this->groupManager = $groupManager; } public function findAll($userInfo) { $userBoards = $this->boardMapper->findAllByUser($userInfo['user']); $groupBoards = $this->boardMapper->findAllByGroups($userInfo['user'], $userInfo['groups']); $complete = array_merge($userBoards, $groupBoards); - return array_map("unserialize", array_unique(array_map("serialize", $complete))); + $result = []; + foreach($complete as &$item) { + if(!array_key_exists($item->getId(), $result)) { + $item = $this->mapOwner($item); + if($item->getAcl() !== null) { + foreach ($item->getAcl() as &$acl) { + $acl = $this->mapAcl($acl); + } + } + $result[$item->getId()] = $item; + } + } + return array_values($result); } public function find($boardId) { $this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_READ); - return $this->boardMapper->find($boardId, true, true); + $board = $this->boardMapper->find($boardId, true, true); + $board = $this->mapOwner($board); + foreach ($board->getAcl() as &$acl) { + if($acl !== null) { + $this->mapAcl($acl); + } + } + return $board; + } + + private function mapAcl(Acl &$acl) { + $userManager = $this->userManager; + $groupManager = $this->groupManager; + $acl->resolveRelation('participant', function($value) use (&$acl, &$userManager, &$groupManager) { + if($acl->getType() === Acl::PERMISSION_TYPE_USER) { + return new User($userManager->get($acl->getParticipant($value))); + } + if($acl->getType() === Acl::PERMISSION_TYPE_GROUP) { + return new Group($groupManager->get($acl->getParticipant($value))); + } + }); + return $acl; + } + + private function mapOwner(Board $board) { + $userManager = $this->userManager; + $board->resolveRelation('owner', function($value) use (&$userManager) { + return new User($userManager->get($value)); + }); + return $board; } public function create($title, $userId, $color) { @@ -113,6 +160,7 @@ class BoardService { $acl->setPermissionEdit($edit); $acl->setPermissionShare($share); $acl->setPermissionManage($manage); + $this->mapAcl($acl); return $this->aclMapper->insert($acl); } @@ -122,12 +170,14 @@ class BoardService { $acl->setPermissionEdit($edit); $acl->setPermissionShare($share); $acl->setPermissionManage($manage); + $this->mapAcl($acl); return $this->aclMapper->update($acl); } public function deleteAcl($id) { $this->permissionService->checkPermission($this->aclMapper, $id, Acl::PERMISSION_SHARE); $acl = $this->aclMapper->find($id); + $this->mapAcl($acl); return $this->aclMapper->delete($acl); } diff --git a/templates/part.board.sidebarView.php b/templates/part.board.sidebarView.php index 6f0e74204..ebb7c9fb6 100644 --- a/templates/part.board.sidebarView.php +++ b/templates/part.board.sidebarView.php @@ -19,10 +19,10 @@ - {{ $item.participant }} + {{ $item.participant.displayname }} - {{ sharee.participant }} + {{ sharee.participant.displayname }} t('No matching user or group found.')); ?> @@ -32,18 +32,18 @@
  • -
    +
    - {{ boardservice.getCurrent().owner }} + {{ boardservice.getCurrent().owner.displayname }}
  • -
    +
    - {{ acl.participant }} + {{ acl.participant.displayname }} diff --git a/templates/part.boardlist.php b/templates/part.boardlist.php index 3436c8db5..98ae6a08a 100644 --- a/templates/part.boardlist.php +++ b/templates/part.boardlist.php @@ -18,9 +18,9 @@
    + displayname="{{ b.owner.uid }}" title="{{ b.owner.displayname }}">
    diff --git a/tests/unit/Db/AclTest.php b/tests/unit/Db/AclTest.php index 8641a0500..befbeb3c5 100644 --- a/tests/unit/Db/AclTest.php +++ b/tests/unit/Db/AclTest.php @@ -23,7 +23,8 @@ namespace OCA\Deck\Db; -class AclTest extends \PHPUnit_Framework_TestCase { +class AclTest extends \Test\TestCase { + private function createAclUser() { $acl = new Acl(); $acl->setId(1); @@ -35,6 +36,7 @@ class AclTest extends \PHPUnit_Framework_TestCase { $acl->setPermissionManage(true); return $acl; } + private function createAclGroup() { $acl = new Acl(); $acl->setId(1); @@ -46,6 +48,7 @@ class AclTest extends \PHPUnit_Framework_TestCase { $acl->setPermissionManage(true); return $acl; } + public function testJsonSerialize() { $acl = $this->createAclUser(); $this->assertEquals([ @@ -70,6 +73,7 @@ class AclTest extends \PHPUnit_Framework_TestCase { 'owner' => false ], $acl->jsonSerialize()); } + public function testSetOwner() { $acl = $this->createAclUser(); $acl->setOwner(1); diff --git a/tests/unit/Service/BoardServiceTest.php b/tests/unit/Service/BoardServiceTest.php index 4330bc31d..21d881eaa 100644 --- a/tests/unit/Service/BoardServiceTest.php +++ b/tests/unit/Service/BoardServiceTest.php @@ -29,68 +29,89 @@ use OCA\Deck\Db\AclMapper; use OCA\Deck\Db\Board; use OCA\Deck\Db\BoardMapper; use OCA\Deck\Db\LabelMapper; +use OCA\Deck\Db\User; use OCP\IGroupManager; use OCP\ILogger; +use OCP\IUser; +use OCP\IUserManager; -class BoardServiceTest extends \PHPUnit_Framework_TestCase { +class BoardServiceTest extends \Test\TestCase { + /** @var BoardService */ private $service; - private $logger; + /** @var L10N */ private $l10n; + /** @var LabelMapper */ private $labelMapper; + /** @var AclMapper */ private $aclMapper; + /** @var BoardMapper */ private $boardMapper; + /** @var IUserManager */ + private $userManager; + /** @var IGroupManager */ private $groupManager; + /** @var PermissionService */ private $permissionService; private $userId = 'admin'; public function setUp() { - $this->l10n = $this->getMockBuilder(L10N::class) - ->disableOriginalConstructor() - ->getMock(); - $this->aclMapper = $this->getMockBuilder(AclMapper::class) - ->disableOriginalConstructor()->getMock(); - $this->boardMapper = $this->getMockBuilder(BoardMapper::class) - ->disableOriginalConstructor()->getMock(); - $this->labelMapper = $this->getMockBuilder(LabelMapper::class) - ->disableOriginalConstructor()->getMock(); - $this->permissionService = $this->getMockBuilder(PermissionService::class) - ->disableOriginalConstructor()->getMock(); + $this->l10n = $this->createMock(L10N::class); + $this->aclMapper = $this->createMock(AclMapper::class); + $this->boardMapper = $this->createMock(BoardMapper::class); + $this->labelMapper = $this->createMock(LabelMapper::class); + $this->permissionService = $this->createMock(PermissionService::class); + $this->userManager = $this->createMock(IUserManager::class); + $this->groupManager = $this->createMock(IGroupManager::class); $this->service = new BoardService( $this->boardMapper, $this->l10n, $this->labelMapper, $this->aclMapper, - $this->permissionService + $this->permissionService, + $this->userManager, + $this->groupManager ); + + $user = $this->createMock(IUser::class); + $user->method('getUID')->willReturn('admin'); + $this->userManager->expects($this->any())->method('get')->with('admin')->willReturn($user); } public function testFindAll() { + $b1 = new Board(); + $b1->setId(1); + $b2 = new Board(); + $b2->setId(2); + $b3 = new Board(); + $b3->setId(3); $this->boardMapper->expects($this->once()) ->method('findAllByUser') ->with('admin') - ->willReturn([1,2,3,6,7]); + ->willReturn([$b1, $b2]); $this->boardMapper->expects($this->once()) ->method('findAllByGroups') ->with('admin', ['a', 'b', 'c']) - ->willReturn([4,5,6,7,8]); + ->willReturn([$b2, $b3]); $userinfo = [ 'user' => 'admin', 'groups' => ['a', 'b', 'c'] ]; $result = $this->service->findAll($userinfo); sort($result); - $this->assertEquals([1,2,3,4,5,6,7,8], $result); + $this->assertEquals([$b1, $b2, $b3], $result); } public function testFind() { + $b1 = new Board(); + $b1->setId(1); $this->boardMapper->expects($this->once()) ->method('find') - ->with(123) - ->willReturn(1); - $this->assertEquals(1, $this->service->find(123)); + ->with(1) + ->willReturn($b1); + $this->assertEquals($b1, $this->service->find(1)); } public function testCreate() { @@ -130,9 +151,11 @@ class BoardServiceTest extends \PHPUnit_Framework_TestCase { } public function testDelete() { + $board = new Board(); + $board->setOwner('admin'); $this->boardMapper->expects($this->once()) ->method('find') - ->willReturn(new Board()); + ->willReturn($board); $this->boardMapper->expects($this->once()) ->method('delete') ->willReturn(1); @@ -140,6 +163,8 @@ class BoardServiceTest extends \PHPUnit_Framework_TestCase { } public function testAddAcl() { + $user = $this->createMock(IUser::class); + $user->method('getUID')->willReturn('admin'); $acl = new Acl(); $acl->setBoardId(123); $acl->setType('user'); @@ -147,6 +172,9 @@ class BoardServiceTest extends \PHPUnit_Framework_TestCase { $acl->setPermissionEdit(true); $acl->setPermissionShare(true); $acl->setPermissionManage(true); + $acl->resolveRelation('participant', function($value) use (&$user) { + return new User($user); + }); $this->aclMapper->expects($this->once()) ->method('insert') ->with($acl)