summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/Controller/SubtaskStatusController.php37
-rw-r--r--app/Controller/TaskListController.php14
-rw-r--r--app/Core/Base.php1
-rw-r--r--app/Core/Session/SessionStorage.php2
-rw-r--r--app/Core/User/UserSession.php11
-rw-r--r--app/Formatter/BoardFormatter.php2
-rw-r--r--app/Formatter/SubtaskListFormatter.php2
-rw-r--r--app/Formatter/TaskListFormatter.php4
-rw-r--r--app/Formatter/TaskListSubtaskFormatter.php29
-rw-r--r--app/Helper/SubtaskHelper.php59
-rw-r--r--app/Helper/UserHelper.php13
-rw-r--r--app/Model/SubtaskModel.php17
-rw-r--r--app/Model/TaskTagModel.php3
-rw-r--r--app/ServiceProvider/FormatterProvider.php1
-rw-r--r--app/Template/board/tooltip_subtasks.php2
-rw-r--r--app/Template/dashboard/subtasks.php2
-rw-r--r--app/Template/subtask/table.php24
-rw-r--r--app/Template/subtask/timer.php13
-rw-r--r--app/Template/task_list/header.php6
-rw-r--r--app/Template/task_list/listing.php13
-rw-r--r--app/Template/task_list/task_subtasks.php22
21 files changed, 207 insertions, 70 deletions
diff --git a/app/Controller/SubtaskStatusController.php b/app/Controller/SubtaskStatusController.php
index d4d356c3..72feb685 100644
--- a/app/Controller/SubtaskStatusController.php
+++ b/app/Controller/SubtaskStatusController.php
@@ -21,15 +21,9 @@ class SubtaskStatusController extends BaseController
$subtask = $this->getSubtask();
$status = $this->subtaskStatusModel->toggleStatus($subtask['id']);
+ $subtask['status'] = $status;
- if ($this->request->getIntegerParam('refresh-table') === 0) {
- $subtask['status'] = $status;
- $html = $this->helper->subtask->toggleStatus($subtask, $task['project_id']);
- } else {
- $html = $this->renderTable($task);
- }
-
- $this->response->html($html);
+ $this->response->html($this->helper->subtask->renderToggleStatus($task, $subtask));
}
/**
@@ -40,32 +34,19 @@ class SubtaskStatusController extends BaseController
public function timer()
{
$task = $this->getTask();
- $subtask_id = $this->request->getIntegerParam('subtask_id');
+ $subtaskId = $this->request->getIntegerParam('subtask_id');
$timer = $this->request->getStringParam('timer');
if ($timer === 'start') {
- $this->subtaskTimeTrackingModel->logStartTime($subtask_id, $this->userSession->getId());
+ $this->subtaskTimeTrackingModel->logStartTime($subtaskId, $this->userSession->getId());
} elseif ($timer === 'stop') {
- $this->subtaskTimeTrackingModel->logEndTime($subtask_id, $this->userSession->getId());
+ $this->subtaskTimeTrackingModel->logEndTime($subtaskId, $this->userSession->getId());
$this->subtaskTimeTrackingModel->updateTaskTimeTracking($task['id']);
}
- $this->response->html($this->renderTable($task));
- }
-
- /**
- * Render table
- *
- * @access private
- * @param array $task
- * @return string
- */
- private function renderTable(array $task)
- {
- return $this->template->render('subtask/table', array(
- 'task' => $task,
- 'subtasks' => $this->subtaskModel->getAll($task['id']),
- 'editable' => true,
- ));
+ $this->response->html($this->template->render('subtask/timer', array(
+ 'task' => $task,
+ 'subtask' => $this->subtaskModel->getByIdWithDetails($subtaskId),
+ )));
}
}
diff --git a/app/Controller/TaskListController.php b/app/Controller/TaskListController.php
index b3709a18..f2f2f6e5 100644
--- a/app/Controller/TaskListController.php
+++ b/app/Controller/TaskListController.php
@@ -23,12 +23,24 @@ class TaskListController extends BaseController
$project = $this->getProject();
$search = $this->helper->projectHeader->getSearchQuery($project);
+ if ($this->request->getIntegerParam('show_subtasks')) {
+ $this->sessionStorage->subtaskListToggle = true;
+ } elseif ($this->request->getIntegerParam('hide_subtasks')) {
+ $this->sessionStorage->subtaskListToggle = false;
+ }
+
+ if ($this->userSession->hasSubtaskListActivated()) {
+ $formatter = $this->taskListSubtaskFormatter;
+ } else {
+ $formatter = $this->taskListFormatter;
+ }
+
$paginator = $this->paginator
->setUrl('TaskListController', 'show', array('project_id' => $project['id']))
->setMax(30)
->setOrder(TaskModel::TABLE.'.id')
->setDirection('DESC')
- ->setFormatter($this->taskListFormatter)
+ ->setFormatter($formatter)
->setQuery($this->taskLexer
->build($search)
->withFilter(new TaskProjectFilter($project['id']))
diff --git a/app/Core/Base.php b/app/Core/Base.php
index 8ef34bfb..28bbd534 100644
--- a/app/Core/Base.php
+++ b/app/Core/Base.php
@@ -76,6 +76,7 @@ use Pimple\Container;
* @property \Kanboard\Formatter\TaskGanttFormatter $taskGanttFormatter
* @property \Kanboard\Formatter\TaskICalFormatter $taskICalFormatter
* @property \Kanboard\Formatter\TaskListFormatter $taskListFormatter
+ * @property \Kanboard\Formatter\TaskListSubtaskFormatter $taskListSubtaskFormatter
* @property \Kanboard\Formatter\TaskSuggestMenuFormatter $taskSuggestMenuFormatter
* @property \Kanboard\Formatter\UserAutoCompleteFormatter $userAutoCompleteFormatter
* @property \Kanboard\Formatter\UserMentionFormatter $userMentionFormatter
diff --git a/app/Core/Session/SessionStorage.php b/app/Core/Session/SessionStorage.php
index e6478d8d..bb6771f1 100644
--- a/app/Core/Session/SessionStorage.php
+++ b/app/Core/Session/SessionStorage.php
@@ -18,7 +18,7 @@ namespace Kanboard\Core\Session;
* @property string $commentSorting
* @property bool $hasSubtaskInProgress
* @property bool $hasRememberMe
- * @property bool $boardCollapsed
+ * @property bool $subtaskListToggle
* @property string $scope
* @property bool $twoFactorBeforeCodeCalled
* @property string $twoFactorSecret
diff --git a/app/Core/User/UserSession.php b/app/Core/User/UserSession.php
index 7917b223..f3f7359a 100644
--- a/app/Core/User/UserSession.php
+++ b/app/Core/User/UserSession.php
@@ -146,6 +146,17 @@ class UserSession extends Base
}
/**
+ * Return true if subtask list toggle is active
+ *
+ * @access public
+ * @return string
+ */
+ public function hasSubtaskListActivated()
+ {
+ return isset($this->sessionStorage->subtaskListToggle) && ! empty($this->sessionStorage->subtaskListToggle);
+ }
+
+ /**
* Check is the user is connected
*
* @access public
diff --git a/app/Formatter/BoardFormatter.php b/app/Formatter/BoardFormatter.php
index 59a8fb18..a43790b0 100644
--- a/app/Formatter/BoardFormatter.php
+++ b/app/Formatter/BoardFormatter.php
@@ -58,7 +58,7 @@ class BoardFormatter extends BaseFormatter implements FormatterInterface
->findAll();
$task_ids = array_column($tasks, 'id');
- $tags = $this->taskTagModel->getTagsByTasks($task_ids);
+ $tags = $this->taskTagModel->getTagsByTaskIds($task_ids);
return $this->boardSwimlaneFormatter
->withSwimlanes($swimlanes)
diff --git a/app/Formatter/SubtaskListFormatter.php b/app/Formatter/SubtaskListFormatter.php
index 5b20111d..8af5b1f5 100644
--- a/app/Formatter/SubtaskListFormatter.php
+++ b/app/Formatter/SubtaskListFormatter.php
@@ -16,7 +16,7 @@ class SubtaskListFormatter extends BaseFormatter implements FormatterInterface
* Apply formatter
*
* @access public
- * @return mixed
+ * @return array
*/
public function format()
{
diff --git a/app/Formatter/TaskListFormatter.php b/app/Formatter/TaskListFormatter.php
index 01fbbae0..2f83469d 100644
--- a/app/Formatter/TaskListFormatter.php
+++ b/app/Formatter/TaskListFormatter.php
@@ -16,13 +16,13 @@ class TaskListFormatter extends BaseFormatter implements FormatterInterface
* Apply formatter
*
* @access public
- * @return mixed
+ * @return array
*/
public function format()
{
$tasks = $this->query->findAll();
$taskIds = array_column($tasks, 'id');
- $tags = $this->taskTagModel->getTagsByTasks($taskIds);
+ $tags = $this->taskTagModel->getTagsByTaskIds($taskIds);
array_merge_relation($tasks, $tags, 'tags', 'id');
return $tasks;
diff --git a/app/Formatter/TaskListSubtaskFormatter.php b/app/Formatter/TaskListSubtaskFormatter.php
new file mode 100644
index 00000000..4f889674
--- /dev/null
+++ b/app/Formatter/TaskListSubtaskFormatter.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace Kanboard\Formatter;
+
+/**
+ * Class TaskListSubtaskFormatter
+ *
+ * @package Kanboard\Formatter
+ * @author Frederic Guillot
+ */
+class TaskListSubtaskFormatter extends TaskListFormatter
+{
+ /**
+ * Apply formatter
+ *
+ * @access public
+ * @return array
+ */
+ public function format()
+ {
+ $tasks = parent::format();
+ $taskIds = array_column($tasks, 'id');
+ $subtasks = $this->subtaskModel->getAllByTaskIds($taskIds);
+ $subtasks = array_column_index($subtasks, 'task_id');
+ array_merge_relation($tasks, $subtasks, 'subtasks', 'id');
+
+ return $tasks;
+ }
+}
diff --git a/app/Helper/SubtaskHelper.php b/app/Helper/SubtaskHelper.php
index 1b8b0e51..eea1ed63 100644
--- a/app/Helper/SubtaskHelper.php
+++ b/app/Helper/SubtaskHelper.php
@@ -12,7 +12,23 @@ use Kanboard\Core\Base;
*/
class SubtaskHelper extends Base
{
- public function getTitle(array $subtask)
+ /**
+ * Return if the current user has a subtask in progress
+ *
+ * @return bool
+ */
+ public function hasSubtaskInProgress()
+ {
+ return isset($this->sessionStorage->hasSubtaskInProgress) && $this->sessionStorage->hasSubtaskInProgress;
+ }
+
+ /**
+ * Render subtask title
+ *
+ * @param array $subtask
+ * @return string
+ */
+ public function renderTitle(array $subtask)
{
if ($subtask['status'] == 0) {
$html = '<i class="fa fa-square-o fa-fw"></i>';
@@ -29,25 +45,46 @@ class SubtaskHelper extends Base
* Get the link to toggle subtask status
*
* @access public
+ * @param array $task
* @param array $subtask
- * @param integer $project_id
- * @param boolean $refresh_table
* @return string
*/
- public function toggleStatus(array $subtask, $project_id, $refresh_table = false)
+ public function renderToggleStatus(array $task, array $subtask)
{
- if (! $this->helper->user->hasProjectAccess('SubtaskController', 'edit', $project_id)) {
- return $this->getTitle($subtask);
+ if (! $this->helper->user->hasProjectAccess('SubtaskController', 'edit', $task['project_id'])) {
+ $html = $this->renderTitle($subtask);
+ } else {
+ $title = $this->renderTitle($subtask);
+ $params = array(
+ 'project_id' => $task['project_id'],
+ 'task_id' => $subtask['task_id'],
+ 'subtask_id' => $subtask['id'],
+ );
+
+ if ($subtask['status'] == 0 && $this->hasSubtaskInProgress()) {
+ $html = $this->helper->url->link($title, 'SubtaskRestrictionController', 'show', $params, false, 'js-modal-confirm');
+ } else {
+ $html = $this->helper->url->link($title, 'SubtaskStatusController', 'change', $params, false, 'js-subtask-toggle-status');
+ }
}
- $params = array('task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id'], 'refresh-table' => (int) $refresh_table);
+ return '<span class="subtask-title">'.$html.'</span>';
+ }
+
+ public function renderTimer(array $task, array $subtask)
+ {
+ $html = '<span class="subtask-timer-toggle">';
- if ($subtask['status'] == 0 && isset($this->sessionStorage->hasSubtaskInProgress) && $this->sessionStorage->hasSubtaskInProgress) {
- return $this->helper->url->link($this->getTitle($subtask), 'SubtaskRestrictionController', 'show', $params, false, 'js-modal-confirm');
+ if ($subtask['is_timer_started']) {
+ $html .= $this->helper->url->icon('pause', t('Stop timer'), 'SubtaskStatusController', 'timer', array('timer' => 'stop', 'project_id' => $task['project_id'], 'task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id']), false, 'js-subtask-toggle-timer');
+ $html .= ' (' . $this->helper->dt->age($subtask['timer_start_date']) .')';
+ } else {
+ $html .= $this->helper->url->icon('play-circle-o', t('Start timer'), 'SubtaskStatusController', 'timer', array('timer' => 'start', 'project_id' => $task['project_id'], 'task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id']), false, 'js-subtask-toggle-timer');
}
- $class = 'subtask-toggle-status '.($refresh_table ? 'subtask-refresh-table' : '');
- return $this->helper->url->link($this->getTitle($subtask), 'SubtaskStatusController', 'change', $params, false, $class);
+ $html .= '</span>';
+
+ return $html;
}
public function renderTitleField(array $values, array $errors = array(), array $attributes = array())
diff --git a/app/Helper/UserHelper.php b/app/Helper/UserHelper.php
index 8c2567b9..02d1d451 100644
--- a/app/Helper/UserHelper.php
+++ b/app/Helper/UserHelper.php
@@ -13,7 +13,18 @@ use Kanboard\Core\Base;
class UserHelper extends Base
{
/**
- * Return true if the logged user as unread notifications
+ * Return subtask list toggle value
+ *
+ * @access public
+ * @return boolean
+ */
+ public function hasSubtaskListActivated()
+ {
+ return $this->userSession->hasSubtaskListActivated();
+ }
+
+ /**
+ * Return true if the logged user has unread notifications
*
* @access public
* @return boolean
diff --git a/app/Model/SubtaskModel.php b/app/Model/SubtaskModel.php
index af9af93b..c62ddb53 100644
--- a/app/Model/SubtaskModel.php
+++ b/app/Model/SubtaskModel.php
@@ -132,6 +132,23 @@ class SubtaskModel extends Base
}
/**
+ * Get subtasks for a list of tasks
+ *
+ * @param array $taskIds
+ * @return array
+ */
+ public function getAllByTaskIds(array $taskIds)
+ {
+ if (empty($taskIds)) {
+ return array();
+ }
+
+ return $this->subtaskListFormatter
+ ->withQuery($this->getQuery()->in('task_id', $taskIds))
+ ->format();
+ }
+
+ /**
* Get a subtask by the id
*
* @access public
diff --git a/app/Model/TaskTagModel.php b/app/Model/TaskTagModel.php
index 0553cc6c..78b7a90a 100644
--- a/app/Model/TaskTagModel.php
+++ b/app/Model/TaskTagModel.php
@@ -59,7 +59,7 @@ class TaskTagModel extends Base
* @param integer[] $task_ids
* @return array
*/
- public function getTagsByTasks($task_ids)
+ public function getTagsByTaskIds($task_ids)
{
if (empty($task_ids)) {
return array();
@@ -69,6 +69,7 @@ class TaskTagModel extends Base
->columns(TagModel::TABLE.'.id', TagModel::TABLE.'.name', self::TABLE.'.task_id')
->in(self::TABLE.'.task_id', $task_ids)
->join(self::TABLE, 'tag_id', 'id')
+ ->asc(TagModel::TABLE.'.name')
->findAll();
return array_column_index($tags, 'task_id');
diff --git a/app/ServiceProvider/FormatterProvider.php b/app/ServiceProvider/FormatterProvider.php
index 54f9f916..feaa597f 100644
--- a/app/ServiceProvider/FormatterProvider.php
+++ b/app/ServiceProvider/FormatterProvider.php
@@ -30,6 +30,7 @@ class FormatterProvider implements ServiceProviderInterface
'TaskGanttFormatter',
'TaskICalFormatter',
'TaskListFormatter',
+ 'TaskListSubtaskFormatter',
'TaskSuggestMenuFormatter',
'UserAutoCompleteFormatter',
'UserMentionFormatter',
diff --git a/app/Template/board/tooltip_subtasks.php b/app/Template/board/tooltip_subtasks.php
index 335c28ce..753e4910 100644
--- a/app/Template/board/tooltip_subtasks.php
+++ b/app/Template/board/tooltip_subtasks.php
@@ -8,7 +8,7 @@
<?php foreach ($subtasks as $subtask): ?>
<tr>
<td>
- <?= $this->subtask->toggleStatus($subtask, $task['project_id']) ?>
+ <?= $this->subtask->renderToggleStatus($task, $subtask) ?>
</td>
<?= $this->hook->render('template:board:tooltip:subtasks:rows', array('subtask' => $subtask)) ?>
<td>
diff --git a/app/Template/dashboard/subtasks.php b/app/Template/dashboard/subtasks.php
index 13770f0f..d86b1ef9 100644
--- a/app/Template/dashboard/subtasks.php
+++ b/app/Template/dashboard/subtasks.php
@@ -25,7 +25,7 @@
<?= $this->url->link($this->text->e($subtask['task_name']), 'TaskViewController', 'show', array('task_id' => $subtask['task_id'], 'project_id' => $subtask['project_id'])) ?>
</td>
<td>
- <?= $this->subtask->toggleStatus($subtask, $subtask['project_id']) ?>
+ <?= $this->subtask->renderToggleStatus(array('project_id' => $subtask['project_id']), $subtask) ?>
</td>
<?= $this->hook->render('template:dashboard:subtasks:rows', array('subtask' => $subtask)) ?>
<td>
diff --git a/app/Template/subtask/table.php b/app/Template/subtask/table.php
index bfb42e75..5488796d 100644
--- a/app/Template/subtask/table.php
+++ b/app/Template/subtask/table.php
@@ -21,9 +21,9 @@
'task' => $task,
'subtask' => $subtask,
)) ?>
- <?= $this->subtask->toggleStatus($subtask, $task['project_id'], true) ?>
+ <?= $this->subtask->renderToggleStatus($task, $subtask, true) ?>
<?php else: ?>
- <?= $this->subtask->getTitle($subtask) ?>
+ <?= $this->subtask->renderTitle($subtask) ?>
<?php endif ?>
</td>
<td>
@@ -33,22 +33,10 @@
</td>
<?= $this->hook->render('template:subtask:table:rows', array('subtask' => $subtask)) ?>
<td>
- <?php if (! empty($subtask['time_spent'])): ?>
- <strong><?= $this->text->e($subtask['time_spent']).'h' ?></strong> <?= t('spent') ?>
- <?php endif ?>
-
- <?php if (! empty($subtask['time_estimated'])): ?>
- <strong><?= $this->text->e($subtask['time_estimated']).'h' ?></strong> <?= t('estimated') ?>
- <?php endif ?>
-
- <?php if ($editable && $subtask['user_id'] == $this->user->getId()): ?>
- <?php if ($subtask['is_timer_started']): ?>
- <?= $this->url->icon('pause', t('Stop timer'), 'SubtaskStatusController', 'timer', array('timer' => 'stop', 'project_id' => $task['project_id'], 'task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id']), false, 'subtask-toggle-timer') ?>
- (<?= $this->dt->age($subtask['timer_start_date']) ?>)
- <?php else: ?>
- <?= $this->url->icon('play-circle-o', t('Start timer'), 'SubtaskStatusController', 'timer', array('timer' => 'start', 'project_id' => $task['project_id'], 'task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id']), false, 'subtask-toggle-timer') ?>
- <?php endif ?>
- <?php endif ?>
+ <?= $this->render('subtask/timer', array(
+ 'task' => $task,
+ 'subtask' => $subtask,
+ )) ?>
</td>
</tr>
<?php endforeach ?>
diff --git a/app/Template/subtask/timer.php b/app/Template/subtask/timer.php
new file mode 100644
index 00000000..2102e6cc
--- /dev/null
+++ b/app/Template/subtask/timer.php
@@ -0,0 +1,13 @@
+<span class="subtask-time-tracking">
+ <?php if (! empty($subtask['time_spent'])): ?>
+ <strong><?= $this->text->e($subtask['time_spent']).'h' ?></strong> <?= t('spent') ?>
+ <?php endif ?>
+
+ <?php if (! empty($subtask['time_estimated'])): ?>
+ <strong><?= $this->text->e($subtask['time_estimated']).'h' ?></strong> <?= t('estimated') ?>
+ <?php endif ?>
+
+ <?php if ($this->user->hasProjectAccess('SubtaskController', 'edit', $task['project_id']) && $subtask['user_id'] == $this->user->getId()): ?>
+ <?= $this->subtask->renderTimer($task, $subtask) ?>
+ <?php endif ?>
+</span>
diff --git a/app/Template/task_list/header.php b/app/Template/task_list/header.php
index fb05dbdc..6501a919 100644
--- a/app/Template/task_list/header.php
+++ b/app/Template/task_list/header.php
@@ -7,6 +7,12 @@
<?php endif ?>
</div>
<div class="table-list-header-menu">
+ <?php if ($this->user->hasSubtaskListActivated()): ?>
+ <?= $this->url->icon('tasks', t('Hide subtasks'), 'TaskListController', 'show', array('project_id' => $project['id'], 'hide_subtasks' => 1)) ?>
+ <?php else: ?>
+ <?= $this->url->icon('tasks', t('Show subtasks'), 'TaskListController', 'show', array('project_id' => $project['id'], 'show_subtasks' => 1)) ?>
+ <?php endif ?>
+
<?= $this->render('task_list/sort_menu', array('paginator' => $paginator)) ?>
</div>
</div> \ No newline at end of file
diff --git a/app/Template/task_list/listing.php b/app/Template/task_list/listing.php
index 171c148d..b3c66aa6 100644
--- a/app/Template/task_list/listing.php
+++ b/app/Template/task_list/listing.php
@@ -5,7 +5,11 @@
<p class="alert"><?= t('No tasks found.') ?></p>
<?php elseif (! $paginator->isEmpty()): ?>
<div class="table-list">
- <?= $this->render('task_list/header', array('paginator' => $paginator)) ?>
+ <?= $this->render('task_list/header', array(
+ 'paginator' => $paginator,
+ 'project' => $project,
+ )) ?>
+
<?php foreach ($paginator->getCollection() as $task): ?>
<div class="table-list-row color-<?= $task['color_id'] ?>">
<?= $this->render('task_list/task_title', array(
@@ -21,8 +25,11 @@
)) ?>
<?= $this->render('task_list/task_icons', array(
- 'project' => $project,
- 'task' => $task,
+ 'task' => $task,
+ )) ?>
+
+ <?= $this->render('task_list/task_subtasks', array(
+ 'task' => $task,
)) ?>
</div>
<?php endforeach ?>
diff --git a/app/Template/task_list/task_subtasks.php b/app/Template/task_list/task_subtasks.php
new file mode 100644
index 00000000..716d6df2
--- /dev/null
+++ b/app/Template/task_list/task_subtasks.php
@@ -0,0 +1,22 @@
+<?php if (! empty($task['subtasks'])): ?>
+<div class="task-list-subtasks">
+ <?php foreach ($task['subtasks'] as $subtask): ?>
+ <div class="task-list-subtask">
+ <span class="subtask-cell column-50">
+ <?= $this->subtask->renderToggleStatus($task, $subtask) ?>
+ </span>
+ <span class="subtask-cell column-20 subtask-assignee">
+ <?php if (! empty($subtask['username'])): ?>
+ <?= $this->text->e($subtask['name'] ?: $subtask['username']) ?>
+ <?php endif ?>
+ </span>
+ <span class="subtask-cell subtask-time-tracking-cell">
+ <?= $this->render('subtask/timer', array(
+ 'task' => $task,
+ 'subtask' => $subtask,
+ )) ?>
+ </span>
+ </div>
+ <?php endforeach ?>
+</div>
+<?php endif ?>