diff --git a/appinfo/routes.php b/appinfo/routes.php index 7d6816b7a..f08c28ff7 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -40,6 +40,7 @@ return [ ['name' => 'board#addAcl', 'url' => '/boards/{boardId}/acl', 'verb' => 'POST'], ['name' => 'board#updateAcl', 'url' => '/boards/{boardId}/acl', 'verb' => 'PUT'], ['name' => 'board#deleteAcl', 'url' => '/boards/{boardId}/acl/{aclId}', 'verb' => 'DELETE'], + ['name' => 'board#clone', 'url' => '/boards/{boardId}/clone', 'verb' => 'POST'], // stacks ['name' => 'stack#index', 'url' => '/stacks/{boardId}', 'verb' => 'GET'], diff --git a/lib/Controller/BoardController.php b/lib/Controller/BoardController.php index 6cb65ff01..b397c63d8 100644 --- a/lib/Controller/BoardController.php +++ b/lib/Controller/BoardController.php @@ -150,4 +150,13 @@ class BoardController extends ApiController { return $this->boardService->deleteAcl($aclId); } + /** + * @NoAdminRequired + * @param $boardId + * @return \OCP\AppFramework\Db\Entity + */ + public function clone($boardId) { + return $this->boardService->clone($boardId); + } + } diff --git a/lib/Service/BoardService.php b/lib/Service/BoardService.php index 1e03b7445..8f5cc58af 100644 --- a/lib/Service/BoardService.php +++ b/lib/Service/BoardService.php @@ -33,6 +33,7 @@ use OCA\Deck\Db\AssignedUsersMapper; use OCA\Deck\Db\ChangeHelper; use OCA\Deck\Db\IPermissionMapper; use OCA\Deck\Db\Label; +use OCA\Deck\Db\Stack; use OCA\Deck\Db\StackMapper; use OCA\Deck\NoPermissionException; use OCA\Deck\Notification\NotificationHelper; @@ -605,6 +606,49 @@ class BoardService { return $delete; } + /** + * @param $id + * @return Board + * @throws DoesNotExistException + * @throws \OCA\Deck\NoPermissionException + * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException + * @throws BadRequestException + */ + public function clone($id) { + + if (is_numeric($id) === false) { + throw new BadRequestException('board id must be a number'); + } + + $this->permissionService->checkPermission($this->boardMapper, $id, Acl::PERMISSION_READ); + + $board = $this->boardMapper->find($id); + $newBoard = new Board(); + $newBoard->setTitle($board->getTitle() . ' (copy)'); + $newBoard->setOwner($board->getOwner()); + $newBoard->setColor($board->getColor()); + $this->boardMapper->insert($newBoard); + + $labels = $this->labelMapper->findAll($id); + foreach ($labels as $label) { + $newLabel = new Label(); + $newLabel->setTitle($label->getTitle()); + $newLabel->setColor($label->getColor()); + $newLabel->setBoardId($newBoard->getId()); + $this->labelMapper->insert($newLabel); + } + + $stacks = $this->stackMapper->findAll($id); + foreach ($stacks as $stack) { + $newStack = new Stack(); + $newStack->setTitle($stack->getTitle()); + $newStack->setBoardId($newBoard->getId()); + $this->stackMapper->insert($newStack); + } + + return $newBoard; + } + private function enrichWithStacks($board, $since = -1) { $stacks = $this->stackMapper->findAll($board->getId(), null, null, $since); diff --git a/src/components/navigation/AppNavigationBoard.vue b/src/components/navigation/AppNavigationBoard.vue index ae4eec965..d7d07f0ec 100644 --- a/src/components/navigation/AppNavigationBoard.vue +++ b/src/components/navigation/AppNavigationBoard.vue @@ -130,6 +130,16 @@ export default { text: t('deck', 'Edit board') }) + actions.push({ + action: () => { + this.hideMenu() + // this.boardApi.cloneBoard(this.board) + this.$store.dispatch('cloneBoard', this.board) + }, + icon: 'icon-clone', + text: t('deck', 'Clone board') + }) + if (!this.board.archived) { actions.push({ action: () => { diff --git a/src/services/BoardApi.js b/src/services/BoardApi.js index 66857269f..ff780ce5d 100644 --- a/src/services/BoardApi.js +++ b/src/services/BoardApi.js @@ -135,6 +135,21 @@ export class BoardApi { }) } + cloneBoard(board) { + return axios.post(this.url(`/boards/${board.id}/clone`)) + .then( + (response) => { + return Promise.resolve(response.data) + }, + (err) => { + return Promise.reject(err) + } + ) + .catch((err) => { + return Promise.reject(err) + }) + } + // Label API Calls deleteLabel(id) { return axios.delete(this.url(`/labels/${id}`)) diff --git a/src/store/main.js b/src/store/main.js index 93aaba30b..4ae00740b 100644 --- a/src/store/main.js +++ b/src/store/main.js @@ -115,6 +115,19 @@ export default new Vuex.Store({ state.boards.push(board) } }, + + cloneBoard(state, board) { + const indexExisting = state.boards.findIndex((b) => { + return board.id === b.id + }) + + if (indexExisting > -1) { + Vue.set(state.boards, indexExisting, board) + } else { + state.boards.push(board) + } + }, + /** * Removes the board from the store. * @@ -267,6 +280,12 @@ export default new Vuex.Store({ commit('addBoard', board) }) }, + cloneBoard({ commit }, boardData) { + apiClient.cloneBoard(boardData) + .then((board) => { + commit('cloneBoard', board) + }) + }, removeBoard({ commit }, board) { commit('removeBoard', board) },