From 003df010ddea2f44b05ecc745de5855cbdcd604c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakob=20R=C3=B6hrl?= Date: Wed, 29 Apr 2020 12:58:10 +0200 Subject: [PATCH] clone stacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jakob Röhrl --- appinfo/routes.php | 1 + lib/Controller/StackController.php | 9 +++ lib/Service/StackService.php | 115 ++++++++++++++++++++++++++++- src/components/board/Stack.vue | 7 +- src/services/StackApi.js | 15 ++++ src/store/stack.js | 12 +++ 6 files changed, 157 insertions(+), 2 deletions(-) diff --git a/appinfo/routes.php b/appinfo/routes.php index 6a1482cd8..f0b10b22a 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -50,6 +50,7 @@ return [ ['name' => 'stack#delete', 'url' => '/stacks/{stackId}', 'verb' => 'DELETE'], ['name' => 'stack#deleted', 'url' => '/{boardId}/stacks/deleted', 'verb' => 'GET'], ['name' => 'stack#archived', 'url' => '/stacks/{boardId}/archived', 'verb' => 'GET'], + ['name' => 'stack#clone', 'url' => '/stacks/{stackId}/clone', 'verb' => 'POST'], // cards ['name' => 'card#read', 'url' => '/cards/{cardId}', 'verb' => 'GET'], diff --git a/lib/Controller/StackController.php b/lib/Controller/StackController.php index 6452ef310..bd1d5fad9 100644 --- a/lib/Controller/StackController.php +++ b/lib/Controller/StackController.php @@ -107,4 +107,13 @@ class StackController extends Controller { public function deleted($boardId) { return $this->stackService->fetchDeleted($boardId); } + + /** + * @NoAdminRequired + * @param $boardId + * @return \OCP\Deck\DB\Board + */ + public function clone($stackId, $boardId) { + return $this->stackService->clone($stackId, $boardId, $this->userId); + } } diff --git a/lib/Service/StackService.php b/lib/Service/StackService.php index 7df627e06..e8158241c 100644 --- a/lib/Service/StackService.php +++ b/lib/Service/StackService.php @@ -30,6 +30,7 @@ use OCA\Deck\BadRequestException; use OCA\Deck\Db\Acl; use OCA\Deck\Db\AssignedUsersMapper; use OCA\Deck\Db\BoardMapper; +use OCA\Deck\Db\Card; use OCA\Deck\Db\CardMapper; use OCA\Deck\Db\ChangeHelper; use OCA\Deck\Db\LabelMapper; @@ -38,6 +39,9 @@ use OCA\Deck\Db\StackMapper; use OCA\Deck\StatusException; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\GenericEvent; +use OCP\IL10N; +use OCA\Deck\Event\FTSEvent; +use OCA\Deck\Service\AssignmentService; class StackService { private $stackMapper; @@ -49,10 +53,13 @@ class StackService { private $cardService; private $assignedUsersMapper; private $attachmentService; + private $activityManager; /** @var EventDispatcherInterface */ private $eventDispatcher; private $changeHelper; + private $l10n; + private $assignmentService; public function __construct( StackMapper $stackMapper, @@ -66,7 +73,9 @@ class StackService { AttachmentService $attachmentService, ActivityManager $activityManager, EventDispatcherInterface $eventDispatcher, - ChangeHelper $changeHelper + ChangeHelper $changeHelper, + IL10N $l10n, + AssignmentService $assignmentService ) { $this->stackMapper = $stackMapper; $this->boardMapper = $boardMapper; @@ -80,6 +89,8 @@ class StackService { $this->activityManager = $activityManager; $this->eventDispatcher = $eventDispatcher; $this->changeHelper = $changeHelper; + $this->l10n = $l10n; + $this->assignmentService = $assignmentService; } private function enrichStackWithCards($stack, $since = -1) { @@ -360,4 +371,106 @@ class StackService { return $result; } + + /** + * @param $id + * @param $boardId + * @param $userId + * @return Stack + * @throws StatusException + * @throws BadRequestException + */ + public function clone($id, $boardId, $userId) { + if (is_numeric($id) === false) { + throw new BadRequestException('stack id must be a number'); + } + if (is_numeric($boardId) === false) { + throw new BadRequestException('board id must be a number'); + } + + $this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_MANAGE); + $this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_READ); + if ($this->boardService->isArchived(null, $boardId)) { + throw new StatusException('Operation not allowed. This board is archived.'); + } + + $stack = $this->stackMapper->find($id); + $board = $this->boardMapper->find($boardId); + + + $newStack = new Stack(); + $newStack->setTitle($stack->getTitle() . ' (' . $this->l10n->t('copy') . ')'); + $newStack->setBoardId($boardId); + $newStack->setOrder($stack->getOrder() +1); + $newStack = $this->stackMapper->insert($newStack); + + $this->activityManager->triggerEvent( + ActivityManager::DECK_OBJECT_BOARD, $newStack, ActivityManager::SUBJECT_STACK_CREATE + ); + $this->changeHelper->boardChanged($boardId); + + $this->eventDispatcher->dispatch( + '\OCA\Deck\Stack::onCreate', + new GenericEvent(null, ['id' => $newStack->getId(), 'stack' => $newStack]) + ); + + $cards = $this->cardMapper->findAll($id); + foreach ($cards as $card) { + + $newCard = new Card(); + $newCard->setTitle($card->getTitle()); + $newCard->setStackId($newStack->getId()); + $newCard->setType($card->getType()); + $newCard->setOrder($card->getOrder()); + $newCard->setOwner($userId); + $newCard->setDescription($card->getDescription()); + $newCard->setDuedate($card->getDuedate()); + + $newCard = $this->cardMapper->insert($newCard); + + $this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $newCard, ActivityManager::SUBJECT_CARD_CREATE); + $this->changeHelper->cardChanged($newCard->getId(), false); + $this->eventDispatcher->dispatch('\OCA\Deck\Card::onCreate', + new FTSEvent( + null, ['id' => $newCard->getId(), 'card' => $newCard, 'userId' => $owner, 'stackId' => $stackId] + ) + ); + + if ($boardId === $stack->getBoardId()) { + $labels = $this->labelMapper->findAll($card->getId()); + $labels = $this->labelMapper->findAssignedLabelsForCard($card->id); + + $l = []; + foreach ($labels as $label) { + $l = $this->cardMapper->assignLabel($newCard->getId(), $label->getId()); + } + $newCard->setLabels($l); + + + $assignedUsers = $this->assignedUsersMapper->find($card->getId()); + /* foreach ($assignedUsers as $assignedUser) { + $u = $this->assignmentService->assignUser($newCard->getId(), $assignedUser->getId()); + $newCard->setAssignedUsers($u); + } */ + + //attachments??? + + + + + } + + } + + $createdCards = $this->cardMapper->findAll($newStack->getId()); + $newStack->setCards($createdCards); + + + + + + + + return $newStack; + } } diff --git a/src/components/board/Stack.vue b/src/components/board/Stack.vue index 27fe84eec..0832d0335 100644 --- a/src/components/board/Stack.vue +++ b/src/components/board/Stack.vue @@ -46,6 +46,9 @@ {{ t('deck', 'Archive all cards') }} + + {{ t('deck', 'Clone list') }} + {{ t('deck', 'Delete list') }} @@ -207,7 +210,6 @@ export default { this.$store.dispatch('deleteStack', stack) }, archiveAllCardsFromStack(stack) { - this.stackTransfer.total = this.cardsByStack.length this.cardsByStack.forEach((card, index) => { this.stackTransfer.current = index @@ -215,6 +217,9 @@ export default { }) this.modalArchivAllCardsShow = false }, + cloneStack(stack) { + this.$store.dispatch('cloneStack', stack) + }, startEditing(stack) { this.copiedStack = Object.assign({}, stack) this.editing = true diff --git a/src/services/StackApi.js b/src/services/StackApi.js index 0ed440a22..c825dee79 100644 --- a/src/services/StackApi.js +++ b/src/services/StackApi.js @@ -140,4 +140,19 @@ export class StackApi { }) } + cloneStack(stack) { + return axios.post(this.url(`/stacks/${stack.id}/clone`), stack) + .then( + (response) => { + return Promise.resolve(response.data) + }, + (err) => { + return Promise.reject(err) + } + ) + .catch((err) => { + return Promise.reject(err) + }) + } + } diff --git a/src/store/stack.js b/src/store/stack.js index 3f428e71f..9a90e25cc 100644 --- a/src/store/stack.js +++ b/src/store/stack.js @@ -58,6 +58,9 @@ export default { state.stacks.splice(existingIndex, 1) } }, + /* cloneStack(state, stack) { + this.commit('addStack', stack) + }, */ updateStack(state, stack) { const existingIndex = state.stacks.findIndex(_stack => _stack.id === stack.id) if (existingIndex !== -1) { @@ -97,6 +100,15 @@ export default { commit('addStack', createdStack) }) }, + cloneStack({ commit }, stack) { + apiClient.cloneStack(stack) + .then((stack) => { + for (const j in stack.cards) { + commit('addCard', stack.cards[j]) + } + commit('addStack', stack) + }) + }, deleteStack({ commit }, stack) { apiClient.deleteStack(stack.id) .then((stack) => {