Merge branch 'master' of next.github.com:nextcloud/deck into feature/update-card-modal-ui

This commit is contained in:
Luka Trovic
2022-03-23 13:22:06 +01:00
29 changed files with 1164 additions and 234 deletions

View File

@@ -50,8 +50,7 @@ ifeq (, $(shell which phpunit 2> /dev/null))
php $(build_tools_directory)/phpunit.phar -c tests/phpunit.xml --coverage-clover build/php-unit.coverage.xml
php $(build_tools_directory)/phpunit.phar -c tests/phpunit.integration.xml --coverage-clover build/php-integration.coverage.xml
else
phpunit -c tests/phpunit.xml --coverage-clover build/php-unit.coverage.xml
phpunit -c tests/phpunit.integration.xml --coverage-clover build/php-integration.coverage.xml
phpunit -c tests/phpunit.integration.xml --testsuite=integration-database --coverage-clover build/php-integration.coverage.xml
endif
test-integration:

View File

@@ -44,6 +44,7 @@
<commands>
<command>OCA\Deck\Command\UserExport</command>
<command>OCA\Deck\Command\BoardImport</command>
<command>OCA\Deck\Command\TransferOwnership</command>
</commands>
<activity>
<settings>

View File

@@ -39,6 +39,7 @@ return [
['name' => 'board#updateAcl', 'url' => '/boards/{boardId}/acl/{aclId}', 'verb' => 'PUT'],
['name' => 'board#deleteAcl', 'url' => '/boards/{boardId}/acl/{aclId}', 'verb' => 'DELETE'],
['name' => 'board#clone', 'url' => '/boards/{boardId}/clone', 'verb' => 'POST'],
['name' => 'board#transferOwner', 'url' => '/boards/{boardId}/transferOwner', 'verb' => 'PUT'],
// stacks
['name' => 'stack#index', 'url' => '/stacks/{boardId}', 'verb' => 'GET'],

View File

@@ -14,7 +14,7 @@
},
"require-dev": {
"roave/security-advisories": "dev-master",
"christophwurst/nextcloud": "^21@dev",
"christophwurst/nextcloud": "dev-master",
"phpunit/phpunit": "^9",
"nextcloud/coding-standard": "^1.0.0",
"symfony/event-dispatcher": "^4.0",

72
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "c3c765fb5379719b5ab8824e9fbb03f9",
"content-hash": "33c2fe0cccf29841e021001c6430310a",
"packages": [
{
"name": "cogpowered/finediff",
@@ -301,25 +301,29 @@
},
{
"name": "christophwurst/nextcloud",
"version": "v21.0.0",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/ChristophWurst/nextcloud_composer.git",
"reference": "41e1476b4aed5bce7371895054049eca353729c5"
"reference": "cd35b7f4519d9b1c836443ec5dcd973d7f0f9cdd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ChristophWurst/nextcloud_composer/zipball/41e1476b4aed5bce7371895054049eca353729c5",
"reference": "41e1476b4aed5bce7371895054049eca353729c5",
"url": "https://api.github.com/repos/ChristophWurst/nextcloud_composer/zipball/cd35b7f4519d9b1c836443ec5dcd973d7f0f9cdd",
"reference": "cd35b7f4519d9b1c836443ec5dcd973d7f0f9cdd",
"shasum": ""
},
"require": {
"php": "^7.3 || ~8.0.0"
"php": "^7.4 || ~8.0 || ~8.1",
"psr/container": "^1.0",
"psr/event-dispatcher": "^1.0",
"psr/log": "^1.1"
},
"default-branch": true,
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "21.0.0-dev"
"dev-master": "24.0.0-dev"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -335,9 +339,9 @@
"description": "Composer package containing Nextcloud's public API (classes, interfaces)",
"support": {
"issues": "https://github.com/ChristophWurst/nextcloud_composer/issues",
"source": "https://github.com/ChristophWurst/nextcloud_composer/tree/v21.0.0"
"source": "https://github.com/ChristophWurst/nextcloud_composer/tree/master"
},
"time": "2021-03-01T08:42:25+00:00"
"time": "2022-03-11T01:33:55+00:00"
},
{
"name": "composer/package-versions-deprecated",
@@ -2304,6 +2308,56 @@
},
"time": "2021-11-05T16:50:12+00:00"
},
{
"name": "psr/event-dispatcher",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/event-dispatcher.git",
"reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0",
"reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0",
"shasum": ""
},
"require": {
"php": ">=7.2.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\EventDispatcher\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Standard interfaces for event handling.",
"keywords": [
"events",
"psr",
"psr-14"
],
"support": {
"issues": "https://github.com/php-fig/event-dispatcher/issues",
"source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0"
},
"time": "2019-01-08T18:20:26+00:00"
},
{
"name": "psr/log",
"version": "1.1.4",

View File

@@ -16,6 +16,7 @@ Overall, Deck is easy to use. You can create boards, add users, share the Deck,
5. [Manage your board](#5-manage-your-board)
6. [Import boards](#6-import-boards)
7. [Search](#7-search)
8. [New owner for the deck entities](#8-new-owner-for-the-deck-entities)
### 1. Create my first board
In this example, we're going to create a board and share it with an other nextcloud user.
@@ -158,4 +159,22 @@ For example the search `project tag:ToDo assigned:alice assigned:bob` will retur
Other text tokens will be used to perform a case-insensitive search on the card title and description
In addition wuotes can be used to pass a query with spaces, e.g. `"Exact match with spaces"` or `title:"My card"`.
In addition, quotes can be used to pass a query with spaces, e.g. `"Exact match with spaces"` or `title:"My card"`.
### 8. New owner for the deck entities
You can transfer ownership of boards, cards, etc to a new user, using `occ` command `deck:transfer-ownership`
```bash
php occ deck:transfer-ownership previousOwner newOwner
```
The transfer will preserve card details linked to the old owner, which can also be remapped by using the `--remap` option on the occ command.
```bash
php occ deck:transfer-ownership --remap previousOwner newOwner
```
Individual boards can be transferred by adding the id of the board to the command:
```bash
php occ deck:transfer-ownership previousOwner newOwner 123
```

View File

@@ -31,7 +31,6 @@ use OCA\Deck\Db\Acl;
use OCA\Deck\Db\AclMapper;
use OCA\Deck\Db\Assignment;
use OCA\Deck\Db\Attachment;
use OCA\Deck\Db\AttachmentMapper;
use OCA\Deck\Db\Board;
use OCA\Deck\Db\BoardMapper;
use OCA\Deck\Db\Card;
@@ -50,12 +49,15 @@ use OCP\L10N\IFactory;
class ActivityManager {
public const DECK_NOAUTHOR_COMMENT_SYSTEM_ENFORCED = 'DECK_NOAUTHOR_COMMENT_SYSTEM_ENFORCED';
public const SUBJECT_PARAMS_MAX_LENGTH = 4000;
public const SHORTENED_DESCRIPTION_MAX_LENGTH = 2000;
private $manager;
private $userId;
private $permissionService;
private $boardMapper;
private $cardMapper;
private $attachmentMapper;
private $aclMapper;
private $stackMapper;
private $l10nFactory;
@@ -110,7 +112,6 @@ class ActivityManager {
BoardMapper $boardMapper,
CardMapper $cardMapper,
StackMapper $stackMapper,
AttachmentMapper $attachmentMapper,
AclMapper $aclMapper,
IFactory $l10nFactory,
$userId
@@ -120,7 +121,6 @@ class ActivityManager {
$this->boardMapper = $boardMapper;
$this->cardMapper = $cardMapper;
$this->stackMapper = $stackMapper;
$this->attachmentMapper = $attachmentMapper;
$this->aclMapper = $aclMapper;
$this->l10nFactory = $l10nFactory;
$this->userId = $userId;
@@ -249,19 +249,6 @@ class ActivityManager {
try {
$event = $this->createEvent($objectType, $entity, $subject, $additionalParams, $author);
if ($event !== null) {
$json = json_encode($event->getSubjectParameters());
if (mb_strlen($json) > 4000) {
$params = json_decode(json_encode($event->getSubjectParameters()), true);
$newContent = $params['after'];
unset($params['before'], $params['after'], $params['card']['description']);
$params['after'] = mb_substr($newContent, 0, 2000);
if (mb_strlen($newContent) > 2000) {
$params['after'] .= '...';
}
$event->setSubject($event->getSubject(), $params);
}
$this->sendToUsers($event);
}
} catch (\Exception $e) {
@@ -410,12 +397,31 @@ class ActivityManager {
$subjectParams['author'] = $author === null ? $this->userId : $author;
$subjectParams = array_merge($subjectParams, $additionalParams);
$json = json_encode($subjectParams);
if (mb_strlen($json) > self::SUBJECT_PARAMS_MAX_LENGTH) {
$params = json_decode(json_encode($subjectParams), true);
if ($subject === self::SUBJECT_CARD_UPDATE_DESCRIPTION && isset($params['after'])) {
$newContent = $params['after'];
unset($params['before'], $params['after'], $params['card']['description']);
$params['after'] = mb_substr($newContent, 0, self::SHORTENED_DESCRIPTION_MAX_LENGTH);
if (mb_strlen($newContent) > self::SHORTENED_DESCRIPTION_MAX_LENGTH) {
$params['after'] .= '...';
}
$subjectParams = $params;
} else {
throw new \Exception('Subject parameters too long');
}
}
$event = $this->manager->generateEvent();
$event->setApp('deck')
->setType($eventType)
->setAuthor($subjectParams['author'])
->setObject($objectType, (int)$object->getId(), $object->getTitle())
->setSubject($subject, array_merge($subjectParams, $additionalParams))
->setSubject($subject, $subjectParams)
->setTimestamp(time());
if ($message !== null) {

View File

@@ -0,0 +1,105 @@
<?php
namespace OCA\Deck\Command;
use OCA\Deck\Db\BoardMapper;
use OCA\Deck\Service\BoardService;
use OCA\Deck\Service\PermissionService;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
final class TransferOwnership extends Command {
protected $boardService;
protected $boardMapper;
protected $permissionService;
protected $questionHelper;
public function __construct(BoardService $boardService, BoardMapper $boardMapper, PermissionService $permissionService, QuestionHelper $questionHelper) {
parent::__construct();
$this->boardService = $boardService;
$this->boardMapper = $boardMapper;
$this->permissionService = $permissionService;
$this->questionHelper = $questionHelper;
}
protected function configure() {
$this
->setName('deck:transfer-ownership')
->setDescription('Change owner of deck boards')
->addArgument(
'owner',
InputArgument::REQUIRED,
'Owner uid'
)
->addArgument(
'newOwner',
InputArgument::REQUIRED,
'New owner uid'
)
->addArgument(
'boardId',
InputArgument::OPTIONAL,
'Single board ID'
)
->addOption(
'remap',
'r',
InputOption::VALUE_NONE,
'Reassign card details of the old owner to the new one'
)
;
}
protected function execute(InputInterface $input, OutputInterface $output): int {
$owner = $input->getArgument('owner');
$newOwner = $input->getArgument('newOwner');
$boardId = $input->getArgument('boardId');
$remapAssignment = $input->getOption('remap');
$this->boardService->setUserId($owner);
$this->permissionService->setUserId($owner);
try {
$board = $boardId ? $this->boardMapper->find($boardId) : null;
} catch (\Exception $e) {
$output->writeln("Could not find a board for the provided id.");
return 1;
}
if ($boardId !== null && $board->getOwner() !== $owner) {
$output->writeln("$owner is not the owner of the board $boardId (" . $board->getTitle() . ")");
return 1;
}
if ($boardId) {
$output->writeln("Transfer board " . $board->getTitle() . " from ". $board->getOwner() ." to $newOwner");
} else {
$output->writeln("Transfer all boards from $owner to $newOwner");
}
$question = new ConfirmationQuestion('Do you really want to continue? (y/n) ', false);
if (!$this->questionHelper->ask($input, $output, $question)) {
return 1;
}
if ($boardId) {
$this->boardService->transferBoardOwnership($boardId, $newOwner, $remapAssignment);
$output->writeln("<info>Board " . $board->getTitle() . " from ". $board->getOwner() ." transferred to $newOwner completed</info>");
return 0;
}
foreach ($this->boardService->transferOwnership($owner, $newOwner, $remapAssignment) as $board) {
$output->writeln(" - " . $board->getTitle() . " transferred");
}
$output->writeln("<info>All boards from $owner to $newOwner transferred</info>");
return 0;
}
}

View File

@@ -24,9 +24,12 @@
namespace OCA\Deck\Controller;
use OCA\Deck\Db\Acl;
use OCA\Deck\Db\Board;
use OCA\Deck\Service\BoardService;
use OCA\Deck\Service\PermissionService;
use OCP\AppFramework\ApiController;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\IRequest;
class BoardController extends ApiController {
@@ -150,9 +153,20 @@ class BoardController extends ApiController {
/**
* @NoAdminRequired
* @param $boardId
* @return \OCP\Deck\DB\Board
* @return Board
*/
public function clone($boardId) {
return $this->boardService->clone($boardId, $this->userId);
}
/**
* @NoAdminRequired
*/
public function transferOwner(int $boardId, string $newOwner): DataResponse {
if ($this->permissionService->userIsBoardOwner($boardId, $this->userId)) {
return new DataResponse($this->boardService->transferBoardOwnership($boardId, $newOwner), HTTP::STATUS_OK);
}
return new DataResponse([], HTTP::STATUS_UNAUTHORIZED);
}
}

View File

@@ -25,6 +25,7 @@ namespace OCA\Deck\Db;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
class AclMapper extends DeckMapper implements IPermissionMapper {
@@ -57,4 +58,16 @@ class AclMapper extends DeckMapper implements IPermissionMapper {
$sql = 'SELECT * from *PREFIX*deck_board_acl WHERE type = ? AND participant = ?';
return $this->findEntities($sql, [$type, $participant]);
}
/**
* @throws \OCP\DB\Exception
*/
public function deleteParticipantFromBoard(int $boardId, int $type, string $participant): void {
$qb = $this->db->getQueryBuilder();
$qb->delete('deck_board_acl')
->where($qb->expr()->eq('type', $qb->createNamedParameter($type, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq('participant', $qb->createNamedParameter($participant, IQueryBuilder::PARAM_STR)))
->andWhere($qb->expr()->eq('board_id', $qb->createNamedParameter($boardId, IQueryBuilder::PARAM_INT)));
$qb->executeStatement();
}
}

View File

@@ -29,6 +29,7 @@ use OCA\Deck\NotFoundException;
use OCA\Deck\Service\CirclesService;
use OCP\AppFramework\Db\Entity;
use OCP\AppFramework\Db\QBMapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\IGroupManager;
use OCP\IUserManager;
@@ -146,4 +147,39 @@ class AssignmentMapper extends QBMapper implements IPermissionMapper {
}
return null;
}
public function remapAssignedUser(int $boardId, string $userId, string $newUserId): void {
$subQuery = $this->db->getQueryBuilder();
$subQuery->selectAlias('a.id', 'id')
->from('deck_assigned_users', 'a')
->innerJoin('a', 'deck_cards', 'c', 'c.id = a.card_id')
->innerJoin('c', 'deck_stacks', 's', 's.id = c.stack_id')
->where($subQuery->expr()->eq('a.type', $subQuery->createNamedParameter(Assignment::TYPE_USER, IQueryBuilder::PARAM_INT)))
->andWhere($subQuery->expr()->eq('a.participant', $subQuery->createNamedParameter($userId, IQueryBuilder::PARAM_STR)))
->andWhere($subQuery->expr()->eq('s.board_id', $subQuery->createNamedParameter($boardId, IQueryBuilder::PARAM_INT)))
->setMaxResults(1000);
$qb = $this->db->getQueryBuilder();
$qb->update('deck_assigned_users')
->set('participant', $qb->createParameter('participant'))
->where($qb->expr()->in('id', $qb->createParameter('ids')));
$moreResults = true;
do {
$result = $subQuery->executeQuery();
$ids = array_map(function ($item) {
return $item['id'];
}, $result->fetchAll());
if (count($ids) === 0 || $result->rowCount() === 0) {
$moreResults = false;
}
$qb->setParameter('participant', $newUserId, IQueryBuilder::PARAM_STR);
$qb->setParameter('ids', $ids, IQueryBuilder::PARAM_INT_ARRAY);
$qb->executeStatement();
} while ($moreResults === true);
$result->closeCursor();
}
}

View File

@@ -475,4 +475,34 @@ class BoardMapper extends QBMapper implements IPermissionMapper {
return null;
});
}
/**
* @throws \OCP\DB\Exception
*/
public function transferOwnership(string $ownerId, string $newOwnerId, $boardId = null): void {
$qb = $this->db->getQueryBuilder();
$qb->update('deck_boards')
->set('owner', $qb->createNamedParameter($newOwnerId, IQueryBuilder::PARAM_STR))
->where($qb->expr()->eq('owner', $qb->createNamedParameter($ownerId, IQueryBuilder::PARAM_STR)));
if ($boardId !== null) {
$qb->andWhere($qb->expr()->eq('id', $qb->createNamedParameter($boardId, IQueryBuilder::PARAM_INT)));
}
$qb->executeStatement();
}
/**
* Reset cache for a given board or a given user
*/
public function flushCache(?int $boardId = null, ?string $userId = null) {
if ($boardId) {
unset($this->boardCache[$boardId]);
} else {
$this->boardCache = null;
}
if ($userId) {
unset($this->userBoardCache[$userId]);
} else {
$this->userBoardCache = null;
}
}
}

View File

@@ -586,4 +586,47 @@ class CardMapper extends QBMapper implements IPermissionMapper {
return null;
});
}
public function transferOwnership(string $ownerId, string $newOwnerId, int $boardId = null): void {
$params = [
'owner' => $ownerId,
'newOwner' => $newOwnerId
];
$sql = "UPDATE `*PREFIX*{$this->tableName}` SET `owner` = :newOwner WHERE `owner` = :owner";
$stmt = $this->db->executeQuery($sql, $params);
$stmt->closeCursor();
}
public function remapCardOwner(int $boardId, string $userId, string $newUserId): void {
$subQuery = $this->db->getQueryBuilder();
$subQuery->selectAlias('c.id', 'id')
->from('deck_cards', 'c')
->innerJoin('c', 'deck_stacks', 's', 's.id = c.stack_id')
->where($subQuery->expr()->eq('c.owner', $subQuery->createNamedParameter($userId, IQueryBuilder::PARAM_STR)))
->andWhere($subQuery->expr()->eq('s.board_id', $subQuery->createNamedParameter($boardId, IQueryBuilder::PARAM_INT)))
->setMaxResults(1000);
$qb = $this->db->getQueryBuilder();
$qb->update('deck_cards')
->set('owner', $qb->createParameter('owner'))
->where($qb->expr()->in('id', $qb->createParameter('ids')));
$moreResults = true;
do {
$result = $subQuery->executeQuery();
$ids = array_map(function ($item) {
return $item['id'];
}, $result->fetchAll());
if (count($ids) === 0 || $result->rowCount() === 0) {
$moreResults = false;
}
$qb->setParameter('owner', $newUserId, IQueryBuilder::PARAM_STR);
$qb->setParameter('ids', $ids, IQueryBuilder::PARAM_INT_ARRAY);
$qb->executeStatement();
} while ($moreResults === true);
$result->closeCursor();
}
}

View File

@@ -24,12 +24,14 @@
namespace OCA\Deck\Service;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use OCA\Deck\Activity\ActivityManager;
use OCA\Deck\Activity\ChangeSet;
use OCA\Deck\AppInfo\Application;
use OCA\Deck\Db\Acl;
use OCA\Deck\Db\AclMapper;
use OCA\Deck\Db\AssignmentMapper;
use OCA\Deck\Db\CardMapper;
use OCA\Deck\Db\ChangeHelper;
use OCA\Deck\Db\IPermissionMapper;
use OCA\Deck\Db\Label;
@@ -69,6 +71,8 @@ class BoardService {
private $activityManager;
private $eventDispatcher;
private $changeHelper;
private $cardMapper;
private $boardsCache = null;
private $urlGenerator;
@@ -83,6 +87,7 @@ class BoardService {
PermissionService $permissionService,
NotificationHelper $notificationHelper,
AssignmentMapper $assignedUsersMapper,
CardMapper $cardMapper,
IUserManager $userManager,
IGroupManager $groupManager,
ActivityManager $activityManager,
@@ -107,6 +112,7 @@ class BoardService {
$this->changeHelper = $changeHelper;
$this->userId = $userId;
$this->urlGenerator = $urlGenerator;
$this->cardMapper = $cardMapper;
}
/**
@@ -518,11 +524,14 @@ class BoardService {
$acl->setPermissionManage($manage);
$newAcl = $this->aclMapper->insert($acl);
$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_BOARD, $newAcl, ActivityManager::SUBJECT_BOARD_SHARE);
$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_BOARD, $newAcl, ActivityManager::SUBJECT_BOARD_SHARE, [], $this->userId);
$this->notificationHelper->sendBoardShared((int)$boardId, $acl);
$this->boardMapper->mapAcl($newAcl);
$this->changeHelper->boardChanged($boardId);
$board = $this->boardMapper->find($boardId);
$this->clearBoardFromCache($board);
// TODO: use the dispatched event for this
try {
$resourceProvider = \OC::$server->query(\OCA\Deck\Collaboration\Resources\ResourceProvider::class);
@@ -673,6 +682,43 @@ class BoardService {
return $newBoard;
}
public function transferBoardOwnership(int $boardId, string $newOwner, bool $changeContent = false): Board {
\OC::$server->getDatabaseConnection()->beginTransaction();
try {
$board = $this->boardMapper->find($boardId);
$previousOwner = $board->getOwner();
$this->clearBoardFromCache($board);
$this->aclMapper->deleteParticipantFromBoard($boardId, Acl::PERMISSION_TYPE_USER, $newOwner);
if (!$changeContent) {
try {
$this->addAcl($boardId, Acl::PERMISSION_TYPE_USER, $previousOwner, true, true, true);
} catch (UniqueConstraintViolationException $e) {
}
}
$this->boardMapper->transferOwnership($previousOwner, $newOwner, $boardId);
// Optionally also change user assignments and card owner information
if ($changeContent) {
$this->assignedUsersMapper->remapAssignedUser($boardId, $previousOwner, $newOwner);
$this->cardMapper->remapCardOwner($boardId, $previousOwner, $newOwner);
}
\OC::$server->getDatabaseConnection()->commit();
return $this->boardMapper->find($boardId);
} catch (\Throwable $e) {
\OC::$server->getDatabaseConnection()->rollBack();
throw $e;
}
}
public function transferOwnership(string $owner, string $newOwner, bool $changeContent = false): \Generator {
$boards = $this->boardMapper->findAllByUser($owner);
foreach ($boards as $board) {
if ($board->getOwner() === $owner) {
yield $this->transferBoardOwnership($board->getId(), $newOwner, $changeContent);
}
}
}
private function enrichWithStacks($board, $since = -1) {
$stacks = $this->stackMapper->findAll($board->getId(), null, null, $since);
@@ -704,4 +750,19 @@ class BoardService {
public function getBoardUrl($endpoint) {
return $this->urlGenerator->linkToRouteAbsolute('deck.page.index') . '#' . $endpoint;
}
private function clearBoardsCache() {
$this->boardsCache = null;
}
/**
* Clean a given board data from the Cache
*/
private function clearBoardFromCache(Board $board) {
$boardId = $board->getId();
$boardOwnerId = $board->getOwner();
$this->boardMapper->flushCache($boardId, $boardOwnerId);
unset($this->boardsCache[$boardId]);
}
}

View File

@@ -241,6 +241,7 @@ class PermissionService {
if (array_key_exists((string) $boardId, $this->users) && !$refresh) {
return $this->users[(string) $boardId];
}
try {
$board = $this->boardMapper->find($boardId);
} catch (DoesNotExistException $e) {
@@ -332,4 +333,13 @@ class PermissionService {
}
return $groups;
}
/**
* Set a different user than the current one, e.g. when no user is available in occ
*
* @param string $userId
*/
public function setUserId(string $userId): void {
$this->userId = $userId;
}
}

View File

@@ -77,6 +77,7 @@ import Controls from '../Controls'
import Stack from './Stack'
import { EmptyContent } from '@nextcloud/vue'
import GlobalSearchResults from '../search/GlobalSearchResults'
import { showError } from '../../helpers/errors'
export default {
name: 'Board',
@@ -139,6 +140,7 @@ export default {
await this.$store.dispatch('loadStacks', this.id)
} catch (e) {
console.error(e)
showError(e)
}
this.loading = false
},

View File

@@ -53,6 +53,9 @@
<ActionCheckbox v-if="canManage" :checked="acl.permissionManage" @change="clickManageAcl(acl)">
{{ t('deck', 'Can manage') }}
</ActionCheckbox>
<ActionCheckbox v-if="acl.type === 0 && isCurrentUser(board.owner.uid)" :checked="acl.owner" @change="clickTransferOwner(acl.participant.uid)">
{{ t('deck', 'Owner') }}
</ActionCheckbox>
<ActionButton v-if="canManage" icon="icon-delete" @click="clickDeleteAcl(acl)">
{{ t('deck', 'Delete') }}
</ActionButton>
@@ -72,7 +75,7 @@ import { Avatar, Multiselect, Actions, ActionButton, ActionCheckbox } from '@nex
import { CollectionList } from 'nextcloud-vue-collections'
import { mapGetters, mapState } from 'vuex'
import { getCurrentUser } from '@nextcloud/auth'
import { showError } from '@nextcloud/dialogs'
import { showError, showSuccess } from '@nextcloud/dialogs'
import debounce from 'lodash/debounce'
export default {
@@ -97,6 +100,7 @@ export default {
isSearching: false,
addAcl: null,
addAclForAPI: null,
newOwner: null,
}
},
computed: {
@@ -194,6 +198,38 @@ export default {
clickDeleteAcl(acl) {
this.$store.dispatch('deleteAclFromCurrentBoard', acl)
},
clickTransferOwner(newOwner) {
OC.dialogs.confirmDestructive(
t('deck', 'Are you sure you want to transfer the board {title} for {user}?', { title: this.board.title, user: newOwner }),
t('deck', 'Transfer the board.'),
{
type: OC.dialogs.YES_NO_BUTTONS,
confirm: t('deck', 'Transfer'),
confirmClasses: 'error',
cancel: t('deck', 'Cancel'),
},
async (result) => {
if (result) {
try {
this.isLoading = true
await this.$store.dispatch('transferOwnership', {
boardId: this.board.id,
newOwner
})
const successMessage = t('deck', 'Transfer the board for {user} successfully', { user: newOwner })
showSuccess(successMessage)
this.$router.push({ name: 'main' })
} catch (e) {
const errorMessage = t('deck', 'Failed to transfer the board for {user}', { user: newOwner.user })
showError(errorMessage)
} finally {
this.isLoading = false
}
}
},
true
)
},
},
}
</script>

View File

@@ -78,7 +78,7 @@ export default {
},
computed: {
boardsSorted() {
return [...this.boards].sort((a, b) => (a.title < b.title) ? -1 : 1)
return [...this.boards].sort((a, b) => a.title.localeCompare(b.title))
},
collapsible() {
return this.boards.length > 0

27
src/helpers/errors.js Normal file
View File

@@ -0,0 +1,27 @@
import { showError as errorDialog } from '@nextcloud/dialogs'
const showAxiosError = err => {
const response = err?.response || {}
const message = response?.data.message
if (message) {
errorDialog(message)
return
}
errorDialog(err.message)
}
const showError = err => {
// axios error
if (err.response) {
showAxiosError(err)
return
}
errorDialog(err.message)
}
export {
showError,
}

View File

@@ -26,7 +26,7 @@ import { loadState } from '@nextcloud/initial-state'
import Vue from 'vue'
import Vuex from 'vuex'
import axios from '@nextcloud/axios'
import { generateOcsUrl } from '@nextcloud/router'
import { generateOcsUrl, generateUrl } from '@nextcloud/router'
import { BoardApi } from '../services/BoardApi'
import actions from './actions'
import stack from './stack'
@@ -486,5 +486,11 @@ export default new Vuex.Store({
dispatch('loadBoardById', acl.boardId)
})
},
async transferOwnership({ commit }, { boardId, owner, newOwner }) {
await axios.put(generateUrl(`apps/deck/boards/${boardId}/transferOwner`), {
owner,
newOwner,
})
},
},
})

View File

@@ -0,0 +1,314 @@
<?php
namespace OCA\Deck\Service;
use OCA\Deck\Db\Acl;
use OCA\Deck\Db\Assignment;
use OCA\Deck\Db\AssignmentMapper;
use OCA\Deck\Db\Board;
use OCA\Deck\Db\Card;
/**
* @group DB
* @coversDefaultClass \OCA\Deck\Service\BoardService
*/
class TransferOwnershipTest extends \Test\TestCase {
private const TEST_USER_1 = 'test-share-user1';
private const TEST_USER_2 = 'test-user2';
private const TEST_USER_3 = 'test-user3';
private const TEST_GROUP = 'test-share-user1';
/** @var BoardService */
protected $boardService;
/** @var CardService */
protected $cardService;
/** @var StackService */
protected $stackService;
/** @var AssignmentMapper */
protected $assignmentMapper;
/** @var AssignmentService */
private $assignmentService;
/** @var Board */
private $board;
private $cards;
private $stacks;
public static function setUpBeforeClass(): void {
parent::setUpBeforeClass();
$backend = new \Test\Util\User\Dummy();
\OC_User::useBackend($backend);
\OC::$server->getUserManager()->registerBackend($backend);
$backend->createUser(self::TEST_USER_1, self::TEST_USER_1);
$backend->createUser(self::TEST_USER_2, self::TEST_USER_2);
$backend->createUser(self::TEST_USER_3, self::TEST_USER_3);
// create group
$groupBackend = new \Test\Util\Group\Dummy();
$groupBackend->createGroup(self::TEST_GROUP);
$groupBackend->addToGroup(self::TEST_USER_1, self::TEST_GROUP);
\OC::$server->getGroupManager()->addBackend($groupBackend);
}
public function setUp(): void {
parent::setUp();
\OC::$server->getUserSession()->login(self::TEST_USER_1, self::TEST_USER_1);
$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->assignmentMapper = \OC::$server->query(AssignmentMapper::class);
$this->createBoardWithExampleData();
}
public function createBoardWithExampleData() {
$stacks = [];
$board = $this->boardService->create('Test', self::TEST_USER_1, '000000');
$id = $board->getId();
$this->boardService->addAcl($id, Acl::PERMISSION_TYPE_GROUP, self::TEST_GROUP, true, true, true);
$this->boardService->addAcl($id, Acl::PERMISSION_TYPE_USER, self::TEST_USER_3, false, true, false);
$stacks[] = $this->stackService->create('Stack A', $id, 1);
$stacks[] = $this->stackService->create('Stack B', $id, 1);
$stacks[] = $this->stackService->create('Stack C', $id, 1);
$cards[] = $this->cardService->create('Card 1', $stacks[0]->getId(), 'text', 0, self::TEST_USER_1);
$cards[] = $this->cardService->create('Card 2', $stacks[0]->getId(), 'text', 0, self::TEST_USER_1);
$this->assignmentService->assignUser($cards[0]->getId(), self::TEST_USER_1);
$this->board = $board;
$this->cards = $cards;
$this->stacks = $stacks;
}
/**
* @covers ::transferOwnership
*/
public function testTransferBoardOwnership() {
iterator_to_array($this->boardService->transferOwnership(self::TEST_USER_1, self::TEST_USER_2));
$board = $this->boardService->find($this->board->getId());
$boardOwner = $board->getOwner();
$this->assertEquals(self::TEST_USER_2, $boardOwner);
}
/**
* @covers ::transferOwnership
*/
public function testTransferBoardOwnershipWithData() {
iterator_to_array($this->boardService->transferOwnership(self::TEST_USER_1, self::TEST_USER_2));
$board = $this->boardService->find($this->board->getId());
$boardOwner = $board->getOwner();
$this->assertEquals(self::TEST_USER_2, $boardOwner);
$cards = $this->cards;
$newOwnerOwnsTheCards = (bool)array_product(array_filter($cards, function (Card $card) {
$cardUpdated = $this->cardService->find($card->getId());
return $cardUpdated->getOwner() === self::TEST_USER_2;
}));
$this->assertTrue($newOwnerOwnsTheCards);
}
/**
* @covers ::transferOwnership
*/
public function testTransferACLOwnership() {
iterator_to_array($this->boardService->transferOwnership(self::TEST_USER_1, self::TEST_USER_2, true));
$board = $this->boardService->find($this->board->getId());
$acl = $board->getAcl();
$this->assertBoardDoesNotHaveAclUser($board, self::TEST_USER_1);
}
/**
* @covers ::transferOwnership
*/
public function testTransferACLOwnershipPreserveOwner() {
iterator_to_array($this->boardService->transferOwnership(self::TEST_USER_1, self::TEST_USER_2, false));
$board = $this->boardService->find($this->board->getId());
$acl = $board->getAcl();
$this->assertBoardHasAclUser($board, self::TEST_USER_1);
}
/**
* @covers ::transferOwnership
*/
public function testNoTransferAclOwnershipIfGroupType() {
iterator_to_array($this->boardService->transferOwnership(self::TEST_USER_1, self::TEST_USER_2));
$board = $this->boardService->find($this->board->getId());
$acl = $board->getAcl();
$isGroupInAcl = (bool)array_filter($acl, function ($item) {
return $item->getParticipant() === self::TEST_GROUP && $item->getType() === Acl::PERMISSION_TYPE_GROUP;
});
$this->assertTrue($isGroupInAcl);
}
/**
* @covers ::transferOwnership
*/
public function testTransferCardOwnership() {
iterator_to_array($this->boardService->transferOwnership(self::TEST_USER_1, self::TEST_USER_2, true));
$card = $this->cardService->find($this->cards[0]->getId());
$cardOwner = $card->getOwner();
$this->assertEquals(self::TEST_USER_2, $cardOwner);
}
/**
* @covers ::transferOwnership
*/
public function testTransferPreserveCardOwnership() {
iterator_to_array($this->boardService->transferOwnership(self::TEST_USER_1, self::TEST_USER_2, false));
$card = $this->cardService->find($this->cards[0]->getId());
$cardOwner = $card->getOwner();
$this->assertEquals(self::TEST_USER_1, $cardOwner);
}
/**
* @covers ::transferOwnership
*/
public function testReassignCardToNewOwner() {
iterator_to_array($this->boardService->transferOwnership(self::TEST_USER_1, self::TEST_USER_2, true));
$participantsUIDs = array_map(function ($user) {
return $user->getParticipant();
}, $this->assignmentMapper->findAll($this->cards[0]->getId()));
$this->assertContains(self::TEST_USER_2, $participantsUIDs);
$this->assertNotContains(self::TEST_USER_1, $participantsUIDs);
}
/**
* @covers ::transferOwnership
*/
public function testNoReassignCardToNewOwner() {
iterator_to_array($this->boardService->transferOwnership(self::TEST_USER_1, self::TEST_USER_2, false));
$participantsUIDs = array_map(function ($user) {
return $user->getParticipant();
}, $this->assignmentMapper->findAll($this->cards[0]->getId()));
$this->assertContains(self::TEST_USER_1, $participantsUIDs);
$this->assertNotContains(self::TEST_USER_2, $participantsUIDs);
}
/**
* @covers ::transferOwnership
*/
public function testReassignCardToNewParticipantOnlyIfParticipantHasUserType() {
$this->assignmentService->assignUser($this->cards[1]->getId(), self::TEST_USER_1, Assignment::TYPE_GROUP);
iterator_to_array($this->boardService->transferOwnership(self::TEST_USER_1, self::TEST_USER_2));
$participantsUIDs = array_map(function ($user) {
return $user->getParticipant();
}, $this->assignmentMapper->findAll($this->cards[1]->getId()));
$this->assertContains(self::TEST_USER_1, $participantsUIDs);
$this->assertNotContains(self::TEST_USER_2, $participantsUIDs);
}
/**
* @covers ::transferOwnership
*/
public function testTargetAlreadyParticipantOfBoard() {
$this->expectNotToPerformAssertions();
iterator_to_array($this->boardService->transferOwnership(self::TEST_USER_1, self::TEST_USER_3));
}
private function assertBoardHasAclUser($board, $userId) {
$hasUser = (bool)array_filter($board->getAcl(), function ($item) use ($userId) {
return $item->getParticipant() === $userId && $item->getType() === Acl::PERMISSION_TYPE_USER;
});
self::assertTrue($hasUser, 'user ' . $userId . ' should be in the board acl list');
}
private function assertBoardDoesNotHaveAclUser($board, $userId) {
$hasUser = (bool)array_filter($board->getAcl(), function ($item) use ($userId) {
return $item->getParticipant() === $userId && $item->getType() === Acl::PERMISSION_TYPE_USER;
});
self::assertFalse($hasUser, 'user ' . $userId . ' should not be in the board acl list');
}
/**
* @covers ::transferOwnership
*/
public function testDontRemoveOldOwnerFromAcl() {
iterator_to_array($this->boardService->transferOwnership(self::TEST_USER_1, self::TEST_USER_2));
$board = $this->boardService->find($this->board->getId());
$this->assertBoardDoesNotHaveAclUser($board, self::TEST_USER_2);
$this->assertBoardHasAclUser($board, self::TEST_USER_3);
$this->assertBoardHasAclUser($board, self::TEST_USER_1);
}
/**
* @covers ::transferOwnership
*/
public function testRemoveOldOwnerFromAclForChange() {
iterator_to_array($this->boardService->transferOwnership(self::TEST_USER_1, self::TEST_USER_2, true));
$board = $this->boardService->find($this->board->getId());
$this->assertBoardDoesNotHaveAclUser($board, self::TEST_USER_2);
$this->assertBoardHasAclUser($board, self::TEST_USER_3);
$this->assertBoardDoesNotHaveAclUser($board, self::TEST_USER_1);
}
/**
* @covers ::transferOwnership
*/
public function testMergePermissions() {
$this->boardService->addAcl($this->board->getId(), Acl::PERMISSION_TYPE_USER, self::TEST_USER_2, true, false, true);
iterator_to_array($this->boardService->transferOwnership(self::TEST_USER_1, self::TEST_USER_3));
$board = $this->boardService->find($this->board->getId());
$acl = $board->getAcl();
$isMerged = (bool)array_filter($acl, function ($item) {
return $item->getParticipant() === self::TEST_USER_1
&& $item->getType() === Acl::PERMISSION_TYPE_USER
&& $item->getPermission(Acl::PERMISSION_EDIT)
&& $item->getPermission(Acl::PERMISSION_SHARE)
&& $item->getPermission(Acl::PERMISSION_MANAGE);
});
$this->assertTrue($isMerged);
}
/**
* @covers ::transferOwnership
*/
public function testTargetAlreadyParticipantOfCard() {
$this->expectNotToPerformAssertions();
$this->assignmentService->assignUser($this->cards[0]->getId(), self::TEST_USER_3, Assignment::TYPE_USER);
iterator_to_array($this->boardService->transferOwnership(self::TEST_USER_1, self::TEST_USER_3));
}
/**
* @covers ::transferOwnership
*/
public function testTransferSingleBoardAssignment() {
// Arrange separate board next to the one being transferred
$board = $this->boardService->create('Test 2', self::TEST_USER_1, '000000');
$id = $board->getId();
$this->boardService->addAcl($id, Acl::PERMISSION_TYPE_USER, self::TEST_USER_1, true, true, true);
$this->boardService->addAcl($id, Acl::PERMISSION_TYPE_GROUP, self::TEST_GROUP, true, true, true);
$this->boardService->addAcl($id, Acl::PERMISSION_TYPE_USER, self::TEST_USER_3, false, true, false);
$stacks[] = $this->stackService->create('Stack A', $id, 1);
$stacks[] = $this->stackService->create('Stack B', $id, 1);
$stacks[] = $this->stackService->create('Stack C', $id, 1);
$cards[] = $this->cardService->create('Card 1', $stacks[0]->getId(), 'text', 0, self::TEST_USER_1);
$cards[] = $this->cardService->create('Card 2', $stacks[0]->getId(), 'text', 0, self::TEST_USER_1);
$this->assignmentService->assignUser($cards[0]->getId(), self::TEST_USER_1);
// Act
$this->boardService->transferBoardOwnership($this->board->getId(), self::TEST_USER_2, true);
// Assert that the selected board was transferred
$card = $this->cardService->find($this->cards[0]->getId());
$this->assertEquals(self::TEST_USER_2, $card->getOwner());
$participantsUIDs = array_map(function ($assignment) {
return $assignment->getParticipant();
}, $this->assignmentMapper->findAll($this->cards[0]->getId()));
$this->assertContains(self::TEST_USER_2, $participantsUIDs);
$this->assertNotContains(self::TEST_USER_1, $participantsUIDs);
// Assert that other board remained unchanged
$card = $this->cardService->find($cards[0]->getId());
$this->assertEquals(self::TEST_USER_1, $card->getOwner());
$participantsUIDs = array_map(function ($assignment) {
return $assignment->getParticipant();
}, $this->assignmentMapper->findAll($cards[0]->getId()));
$this->assertContains(self::TEST_USER_1, $participantsUIDs);
$this->assertNotContains(self::TEST_USER_2, $participantsUIDs);
}
public function tearDown(): void {
$this->boardService->deleteForce($this->board->getId());
parent::tearDown();
}
}

View File

@@ -26,7 +26,7 @@ composer dump-autoload
if [ -z "$EXECUTOR_NUMBER" ]; then
EXECUTOR_NUMBER=0
fi
PORT=$((8080 + $EXECUTOR_NUMBER))
PORT=$((9090 + $EXECUTOR_NUMBER))
echo $PORT
php -S localhost:$PORT -t $OC_PATH &
PHPPID=$!

View File

@@ -26,7 +26,6 @@ namespace OCA\Deck\Activity;
use OCA\Deck\Db\AclMapper;
use OCA\Deck\Db\Assignment;
use OCA\Deck\Db\Attachment;
use OCA\Deck\Db\AttachmentMapper;
use OCA\Deck\Db\Board;
use OCA\Deck\Db\BoardMapper;
use OCA\Deck\Db\Card;
@@ -41,7 +40,7 @@ use OCP\IL10N;
use OCP\IUser;
use OCP\L10N\IFactory;
use PHPUnit\Framework\TestCase;
use PHPUnit_Framework_MockObject_MockObject as MockObject;
use PHPUnit\Framework\MockObject\MockObject;
class ActivityManagerTest extends TestCase {
@@ -57,8 +56,6 @@ class ActivityManagerTest extends TestCase {
private $cardMapper;
/** @var StackMapper|MockObject */
private $stackMapper;
/** @var AttachmentMapper|MockObject */
private $attachmentMapper;
/** @var AclMapper|MockObject */
private $aclMapper;
/** @var IFactory|MockObject */
@@ -74,7 +71,6 @@ class ActivityManagerTest extends TestCase {
$this->boardMapper = $this->createMock(BoardMapper::class);
$this->cardMapper = $this->createMock(CardMapper::class);
$this->stackMapper = $this->createMock(StackMapper::class);
$this->attachmentMapper = $this->createMock(AttachmentMapper::class);
$this->aclMapper = $this->createMock(AclMapper::class);
$this->l10nFactory = $this->createMock(IFactory::class);
$this->l10n = $this->createMock(IL10N::class);
@@ -84,7 +80,6 @@ class ActivityManagerTest extends TestCase {
$this->boardMapper,
$this->cardMapper,
$this->stackMapper,
$this->attachmentMapper,
$this->aclMapper,
$this->l10nFactory,
$this->userId
@@ -93,7 +88,7 @@ class ActivityManagerTest extends TestCase {
public function testGetActivityFormatOwn() {
$managerClass = new \ReflectionClass(ActivityManager::class);
$this->l10n->expects($this->any())
$this->l10n->expects(self::any())
->method('t')
->will($this->returnCallback(function ($s) {
return $s;
@@ -122,22 +117,29 @@ class ActivityManagerTest extends TestCase {
}
}
private function expectEventCreation($subject, $subjectParams) {
$event = $this->createMock(IEvent::class);
$this->manager->expects(self::once())
->method('generateEvent')
->willReturn($event);
$event->expects(self::once())->method('setApp')->willReturn($event);
$event->expects(self::once())->method('setType')->willReturn($event);
$event->expects(self::once())->method('setAuthor')->willReturn($event);
$event->expects(self::once())->method('setObject')->willReturn($event);
$event->expects(self::once())->method('setSubject')->with($subject, $subjectParams)->willReturn($event);
$event->expects(self::once())->method('setTimestamp')->willReturn($event);
return $event;
}
public function testCreateEvent() {
$board = new Board();
$board->setTitle('');
$this->boardMapper->expects($this->once())
$this->boardMapper->expects(self::once())
->method('find')
->willReturn($board);
$event = $this->createMock(IEvent::class);
$this->manager->expects($this->once())
->method('generateEvent')
->willReturn($event);
$event->expects($this->once())->method('setApp')->willReturn($event);
$event->expects($this->once())->method('setType')->willReturn($event);
$event->expects($this->once())->method('setAuthor')->willReturn($event);
$event->expects($this->once())->method('setObject')->willReturn($event);
$event->expects($this->once())->method('setSubject')->willReturn($event);
$event->expects($this->once())->method('setTimestamp')->willReturn($event);
$event = $this->expectEventCreation(ActivityManager::SUBJECT_BOARD_CREATE, [
'author' => 'admin'
]);
$actual = $this->invokePrivate($this->activityManager, 'createEvent', [
ActivityManager::DECK_OBJECT_BOARD,
$board,
@@ -146,6 +148,133 @@ class ActivityManagerTest extends TestCase {
$this->assertEquals($event, $actual);
}
public function testCreateEventDescription() {
$board = new Board();
$board->setTitle('');
$this->boardMapper->expects(self::once())
->method('find')
->willReturn($board);
$card = Card::fromRow([
'id' => 123,
'title' => 'My card',
'description' => str_repeat('A', 1000),
]);
$this->cardMapper->expects(self::any())
->method('find')
->willReturn($card);
$stack = Stack::fromRow([]);
$this->stackMapper->expects(self::any())
->method('find')
->willReturn($stack);
$expectedCard = $card->jsonSerialize();
unset($expectedCard['description']);
$event = $this->expectEventCreation(ActivityManager::SUBJECT_CARD_UPDATE_DESCRIPTION, [
'card' => $expectedCard,
'stack' => $stack->jsonSerialize(),
'board' => $board->jsonSerialize(),
'diff' => true,
'author' => 'admin',
'after' => str_repeat('C', 2000),
]);
$actual = $this->invokePrivate($this->activityManager, 'createEvent', [
ActivityManager::DECK_OBJECT_CARD,
$card,
ActivityManager::SUBJECT_CARD_UPDATE_DESCRIPTION,
[
'before' => str_repeat('B', 2000),
'after' => str_repeat('C', 2000)
],
]);
$this->assertEquals($event, $actual);
}
public function testCreateEventLongDescription() {
$board = new Board();
$board->setTitle('');
$this->boardMapper->expects(self::once())
->method('find')
->willReturn($board);
$card = new Card();
$card->setDescription(str_repeat('A', 5000));
$card->setTitle('My card');
$card->setId(123);
$this->cardMapper->expects(self::any())
->method('find')
->willReturn($card);
$stack = new Stack();
$this->stackMapper->expects(self::any())
->method('find')
->willReturn($stack);
$expectedCard = $card->jsonSerialize();
unset($expectedCard['description']);
$event = $this->expectEventCreation(ActivityManager::SUBJECT_CARD_UPDATE_DESCRIPTION, [
'card' => $expectedCard,
'stack' => $stack->jsonSerialize(),
'board' => $board->jsonSerialize(),
'diff' => true,
'author' => 'admin',
'after' => str_repeat('C', 2000) . '...',
]);
$actual = $this->invokePrivate($this->activityManager, 'createEvent', [
ActivityManager::DECK_OBJECT_CARD,
$card,
ActivityManager::SUBJECT_CARD_UPDATE_DESCRIPTION,
[
'before' => str_repeat('B', 5000),
'after' => str_repeat('C', 5000)
],
]);
$this->assertEquals($event, $actual);
}
public function testCreateEventLabel() {
$board = Board::fromRow([
'title' => 'My board'
]);
$this->boardMapper->expects(self::once())
->method('find')
->willReturn($board);
$card = Card::fromParams([]);
$card->setDescription(str_repeat('A', 5000));
$card->setTitle('My card');
$card->setId(123);
$this->cardMapper->expects(self::any())
->method('find')
->willReturn($card);
$stack = Stack::fromParams([]);
$this->stackMapper->expects(self::any())
->method('find')
->willReturn($stack);
$event = $this->expectEventCreation(ActivityManager::SUBJECT_CARD_UPDATE_TITLE, [
'card' => [
'id' => 123,
'title' => 'My card',
'archived' => false,
],
'stack' => $stack,
'board' => $board,
'author' => 'admin',
]);
$actual = $this->invokePrivate($this->activityManager, 'createEvent', [
ActivityManager::DECK_OBJECT_CARD,
$card,
ActivityManager::SUBJECT_CARD_UPDATE_TITLE
]);
$this->assertEquals($event, $actual);
}
public function dataSendToUsers() {
return [
[ActivityManager::DECK_OBJECT_BOARD],
@@ -155,7 +284,7 @@ class ActivityManagerTest extends TestCase {
private function mockUser($uid) {
$user = $this->createMock(IUser::class);
$user->expects($this->any())
$user->expects(self::any())
->method('getUID')
->willReturn($uid);
return $user;
@@ -169,18 +298,21 @@ class ActivityManagerTest extends TestCase {
$this->mockUser('user2'),
];
$event = $this->createMock(IEvent::class);
$event->expects($this->at(0))
$event->expects(self::once())
->method('getObjectType')
->willReturn($objectType);
$event->expects($this->at(0))
$event->expects(self::once())
->method('getObjectId')
->willReturn(1);
$event->expects($this->at(2))
$event->expects(self::exactly(2))
->method('setAffectedUser')
->with('user1');
$event->expects($this->at(3))
->method('setAffectedUser')
->with('user2');
->withConsecutive(
['user1'],
['user2'],
)
->willReturnSelf();
$mapper = null;
switch ($objectType) {
case ActivityManager::DECK_OBJECT_BOARD:
@@ -190,16 +322,14 @@ class ActivityManagerTest extends TestCase {
$mapper = $this->cardMapper;
break;
}
$mapper->expects($this->once())
$mapper->expects(self::once())
->method('findBoardId')
->willReturn(123);
$this->permissionService->expects($this->once())
$this->permissionService->expects(self::once())
->method('findUsers')
->willReturn($users);
$this->manager->expects($this->at(0))
->method('publish')
->with($event);
$this->manager->expects($this->at(1))
$this->manager->expects(self::exactly(2))
->method('publish')
->with($event);
$this->invokePrivate($this->activityManager, 'sendToUsers', [$event]);
@@ -243,14 +373,14 @@ class ActivityManagerTest extends TestCase {
$card->setId(3);
$expected = null;
if ($objectType === ActivityManager::DECK_OBJECT_BOARD) {
$this->boardMapper->expects($this->once())
$this->boardMapper->expects(self::once())
->method('find')
->with(1)
->willReturn($board);
$expected = $board;
}
if ($objectType === ActivityManager::DECK_OBJECT_CARD) {
$this->cardMapper->expects($this->once())
$this->cardMapper->expects(self::once())
->method('find')
->with(3)
->willReturn($card);
@@ -266,11 +396,11 @@ class ActivityManagerTest extends TestCase {
$stack->setBoardId(999);
$board = new Board();
$board->setId(999);
$this->stackMapper->expects($this->once())
$this->stackMapper->expects(self::once())
->method('find')
->with(123)
->willReturn($stack);
$this->boardMapper->expects($this->once())->method('find')
$this->boardMapper->expects(self::once())->method('find')
->with(999)
->willReturn($board);
$this->assertEquals([
@@ -289,15 +419,15 @@ class ActivityManagerTest extends TestCase {
$stack->setBoardId(999);
$board = new Board();
$board->setId(999);
$this->cardMapper->expects($this->once())
$this->cardMapper->expects(self::once())
->method('find')
->with(555)
->willReturn($card);
$this->stackMapper->expects($this->once())
$this->stackMapper->expects(self::once())
->method('find')
->with(123)
->willReturn($stack);
$this->boardMapper->expects($this->once())->method('find')
$this->boardMapper->expects(self::once())->method('find')
->with(999)
->willReturn($board);
$this->assertEquals([
@@ -323,15 +453,15 @@ class ActivityManagerTest extends TestCase {
$stack->setBoardId(999);
$board = new Board();
$board->setId(999);
$this->cardMapper->expects($this->once())
$this->cardMapper->expects(self::once())
->method('find')
->with(555)
->willReturn($card);
$this->stackMapper->expects($this->once())
$this->stackMapper->expects(self::once())
->method('find')
->with(123)
->willReturn($stack);
$this->boardMapper->expects($this->once())->method('find')
$this->boardMapper->expects(self::once())->method('find')
->with(999)
->willReturn($board);
$this->assertEquals([

View File

@@ -66,18 +66,14 @@ class DeleteCronTest extends \Test\TestCase {
$this->boardMapper->expects($this->once())
->method('findToDelete')
->willReturn($boards);
$this->boardMapper->expects($this->at(1))
$this->boardMapper->expects($this->exactly(count($boards)))
->method('delete')
->with($boards[0]);
$this->boardMapper->expects($this->at(2))
->method('delete')
->with($boards[1]);
$this->boardMapper->expects($this->at(3))
->method('delete')
->with($boards[2]);
$this->boardMapper->expects($this->at(4))
->method('delete')
->with($boards[3]);
->withConsecutive(
[$boards[0]],
[$boards[1]],
[$boards[2]],
[$boards[3]]
);
$attachment = new Attachment();
$attachment->setType('deck_file');

View File

@@ -54,10 +54,7 @@ class ScheduledNoificationsTest extends \Test\TestCase {
$this->cardMapper->expects($this->once())
->method('findOverdue')
->willReturn($cards);
$this->notificationHelper->expects($this->at(0))
->method('sendCardDuedate')
->with($c1);
$this->notificationHelper->expects($this->at(1))
$this->notificationHelper->expects($this->exactly(2))
->method('sendCardDuedate')
->with($c1);
$this->scheduledNotifications->run(null);

View File

@@ -114,17 +114,18 @@ class NotificationHelperTest extends \Test\TestCase {
}
public function testSendCardDuedate() {
$this->config->expects($this->at(0))
$param1 = ['foo', 'bar', 'asd'];
$param2 = 'deck';
$param3 = 'board:234:notify-due';
$DUE_ASSIGNED = ConfigService::SETTING_BOARD_NOTIFICATION_DUE_ASSIGNED;
$this->config->expects($this->exactly(3))
->method('getUserValue')
->with('foo', 'deck', 'board:234:notify-due', ConfigService::SETTING_BOARD_NOTIFICATION_DUE_ASSIGNED)
->willReturn(ConfigService::SETTING_BOARD_NOTIFICATION_DUE_ALL);
$this->config->expects($this->at(1))
->method('getUserValue')
->with('bar', 'deck', 'board:234:notify-due', ConfigService::SETTING_BOARD_NOTIFICATION_DUE_ASSIGNED)
->willReturn(ConfigService::SETTING_BOARD_NOTIFICATION_DUE_ALL);
$this->config->expects($this->at(2))
->method('getUserValue')
->with('asd', 'deck', 'board:234:notify-due', ConfigService::SETTING_BOARD_NOTIFICATION_DUE_ASSIGNED)
->withConsecutive(
[$param1[0], $param2, $param3, $DUE_ASSIGNED],
[$param1[1], $param2, $param3, $DUE_ASSIGNED],
[$param1[2], $param2, $param3, $DUE_ASSIGNED],
)
->willReturn(ConfigService::SETTING_BOARD_NOTIFICATION_DUE_ALL);
$card = Card::fromParams([
@@ -180,24 +181,12 @@ class NotificationHelperTest extends \Test\TestCase {
$n3->expects($this->once())->method('setSubject')->with('card-overdue', ['MyCardTitle', 'MyBoardTitle'])->willReturn($n3);
$n3->expects($this->once())->method('setDateTime')->willReturn($n3);
$this->notificationManager->expects($this->at(0))
$this->notificationManager->expects($this->exactly(3))
->method('createNotification')
->willReturn($n1);
$this->notificationManager->expects($this->at(1))
->willReturnOnConsecutiveCalls($n1, $n2, $n3);
$this->notificationManager->expects($this->exactly(3))
->method('notify')
->with($n1);
$this->notificationManager->expects($this->at(2))
->method('createNotification')
->willReturn($n2);
$this->notificationManager->expects($this->at(3))
->method('notify')
->with($n2);
$this->notificationManager->expects($this->at(4))
->method('createNotification')
->willReturn($n3);
$this->notificationManager->expects($this->at(5))
->method('notify')
->with($n3);
->withConsecutive([$n1], [$n2], [$n3]);
$this->cardMapper->expects($this->once())
->method('markNotified')
@@ -207,18 +196,19 @@ class NotificationHelperTest extends \Test\TestCase {
}
public function testSendCardDuedateAssigned() {
$this->config->expects($this->at(0))
$param1 = ['foo', 'bar', 'asd'];
$param2 = 'deck';
$param3 = 'board:234:notify-due';
$DUE_ASSIGNED = ConfigService::SETTING_BOARD_NOTIFICATION_DUE_ASSIGNED;
$this->config->expects($this->exactly(3))
->method('getUserValue')
->with('foo', 'deck', 'board:234:notify-due', ConfigService::SETTING_BOARD_NOTIFICATION_DUE_ASSIGNED)
->willReturn(ConfigService::SETTING_BOARD_NOTIFICATION_DUE_ASSIGNED);
$this->config->expects($this->at(1))
->method('getUserValue')
->with('bar', 'deck', 'board:234:notify-due', ConfigService::SETTING_BOARD_NOTIFICATION_DUE_ASSIGNED)
->willReturn(ConfigService::SETTING_BOARD_NOTIFICATION_DUE_ASSIGNED);
$this->config->expects($this->at(2))
->method('getUserValue')
->with('asd', 'deck', 'board:234:notify-due', ConfigService::SETTING_BOARD_NOTIFICATION_DUE_ASSIGNED)
->willReturn(ConfigService::SETTING_BOARD_NOTIFICATION_DUE_ASSIGNED);
->withConsecutive(
[$param1[0], $param2, $param3, $DUE_ASSIGNED],
[$param1[1], $param2, $param3, $DUE_ASSIGNED],
[$param1[2], $param2, $param3, $DUE_ASSIGNED]
)
->willReturn($DUE_ASSIGNED);
$users = [
new DummyUser('foo'), new DummyUser('bar'), new DummyUser('asd')
@@ -278,24 +268,12 @@ class NotificationHelperTest extends \Test\TestCase {
$n3->expects($this->once())->method('setSubject')->with('card-overdue', ['MyCardTitle', 'MyBoardTitle'])->willReturn($n3);
$n3->expects($this->once())->method('setDateTime')->willReturn($n3);
$this->notificationManager->expects($this->at(0))
$this->notificationManager->expects($this->exactly(3))
->method('createNotification')
->willReturn($n1);
$this->notificationManager->expects($this->at(1))
->willReturnOnConsecutiveCalls($n1, $n2, $n3);
$this->notificationManager->expects($this->exactly(3))
->method('notify')
->with($n1);
$this->notificationManager->expects($this->at(2))
->method('createNotification')
->willReturn($n2);
$this->notificationManager->expects($this->at(3))
->method('notify')
->with($n2);
$this->notificationManager->expects($this->at(4))
->method('createNotification')
->willReturn($n3);
$this->notificationManager->expects($this->at(5))
->method('notify')
->with($n3);
->withConsecutive([$n1], [$n2], [$n3]);
$this->cardMapper->expects($this->once())
->method('markNotified')
@@ -306,18 +284,20 @@ class NotificationHelperTest extends \Test\TestCase {
public function testSendCardDuedateNever() {
$this->config->expects($this->at(0))
$param1 = ['foo', 'bar', 'asd'];
$param2 = 'deck';
$param3 = 'board:234:notify-due';
$DUE_ASSIGNED = ConfigService::SETTING_BOARD_NOTIFICATION_DUE_ASSIGNED;
$DUE_OFF = ConfigService::SETTING_BOARD_NOTIFICATION_DUE_OFF;
$this->config->expects($this->exactly(3))
->method('getUserValue')
->with('foo', 'deck', 'board:234:notify-due', ConfigService::SETTING_BOARD_NOTIFICATION_DUE_ASSIGNED)
->willReturn(ConfigService::SETTING_BOARD_NOTIFICATION_DUE_ASSIGNED);
$this->config->expects($this->at(1))
->method('getUserValue')
->with('bar', 'deck', 'board:234:notify-due', ConfigService::SETTING_BOARD_NOTIFICATION_DUE_ASSIGNED)
->willReturn(ConfigService::SETTING_BOARD_NOTIFICATION_DUE_ASSIGNED);
$this->config->expects($this->at(2))
->method('getUserValue')
->with('asd', 'deck', 'board:234:notify-due', ConfigService::SETTING_BOARD_NOTIFICATION_DUE_ASSIGNED)
->willReturn(ConfigService::SETTING_BOARD_NOTIFICATION_DUE_OFF);
->withConsecutive(
[$param1[0], $param2, $param3, $DUE_ASSIGNED],
[$param1[1], $param2, $param3, $DUE_ASSIGNED],
[$param1[2], $param2, $param3, $DUE_ASSIGNED]
)
->willReturnOnConsecutiveCalls($DUE_ASSIGNED, $DUE_ASSIGNED, $DUE_OFF);
$users = [
new DummyUser('foo'), new DummyUser('bar'), new DummyUser('asd')
@@ -370,18 +350,12 @@ class NotificationHelperTest extends \Test\TestCase {
$n2->expects($this->once())->method('setSubject')->with('card-overdue', ['MyCardTitle', 'MyBoardTitle'])->willReturn($n2);
$n2->expects($this->once())->method('setDateTime')->willReturn($n2);
$this->notificationManager->expects($this->at(0))
$this->notificationManager->expects($this->exactly(2))
->method('createNotification')
->willReturn($n1);
$this->notificationManager->expects($this->at(1))
->willReturnOnConsecutiveCalls($n1, $n2);
$this->notificationManager->expects($this->exactly(2))
->method('notify')
->with($n1);
$this->notificationManager->expects($this->at(2))
->method('createNotification')
->willReturn($n2);
$this->notificationManager->expects($this->at(3))
->method('notify')
->with($n2);
->withConsecutive([$n1], [$n2]);
$this->cardMapper->expects($this->once())
->method('markNotified')
@@ -423,10 +397,10 @@ class NotificationHelperTest extends \Test\TestCase {
$notification->expects($this->once())->method('setSubject')->with('card-assigned', ['MyCardTitle', 'MyBoardTitle', 'admin'])->willReturn($notification);
$notification->expects($this->once())->method('setDateTime')->willReturn($notification);
$this->notificationManager->expects($this->at(0))
$this->notificationManager->expects($this->once())
->method('createNotification')
->willReturn($notification);
$this->notificationManager->expects($this->at(1))
$this->notificationManager->expects($this->once())
->method('notify')
->with($notification);
@@ -451,10 +425,10 @@ class NotificationHelperTest extends \Test\TestCase {
$notification->expects($this->once())->method('setSubject')->with('board-shared', ['MyBoardTitle', 'admin'])->willReturn($notification);
$notification->expects($this->once())->method('setDateTime')->willReturn($notification);
$this->notificationManager->expects($this->at(0))
$this->notificationManager->expects($this->once())
->method('createNotification')
->willReturn($notification);
$this->notificationManager->expects($this->at(1))
$this->notificationManager->expects($this->once())
->method('notify')
->with($notification);
@@ -490,10 +464,10 @@ class NotificationHelperTest extends \Test\TestCase {
$notification->expects($this->once())->method('setSubject')->with('board-shared', ['MyBoardTitle', 'admin'])->willReturn($notification);
$notification->expects($this->once())->method('setDateTime')->willReturn($notification);
$this->notificationManager->expects($this->at(0))
$this->notificationManager->expects($this->once())
->method('createNotification')
->willReturn($notification);
$this->notificationManager->expects($this->at(1))
$this->notificationManager->expects($this->once())
->method('notify')
->with($notification);
@@ -540,19 +514,12 @@ class NotificationHelperTest extends \Test\TestCase {
$notification2->expects($this->once())->method('setSubject')->with('card-comment-mentioned', ['MyCard', 1, 'admin'])->willReturn($notification2);
$notification2->expects($this->once())->method('setDateTime')->willReturn($notification2);
$this->notificationManager->expects($this->at(0))
$this->notificationManager->expects($this->exactly(2))
->method('createNotification')
->willReturn($notification1);
$this->notificationManager->expects($this->at(1))
->willReturnOnConsecutiveCalls($notification1, $notification2);
$this->notificationManager->expects($this->exactly(2))
->method('notify')
->with($notification1);
$this->notificationManager->expects($this->at(2))
->method('createNotification')
->willReturn($notification2);
$this->notificationManager->expects($this->at(3))
->method('notify')
->with($notification2);
->withConsecutive([$notification1], [$notification2]);
$this->notificationHelper->sendMention($comment);
}

View File

@@ -110,8 +110,16 @@ class AttachmentServiceTest extends TestCase {
$this->cache = $this->createMock(ICache::class);
$this->cacheFactory->expects($this->any())->method('createDistributed')->willReturn($this->cache);
$this->appContainer->expects($this->at(0))->method('query')->with(FileService::class)->willReturn($this->attachmentServiceImpl);
$this->appContainer->expects($this->at(1))->method('query')->with(FilesAppService::class)->willReturn($this->filesAppServiceImpl);
$this->appContainer->expects($this->exactly(2))
->method('query')
->withConsecutive(
[FileService::class],
[FilesAppService::class]
)
->willReturnOnConsecutiveCalls(
$this->attachmentServiceImpl,
$this->filesAppServiceImpl
);
$this->application->expects($this->any())
->method('getContainer')
@@ -129,9 +137,18 @@ class AttachmentServiceTest extends TestCase {
$fileServiceMock = $this->createMock(FileService::class);
$fileAppServiceMock = $this->createMock(FilesAppService::class);
$appContainer->expects($this->at(0))->method('query')->with(FileService::class)->willReturn($fileServiceMock);
$appContainer->expects($this->at(1))->method('query')->with(FilesAppService::class)->willReturn($fileAppServiceMock);
$appContainer->expects($this->at(2))->method('query')->with(MyAttachmentService::class)->willReturn(new MyAttachmentService());
$appContainer->expects($this->exactly(3))
->method('query')
->withConsecutive(
[FileService::class],
[FilesAppService::class],
[MyAttachmentService::class]
)
->willReturnOnConsecutiveCalls(
$fileServiceMock,
$fileAppServiceMock,
new MyAttachmentService()
);
$application->expects($this->any())
->method('getContainer')
@@ -148,12 +165,24 @@ class AttachmentServiceTest extends TestCase {
$appContainer = $this->createMock(IAppContainer::class);
$fileServiceMock = $this->createMock(FileService::class);
$fileAppServiceMock = $this->createMock(FilesAppService::class);
$appContainer->expects($this->at(0))->method('query')->with(FileService::class)->willReturn($fileServiceMock);
$appContainer->expects($this->at(1))->method('query')->with(FilesAppService::class)->willReturn($fileAppServiceMock);
$appContainer->expects($this->at(2))->method('query')->with(MyAttachmentService::class)->willReturn(new MyAttachmentService());
$appContainer->expects($this->exactly(3))
->method('query')
->withConsecutive(
[FileService::class],
[FilesAppService::class],
[MyAttachmentService::class]
)
->willReturnOnConsecutiveCalls(
$fileServiceMock,
$fileAppServiceMock,
new MyAttachmentService()
);
$application->expects($this->any())
->method('getContainer')
->willReturn($appContainer);
$attachmentService = new AttachmentService($this->attachmentMapper, $this->cardMapper, $this->changeHelper, $this->permissionService, $application, $this->cacheFactory, $this->userId, $this->l10n, $this->activityManager);
$attachmentService->registerAttachmentService('custom', MyAttachmentService::class);
$attachmentService->getService('deck_file_invalid');
@@ -185,12 +214,17 @@ class AttachmentServiceTest extends TestCase {
->with(123)
->willReturn($attachments);
$this->attachmentServiceImpl->expects($this->at(0))
$this->attachmentServiceImpl->expects($this->exactly(2))
->method('extendData')
->with($attachments[0]);
$this->attachmentServiceImpl->expects($this->at(1))
->method('extendData')
->with($attachments[1]);
->withConsecutive(
[$attachments[0]],
[$attachments[1]],
)
->willReturnOnConsecutiveCalls(
$attachments[0],
$attachments[1],
);
$this->assertEquals($attachments, $this->attachmentService->findAll(123, false));
}
@@ -215,12 +249,15 @@ class AttachmentServiceTest extends TestCase {
->with(123, false)
->willReturn($attachmentsDeleted);
$this->attachmentServiceImpl->expects($this->at(0))
$this->attachmentServiceImpl->expects($this->exactly(4))
->method('extendData')
->with($attachments[0]);
$this->attachmentServiceImpl->expects($this->at(1))
->method('extendData')
->with($attachments[1]);
->withConsecutive(
[$attachments[0]],
[$attachments[1]],
[$attachmentsDeleted[0]],
[$attachmentsDeleted[1]]
);
$this->assertEquals(array_merge($attachments, $attachmentsDeleted), $this->attachmentService->findAll(123, true));
}
@@ -396,5 +433,6 @@ class AttachmentServiceTest extends TestCase {
->method('allowUndo')
->willReturn(false);
$actual = $this->attachmentService->restore(1, 1);
$this->assertEquals($expected, $actual);
}
}

View File

@@ -31,6 +31,7 @@ use OCA\Deck\Db\Assignment;
use OCA\Deck\Db\AssignmentMapper;
use OCA\Deck\Db\Board;
use OCA\Deck\Db\BoardMapper;
use OCA\Deck\Db\CardMapper;
use OCA\Deck\Db\ChangeHelper;
use OCA\Deck\Db\LabelMapper;
use OCA\Deck\Db\StackMapper;
@@ -58,6 +59,8 @@ class BoardServiceTest extends TestCase {
private $boardMapper;
/** @var StackMapper */
private $stackMapper;
/** @var CardMapper */
private $cardMapper;
/** @var PermissionService */
private $permissionService;
/** @var NotificationHelper */
@@ -85,6 +88,7 @@ class BoardServiceTest extends TestCase {
$this->boardMapper = $this->createMock(BoardMapper::class);
$this->stackMapper = $this->createMock(StackMapper::class);
$this->config = $this->createMock(IConfig::class);
$this->cardMapper = $this->createMock(CardMapper::class);
$this->labelMapper = $this->createMock(LabelMapper::class);
$this->permissionService = $this->createMock(PermissionService::class);
$this->notificationHelper = $this->createMock(NotificationHelper::class);
@@ -106,6 +110,7 @@ class BoardServiceTest extends TestCase {
$this->permissionService,
$this->notificationHelper,
$this->assignedUsersMapper,
$this->cardMapper,
$this->userManager,
$this->groupManager,
$this->activityManager,
@@ -149,7 +154,7 @@ class BoardServiceTest extends TestCase {
->method('find')
->with(1)
->willReturn($b1);
$this->permissionService->expects($this->once())
$this->permissionService->expects($this->any())
->method('findUsers')
->willReturn([
'admin' => 'admin',
@@ -253,6 +258,11 @@ class BoardServiceTest extends TestCase {
->method('insert')
->with($acl)
->willReturn($acl);
$this->permissionService->expects($this->any())
->method('findUsers')
->willReturn([
'admin' => 'admin',
]);
$this->assertEquals($acl, $this->service->addAcl(
123, 'user', 'admin', true, true, true
));
@@ -295,30 +305,39 @@ class BoardServiceTest extends TestCase {
$existingAcl->setPermissionEdit($currentUserAcl[0]);
$existingAcl->setPermissionShare($currentUserAcl[1]);
$existingAcl->setPermissionManage($currentUserAcl[2]);
$this->permissionService->expects($this->at(0))
->method('checkPermission')
->with($this->boardMapper, 123, Acl::PERMISSION_SHARE, null);
if ($currentUserAcl[2]) {
$this->permissionService->expects($this->at(1))
$this->permissionService->expects($this->exactly(2))
->method('checkPermission')
->with($this->boardMapper, 123, Acl::PERMISSION_MANAGE, null);
->withConsecutive(
[$this->boardMapper, 123, Acl::PERMISSION_SHARE, null],
[$this->boardMapper, 123, Acl::PERMISSION_MANAGE, null]
);
} else {
$this->aclMapper->expects($this->once())
->method('findAll')
->willReturn([$existingAcl]);
$this->permissionService->expects($this->at(1))
$this->permissionService->expects($this->exactly(2))
->method('checkPermission')
->with($this->boardMapper, 123, Acl::PERMISSION_MANAGE, null)
->willThrowException(new NoPermissionException('No permission'));
$this->permissionService->expects($this->at(2))
->withConsecutive(
[$this->boardMapper, 123, Acl::PERMISSION_SHARE, null],
[$this->boardMapper, 123, Acl::PERMISSION_MANAGE, null]
)
->will(
$this->onConsecutiveCalls(
true,
$this->throwException(new NoPermissionException('No permission'))
)
);
$this->permissionService->expects($this->exactly(3))
->method('userCan')
->willReturn($currentUserAcl[0]);
$this->permissionService->expects($this->at(3))
->method('userCan')
->willReturn($currentUserAcl[1]);
$this->permissionService->expects($this->at(4))
->method('userCan')
->willReturn($currentUserAcl[2]);
->willReturnOnConsecutiveCalls(
$currentUserAcl[0],
$currentUserAcl[1],
$currentUserAcl[2]
);
}
$user = $this->createMock(IUser::class);
@@ -333,6 +352,11 @@ class BoardServiceTest extends TestCase {
$acl->resolveRelation('participant', function ($participant) use (&$user) {
return null;
});
$this->permissionService->expects($this->any())
->method('findUsers')
->willReturn([
'admin' => 'admin',
]);
$this->notificationHelper->expects($this->once())
->method('sendBoardShared');
$expected = clone $acl;

View File

@@ -139,13 +139,17 @@ class PermissionServiceTest extends \Test\TestCase {
}
public function testUserIsBoardOwner() {
$board = new Board();
$board->setOwner('admin');
$this->boardMapper->expects($this->at(0))->method('find')->with(123)->willReturn($board);
$adminBoard = new Board();
$adminBoard->setOwner('admin');
$userBoard = new Board();
$userBoard->setOwner('user1');
$this->boardMapper->expects($this->exactly(2))
->method('find')
->withConsecutive([123], [234])
->willReturnOnConsecutiveCalls($adminBoard, $userBoard);
$this->assertEquals(true, $this->service->userIsBoardOwner(123));
$board = new Board();
$board->setOwner('user1');
$this->boardMapper->expects($this->at(0))->method('find')->with(234)->willReturn($board);
$this->assertEquals(false, $this->service->userIsBoardOwner(234));
}
@@ -336,7 +340,7 @@ class PermissionServiceTest extends \Test\TestCase {
$aclGroup->setParticipant('group1');
$board = $this->createMock(Board::class);
$board->expects($this->at(0))
$board->expects($this->once())
->method('__call')
->with('getOwner', [])
->willReturn('user1');
@@ -348,14 +352,11 @@ class PermissionServiceTest extends \Test\TestCase {
->method('find')
->with(123)
->willReturn($board);
$this->userManager->expects($this->at(0))
$this->userManager->expects($this->exactly(2))
->method('get')
->with('user1')
->willReturn($user1);
$this->userManager->expects($this->at(1))
->method('get')
->with('user2')
->willReturn($user2);
->withConsecutive(['user1'], ['user2'])
->willReturnOnConsecutiveCalls($user1, $user2);
$group = $this->createMock(IGroup::class);
$group->expects($this->once())
->method('getUsers')