From b8fa0246803dab40cf57d40b45984c53046f2d55 Mon Sep 17 00:00:00 2001 From: "Dzial Techniczny WMW Projekt s.c" Date: Tue, 10 Dec 2019 11:34:53 +0100 Subject: Plugins directory and local modifications --- plugins/Bigboard/Asset/js/BoardDragAndDrop.js | 124 ++++++++++++++++++ plugins/Bigboard/Asset/js/BoardPolling.js | 43 ++++++ plugins/Bigboard/Controller/Bigboard.php | 103 +++++++++++++++ .../Bigboard/Controller/BoardAjaxController.php | 145 +++++++++++++++++++++ plugins/Bigboard/Plugin.php | 70 ++++++++++ plugins/Bigboard/README.md | 10 ++ plugins/Bigboard/Template/Bigboard.php | 15 +++ plugins/Bigboard/Template/board/dropdown.php | 32 +++++ plugins/Bigboard/Template/board/show.php | 5 + .../Bigboard/Template/board/table_container.php | 58 +++++++++ plugins/Bigboard/Template/board/table_tasks.php | 38 ++++++ plugins/Bigboard/Template/board/task_private.php | 66 ++++++++++ plugins/Bigboard/Template/board/view.php | 20 +++ plugins/Bigboard/UserSession.php | 29 +++++ 14 files changed, 758 insertions(+) create mode 100644 plugins/Bigboard/Asset/js/BoardDragAndDrop.js create mode 100644 plugins/Bigboard/Asset/js/BoardPolling.js create mode 100644 plugins/Bigboard/Controller/Bigboard.php create mode 100644 plugins/Bigboard/Controller/BoardAjaxController.php create mode 100644 plugins/Bigboard/Plugin.php create mode 100644 plugins/Bigboard/README.md create mode 100644 plugins/Bigboard/Template/Bigboard.php create mode 100644 plugins/Bigboard/Template/board/dropdown.php create mode 100644 plugins/Bigboard/Template/board/show.php create mode 100644 plugins/Bigboard/Template/board/table_container.php create mode 100644 plugins/Bigboard/Template/board/table_tasks.php create mode 100644 plugins/Bigboard/Template/board/task_private.php create mode 100644 plugins/Bigboard/Template/board/view.php create mode 100644 plugins/Bigboard/UserSession.php (limited to 'plugins/Bigboard') diff --git a/plugins/Bigboard/Asset/js/BoardDragAndDrop.js b/plugins/Bigboard/Asset/js/BoardDragAndDrop.js new file mode 100644 index 00000000..b6b7b8c3 --- /dev/null +++ b/plugins/Bigboard/Asset/js/BoardDragAndDrop.js @@ -0,0 +1,124 @@ +Kanboard.BoardDragAndDrop = function(app) { + this.app = app; + this.savingInProgress = false; +}; + +Kanboard.BoardDragAndDrop.prototype.execute = function() { + if (this.app.hasId("board")) { + this.executeListeners(); + this.dragAndDrop(); + } +}; + +Kanboard.BoardDragAndDrop.prototype.dragAndDrop = function() { + var self = this; + var dropzone = $(".board-task-list"); + + // Run for every Board List, connecting the Items within the same project id + dropzone.each(function() { + // Set dropzone height to the height of the table cell + $(this).css("min-height", $(this).parent().height()); + + var project_id = $(this).closest("table[id=board]").attr("data-project-id"); + + var params = { + forcePlaceholderSize: true, + tolerance: "pointer", + connectWith: ".sortable-column[data-project-id=" + project_id + "]", + placeholder: "draggable-placeholder", + items: ".draggable-item[data-project-id=" + project_id + "]", + stop: function(event, ui) { + var task = ui.item; + var taskId = task.attr('data-task-id'); + var taskPosition = task.attr('data-position'); + var taskColumnId = task.attr('data-column-id'); + var taskSwimlaneId = task.attr('data-swimlane-id'); + + var newColumnId = task.parent().attr("data-column-id"); + var newSwimlaneId = task.parent().attr('data-swimlane-id'); + var newPosition = task.index() + 1; + + var boardId = task.closest("table").attr("data-project-id"); + var saveURL = task.closest("table").attr("data-save-url"); + + task.removeClass("draggable-item-selected"); + + if (newColumnId != taskColumnId || newSwimlaneId != taskSwimlaneId || newPosition != taskPosition) { + self.changeTaskState(taskId); + self.save(saveURL, boardId, taskId, taskColumnId, newColumnId, newPosition, newSwimlaneId); + } + }, + start: function(event, ui) { + ui.item.addClass("draggable-item-selected"); + ui.placeholder.height(ui.item.height()); + } + }; + + if (isMobile.any) { + $(".task-board-sort-handle").css("display", "inline"); + params.handle = ".task-board-sort-handle"; + } + + $(this).sortable(params); + }); +}; + +Kanboard.BoardDragAndDrop.prototype.changeTaskState = function(taskId) { + var task = $("div[data-task-id=" + taskId + "]"); + task.addClass('task-board-saving-state'); + task.find('.task-board-saving-icon').show(); +}; + +Kanboard.BoardDragAndDrop.prototype.save = function(saveURL, boardId, taskId, srcColumnId, dstColumnId, position, swimlaneId) { + var self = this; + self.app.showLoadingIcon(); + self.savingInProgress = true; + + $.ajax({ + cache: false, + url: saveURL, + contentType: "application/json", + type: "POST", + processData: false, + data: JSON.stringify({ + "task_id": taskId, + "src_column_id": srcColumnId, + "dst_column_id": dstColumnId, + "swimlane_id": swimlaneId, + "position": position + }), + success: function(data) { + self.refresh(boardId,data); + self.savingInProgress = false; + }, + error: function() { + self.app.hideLoadingIcon(); + self.savingInProgress = false; + }, + statusCode: { + 403: function(data) { + window.alert(data.responseJSON.message); + document.location.reload(true); + } + } + }); +}; + +Kanboard.BoardDragAndDrop.prototype.refresh = function(boardId, data) { + + $("div[id=board-container][data-project-id=" + boardId + "]").replaceWith(data); + + this.app.hideLoadingIcon(); + this.executeListeners(); + this.dragAndDrop(); +}; + +Kanboard.BoardDragAndDrop.prototype.executeListeners = function() { + for (var className in this.app.controllers) { + var controller = this.app.get(className); + + if (typeof controller.onBoardRendered === "function") { + controller.onBoardRendered(); + } + } +}; diff --git a/plugins/Bigboard/Asset/js/BoardPolling.js b/plugins/Bigboard/Asset/js/BoardPolling.js new file mode 100644 index 00000000..6d5499fd --- /dev/null +++ b/plugins/Bigboard/Asset/js/BoardPolling.js @@ -0,0 +1,43 @@ +Kanboard.BoardPolling = function(app) { + this.app = app; +}; + +Kanboard.BoardPolling.prototype.execute = function() { + if (this.app.hasId("board")) { + var interval = parseInt($("#board").attr("data-check-interval")); + + if (interval > 0) { + window.setInterval(this.check.bind(this), interval * 1000); + } + } +}; + +Kanboard.BoardPolling.prototype.check = function() { + if (KB.utils.isVisible() && !this.app.get("BoardDragAndDrop").savingInProgress) { + var self = this; + if (!$("#app-loading-icon").length) this.app.showLoadingIcon(); + var pollsinprogress=0; + + $("table.board-project").each(function() { + var boardId = $(this).attr("data-project-id") + var url = $(this).attr("data-check-url"); + pollsinprogress++; + $.ajax({ + cache: false, + url: url, + statusCode: { + 200: function(data) { + pollsinprogress--; + if (pollsinprogress <= 0) self.app.hideLoadingIcon(); + self.app.get("BoardDragAndDrop").refresh(boardId, data); + }, + 304: function () { + pollsinprogress--; + if (pollsinprogress <= 0) self.app.hideLoadingIcon(); + } + } + }); + }); + if (pollsinprogress <= 0) self.app.hideLoadingIcon(); + } +}; diff --git a/plugins/Bigboard/Controller/Bigboard.php b/plugins/Bigboard/Controller/Bigboard.php new file mode 100644 index 00000000..80573d07 --- /dev/null +++ b/plugins/Bigboard/Controller/Bigboard.php @@ -0,0 +1,103 @@ +userSession->isAdmin()) { + $project_ids = $this->projectModel->getAllIds(); + } else { + $project_ids = $this->projectPermissionModel->getActiveProjectIds($this->userSession->getId()); + } + + $nb_projects = count($project_ids); + // Draw a header First + $this->response->html($this->helper->layout->app('bigboard:board/show', array( + 'title' => t('Bigboard').' ('.$nb_projects.')', + 'board_selector' => false, + ))); + + echo $this->template->render('bigboard:board/dropdown', array( + 'bigboarddisplaymode' => $this->userSession->isBigboardCollapsed(), + )); + + $this->showProjects($project_ids); + + } + + /** + * Show projects. + * + * @param $project_ids list of project ids to show + * + * @return bool + */ + private function showProjects($project_ids) + { + print "
"; + + foreach ($project_ids as $project_id) { + $project = $this->projectModel->getByIdWithOwner($project_id); + $search = $this->helper->projectHeader->getSearchQuery($project); + + $this->userMetadataCacheDecorator->set(UserMetadataModel::KEY_BOARD_COLLAPSED.$project_id, $this->userSession->isBigboardCollapsed()); + + echo $this->template->render('bigboard:board/view', array( + 'no_layout' => true, + 'board_selector' => false, + 'project' => $project, + 'title' => $project['name'], + 'description' => $this->helper->projectHeader->getDescription($project), + 'board_private_refresh_interval' => $this->configModel->get('board_private_refresh_interval'), + 'board_highlight_period' => $this->configModel->get('board_highlight_period'), + 'swimlanes' => $this->taskLexer + ->build($search) + ->format(BoardFormatter::getInstance($this->container)->withProjectId($project['id'])), + )); + } + + print "
"; + + } + + public function collapseAll() + { + $this->changeDisplayMode(true); + } + + public function expandAll() + { + $this->changeDisplayMode(false); + } + + private function changeDisplayMode($mode) + { + session_set('bigboardCollapsed', $mode); + + if ($this->userSession->isAdmin()) { + $project_ids = $this->projectModel->getAllIds(); + } else { + $project_ids = $this->projectPermissionModel->getActiveProjectIds(session_get('user')['id']); + } + + if ($this->request->isAjax()) { + $this->showProjects($project_ids); + } else { + $this->response->redirect($this->helper->url->to('Bigboard', 'index', array('plugin' => 'Bigboard'))); + } + } + } diff --git a/plugins/Bigboard/Controller/BoardAjaxController.php b/plugins/Bigboard/Controller/BoardAjaxController.php new file mode 100644 index 00000000..57832fd5 --- /dev/null +++ b/plugins/Bigboard/Controller/BoardAjaxController.php @@ -0,0 +1,145 @@ +request->getIntegerParam('project_id'); + + if (! $project_id || ! $this->request->isAjax()) { + throw new AccessForbiddenException(); + } + + $values = $this->request->getJson(); + + if (! $this->helper->projectRole->canMoveTask($project_id, $values['src_column_id'], $values['dst_column_id'])) { + throw new AccessForbiddenException(e("You don't have the permission to move this task")); + } + + $result =$this->taskPositionModel->movePosition( + $project_id, + $values['task_id'], + $values['dst_column_id'], + $values['position'], + $values['swimlane_id'] + ); + + if (! $result) { + $this->response->status(400); + } else { + $this->response->html($this->renderBoard($project_id), 201); + } + } + + /** + * Check if the board have been changed + * + * @access public + */ + public function check() + { + $project_id = $this->request->getIntegerParam('project_id'); + $timestamp = $this->request->getIntegerParam('timestamp'); + + if (! $project_id || ! $this->request->isAjax()) { + throw new AccessForbiddenException(); + } elseif (! $this->projectModel->isModifiedSince($project_id, $timestamp)) { + $this->response->status(304); + } else { + $this->response->html($this->renderBoard($project_id)); + } + } + + /** + * Reload the board with new filters + * + * @access public + */ + public function reload() + { + $project_id = $this->request->getIntegerParam('project_id'); + + if (! $project_id || ! $this->request->isAjax()) { + throw new AccessForbiddenException(); + } + + $values = $this->request->getJson(); + $this->userSession->setFilters($project_id, empty($values['search']) ? '' : $values['search']); + + $this->response->html($this->renderBoard($project_id)); + } + + /** + * Enable collapsed mode + * + * @access public + */ + public function collapse() + { + $this->changeDisplayMode(1); + } + + /** + * Enable expanded mode + * + * @access public + */ + public function expand() + { + $this->changeDisplayMode(0); + } + + /** + * Change display mode + * + * @access private + * @param int $mode + */ + private function changeDisplayMode($mode) + { + $project_id = $this->request->getIntegerParam('project_id'); + $this->userMetadataCacheDecorator->set(UserMetadataModel::KEY_BOARD_COLLAPSED.$project_id, $mode); + + if ($this->request->isAjax()) { + $this->response->html($this->renderBoard($project_id)); + } else { + $this->response->redirect($this->helper->url->to('BoardViewController', 'show', array('project_id' => $project_id))); + } + } + + /** + * Render board + * + * @access protected + * @param integer $project_id + * @return string + */ + protected function renderBoard($project_id) + { + return $this->template->render('bigboard:board/table_container', array( + 'project' => $this->projectModel->getById($project_id), + 'board_private_refresh_interval' => $this->configModel->get('board_private_refresh_interval'), + 'board_highlight_period' => $this->configModel->get('board_highlight_period'), + 'swimlanes' => $this->taskLexer + ->build($this->userSession->getFilters($project_id)) + ->format($this->boardFormatter->withProjectId($project_id)) + )); + } +} diff --git a/plugins/Bigboard/Plugin.php b/plugins/Bigboard/Plugin.php new file mode 100644 index 00000000..aa43c48a --- /dev/null +++ b/plugins/Bigboard/Plugin.php @@ -0,0 +1,70 @@ +template->hook->attach('template:project-list:menu:before', 'bigboard:Bigboard'); + $this->template->setTemplateOverride('board/table_container','bigboard:board/table_container'); + $this->template->setTemplateOverride('board/table_tasks','bigboard:board/table_tasks'); + $this->template->setTemplateOverride('board/table_private','bigboard:board/table_private'); + $this->hook->on('template:layout:js', array('template' => 'plugins/Bigboard/Asset/js/BoardDragAndDrop.js')); + $this->hook->on('template:layout:js', array('template' => 'plugins/Bigboard/Asset/js/BoardPolling.js')); + } + + public function getClasses() + { + return array( + 'Plugin\Bigboard' => array( + 'UserSession' + ), + 'Plugin\Bigboard\Controller' => array( + 'Bigboard', + 'BoardAjaxController' + ) + ); + } + + public function onStartup() + { + Translator::load($this->languageModel->getCurrentLanguage(), __DIR__.'/Locale'); + } + + public function getPluginName() + { + return 'Bigboard'; + } + + public function getPluginDescription() + { + return t('Kanboard that displays multiple projects'); + } + + public function getPluginAuthor() + { + return 'Thomas Stinner'; + } + + public function getPluginVersion() + { + return '1.0.5'; + } + + public function getPluginHomepage() + { + return 'https://github.com/stinnux/kanboard-bigboard'; + } + + public function getCompatibleVersion() + { + return '>=1.2.4'; + } + +} diff --git a/plugins/Bigboard/README.md b/plugins/Bigboard/README.md new file mode 100644 index 00000000..aca27a38 --- /dev/null +++ b/plugins/Bigboard/README.md @@ -0,0 +1,10 @@ +kanboard-bigboard +================= + +A Kanboard that can display multiple projects. + +Go To Project-Management->BigBoard. Here you will see all projects you have access to in one page. + +All functionality is still available, so you can drag-and-drop tasks, add tasks etc. + +Comments Welcome. diff --git a/plugins/Bigboard/Template/Bigboard.php b/plugins/Bigboard/Template/Bigboard.php new file mode 100644 index 00000000..6767da64 --- /dev/null +++ b/plugins/Bigboard/Template/Bigboard.php @@ -0,0 +1,15 @@ +app->getRouterController(); + $routerPlugin = $this->app->getPluginName(); + + $active = $routerController == 'Bigboard' && $routerPlugin == 'Bigboard'; +?> +
  • + + url->link( + 'Bigboard', + 'Bigboard', + 'index', + ['plugin' => 'Bigboard', ] + ) ?> +
  • diff --git a/plugins/Bigboard/Template/board/dropdown.php b/plugins/Bigboard/Template/board/dropdown.php new file mode 100644 index 00000000..de64917d --- /dev/null +++ b/plugins/Bigboard/Template/board/dropdown.php @@ -0,0 +1,32 @@ +
    + +
    diff --git a/plugins/Bigboard/Template/board/show.php b/plugins/Bigboard/Template/board/show.php new file mode 100644 index 00000000..dcc10e71 --- /dev/null +++ b/plugins/Bigboard/Template/board/show.php @@ -0,0 +1,5 @@ + diff --git a/plugins/Bigboard/Template/board/table_container.php b/plugins/Bigboard/Template/board/table_container.php new file mode 100644 index 00000000..e7fc6116 --- /dev/null +++ b/plugins/Bigboard/Template/board/table_container.php @@ -0,0 +1,58 @@ +
    '> + +

    + + + + + +
    $project['id'])) ?>" + data-reload-url="url->href('BoardAjaxController', 'reload', array('plugin' => "Bigboard", 'project_id' => $project['id'])) ?>" + data-check-url="url->href('BoardAjaxController', 'check', array('plugin' => "Bigboard", 'project_id' => $project['id'], 'timestamp' => time())) ?>" + data-task-creation-url="url->href('TaskCreationController', 'show', array('plugin' => "Bigboard", 'project_id' => $project['id'])) ?>" + > + + + $swimlane): ?> + + + + 0 && $swimlane['nb_swimlanes'] > 1): ?> + render('board/table_swimlane', array( + 'project' => $project, + 'swimlane' => $swimlane, + 'not_editable' => isset($not_editable), + )) ?> + + + render('board/table_column', array( + 'swimlane' => $swimlane, + 'not_editable' => isset($not_editable), + )) ?> + + 1): ?> + render('board/table_swimlane', array( + 'project' => $project, + 'swimlane' => $swimlane, + 'not_editable' => isset($not_editable), + )) ?> + + + render('bigboard:board/table_tasks', array( + 'project' => $project, + 'swimlane' => $swimlane, + 'not_editable' => isset($not_editable), + 'board_highlight_period' => $board_highlight_period, + )) ?> + + + + +
    + + +
    diff --git a/plugins/Bigboard/Template/board/table_tasks.php b/plugins/Bigboard/Template/board/table_tasks.php new file mode 100644 index 00000000..3d280489 --- /dev/null +++ b/plugins/Bigboard/Template/board/table_tasks.php @@ -0,0 +1,38 @@ + + + + + + +
    + + + render($not_editable ? 'board/task_public' : 'bigboard:board/task_private', array( + 'project' => $project, + 'task' => $task, + 'board_highlight_period' => $board_highlight_period, + 'not_editable' => $not_editable, + )) ?> + +
    + + +
    +
    +
    + text->e($column['title']) ?> +
    +
    +
    + + + diff --git a/plugins/Bigboard/Template/board/task_private.php b/plugins/Bigboard/Template/board/task_private.php new file mode 100644 index 00000000..d97e0e2c --- /dev/null +++ b/plugins/Bigboard/Template/board/task_private.php @@ -0,0 +1,66 @@ +
    + + + + board->isCollapsed($task['project_id'])): ?> +
    + + user->hasProjectAccess('TaskModificationController', 'edit', $task['project_id'])): ?> + render('task/dropdown', array('task' => $task)) ?> + + + + + + + text->e($this->user->getInitials($task['assignee_name'] ?: $task['assignee_username'])) ?> + - + + url->link($this->text->e($task['title']), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'tooltip', $this->text->e($task['title'])) ?> +
    + +
    + +
    + user->hasProjectAccess('TaskModificationController', 'edit', $task['project_id'])): ?> + render('task/dropdown', array('task' => $task)) ?> + + + + + + + text->e($task['assignee_name'] ?: $task['assignee_username']) ?> + + + + render('board/task_avatar', array('task' => $task)) ?> +
    + + hook->render('template:board:private:task:before-title', array('task' => $task)) ?> +
    + url->link($this->text->e($task['title']), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> +
    + hook->render('template:board:private:task:after-title', array('task' => $task)) ?> + + render('board/task_footer', array( + 'task' => $task, + 'not_editable' => $not_editable, + 'project' => $project, + )) ?> +
    + +
    diff --git a/plugins/Bigboard/Template/board/view.php b/plugins/Bigboard/Template/board/view.php new file mode 100644 index 00000000..7987e7c2 --- /dev/null +++ b/plugins/Bigboard/Template/board/view.php @@ -0,0 +1,20 @@ +
    + +

    text->e($project['name']) ?> + + + app->tooltipMarkdown($project['description']) ?> + + + +

    + + + render('bigboard:board/table_container', array( + 'project' => $project, + 'swimlanes' => $swimlanes, + 'board_private_refresh_interval' => $board_private_refresh_interval, + 'board_highlight_period' => $board_highlight_period, + )) ?> + +
    diff --git a/plugins/Bigboard/UserSession.php b/plugins/Bigboard/UserSession.php new file mode 100644 index 00000000..8bdea501 --- /dev/null +++ b/plugins/Bigboard/UserSession.php @@ -0,0 +1,29 @@ +