diff --git a/appinfo/routes.php b/appinfo/routes.php index 0c247b642..4ab64f84a 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -64,7 +64,7 @@ return [ ['name' => 'card#assignLabel', 'url' => '/cards/{cardId}/label/{labelId}', 'verb' => 'POST'], ['name' => 'card#removeLabel', 'url' => '/cards/{cardId}/label/{labelId}', 'verb' => 'DELETE'], ['name' => 'card#assignUser', 'url' => '/cards/{cardId}/assign', 'verb' => 'POST'], - ['name' => 'card#unassignUser', 'url' => '/cards/{cardId}/assign/{userId}', 'verb' => 'DELETE'], + ['name' => 'card#unassignUser', 'url' => '/cards/{cardId}/unassign', 'verb' => 'PUT'], ['name' => 'attachment#getAll', 'url' => '/cards/{cardId}/attachments', 'verb' => 'GET'], ['name' => 'attachment#create', 'url' => '/cards/{cardId}/attachment', 'verb' => 'POST'], diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 3d2885c56..1bb13c028 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -43,6 +43,8 @@ use OCP\AppFramework\App; use OCP\Collaboration\Resources\IManager; use OCP\Collaboration\Resources\IProviderManager; use OCP\Comments\CommentsEntityEvent; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventDispatcher; use OCP\FullTextSearch\IFullTextSearchManager; use OCP\IGroup; use OCP\IServerContainer; @@ -217,34 +219,35 @@ class Application extends App { return; } - $eventDispatcher = $this->server->getEventDispatcher(); + /** @var IEventDispatcher $eventDispatcher */ + $eventDispatcher = $this->server->query(IEventDispatcher::class); $eventDispatcher->addListener( - '\OCA\Deck\Card::onCreate', function(GenericEvent $e) { + '\OCA\Deck\Card::onCreate', function(Event $e) { $this->fullTextSearchService->onCardCreated($e); } ); $eventDispatcher->addListener( - '\OCA\Deck\Card::onUpdate', function(GenericEvent $e) { + '\OCA\Deck\Card::onUpdate', function(Event $e) { $this->fullTextSearchService->onCardUpdated($e); } ); $eventDispatcher->addListener( - '\OCA\Deck\Card::onDelete', function(GenericEvent $e) { + '\OCA\Deck\Card::onDelete', function(Event $e) { $this->fullTextSearchService->onCardDeleted($e); } ); $eventDispatcher->addListener( - '\OCA\Deck\Board::onShareNew', function(GenericEvent $e) { + '\OCA\Deck\Board::onShareNew', function(Event $e) { $this->fullTextSearchService->onBoardShares($e); } ); $eventDispatcher->addListener( - '\OCA\Deck\Board::onShareEdit', function(GenericEvent $e) { + '\OCA\Deck\Board::onShareEdit', function(Event $e) { $this->fullTextSearchService->onBoardShares($e); } ); $eventDispatcher->addListener( - '\OCA\Deck\Board::onShareDelete', function(GenericEvent $e) { + '\OCA\Deck\Board::onShareDelete', function(Event $e) { $this->fullTextSearchService->onBoardShares($e); } ); diff --git a/lib/Controller/CardApiController.php b/lib/Controller/CardApiController.php index 805bbe6e6..1a722ac10 100644 --- a/lib/Controller/CardApiController.php +++ b/lib/Controller/CardApiController.php @@ -25,6 +25,7 @@ namespace OCA\Deck\Controller; + use OCA\Deck\Service\AssignmentService; use OCP\AppFramework\ApiController; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; @@ -39,6 +40,7 @@ class CardApiController extends ApiController { private $cardService; private $userId; + private $assignmentService; /** * @param string $appName @@ -46,10 +48,11 @@ class CardApiController extends ApiController { * @param CardService $cardService * @param $userId */ - public function __construct($appName, IRequest $request, CardService $cardService, $userId) { + public function __construct($appName, IRequest $request, CardService $cardService, AssignmentService $assignmentService, $userId) { parent::__construct($appName, $request); $this->cardService = $cardService; $this->userId = $userId; + $this->assignmentService = $assignmentService; } /** @@ -135,10 +138,10 @@ class CardApiController extends ApiController { * @CORS * @NoCSRFRequired * - * Unassign a user from a card + * Assign a user to a card */ - public function unassignUser($userId) { - $card = $this->cardService->unassignUser($this->request->getParam('cardId'), $userId); + public function assignUser($cardId, $userId, $type = 0) { + $card = $this->assignmentService->assignUser($cardId, $userId, $type); return new DataResponse($card, HTTP::STATUS_OK); } @@ -147,10 +150,10 @@ class CardApiController extends ApiController { * @CORS * @NoCSRFRequired * - * Assign a user to a card + * Unassign a user from a card */ - public function assignUser($userId) { - $card = $this->cardService->assignUser($this->request->getParam('cardId'), $userId);; + public function unassignUser($cardId, $userId, $type = 0) { + $card = $this->assignmentService->unassignUser($cardId, $userId, $type); return new DataResponse($card, HTTP::STATUS_OK); } diff --git a/lib/Controller/CardController.php b/lib/Controller/CardController.php index 4eb8afcf9..89a358eec 100644 --- a/lib/Controller/CardController.php +++ b/lib/Controller/CardController.php @@ -5,24 +5,25 @@ * @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\Controller; +use OCA\Deck\Service\AssignmentService; use OCA\Deck\Service\CardService; use OCP\IRequest; use OCP\AppFramework\Controller; @@ -31,11 +32,13 @@ class CardController extends Controller { private $userId; private $cardService; + private $assignmentService; - public function __construct($appName, IRequest $request, CardService $cardService, $userId) { + public function __construct($appName, IRequest $request, CardService $cardService, AssignmentService $assignmentService, $userId) { parent::__construct($appName, $request); $this->userId = $userId; $this->cardService = $cardService; + $this->assignmentService = $assignmentService; } /** @@ -153,15 +156,15 @@ class CardController extends Controller { /** * @NoAdminRequired */ - public function assignUser($cardId, $userId) { - return $this->cardService->assignUser($cardId, $userId); + public function assignUser($cardId, $userId, $type = 0) { + return $this->assignmentService->assignUser($cardId, $userId, $type); } /** * @NoAdminRequired */ - public function unassignUser($cardId, $userId) { - return $this->cardService->unassignUser($cardId, $userId); + public function unassignUser($cardId, $userId, $type = 0) { + return $this->assignmentService->unassignUser($cardId, $userId, $type); } diff --git a/lib/Db/AssignedUsers.php b/lib/Db/AssignedUsers.php index 69be2ec4e..9e106ec2f 100644 --- a/lib/Db/AssignedUsers.php +++ b/lib/Db/AssignedUsers.php @@ -30,10 +30,16 @@ class AssignedUsers extends RelationalEntity implements JsonSerializable { public $id; protected $participant; protected $cardId; + protected $type; + + public const TYPE_USER = Acl::PERMISSION_TYPE_USER; + public const TYPE_GROUP = Acl::PERMISSION_TYPE_GROUP; + public const TYPE_CIRCLE = Acl::PERMISSION_TYPE_CIRCLE; public function __construct() { $this->addType('id', 'integer'); $this->addType('cardId', 'integer'); + $this->addType('type', 'integer'); $this->addResolvable('participant'); } diff --git a/lib/Db/AssignedUsersMapper.php b/lib/Db/AssignedUsersMapper.php index a7e0ea0b6..ccb2d785f 100644 --- a/lib/Db/AssignedUsersMapper.php +++ b/lib/Db/AssignedUsersMapper.php @@ -26,6 +26,7 @@ namespace OCA\Deck\Db; use OCP\AppFramework\Db\Entity; use OCP\IDBConnection; +use OCP\IGroupManager; use OCP\IUserManager; @@ -33,11 +34,16 @@ class AssignedUsersMapper extends DeckMapper implements IPermissionMapper { private $cardMapper; private $userManager; + /** + * @var IGroupManager + */ + private $groupManager; - public function __construct(IDBConnection $db, CardMapper $cardMapper, IUserManager $userManager) { + public function __construct(IDBConnection $db, CardMapper $cardMapper, IUserManager $userManager, IGroupManager $groupManager) { parent::__construct($db, 'deck_assigned_users', AssignedUsers::class); $this->cardMapper = $cardMapper; $this->userManager = $userManager; + $this->groupManager = $groupManager; } /** @@ -78,8 +84,8 @@ class AssignedUsersMapper extends DeckMapper implements IPermissionMapper { * @return null|Entity */ public function insert(Entity $entity) { - $user = $this->userManager->get($entity->getParticipant()); - if ($user !== null) { + $origin = $this->getOrigin($entity); + if ($origin !== null) { /** @var AssignedUsers $assignment */ $assignment = parent::insert($entity); $this->mapParticipant($assignment); @@ -89,15 +95,26 @@ class AssignedUsersMapper extends DeckMapper implements IPermissionMapper { } public function mapParticipant(AssignedUsers &$assignment) { - $userManager = $this->userManager; - $assignment->resolveRelation('participant', function() use (&$userManager, &$assignment) { - $user = $userManager->get($assignment->getParticipant()); - if ($user !== null) { - return new User($user); - } - return null; + $self = $this; + $assignment->resolveRelation('participant', function() use (&$self, &$assignment) { + return $self->getOrigin($assignment); }); } + private function getOrigin(AssignedUsers $assignment) { + if ($assignment->getType() === AssignedUsers::TYPE_USER) { + $origin = $this->userManager->get($assignment->getParticipant()); + return $origin ? new User($origin) : null; + } + if ($assignment->getType() === AssignedUsers::TYPE_GROUP) { + $origin = $this->groupManager->get($assignment->getParticipant()); + return $origin ? new Group($origin) : null; + } + if ($assignment->getType() === AssignedUsers::TYPE_CIRCLE) { + $origin = $this->groupManager->get($assignment->getParticipant()); + return $origin ? new Circle($origin) : null; + } + return null; + } -} \ No newline at end of file +} diff --git a/lib/Db/BoardMapper.php b/lib/Db/BoardMapper.php index 05bce17f9..378e0f01a 100644 --- a/lib/Db/BoardMapper.php +++ b/lib/Db/BoardMapper.php @@ -232,7 +232,10 @@ class BoardMapper extends DeckMapper implements IPermissionMapper { \OC::$server->getLogger()->debug('Group ' . $acl->getId() . ' not found when mapping acl ' . $acl->getParticipant()); return null; } - if ($acl->getType() === Acl::PERMISSION_TYPE_CIRCLE && $this->circlesEnabled) { + if ($acl->getType() === Acl::PERMISSION_TYPE_CIRCLE) { + if (!$this->circlesEnabled) { + return null; + } try { $circle = \OCA\Circles\Api\v1\Circles::detailsCircle($acl->getParticipant(), true); if ($circle) { diff --git a/lib/Db/Circle.php b/lib/Db/Circle.php index 6ea1e39eb..f4a4fa568 100644 --- a/lib/Db/Circle.php +++ b/lib/Db/Circle.php @@ -24,13 +24,15 @@ namespace OCA\Deck\Db; +use OCP\Share\IShare; + class Circle extends RelationalObject { /** @var \OCA\Circles\Model\Circle */ protected $object; public function __construct(\OCA\Circles\Model\Circle $circle) { - $primaryKey = $circle->getUniqueId(); + $primaryKey = IShare::TYPE_CIRCLE . ':' . $circle->getUniqueId(); parent::__construct($primaryKey, $circle); } @@ -42,4 +44,4 @@ class Circle extends RelationalObject { 'circleOwner' => $this->object->getOwner() ]; } -} \ No newline at end of file +} diff --git a/lib/Db/Group.php b/lib/Db/Group.php index f668282b7..330b592d2 100644 --- a/lib/Db/Group.php +++ b/lib/Db/Group.php @@ -24,11 +24,12 @@ namespace OCA\Deck\Db; use OCP\IGroup; +use OCP\Share\IShare; class Group extends RelationalObject { public function __construct(IGroup $group) { - $primaryKey = $group->getGID(); + $primaryKey = IShare::TYPE_GROUP . ':' . $group->getGID(); parent::__construct($primaryKey, $group); } @@ -38,4 +39,4 @@ class Group extends RelationalObject { 'displayname' => $this->object->getDisplayName() ]; } -} \ No newline at end of file +} diff --git a/lib/Db/User.php b/lib/Db/User.php index 89a386a8e..6f6d70ce0 100644 --- a/lib/Db/User.php +++ b/lib/Db/User.php @@ -24,11 +24,12 @@ namespace OCA\Deck\Db; use OCP\IUser; +use OCP\Share\IShare; class User extends RelationalObject { public function __construct(IUser $user) { - $primaryKey = $user->getUID(); + $primaryKey = IShare::TYPE_USER . ':' . $user->getUID(); parent::__construct($primaryKey, $user); } @@ -46,4 +47,4 @@ class User extends RelationalObject { public function getDisplayName() { return $this->object->getDisplayName(); } -} \ No newline at end of file +} diff --git a/lib/Event/FTSEvent.php b/lib/Event/FTSEvent.php new file mode 100644 index 000000000..c00077d22 --- /dev/null +++ b/lib/Event/FTSEvent.php @@ -0,0 +1,55 @@ + + * + * @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\Event; + + +use OCP\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\GenericEvent; + +/** + * This is a class to keep compatibility for currently used events in full text search integration + */ +class FTSEvent extends Event { + + /** + * @var array + */ + private $arguments; + + public function __construct($subject, $arguments = []) { + parent::__construct(); + + $this->arguments = $arguments; + } + + public function getArgument($key) + { + if ($this->hasArgument($key)) { + return $this->arguments[$key]; + } + + throw new \InvalidArgumentException(sprintf('Argument "%s" not found.', $key)); + } + +} diff --git a/lib/Migration/Version1000Date20200308073933.php b/lib/Migration/Version1000Date20200308073933.php new file mode 100644 index 000000000..3fcab3dfe --- /dev/null +++ b/lib/Migration/Version1000Date20200308073933.php @@ -0,0 +1,54 @@ +getTable('deck_assigned_users'); + + // Defaults to TYPE_USER = 0 + $table->addColumn('type', 'integer', [ + 'notnull' => true, + 'default' => 0 + ]); + $table->addIndex(['participant'], 'deck_assigned_users_idx_t'); + + return $schema; + } + + /** + * @param IOutput $output + * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + * @param array $options + */ + public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { + } +} diff --git a/lib/Service/AssignmentService.php b/lib/Service/AssignmentService.php new file mode 100644 index 000000000..0ba1071a3 --- /dev/null +++ b/lib/Service/AssignmentService.php @@ -0,0 +1,203 @@ + + * + * @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\Service; + + +use OCA\Deck\Activity\ActivityManager; +use OCA\Deck\BadRequestException; +use OCA\Deck\Db\Acl; +use OCA\Deck\Db\AclMapper; +use OCA\Deck\Db\AssignedUsers; +use OCA\Deck\Db\AssignedUsersMapper; +use OCA\Deck\Db\CardMapper; +use OCA\Deck\Db\ChangeHelper; +use OCA\Deck\Event\FTSEvent; +use OCA\Deck\NoPermissionException; +use OCA\Deck\NotFoundException; +use OCA\Deck\Notification\NotificationHelper; +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Db\Entity; +use OCP\AppFramework\Db\MultipleObjectsReturnedException; +use OCP\EventDispatcher\IEventDispatcher; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\EventDispatcher\GenericEvent; + +class AssignmentService { + + /** + * @var PermissionService + */ + private $permissionService; + /** + * @var CardMapper + */ + private $cardMapper; + /** + * @var AssignedUsersMapper + */ + private $assignedUsersMapper; + /** + * @var AclMapper + */ + private $aclMapper; + /** + * @var NotificationHelper + */ + private $notificationHelper; + /** + * @var ChangeHelper + */ + private $changeHelper; + /** + * @var ActivityManager + */ + private $activityManager; + /** + * @var IEventDispatcher + */ + private $eventDispatcher; + + public function __construct( + PermissionService $permissionService, + CardMapper $cardMapper, + AssignedUsersMapper $assignedUsersMapper, + AclMapper $aclMapper, + NotificationHelper $notificationHelper, + ActivityManager $activityManager, + ChangeHelper $changeHelper, + IEventDispatcher $eventDispatcher, + $userId + ) { + $this->permissionService = $permissionService; + $this->cardMapper = $cardMapper; + $this->assignedUsersMapper = $assignedUsersMapper; + $this->aclMapper = $aclMapper; + $this->notificationHelper = $notificationHelper; + $this->changeHelper = $changeHelper; + $this->activityManager = $activityManager; + $this->eventDispatcher = $eventDispatcher; + $this->currentUser = $userId; + } + + /** + * @param $cardId + * @param $userId + * @return bool|null|Entity + * @throws BadRequestException + * @throws NoPermissionException + * @throws MultipleObjectsReturnedException + * @throws DoesNotExistException + */ + public function assignUser($cardId, $userId, int $type = AssignedUsers::TYPE_USER) { + + if (is_numeric($cardId) === false) { + throw new BadRequestException('card id must be a number'); + } + + if ($userId === false || $userId === null) { + throw new BadRequestException('user id must be provided'); + } + + if ($type !== AssignedUsers::TYPE_USER && $type !== AssignedUsers::TYPE_GROUP) { + throw new BadRequestException('Invalid type provided for assignemnt'); + } + + $this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_EDIT); + $assignments = $this->assignedUsersMapper->find($cardId); + foreach ($assignments as $assignment) { + if ($assignment->getParticipant() === $userId && $assignment->getType() === $type) { + throw new BadRequestException('The user is already assigned to the card'); + } + } + + $card = $this->cardMapper->find($cardId); + $boardId = $this->cardMapper->findBoardId($cardId); + $boardUsers = array_keys($this->permissionService->findUsers($boardId, true)); + $groups = array_filter($this->aclMapper->findAll($boardId), function (Acl $acl) use ($userId) { + return $acl->getType() === Acl::PERMISSION_TYPE_GROUP && $acl->getParticipant() === $userId; + }); + if (!in_array($userId, $boardUsers) && count($groups) !== 1) { + throw new BadRequestException('The user is not part of the board'); + } + + + if ($userId !== $this->currentUser) { + /* Notifyuser about the card assignment */ + $this->notificationHelper->sendCardAssigned($card, $userId); + } + + $assignment = new AssignedUsers(); + $assignment->setCardId($cardId); + $assignment->setParticipant($userId); + $assignment->setType($type); + $assignment = $this->assignedUsersMapper->insert($assignment); + $this->changeHelper->cardChanged($cardId, false); + $this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $card, ActivityManager::SUBJECT_CARD_USER_ASSIGN, ['assigneduser' => $userId]); + + $this->eventDispatcher->dispatch( + '\OCA\Deck\Card::onUpdate', new FTSEvent(null, ['id' => $cardId, 'card' => $card]) + ); + + return $assignment; + } + + /** + * @param $cardId + * @param $userId + * @return Entity + * @throws BadRequestException + * @throws NotFoundException + * @throws NoPermissionException + * @throws DoesNotExistException + * @throws MultipleObjectsReturnedException + */ + public function unassignUser($cardId, $userId, $type = 0) { + $this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_EDIT); + + if (is_numeric($cardId) === false) { + throw new BadRequestException('card id must be a number'); + } + + if ($userId === false || $userId === null) { + throw new BadRequestException('user must be provided'); + } + + $assignments = $this->assignedUsersMapper->find($cardId); + foreach ($assignments as $assignment) { + if ($assignment->getParticipant() === $userId && $assignment->getType() === $type) { + $assignment = $this->assignedUsersMapper->delete($assignment); + $card = $this->cardMapper->find($cardId); + $this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $card, ActivityManager::SUBJECT_CARD_USER_UNASSIGN, ['assigneduser' => $userId]); + $this->changeHelper->cardChanged($cardId, false); + + $this->eventDispatcher->dispatch( + '\OCA\Deck\Card::onUpdate', new FTSEvent(null, ['id' => $cardId, 'card' => $card]) + ); + + return $assignment; + } + } + throw new NotFoundException('No assignment for ' . $userId . 'found.'); + } +} diff --git a/lib/Service/CardService.php b/lib/Service/CardService.php index 9a85ac043..afc8ab3da 100644 --- a/lib/Service/CardService.php +++ b/lib/Service/CardService.php @@ -35,13 +35,17 @@ use OCA\Deck\Db\CardMapper; use OCA\Deck\Db\Acl; use OCA\Deck\Db\ChangeHelper; use OCA\Deck\Db\StackMapper; +use OCA\Deck\Event\FTSEvent; use OCA\Deck\Notification\NotificationHelper; use OCA\Deck\Db\BoardMapper; use OCA\Deck\Db\LabelMapper; use OCA\Deck\NotFoundException; use OCA\Deck\StatusException; use OCA\Deck\BadRequestException; +use OCP\Activity\IEvent; use OCP\Comments\ICommentsManager; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventDispatcher; use OCP\IUserManager; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\GenericEvent; @@ -61,7 +65,6 @@ class CardService { private $activityManager; private $commentsManager; private $changeHelper; - /** @var EventDispatcherInterface */ private $eventDispatcher; private $userManager; @@ -79,7 +82,7 @@ class CardService { ICommentsManager $commentsManager, IUserManager $userManager, ChangeHelper $changeHelper, - EventDispatcherInterface $eventDispatcher, + IEventDispatcher $eventDispatcher, $userId ) { $this->cardMapper = $cardMapper; @@ -197,7 +200,7 @@ class CardService { $this->eventDispatcher->dispatch( '\OCA\Deck\Card::onCreate', - new GenericEvent( + new FTSEvent( null, ['id' => $card->getId(), 'card' => $card, 'userId' => $owner, 'stackId' => $stackId] ) ); @@ -231,7 +234,7 @@ class CardService { $this->changeHelper->cardChanged($card->getId(), false); $this->eventDispatcher->dispatch( - '\OCA\Deck\Card::onDelete', new GenericEvent(null, ['id' => $id, 'card' => $card]) + '\OCA\Deck\Card::onDelete', new FTSEvent(null, ['id' => $id, 'card' => $card]) ); return $card; @@ -327,7 +330,7 @@ class CardService { $this->changeHelper->cardChanged($card->getId(), true); $this->eventDispatcher->dispatch( - '\OCA\Deck\Card::onUpdate', new GenericEvent(null, ['id' => $id, 'card' => $card]) + '\OCA\Deck\Card::onUpdate', new FTSEvent(null, ['id' => $id, 'card' => $card]) ); return $card; @@ -366,7 +369,7 @@ class CardService { $update = $this->cardMapper->update($card); $this->eventDispatcher->dispatch( - '\OCA\Deck\Card::onUpdate', new GenericEvent(null, ['id' => $id, 'card' => $card]) + '\OCA\Deck\Card::onUpdate', new FTSEvent(null, ['id' => $id, 'card' => $card]) ); return $update; @@ -462,7 +465,7 @@ class CardService { $this->changeHelper->cardChanged($id, false); $this->eventDispatcher->dispatch( - '\OCA\Deck\Card::onUpdate', new GenericEvent(null, ['id' => $id, 'card' => $card]) + '\OCA\Deck\Card::onUpdate', new FTSEvent(null, ['id' => $id, 'card' => $card]) ); return $newCard; @@ -494,7 +497,7 @@ class CardService { $this->changeHelper->cardChanged($id, false); $this->eventDispatcher->dispatch( - '\OCA\Deck\Card::onUpdate', new GenericEvent(null, ['id' => $id, 'card' => $card]) + '\OCA\Deck\Card::onUpdate', new FTSEvent(null, ['id' => $id, 'card' => $card]) ); return $newCard; @@ -533,7 +536,7 @@ class CardService { $this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $card, ActivityManager::SUBJECT_LABEL_ASSIGN, ['label' => $label]); $this->eventDispatcher->dispatch( - '\OCA\Deck\Card::onUpdate', new GenericEvent(null, ['id' => $cardId, 'card' => $card]) + '\OCA\Deck\Card::onUpdate', new FTSEvent(null, ['id' => $cardId, 'card' => $card]) ); } @@ -570,100 +573,8 @@ class CardService { $this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $card, ActivityManager::SUBJECT_LABEL_UNASSING, ['label' => $label]); $this->eventDispatcher->dispatch( - '\OCA\Deck\Card::onUpdate', new GenericEvent(null, ['id' => $cardId, 'card' => $card]) + '\OCA\Deck\Card::onUpdate', new FTSEvent(null, ['id' => $cardId, 'card' => $card]) ); } - /** - * @param $cardId - * @param $userId - * @return bool|null|\OCP\AppFramework\Db\Entity - * @throws BadRequestException - * @throws \OCA\Deck\NoPermissionException - * @throws \OCP\AppFramework\Db\DoesNotExistException - * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException - */ - public function assignUser($cardId, $userId) { - - if (is_numeric($cardId) === false) { - throw new BadRequestException('card id must be a number'); - } - - if ($userId === false || $userId === null) { - throw new BadRequestException('user id must be provided'); - } - - $this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_EDIT); - $assignments = $this->assignedUsersMapper->find($cardId); - foreach ($assignments as $assignment) { - if ($assignment->getParticipant() === $userId) { - throw new BadRequestException('The user is already assigned to the card'); - } - } - - $card = $this->cardMapper->find($cardId); - $boardId = $this->cardMapper->findBoardId($cardId); - $boardUsers = array_keys($this->permissionService->findUsers($boardId, true)); - if (!in_array($userId, $boardUsers)) { - throw new BadRequestException('The user is not part of the board'); - } - - - if ($userId !== $this->currentUser) { - /* Notifyuser about the card assignment */ - $this->notificationHelper->sendCardAssigned($card, $userId); - } - - $assignment = new AssignedUsers(); - $assignment->setCardId($cardId); - $assignment->setParticipant($userId); - $assignment = $this->assignedUsersMapper->insert($assignment); - $this->changeHelper->cardChanged($cardId, false); - $this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $card, ActivityManager::SUBJECT_CARD_USER_ASSIGN, ['assigneduser' => $userId]); - - $this->eventDispatcher->dispatch( - '\OCA\Deck\Card::onUpdate', new GenericEvent(null, ['id' => $cardId, 'card' => $card]) - ); - - return $assignment; - } - - /** - * @param $cardId - * @param $userId - * @return \OCP\AppFramework\Db\Entity - * @throws BadRequestException - * @throws NotFoundException - * @throws \OCA\Deck\NoPermissionException - * @throws \OCP\AppFramework\Db\DoesNotExistException - * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException - */ - public function unassignUser($cardId, $userId) { - $this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_EDIT); - - if (is_numeric($cardId) === false) { - throw new BadRequestException('card id must be a number'); - } - - if ($userId === false || $userId === null) { - throw new BadRequestException('user must be provided'); - } - - $assignments = $this->assignedUsersMapper->find($cardId); - foreach ($assignments as $assignment) { - if ($assignment->getParticipant() === $userId) { - $assignment = $this->assignedUsersMapper->delete($assignment); - $card = $this->cardMapper->find($cardId); - $this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $card, ActivityManager::SUBJECT_CARD_USER_UNASSIGN, ['assigneduser' => $userId]); - $this->changeHelper->cardChanged($cardId, false); - - $this->eventDispatcher->dispatch( - '\OCA\Deck\Card::onUpdate', new GenericEvent(null, ['id' => $cardId, 'card' => $card]) - ); - - return $assignment; - } - } - throw new NotFoundException('No assignment for ' . $userId . 'found.'); - } } diff --git a/lib/Service/FullTextSearchService.php b/lib/Service/FullTextSearchService.php index 482f00bc9..583b7bc20 100644 --- a/lib/Service/FullTextSearchService.php +++ b/lib/Service/FullTextSearchService.php @@ -37,6 +37,7 @@ use OCA\Deck\Db\Card; use OCA\Deck\Db\CardMapper; use OCA\Deck\Db\Stack; use OCA\Deck\Db\StackMapper; +use OCA\Deck\Event\FTSEvent; use OCA\Deck\Provider\DeckProvider; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\MultipleObjectsReturnedException; @@ -45,7 +46,6 @@ use OCP\FullTextSearch\IFullTextSearchManager; use OCP\FullTextSearch\Model\IDocumentAccess; use OCP\FullTextSearch\Model\IIndex; use OCP\FullTextSearch\Model\IIndexDocument; -use Symfony\Component\EventDispatcher\GenericEvent; /** @@ -90,9 +90,9 @@ class FullTextSearchService { /** - * @param GenericEvent $e + * @param FTSEvent $e */ - public function onCardCreated(GenericEvent $e) { + public function onCardCreated(FTSEvent $e) { $cardId = $e->getArgument('id'); $userId = $e->getArgument('userId'); @@ -106,9 +106,9 @@ class FullTextSearchService { /** - * @param GenericEvent $e + * @param FTSEvent $e */ - public function onCardUpdated(GenericEvent $e) { + public function onCardUpdated(FTSEvent $e) { $cardId = $e->getArgument('id'); try { @@ -121,9 +121,9 @@ class FullTextSearchService { /** - * @param GenericEvent $e + * @param FTSEvent $e */ - public function onCardDeleted(GenericEvent $e) { + public function onCardDeleted(FTSEvent $e) { $cardId = $e->getArgument('id'); try { @@ -136,9 +136,9 @@ class FullTextSearchService { /** - * @param GenericEvent $e + * @param FTSEvent $e */ - public function onBoardShares(GenericEvent $e) { + public function onBoardShares(FTSEvent $e) { $boardId = (int)$e->getArgument('boardId'); $cards = array_map( diff --git a/src/components/board/SharingTabSidebar.vue b/src/components/board/SharingTabSidebar.vue index 53bb7e402..7fd82d148 100644 --- a/src/components/board/SharingTabSidebar.vue +++ b/src/components/board/SharingTabSidebar.vue @@ -23,7 +23,7 @@ -
  • +
  • @@ -33,10 +33,10 @@ {{ t('deck', '(Circle)') }} - + {{ t('deck', 'Can edit') }} - + {{ t('deck', 'Can share') }} @@ -60,7 +60,7 @@ diff --git a/src/components/cards/CardItem.vue b/src/components/cards/CardItem.vue index ba8e8e553..795fbdb33 100644 --- a/src/components/cards/CardItem.vue +++ b/src/components/cards/CardItem.vue @@ -196,8 +196,13 @@ export default { }, assignCardToMe() { this.copiedCard = Object.assign({}, this.card) - this.copiedCard.newUserUid = this.card.owner.uid - this.$store.dispatch('assignCardToUser', this.copiedCard) + this.$store.dispatch('assignCardToUser', { + card: this.copiedCard, + assignee: { + userId: OC.getCurrentUser().uid, + type: 0, + }, + }) }, async loadStacksFromBoard(board) { try { @@ -234,6 +239,7 @@ export default { } .card { + transition: box-shadow 0.1s ease-in-out; box-shadow: 0 0 2px 0 var(--color-box-shadow); border-radius: 3px; font-size: 100%; diff --git a/src/services/CardApi.js b/src/services/CardApi.js index b439b45bd..c5d7037af 100644 --- a/src/services/CardApi.js +++ b/src/services/CardApi.js @@ -105,8 +105,8 @@ export class CardApi { }) } - assignUser(card) { - return axios.post(this.url(`/cards/${card.id}/assign`), { userId: card.newUserUid }) + assignUser(cardId, id, type) { + return axios.post(this.url(`/cards/${cardId}/assign`), { userId: id, type: type }) .then( (response) => { return Promise.resolve(response.data) @@ -120,8 +120,8 @@ export class CardApi { }) } - removeUser(card) { - return axios.delete(this.url(`/cards/${card.id}/assign/${card.removeUserUid}`)) + removeUser(cardId, id, type) { + return axios.put(this.url(`/cards/${cardId}/unassign`), { userId: id, type: type }) .then( (response) => { return Promise.resolve(response.data) diff --git a/src/store/card.js b/src/store/card.js index 306749c6b..4e9b40f89 100644 --- a/src/store/card.js +++ b/src/store/card.js @@ -198,12 +198,12 @@ export default { const updatedCard = await apiClient[call](card) commit('deleteCard', updatedCard) }, - async assignCardToUser({ commit }, card) { - const user = await apiClient.assignUser(card) + async assignCardToUser({ commit }, { card, assignee }) { + const user = await apiClient.assignUser(card.id, assignee.userId, assignee.type) commit('assignCardToUser', user) }, - async removeUserFromCard({ commit }, card) { - const user = await apiClient.removeUser(card) + async removeUserFromCard({ commit }, { card, assignee }) { + const user = await apiClient.removeUser(card.id, assignee.userId, assignee.type) commit('removeUserFromCard', user) }, async addLabel({ commit }, data) { @@ -219,7 +219,7 @@ export default { commit('updateCardProperty', { property: 'description', card: updatedCard }) }, async updateCardDue({ commit }, card) { - const updatedCard = apiClient.updateCard(card) + const updatedCard = await apiClient.updateCard(card) commit('updateCardProperty', { property: 'duedate', card: updatedCard }) }, }, diff --git a/src/store/main.js b/src/store/main.js index 1fef80971..3339ad5e4 100644 --- a/src/store/main.js +++ b/src/store/main.js @@ -78,8 +78,12 @@ export default new Vuex.Store({ boards: state => { return state.boards }, - sharees: state => { - return state.sharees + assignables: state => { + return [ + ...state.assignableUsers.map((user) => ({ ...user, type: 0 })), + ...state.currentBoard.acl.filter((acl) => acl.type === 1 && typeof acl.participant === 'object').map((group) => ({ ...group.participant, type: 1 })), + ...state.currentBoard.acl.filter((acl) => acl.type === 7 && typeof acl.participant === 'object').map((circle) => ({ ...circle.participant, type: 7 })), + ] }, noneArchivedBoards: state => { return state.boards.filter(board => { diff --git a/tests/integration/database/AssignedUsersMapperTest.php b/tests/integration/database/AssignedUsersMapperTest.php index b5d86fee9..58ff07e6b 100644 --- a/tests/integration/database/AssignedUsersMapperTest.php +++ b/tests/integration/database/AssignedUsersMapperTest.php @@ -23,21 +23,22 @@ namespace OCA\Deck\Db; +use OCA\Deck\Service\AssignmentService; use OCA\Deck\Service\BoardService; use OCA\Deck\Service\StackService; use OCA\Deck\Service\CardService; -use OCP\IDBConnection; /** * @group DB * @coversDefaultClass OCA\Deck\Db\AssignedUsersMapper */ class AssignedUsersMapperTest extends \Test\TestCase { - const TEST_USER1 = "test-share-user1"; - const TEST_USER2 = "test-share-user2"; - const TEST_USER3 = "test-share-user3"; - const TEST_USER4 = "test-share-user4"; - const TEST_GROUP1 = "test-share-group1"; + + private const TEST_USER1 = 'test-share-user1'; + private const TEST_USER3 = 'test-share-user3'; + private const TEST_USER2 = 'test-share-user2'; + private const TEST_USER4 = 'test-share-user4'; + private const TEST_GROUP1 = 'test-share-group1'; /** @var BoardService */ protected $boardService; @@ -45,8 +46,10 @@ class AssignedUsersMapperTest extends \Test\TestCase { protected $cardService; /** @var StackService */ protected $stackService; - /** @var \OCA\Deck\Db\AssignedUsersMapper */ + /** @var AssignedUsersMapper */ protected $assignedUsersMapper; + /** @var AssignmentService */ + private $assignmentService; public static function setUpBeforeClass(): void { parent::setUpBeforeClass(); @@ -78,10 +81,11 @@ class AssignedUsersMapperTest extends \Test\TestCase { public function setUp(): void { parent::setUp(); \OC::$server->getUserSession()->login(self::TEST_USER1, self::TEST_USER1); - $this->boardService = \OC::$server->query("\OCA\Deck\Service\BoardService"); - $this->stackService = \OC::$server->query("\OCA\Deck\Service\StackService"); - $this->cardService = \OC::$server->query("\OCA\Deck\Service\CardService"); - $this->assignedUsersMapper = \OC::$server->query(\OCA\Deck\Db\AssignedUsersMapper::class); + $this->boardService = \OC::$server->query(BoardService::class); + $this->stackService = \OC::$server->query(StackService::class); + $this->cardService = \OC::$server->query(CardService::class); + $this->assignmentService = \OC::$server->query(AssignmentService::class); + $this->assignedUsersMapper = \OC::$server->query(AssignedUsersMapper::class); $this->createBoardWithExampleData(); } @@ -107,8 +111,8 @@ class AssignedUsersMapperTest extends \Test\TestCase { */ public function testFind() { $uids = []; - $this->cardService->assignUser($this->cards[0]->getId(), self::TEST_USER1); - $this->cardService->assignUser($this->cards[0]->getId(), self::TEST_USER2); + $this->assignmentService->assignUser($this->cards[0]->getId(), self::TEST_USER1); + $this->assignmentService->assignUser($this->cards[0]->getId(), self::TEST_USER2); $assignedUsers = $this->assignedUsersMapper->find($this->cards[0]->getId()); foreach ($assignedUsers as $user) { @@ -119,8 +123,8 @@ class AssignedUsersMapperTest extends \Test\TestCase { $this->assertArrayNotHasKey(self::TEST_USER3, $uids); $this->assertArrayNotHasKey(self::TEST_USER4, $uids); - $this->cardService->unassignUser($this->cards[0]->getId(), self::TEST_USER1); - $this->cardService->unassignUser($this->cards[0]->getId(), self::TEST_USER2); + $this->assignmentService->unassignUser($this->cards[0]->getId(), self::TEST_USER1); + $this->assignmentService->unassignUser($this->cards[0]->getId(), self::TEST_USER2); } /** @@ -145,6 +149,7 @@ class AssignedUsersMapperTest extends \Test\TestCase { $assignment = new AssignedUsers(); $assignment->setCardId($this->cards[1]->getId()); $assignment->setParticipant(self::TEST_USER4); + $assignment->setType(AssignedUsers::TYPE_USER); $this->assignedUsersMapper->insert($assignment); $actual = $this->assignedUsersMapper->find($this->cards[1]->getId()); @@ -160,6 +165,7 @@ class AssignedUsersMapperTest extends \Test\TestCase { $assignment = new AssignedUsers(); $assignment->setCardId($this->cards[1]->getId()); $assignment->setParticipant('invalid-username'); + $assignment->setType(AssignedUsers::TYPE_USER); $actual = $this->assignedUsersMapper->insert($assignment); $this->assertNull($actual); } @@ -171,12 +177,14 @@ class AssignedUsersMapperTest extends \Test\TestCase { $assignment = new AssignedUsers(); $assignment->setCardId($this->cards[1]->getId()); $assignment->setParticipant(self::TEST_USER4); + $assignment->setType(AssignedUsers::TYPE_USER); $this->assignedUsersMapper->mapParticipant($assignment); $this->assertInstanceOf(User::class, $assignment->resolveParticipant()); $assignment = new AssignedUsers(); $assignment->setCardId($this->cards[1]->getId()); $assignment->setParticipant('invalid-username'); + $assignment->setType(AssignedUsers::TYPE_USER); $this->assignedUsersMapper->mapParticipant($assignment); $this->assertEquals('invalid-username', $assignment->resolveParticipant()); } diff --git a/tests/unit/Db/GroupTest.php b/tests/unit/Db/GroupTest.php index 8f0678c62..163974871 100644 --- a/tests/unit/Db/GroupTest.php +++ b/tests/unit/Db/GroupTest.php @@ -57,7 +57,7 @@ class GroupTest extends \Test\TestCase { $expected = [ 'uid' => 'mygroup', 'displayname' => 'My Group', - 'primaryKey' => 'mygroup' + 'primaryKey' => '1:mygroup' ]; $actual = $groupRelationalObject->jsonSerialize(); diff --git a/tests/unit/Db/UserTest.php b/tests/unit/Db/UserTest.php index 43b15dda1..221a7f06d 100644 --- a/tests/unit/Db/UserTest.php +++ b/tests/unit/Db/UserTest.php @@ -57,8 +57,8 @@ class UserTest extends \Test\TestCase { $expected = [ 'uid' => 'myuser', 'displayname' => 'myuser displayname', - 'primaryKey' => 'myuser' + 'primaryKey' => '0:myuser' ]; $this->assertEquals($expected, $userRelationalObject->jsonSerialize()); } -} \ No newline at end of file +} diff --git a/tests/unit/Service/AssignmentServiceTest.php b/tests/unit/Service/AssignmentServiceTest.php new file mode 100644 index 000000000..033b83b6a --- /dev/null +++ b/tests/unit/Service/AssignmentServiceTest.php @@ -0,0 +1,236 @@ + + * + * @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\Service; + + +use OCA\Deck\Activity\ActivityManager; +use OCA\Deck\BadRequestException; +use OCA\Deck\Db\AclMapper; +use OCA\Deck\Db\AssignedUsers; +use OCA\Deck\Db\AssignedUsersMapper; +use OCA\Deck\Db\Card; +use OCA\Deck\Db\CardMapper; +use OCA\Deck\Db\ChangeHelper; +use OCA\Deck\Db\StackMapper; +use OCA\Deck\Db\BoardMapper; +use OCA\Deck\Db\LabelMapper; +use OCA\Deck\NotFoundException; +use OCA\Deck\Notification\NotificationHelper; +use OCA\Deck\StatusException; +use OCP\Activity\IEvent; +use OCP\Comments\ICommentsManager; +use OCP\EventDispatcher\ABroadcastedEvent; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\IUser; +use OCP\IUserManager; +use PHPUnit\Framework\MockObject\MockObject; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Test\TestCase; + +class AssignmentServiceTest extends TestCase { + + + /** + * @var MockObject|PermissionService + */ + private $permissionService; + /** + * @var MockObject|CardMapper + */ + private $cardMapper; + /** + * @var MockObject|AssignedUsersMapper + */ + private $assignedUsersMapper; + /** + * @var MockObject|AclMapper + */ + private $aclMapper; + /** + * @var MockObject|NotificationHelper + */ + private $notificationHelper; + /** + * @var MockObject|ChangeHelper + */ + private $changeHelper; + /** + * @var MockObject|ActivityManager + */ + private $activityManager; + /** + * @var MockObject|IEventDispatcher + */ + private $eventDispatcher; + /** + * @var AssignmentService + */ + private $assignmentService; + + public function setUp(): void { + parent::setUp(); + $this->aclMapper = $this->createMock(AclMapper::class); + $this->permissionService = $this->createMock(PermissionService::class); + $this->cardMapper = $this->createMock(CardMapper::class); + $this->notificationHelper = $this->createMock(NotificationHelper::class); + $this->assignedUsersMapper = $this->createMock(AssignedUsersMapper::class); + $this->activityManager = $this->createMock(ActivityManager::class); + $this->eventDispatcher = $this->createMock(IEventDispatcher::class); + $this->changeHelper = $this->createMock(ChangeHelper::class); + $this->assignmentService = new AssignmentService( + $this->permissionService, + $this->cardMapper, + $this->assignedUsersMapper, + $this->aclMapper, + $this->notificationHelper, + $this->activityManager, + $this->changeHelper, + $this->eventDispatcher, + 'admin' + ); + } + + public function mockActivity($type, $object, $subject) { + // ActivityManager::DECK_OBJECT_BOARD, $newAcl, ActivityManager::SUBJECT_BOARD_SHARE + $event = $this->createMock(IEvent::class); + $this->activityManager->expects($this->once()) + ->method('createEvent') + ->with($type, $object, $subject) + ->willReturn($event); + $this->activityManager->expects($this->once()) + ->method('sendToUsers') + ->with($event); + } + + public function testAssignUser() { + $assignments = []; + $this->assignedUsersMapper->expects($this->once()) + ->method('find') + ->with(123) + ->willReturn($assignments); + $assignment = new AssignedUsers(); + $assignment->setCardId(123); + $assignment->setParticipant('admin'); + $assignment->setType(AssignedUsers::TYPE_USER); + $this->cardMapper->expects($this->once()) + ->method('findBoardId') + ->willReturn(1); + $this->permissionService->expects($this->once()) + ->method('findUsers') + ->with(1) + ->willReturn(['admin' => 'admin', 'user1' => 'user1']); + $this->aclMapper->expects($this->once()) + ->method('findAll') + ->willReturn([]); + $this->assignedUsersMapper->expects($this->once()) + ->method('insert') + ->with($assignment) + ->willReturn($assignment); + $actual = $this->assignmentService->assignUser(123, 'admin'); + $this->assertEquals($assignment, $actual); + } + + public function testAssignUserNoParticipant() { + $this->expectException(BadRequestException::class); + $this->expectExceptionMessage('The user is not part of the board'); + $assignments = []; + $this->assignedUsersMapper->expects($this->once()) + ->method('find') + ->with(123) + ->willReturn($assignments); + $assignment = new AssignedUsers(); + $assignment->setCardId(123); + $assignment->setParticipant('admin'); + $assignment->setType(AssignedUsers::TYPE_USER); + $this->cardMapper->expects($this->once()) + ->method('findBoardId') + ->willReturn(1); + $this->permissionService->expects($this->once()) + ->method('findUsers') + ->with(1) + ->willReturn(['user2' => 'user2', 'user1' => 'user1']); + $this->aclMapper->expects($this->once()) + ->method('findAll') + ->willReturn([]); + $actual = $this->assignmentService->assignUser(123, 'admin'); + } + + public function testAssignUserExisting() { + $this->expectException(BadRequestException::class); + $this->expectExceptionMessage('The user is already assigned to the card'); + $assignment = new AssignedUsers(); + $assignment->setCardId(123); + $assignment->setParticipant('admin'); + $assignment->setType(AssignedUsers::TYPE_USER); + $assignments = [ + $assignment + ]; + $this->assignedUsersMapper->expects($this->once()) + ->method('find') + ->with(123) + ->willReturn($assignments); + $actual = $this->assignmentService->assignUser(123, 'admin'); + $this->assertFalse($actual); + } + + public function testUnassignUserExisting() { + $assignment = new AssignedUsers(); + $assignment->setCardId(123); + $assignment->setParticipant('admin'); + $assignment->setType(AssignedUsers::TYPE_USER); + $assignments = [ + $assignment + ]; + $this->assignedUsersMapper->expects($this->once()) + ->method('find') + ->with(123) + ->willReturn($assignments); + $this->assignedUsersMapper->expects($this->once()) + ->method('delete') + ->with($assignment) + ->willReturn($assignment); + $actual = $this->assignmentService->unassignUser(123, 'admin'); + $this->assertEquals($assignment, $actual); + } + + public function testUnassignUserNotExisting() { + $this->expectException(NotFoundException::class); + $assignment = new AssignedUsers(); + $assignment->setCardId(123); + $assignment->setParticipant('admin'); + $assignment->setType(AssignedUsers::TYPE_USER); + $assignments = [ + $assignment + ]; + $this->assignedUsersMapper->expects($this->once()) + ->method('find') + ->with(123) + ->willReturn($assignments); + $this->expectException(NotFoundException::class); + $actual = $this->assignmentService->unassignUser(123, 'user'); + } + + +} diff --git a/tests/unit/Service/CardServiceTest.php b/tests/unit/Service/CardServiceTest.php index 5871b333d..b6b6b033e 100644 --- a/tests/unit/Service/CardServiceTest.php +++ b/tests/unit/Service/CardServiceTest.php @@ -39,6 +39,9 @@ use OCA\Deck\Notification\NotificationHelper; use OCA\Deck\StatusException; use OCP\Activity\IEvent; use OCP\Comments\ICommentsManager; +use OCP\EventDispatcher\ABroadcastedEvent; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventDispatcher; use OCP\IUser; use OCP\IUserManager; use PHPUnit\Framework\MockObject\MockObject; @@ -91,7 +94,7 @@ class CardServiceTest extends TestCase { $this->activityManager = $this->createMock(ActivityManager::class); $this->commentsManager = $this->createMock(ICommentsManager::class); $this->userManager = $this->createMock(IUserManager::class); - $this->eventDispatcher = $this->createMock(EventDispatcherInterface::class); + $this->eventDispatcher = $this->createMock(IEventDispatcher::class); $this->changeHelper = $this->createMock(ChangeHelper::class); $this->cardService = new CardService( $this->cardMapper, @@ -318,102 +321,4 @@ class CardServiceTest extends TestCase { $this->cardService->removeLabel(123, 999); } - public function testAssignUser() { - $assignments = []; - $this->assignedUsersMapper->expects($this->once()) - ->method('find') - ->with(123) - ->willReturn($assignments); - $assignment = new AssignedUsers(); - $assignment->setCardId(123); - $assignment->setParticipant('admin'); - $this->cardMapper->expects($this->once()) - ->method('findBoardId') - ->willReturn(1); - $this->permissionService->expects($this->once()) - ->method('findUsers') - ->with(1) - ->willReturn(['admin' => 'admin', 'user1' => 'user1']); - $this->assignedUsersMapper->expects($this->once()) - ->method('insert') - ->with($assignment) - ->willReturn($assignment); - $actual = $this->cardService->assignUser(123, 'admin'); - $this->assertEquals($assignment, $actual); - } - - public function testAssignUserNoParticipant() { - $this->expectException(BadRequestException::class); - $this->expectExceptionMessage('The user is not part of the board'); - $assignments = []; - $this->assignedUsersMapper->expects($this->once()) - ->method('find') - ->with(123) - ->willReturn($assignments); - $assignment = new AssignedUsers(); - $assignment->setCardId(123); - $assignment->setParticipant('admin'); - $this->cardMapper->expects($this->once()) - ->method('findBoardId') - ->willReturn(1); - $this->permissionService->expects($this->once()) - ->method('findUsers') - ->with(1) - ->willReturn(['user2' => 'user2', 'user1' => 'user1']); - $actual = $this->cardService->assignUser(123, 'admin'); - } - - public function testAssignUserExisting() { - $this->expectException(BadRequestException::class); - $this->expectExceptionMessage('The user is already assigned to the card'); - $assignment = new AssignedUsers(); - $assignment->setCardId(123); - $assignment->setParticipant('admin'); - $assignments = [ - $assignment - ]; - $this->assignedUsersMapper->expects($this->once()) - ->method('find') - ->with(123) - ->willReturn($assignments); - $actual = $this->cardService->assignUser(123, 'admin'); - $this->assertFalse($actual); - } - - public function testUnassignUserExisting() { - $assignment = new AssignedUsers(); - $assignment->setCardId(123); - $assignment->setParticipant('admin'); - $assignments = [ - $assignment - ]; - $this->assignedUsersMapper->expects($this->once()) - ->method('find') - ->with(123) - ->willReturn($assignments); - $this->assignedUsersMapper->expects($this->once()) - ->method('delete') - ->with($assignment) - ->willReturn($assignment); - $actual = $this->cardService->unassignUser(123, 'admin'); - $this->assertEquals($assignment, $actual); - } - - public function testUnassignUserNotExisting() { - $this->expectException(NotFoundException::class); - $assignment = new AssignedUsers(); - $assignment->setCardId(123); - $assignment->setParticipant('admin'); - $assignments = [ - $assignment - ]; - $this->assignedUsersMapper->expects($this->once()) - ->method('find') - ->with(123) - ->willReturn($assignments); - $this->expectException(NotFoundException::class); - $actual = $this->cardService->unassignUser(123, 'user'); - } - - } diff --git a/tests/unit/controller/CardApiControllerTest.php b/tests/unit/controller/CardApiControllerTest.php index 2956b239c..7299da9dc 100644 --- a/tests/unit/controller/CardApiControllerTest.php +++ b/tests/unit/controller/CardApiControllerTest.php @@ -22,6 +22,7 @@ */ namespace OCA\Deck\Controller; +use OCA\Deck\Service\AssignmentService; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; use OCP\IRequest; @@ -37,12 +38,14 @@ class CardApiControllerTest extends \Test\TestCase { private $userId = 'admin'; private $cardExample; private $stackExample; + private $assignmentService; public function setUp(): void { parent::setUp(); $this->request = $this->createMock(IRequest::class); $this->cardService = $this->createMock(CardService::class); + $this->assignmentService = $this->createMock(AssignmentService::class); $this->cardExample['id'] = 1; $this->stackExample['id'] = 1; @@ -51,6 +54,7 @@ class CardApiControllerTest extends \Test\TestCase { $appName = 'deck', $this->request, $this->cardService, + $this->assignmentService, $this->userId ); } diff --git a/tests/unit/controller/CardControllerTest.php b/tests/unit/controller/CardControllerTest.php index 5ecf23893..53f73ff25 100644 --- a/tests/unit/controller/CardControllerTest.php +++ b/tests/unit/controller/CardControllerTest.php @@ -23,34 +23,35 @@ namespace OCA\Deck\Controller; +use OCA\Deck\Service\AssignmentService; use OCA\Deck\Service\CardService; -use OCP\AppFramework\Controller; use OCP\IRequest; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; -class CardControllerTest extends \Test\TestCase { +class CardControllerTest extends TestCase { - /** @var CardController|\PHPUnit\Framework\MockObject\MockObject */ + /** @var CardController|MockObject */ private $controller; - /** @var IRequest|\PHPUnit\Framework\MockObject\MockObject */ + /** @var IRequest|MockObject */ private $request; - /** @var CardService|\PHPUnit\Framework\MockObject\MockObject */ + /** @var CardService|MockObject */ private $cardService; + /** @var AssignmentService|MockObject */ + private $assignmentService; /** @var string */ private $userId = 'user'; + public function setUp(): void { - $this->request = $this->getMockBuilder( - '\OCP\IRequest') - ->disableOriginalConstructor() - ->getMock(); - $this->cardService = $this->getMockBuilder( - '\OCA\Deck\Service\CardService') - ->disableOriginalConstructor() - ->getMock(); + $this->request = $this->createMock(IRequest::class); + $this->cardService = $this->createMock(CardService::class); + $this->assignmentService = $this->createMock(AssignmentService::class); $this->controller = new CardController( 'deck', $this->request, $this->cardService, + $this->assignmentService, $this->userId ); } @@ -115,12 +116,12 @@ class CardControllerTest extends \Test\TestCase { } public function testAssignUser() { - $this->cardService->expects($this->once())->method('assignUser'); + $this->assignmentService->expects($this->once())->method('assignUser'); $this->controller->assignUser(1, 'admin'); } public function testUnssignUser() { - $this->cardService->expects($this->once())->method('unassignUser'); + $this->assignmentService->expects($this->once())->method('unassignUser'); $this->controller->unassignUser(1, 'admin'); }