├── .gitignore
├── Template
├── table_view
│ ├── position.php
│ ├── reference.php
│ ├── subtask.php
│ ├── column.php
│ ├── swimlane.php
│ ├── priority.php
│ ├── meta_magik.php
│ ├── start_date.php
│ ├── title.php
│ ├── tag.php
│ ├── other_assignees.php
│ ├── task_id.php
│ ├── assigned_group.php
│ ├── due_date.php
│ ├── sort_menu.php
│ ├── category.php
│ ├── assignee.php
│ └── header.php
├── project_header
│ └── views.php
└── project_dashboard
│ └── show.php
├── Screenshot
└── 1.png
├── Asset
├── main.css
├── hide-list.js
└── main.js
├── Locale
└── zh_CN
│ └── translations.php
├── LICENSE
├── config.default.php
├── Helper
└── TableDataHelper.php
├── Controller
├── ExportController.php
└── TableViewController.php
├── Plugin.php
├── README.md
└── Model
└── FieldDataModel.php
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /config.php
3 |
--------------------------------------------------------------------------------
/Template/table_view/position.php:
--------------------------------------------------------------------------------
1 | = $task['position'] ?>
2 |
--------------------------------------------------------------------------------
/Template/table_view/reference.php:
--------------------------------------------------------------------------------
1 | = $task['reference'] ?>
2 |
--------------------------------------------------------------------------------
/Template/table_view/subtask.php:
--------------------------------------------------------------------------------
1 | = count($task['subtasks']) ?>
2 |
--------------------------------------------------------------------------------
/Template/table_view/column.php:
--------------------------------------------------------------------------------
1 | = $this->text->e($task['column_name']) ?>
2 |
--------------------------------------------------------------------------------
/Template/table_view/swimlane.php:
--------------------------------------------------------------------------------
1 | = $this->text->e($task['swimlane_name']) ?>
2 |
--------------------------------------------------------------------------------
/Screenshot/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/greyaz/TableView/HEAD/Screenshot/1.png
--------------------------------------------------------------------------------
/Template/table_view/priority.php:
--------------------------------------------------------------------------------
1 | = $this->task->renderPriority($task['priority']) ?>
2 |
--------------------------------------------------------------------------------
/Asset/main.css:
--------------------------------------------------------------------------------
1 | #table-view .status-closed{
2 | text-decoration:line-through;
3 | }
4 |
5 |
--------------------------------------------------------------------------------
/Template/table_view/meta_magik.php:
--------------------------------------------------------------------------------
1 |
2 | = $metadata[$key] ?>
3 |
4 |
--------------------------------------------------------------------------------
/Template/table_view/start_date.php:
--------------------------------------------------------------------------------
1 |
2 | = $this->dt->date($task['date_started']) ?>
3 |
4 |
--------------------------------------------------------------------------------
/Locale/zh_CN/translations.php:
--------------------------------------------------------------------------------
1 | "表格",
5 | 'DESC' => "降序",
6 | 'ASC' => "升序",
7 | 'Export All' => "全部导出"
8 | );
9 |
--------------------------------------------------------------------------------
/Template/table_view/title.php:
--------------------------------------------------------------------------------
1 | = $this->url->link($this->text->e($task['title']), 'TaskViewController', 'show', array('task_id' => $task['id'])) ?>
2 |
--------------------------------------------------------------------------------
/Template/table_view/tag.php:
--------------------------------------------------------------------------------
1 |
2 | ">
3 | = $this->text->e($tag['name']) ?>
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Template/table_view/other_assignees.php:
--------------------------------------------------------------------------------
1 | 0 && count($this->task->multiselectMemberModel->getMembers($task['owner_ms'])) > 0) : ?>
2 | = $this->helper->sizeAvatarHelperExtend->sizeMultiple($task['owner_ms'], 'avatar-inline avatar-bdyn', 20) ?>
3 |
4 |
--------------------------------------------------------------------------------
/Template/table_view/task_id.php:
--------------------------------------------------------------------------------
1 | user->hasProjectAccess('TaskModificationController', 'edit', $task['project_id'])): ?>
2 | = $this->render('task/dropdown', array('task' => $task, 'redirect' => isset($redirect) ? $redirect : '')) ?>
3 |
4 | = '#'.$task['id'] ?>
5 |
6 |
--------------------------------------------------------------------------------
/Asset/hide-list.js:
--------------------------------------------------------------------------------
1 | (function($){
2 | if ($){
3 | $(document).ready(function(){
4 | var $listView = $(".views-switcher-component .views li .view-listing");
5 | if ($listView.length == 1){
6 | $listView.parent().hide();
7 | }
8 | });
9 | }
10 | })(typeof jQuery == "undefined" ? null: jQuery);
11 |
--------------------------------------------------------------------------------
/Template/table_view/assigned_group.php:
--------------------------------------------------------------------------------
1 |
2 | = $this->text->e($task['assigned_groupname'] ?: $task['owner_gp']) ?>
3 |
4 |
--------------------------------------------------------------------------------
/Template/table_view/due_date.php:
--------------------------------------------------------------------------------
1 |
2 |
9 | = $this->dt->datetime($task['date_due']) ?>
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Template/project_header/views.php:
--------------------------------------------------------------------------------
1 |
app->checkMenuSelection('TableViewController') ?>>
2 | = $this->url->icon('table', t('Table'), 'TableViewController', 'show',
3 | array(
4 | 'project_id' => $project['id'],
5 | 'search' => $filters['search'],
6 | 'plugin' => 'TableView'
7 | ),
8 | false,
9 | "table-view",
10 | t('Keyboard shortcut: "%s"', 'v t')
11 | ) ?>
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Template/table_view/sort_menu.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $sort): ?>
5 |
6 | -
7 | = $paginator->order($name, $sort) ?>
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Asset/main.js:
--------------------------------------------------------------------------------
1 | (function(KB){
2 | if (KB){
3 | KB.on('dom.ready', function () {
4 | function goToLink (selector) {
5 | if (! KB.modal.isOpen()) {
6 | var element = KB.find(selector);
7 |
8 | if (element !== null) {
9 | window.location = element.attr('href');
10 | }
11 | }
12 | }
13 |
14 | KB.onKey('v+t', function () {
15 | goToLink('a.table-view');
16 | });
17 | });
18 | }
19 | })(typeof KB == "undefined" ? null: KB);
20 |
21 |
--------------------------------------------------------------------------------
/Template/table_view/category.php:
--------------------------------------------------------------------------------
1 |
2 | ">
3 | user->hasProjectAccess('TaskModificationController', 'edit', $task['project_id'])): ?>
4 | = $this->url->link(
5 | $this->text->e($task['category_name']),
6 | 'TaskModificationController',
7 | 'edit',
8 | array('task_id' => $task['id']),
9 | false,
10 | 'js-modal-medium' . (! empty($task['category_description']) ? ' tooltip' : ''),
11 | t('Change category')
12 | ) ?>
13 |
14 | = $this->app->tooltipMarkdown($task['category_description']) ?>
15 |
16 |
17 | = $this->text->e($task['category_name']) ?>
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Template/table_view/assignee.php:
--------------------------------------------------------------------------------
1 |
2 |
3 | user->hasProjectAccess('TaskModificationController', 'edit', $task['project_id'])): ?>
5 | class="task-board-change-assignee" data-url="= $this->url->href('TaskModificationController', 'edit', array('task_id' => $task['id'])) ?>">
6 |
7 | class="task-board-assignee">
8 |
9 | = $this->avatar->small(
10 | $task['owner_id'],
11 | $task['assignee_username'],
12 | $task['assignee_name'],
13 | $task['assignee_email'],
14 | $task['assignee_avatar_path'],
15 | 'avatar-inline'
16 | ) ?>
17 | = $this->text->e($task['assignee_name'] ?: $task['assignee_username']) ?>
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Yang Zhang
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/config.default.php:
--------------------------------------------------------------------------------
1 | "Progress",
41 | );
42 |
--------------------------------------------------------------------------------
/Helper/TableDataHelper.php:
--------------------------------------------------------------------------------
1 | paginator
12 | ->setUrl('TableViewController', 'show', array(
13 | 'project_id' => $project_id,
14 | 'search' => $search,
15 | 'plugin' => 'TableView',
16 | 'csrf_token' => $this->token->getReusableCSRFToken()
17 | ))
18 | ->setMax($amount)
19 | ->setOrder($order)
20 | ->setDirection($direction)
21 | ->setFormatter($this->taskListSubtaskFormatter)
22 | ->setQuery($this->taskLexer
23 | ->build($search)
24 | ->withFilter(new TaskProjectFilter($project_id))
25 | ->getQuery()
26 | )
27 | ->calculate();
28 | }
29 |
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/Controller/ExportController.php:
--------------------------------------------------------------------------------
1 | request->getStringParam('count');
35 | $project_id = (int)$this->request->getStringParam('project_id');
36 | $project = $this->getProject($project_id);
37 | $search = $this->helper->projectHeader->getSearchQuery($project);
38 | list($order, $direction) = $this->userSession->getListOrder($project_id);
39 |
40 | $paginator = $this->helper->tableDataHelper->getPaginator($project_id, $search, $count, $order, $direction);
41 | $tasks = $paginator->getCollection();
42 | $data = $this->getData($tasks);
43 |
44 | $this->response->withFileDownload('tasks.csv');
45 | $this->response->csv($data);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Plugin.php:
--------------------------------------------------------------------------------
1 | helper->register('tableDataHelper', '\Kanboard\Plugin\TableView\Helper\TableDataHelper');
18 |
19 | $this->template->hook->attach('template:project-header:view-switcher', 'TableView:project_header/views');
20 | $this->hook->on('template:layout:css', array('template' => 'plugins/TableView/Asset/main.css'));
21 | $this->hook->on('template:layout:js', array('template' => 'plugins/TableView/Asset/main.js'));
22 |
23 | if (!isset($configs["HIDE_LIST_VIEW"]) || $configs["HIDE_LIST_VIEW"] === true)
24 | {
25 | $this->hook->on('template:layout:js', array('template' => 'plugins/TableView/Asset/hide-list.js'));
26 | }
27 | }
28 | }
29 |
30 | public function onStartup()
31 | {
32 | Translator::load($this->languageModel->getCurrentLanguage(), __DIR__.'/Locale');
33 | }
34 |
35 | public function getPluginName()
36 | {
37 | return 'TableView';
38 | }
39 |
40 | public function getPluginDescription()
41 | {
42 | return t('A Kanboard plugin that provides a table view of tasks in your project.');
43 | }
44 |
45 | public function getPluginAuthor()
46 | {
47 | return 'Greyaz';
48 | }
49 |
50 | public function getPluginVersion()
51 | {
52 | return '0.2.0';
53 | }
54 |
55 | public function getPluginHomepage()
56 | {
57 | return 'https://github.com/greyaz/TableView';
58 | }
59 | }
60 |
61 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Table View
2 | A [Kanboard](https://github.com/kanboard/kanboard) plugin that provides a table view of tasks in your project.
3 |
4 | 
5 |
6 | ## Features
7 | 1. Hide orignal list view or not
8 | 2. Customizable table fields
9 | 3. Compatible with the plugins "Group_assigne" and "metaMagik"
10 | 4. Export tasks
11 |
12 | ## Getting started
13 | 1. Install from the Kanboard plugin manager directly. Or clone this repository to your plugin folder.
14 | 2. Copy and rename the file `config.default.php` to `config.php`, then edit it by following the instructions in the comments.
15 |
16 | ## Configuration Items
17 |
18 | **$configs["HIDE_LIST_VIEW"] : Boolean**
19 | > Hide the list view or not. Default: true
20 | > ```php
21 | > $configs["HIDE_LIST_VIEW"] = true;
22 | > ```
23 |
24 |
25 |
26 | **$configs["TABLE_FIELDS"] : Array**
27 | > The fields display in the table by the sequence in this array.
28 | > ```php
29 | > $configs["TABLE_FIELDS"] = array(
30 | > "::PRIORITY", "::TASK_ID", "::TITLE", "::COLUMN", "::ASSIGNEE", "::DUE_DATE", "::METAMAGIK::expected_launch_date"
31 | > );
32 | > ```
33 | > The following keywords are supported by default:
34 | > - ::ASSIGNEE
35 | > - ::CATEGORY
36 | > - ::COLUMN
37 | > - ::DUE_DATE
38 | > - ::PRIORITY
39 | > - ::POSITION
40 | > - ::REFERENCE
41 | > - ::START_DATE
42 | > - ::SUBTASK_NUMBER
43 | > - ::SWIMLANE
44 | > - ::TAG
45 | > - ::TASK_ID
46 | > - ::TITLE
47 | >
48 | > The following keywords are supported after installing the plugin "Group_assign":
49 | > - ::ASSIGNED_GROUP
50 | > - ::OTHER_ASSIGNEES
51 | >
52 | > If the plugin "metaMagik" is installed, your custom field can be loaded via the prefix "::METAMAGIK::" with your field name. Example:
53 | > - ::METAMAGIK::expected_launch_date
54 |
55 |
56 |
57 | **$configs["CUSTOMIZED_FIELD_NAMES"] : Array**
58 | > Optional. Customize the names of the fields. Example:
59 | > ```php
60 | > $configs["CUSTOMIZED_FIELD_NAMES"] = array(
61 | > "::COLUMN" => "Progress",
62 | > );
63 | > ```
64 |
65 | ## Author
66 | Greyaz
67 |
68 | ## License
69 | License MIT
70 |
--------------------------------------------------------------------------------
/Controller/TableViewController.php:
--------------------------------------------------------------------------------
1 | fieldDataModel = new FieldDataModel($c);
17 | }
18 |
19 | public function show()
20 | {
21 | $project = $this->getProject();
22 | $search = $this->helper->projectHeader->getSearchQuery($project);
23 |
24 | $req_direction = $this->request->getStringParam('direction');
25 | $req_order = $this->request->getStringParam('order');
26 | if (!empty($req_direction) || !empty($req_order)) {
27 | $this->checkReusableGETCSRFParam();
28 | }
29 |
30 | list($order, $direction) = $this->userSession->getListOrder($project['id']);
31 | if (!empty($req_direction)){
32 | $direction = $req_direction;
33 | }
34 | if (!empty($req_order)){
35 | $order = $req_order;
36 | }
37 | $this->userSession->setListOrder($project['id'], $order, $direction);
38 |
39 | $paginator = $this->helper->tableDataHelper->getPaginator($project['id'], $search, 30, $order, $direction);
40 | $fieldNames = array();
41 | foreach($GLOBALS["configs"]["TABLE_FIELDS"] as $field){
42 | $fieldNames[] = $this->fieldDataModel->getName($field);
43 | }
44 |
45 | $this->response->html($this->helper->layout->app('TableView:project_dashboard/show', array(
46 | 'project' => $project,
47 | 'title' => $project["name"],
48 | 'description' => $this->helper->projectHeader->getDescription($project),
49 | 'paginator' => $paginator,
50 | 'fields' => $GLOBALS["configs"]["TABLE_FIELDS"],
51 | 'field_names' => $fieldNames,
52 | 'order' => $order,
53 | 'direction' => $direction,
54 | 'all_sorts' => $this->fieldDataModel->getAllSorts(),
55 | )));
56 | }
57 | }
58 |
59 |
--------------------------------------------------------------------------------
/Template/table_view/header.php:
--------------------------------------------------------------------------------
1 |
39 |
--------------------------------------------------------------------------------
/Model/FieldDataModel.php:
--------------------------------------------------------------------------------
1 | defaultNames = array(
14 | "::ASSIGNEE" => array("name" => t("Assignee"), "sort" => "assignee_name"),
15 | "::CATEGORY" => array("name" => t("Category"), "sort" => "category_name"),
16 | "::COLUMN" => array("name" => t("Column"), "sort" => "column_name"),
17 | "::DUE_DATE" => array("name" => t("Due date"), "sort" => \Kanboard\Model\TaskModel::TABLE.".date_due"),
18 | "::PRIORITY" => array("name" => t("Priority"), "sort" => \Kanboard\Model\TaskModel::TABLE.".priority"),
19 | "::POSITION" => array("name" => t("Position"), "sort" => \Kanboard\Model\TaskModel::TABLE.".position"),
20 | "::REFERENCE" => array("name" => t("Reference"), "sort" => \Kanboard\Model\TaskModel::TABLE.".reference"),
21 | "::START_DATE" => array("name" => t("Start date"), "sort" => \Kanboard\Model\TaskModel::TABLE.".date_started"),
22 | "::SUBTASK_NUMBER" => array("name" => t("Subtask"), "sort" => ""),
23 | "::SWIMLANE" => array("name" => t("Swimlane"), "sort" => "swimlane_name"),
24 | "::TAG" => array("name" => t("Tag"), "sort" => ""),
25 | "::TASK_ID" => array("name" => t("Task ID"), "sort" => \Kanboard\Model\TaskModel::TABLE.".id"),
26 | "::TITLE" => array("name" => t("Title"), "sort" => \Kanboard\Model\TaskModel::TABLE.".title"),
27 | "::ASSIGNED_GROUP" => array("name" => t("Assigned Group"), "sort" => ""),
28 | "::OTHER_ASSIGNEES" => array("name" => t("Other Assignees"), "sort" => ""),
29 | );
30 | }
31 |
32 | public function getName($field){
33 | if (!empty($GLOBALS["configs"]["CUSTOMIZED_FIELD_NAMES"][$field])){
34 | return $GLOBALS["configs"]["CUSTOMIZED_FIELD_NAMES"][$field];
35 | }
36 | if (!empty($this->defaultNames[$field])){
37 | return $this->defaultNames[$field]["name"];
38 | }
39 | $splited = explode("::METAMAGIK::", $field);
40 | if ($splited[0] != $field){
41 | return str_replace("_", " ", $splited[1]);
42 | }
43 | return $field;
44 | }
45 |
46 | public function getAllSorts(){
47 | $sortList = array();
48 | foreach($this->defaultNames as $key => $value){
49 | $sortList[$this->getName($key)] = $this->defaultNames[$key]["sort"];
50 | }
51 | return $sortList;
52 | }
53 | }
54 |
55 |
--------------------------------------------------------------------------------
/Template/project_dashboard/show.php:
--------------------------------------------------------------------------------
1 |
2 | = $this->projectHeader->render($project, 'TableViewController', 'show', false, 'TableView') ?>
3 |
67 |
68 |
--------------------------------------------------------------------------------