Add toggle button to show/hide subtasks in task list view
This commit is contained in:
@@ -7,6 +7,7 @@ New features:
|
|||||||
|
|
||||||
Improvements:
|
Improvements:
|
||||||
|
|
||||||
|
* Add toggle button to show/hide subtasks in task list view
|
||||||
* Display tags in task list view
|
* Display tags in task list view
|
||||||
* Change users list layout
|
* Change users list layout
|
||||||
* Project priority is always rendered now
|
* Project priority is always rendered now
|
||||||
|
|||||||
@@ -21,15 +21,9 @@ class SubtaskStatusController extends BaseController
|
|||||||
$subtask = $this->getSubtask();
|
$subtask = $this->getSubtask();
|
||||||
|
|
||||||
$status = $this->subtaskStatusModel->toggleStatus($subtask['id']);
|
$status = $this->subtaskStatusModel->toggleStatus($subtask['id']);
|
||||||
|
$subtask['status'] = $status;
|
||||||
|
|
||||||
if ($this->request->getIntegerParam('refresh-table') === 0) {
|
$this->response->html($this->helper->subtask->renderToggleStatus($task, $subtask));
|
||||||
$subtask['status'] = $status;
|
|
||||||
$html = $this->helper->subtask->toggleStatus($subtask, $task['project_id']);
|
|
||||||
} else {
|
|
||||||
$html = $this->renderTable($task);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->response->html($html);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -40,32 +34,19 @@ class SubtaskStatusController extends BaseController
|
|||||||
public function timer()
|
public function timer()
|
||||||
{
|
{
|
||||||
$task = $this->getTask();
|
$task = $this->getTask();
|
||||||
$subtask_id = $this->request->getIntegerParam('subtask_id');
|
$subtaskId = $this->request->getIntegerParam('subtask_id');
|
||||||
$timer = $this->request->getStringParam('timer');
|
$timer = $this->request->getStringParam('timer');
|
||||||
|
|
||||||
if ($timer === 'start') {
|
if ($timer === 'start') {
|
||||||
$this->subtaskTimeTrackingModel->logStartTime($subtask_id, $this->userSession->getId());
|
$this->subtaskTimeTrackingModel->logStartTime($subtaskId, $this->userSession->getId());
|
||||||
} elseif ($timer === 'stop') {
|
} elseif ($timer === 'stop') {
|
||||||
$this->subtaskTimeTrackingModel->logEndTime($subtask_id, $this->userSession->getId());
|
$this->subtaskTimeTrackingModel->logEndTime($subtaskId, $this->userSession->getId());
|
||||||
$this->subtaskTimeTrackingModel->updateTaskTimeTracking($task['id']);
|
$this->subtaskTimeTrackingModel->updateTaskTimeTracking($task['id']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->response->html($this->renderTable($task));
|
$this->response->html($this->template->render('subtask/timer', array(
|
||||||
}
|
'task' => $task,
|
||||||
|
'subtask' => $this->subtaskModel->getByIdWithDetails($subtaskId),
|
||||||
/**
|
)));
|
||||||
* 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,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,12 +23,24 @@ class TaskListController extends BaseController
|
|||||||
$project = $this->getProject();
|
$project = $this->getProject();
|
||||||
$search = $this->helper->projectHeader->getSearchQuery($project);
|
$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
|
$paginator = $this->paginator
|
||||||
->setUrl('TaskListController', 'show', array('project_id' => $project['id']))
|
->setUrl('TaskListController', 'show', array('project_id' => $project['id']))
|
||||||
->setMax(30)
|
->setMax(30)
|
||||||
->setOrder(TaskModel::TABLE.'.id')
|
->setOrder(TaskModel::TABLE.'.id')
|
||||||
->setDirection('DESC')
|
->setDirection('DESC')
|
||||||
->setFormatter($this->taskListFormatter)
|
->setFormatter($formatter)
|
||||||
->setQuery($this->taskLexer
|
->setQuery($this->taskLexer
|
||||||
->build($search)
|
->build($search)
|
||||||
->withFilter(new TaskProjectFilter($project['id']))
|
->withFilter(new TaskProjectFilter($project['id']))
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ use Pimple\Container;
|
|||||||
* @property \Kanboard\Formatter\TaskGanttFormatter $taskGanttFormatter
|
* @property \Kanboard\Formatter\TaskGanttFormatter $taskGanttFormatter
|
||||||
* @property \Kanboard\Formatter\TaskICalFormatter $taskICalFormatter
|
* @property \Kanboard\Formatter\TaskICalFormatter $taskICalFormatter
|
||||||
* @property \Kanboard\Formatter\TaskListFormatter $taskListFormatter
|
* @property \Kanboard\Formatter\TaskListFormatter $taskListFormatter
|
||||||
|
* @property \Kanboard\Formatter\TaskListSubtaskFormatter $taskListSubtaskFormatter
|
||||||
* @property \Kanboard\Formatter\TaskSuggestMenuFormatter $taskSuggestMenuFormatter
|
* @property \Kanboard\Formatter\TaskSuggestMenuFormatter $taskSuggestMenuFormatter
|
||||||
* @property \Kanboard\Formatter\UserAutoCompleteFormatter $userAutoCompleteFormatter
|
* @property \Kanboard\Formatter\UserAutoCompleteFormatter $userAutoCompleteFormatter
|
||||||
* @property \Kanboard\Formatter\UserMentionFormatter $userMentionFormatter
|
* @property \Kanboard\Formatter\UserMentionFormatter $userMentionFormatter
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ namespace Kanboard\Core\Session;
|
|||||||
* @property string $commentSorting
|
* @property string $commentSorting
|
||||||
* @property bool $hasSubtaskInProgress
|
* @property bool $hasSubtaskInProgress
|
||||||
* @property bool $hasRememberMe
|
* @property bool $hasRememberMe
|
||||||
* @property bool $boardCollapsed
|
* @property bool $subtaskListToggle
|
||||||
* @property string $scope
|
* @property string $scope
|
||||||
* @property bool $twoFactorBeforeCodeCalled
|
* @property bool $twoFactorBeforeCodeCalled
|
||||||
* @property string $twoFactorSecret
|
* @property string $twoFactorSecret
|
||||||
|
|||||||
@@ -145,6 +145,17 @@ class UserSession extends Base
|
|||||||
return isset($this->sessionStorage->user['username']) ? $this->sessionStorage->user['username'] : '';
|
return isset($this->sessionStorage->user['username']) ? $this->sessionStorage->user['username'] : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
* Check is the user is connected
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ class BoardFormatter extends BaseFormatter implements FormatterInterface
|
|||||||
->findAll();
|
->findAll();
|
||||||
|
|
||||||
$task_ids = array_column($tasks, 'id');
|
$task_ids = array_column($tasks, 'id');
|
||||||
$tags = $this->taskTagModel->getTagsByTasks($task_ids);
|
$tags = $this->taskTagModel->getTagsByTaskIds($task_ids);
|
||||||
|
|
||||||
return $this->boardSwimlaneFormatter
|
return $this->boardSwimlaneFormatter
|
||||||
->withSwimlanes($swimlanes)
|
->withSwimlanes($swimlanes)
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class SubtaskListFormatter extends BaseFormatter implements FormatterInterface
|
|||||||
* Apply formatter
|
* Apply formatter
|
||||||
*
|
*
|
||||||
* @access public
|
* @access public
|
||||||
* @return mixed
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function format()
|
public function format()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,13 +16,13 @@ class TaskListFormatter extends BaseFormatter implements FormatterInterface
|
|||||||
* Apply formatter
|
* Apply formatter
|
||||||
*
|
*
|
||||||
* @access public
|
* @access public
|
||||||
* @return mixed
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function format()
|
public function format()
|
||||||
{
|
{
|
||||||
$tasks = $this->query->findAll();
|
$tasks = $this->query->findAll();
|
||||||
$taskIds = array_column($tasks, 'id');
|
$taskIds = array_column($tasks, 'id');
|
||||||
$tags = $this->taskTagModel->getTagsByTasks($taskIds);
|
$tags = $this->taskTagModel->getTagsByTaskIds($taskIds);
|
||||||
array_merge_relation($tasks, $tags, 'tags', 'id');
|
array_merge_relation($tasks, $tags, 'tags', 'id');
|
||||||
|
|
||||||
return $tasks;
|
return $tasks;
|
||||||
|
|||||||
29
app/Formatter/TaskListSubtaskFormatter.php
Normal file
29
app/Formatter/TaskListSubtaskFormatter.php
Normal file
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,7 +12,23 @@ use Kanboard\Core\Base;
|
|||||||
*/
|
*/
|
||||||
class SubtaskHelper extends 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) {
|
if ($subtask['status'] == 0) {
|
||||||
$html = '<i class="fa fa-square-o fa-fw"></i>';
|
$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
|
* Get the link to toggle subtask status
|
||||||
*
|
*
|
||||||
* @access public
|
* @access public
|
||||||
|
* @param array $task
|
||||||
* @param array $subtask
|
* @param array $subtask
|
||||||
* @param integer $project_id
|
|
||||||
* @param boolean $refresh_table
|
|
||||||
* @return string
|
* @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)) {
|
if (! $this->helper->user->hasProjectAccess('SubtaskController', 'edit', $task['project_id'])) {
|
||||||
return $this->getTitle($subtask);
|
$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>';
|
||||||
|
}
|
||||||
|
|
||||||
if ($subtask['status'] == 0 && isset($this->sessionStorage->hasSubtaskInProgress) && $this->sessionStorage->hasSubtaskInProgress) {
|
public function renderTimer(array $task, array $subtask)
|
||||||
return $this->helper->url->link($this->getTitle($subtask), 'SubtaskRestrictionController', 'show', $params, false, 'js-modal-confirm');
|
{
|
||||||
|
$html = '<span class="subtask-timer-toggle">';
|
||||||
|
|
||||||
|
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' : '');
|
$html .= '</span>';
|
||||||
return $this->helper->url->link($this->getTitle($subtask), 'SubtaskStatusController', 'change', $params, false, $class);
|
|
||||||
|
return $html;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function renderTitleField(array $values, array $errors = array(), array $attributes = array())
|
public function renderTitleField(array $values, array $errors = array(), array $attributes = array())
|
||||||
|
|||||||
@@ -13,7 +13,18 @@ use Kanboard\Core\Base;
|
|||||||
class UserHelper extends 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
|
* @access public
|
||||||
* @return boolean
|
* @return boolean
|
||||||
|
|||||||
@@ -131,6 +131,23 @@ class SubtaskModel extends Base
|
|||||||
->format();
|
->format();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
* Get a subtask by the id
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ class TaskTagModel extends Base
|
|||||||
* @param integer[] $task_ids
|
* @param integer[] $task_ids
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getTagsByTasks($task_ids)
|
public function getTagsByTaskIds($task_ids)
|
||||||
{
|
{
|
||||||
if (empty($task_ids)) {
|
if (empty($task_ids)) {
|
||||||
return array();
|
return array();
|
||||||
@@ -69,6 +69,7 @@ class TaskTagModel extends Base
|
|||||||
->columns(TagModel::TABLE.'.id', TagModel::TABLE.'.name', self::TABLE.'.task_id')
|
->columns(TagModel::TABLE.'.id', TagModel::TABLE.'.name', self::TABLE.'.task_id')
|
||||||
->in(self::TABLE.'.task_id', $task_ids)
|
->in(self::TABLE.'.task_id', $task_ids)
|
||||||
->join(self::TABLE, 'tag_id', 'id')
|
->join(self::TABLE, 'tag_id', 'id')
|
||||||
|
->asc(TagModel::TABLE.'.name')
|
||||||
->findAll();
|
->findAll();
|
||||||
|
|
||||||
return array_column_index($tags, 'task_id');
|
return array_column_index($tags, 'task_id');
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ class FormatterProvider implements ServiceProviderInterface
|
|||||||
'TaskGanttFormatter',
|
'TaskGanttFormatter',
|
||||||
'TaskICalFormatter',
|
'TaskICalFormatter',
|
||||||
'TaskListFormatter',
|
'TaskListFormatter',
|
||||||
|
'TaskListSubtaskFormatter',
|
||||||
'TaskSuggestMenuFormatter',
|
'TaskSuggestMenuFormatter',
|
||||||
'UserAutoCompleteFormatter',
|
'UserAutoCompleteFormatter',
|
||||||
'UserMentionFormatter',
|
'UserMentionFormatter',
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<?php foreach ($subtasks as $subtask): ?>
|
<?php foreach ($subtasks as $subtask): ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<?= $this->subtask->toggleStatus($subtask, $task['project_id']) ?>
|
<?= $this->subtask->renderToggleStatus($task, $subtask) ?>
|
||||||
</td>
|
</td>
|
||||||
<?= $this->hook->render('template:board:tooltip:subtasks:rows', array('subtask' => $subtask)) ?>
|
<?= $this->hook->render('template:board:tooltip:subtasks:rows', array('subtask' => $subtask)) ?>
|
||||||
<td>
|
<td>
|
||||||
|
|||||||
@@ -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'])) ?>
|
<?= $this->url->link($this->text->e($subtask['task_name']), 'TaskViewController', 'show', array('task_id' => $subtask['task_id'], 'project_id' => $subtask['project_id'])) ?>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<?= $this->subtask->toggleStatus($subtask, $subtask['project_id']) ?>
|
<?= $this->subtask->renderToggleStatus(array('project_id' => $subtask['project_id']), $subtask) ?>
|
||||||
</td>
|
</td>
|
||||||
<?= $this->hook->render('template:dashboard:subtasks:rows', array('subtask' => $subtask)) ?>
|
<?= $this->hook->render('template:dashboard:subtasks:rows', array('subtask' => $subtask)) ?>
|
||||||
<td>
|
<td>
|
||||||
|
|||||||
@@ -21,9 +21,9 @@
|
|||||||
'task' => $task,
|
'task' => $task,
|
||||||
'subtask' => $subtask,
|
'subtask' => $subtask,
|
||||||
)) ?>
|
)) ?>
|
||||||
<?= $this->subtask->toggleStatus($subtask, $task['project_id'], true) ?>
|
<?= $this->subtask->renderToggleStatus($task, $subtask, true) ?>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<?= $this->subtask->getTitle($subtask) ?>
|
<?= $this->subtask->renderTitle($subtask) ?>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@@ -33,22 +33,10 @@
|
|||||||
</td>
|
</td>
|
||||||
<?= $this->hook->render('template:subtask:table:rows', array('subtask' => $subtask)) ?>
|
<?= $this->hook->render('template:subtask:table:rows', array('subtask' => $subtask)) ?>
|
||||||
<td>
|
<td>
|
||||||
<?php if (! empty($subtask['time_spent'])): ?>
|
<?= $this->render('subtask/timer', array(
|
||||||
<strong><?= $this->text->e($subtask['time_spent']).'h' ?></strong> <?= t('spent') ?>
|
'task' => $task,
|
||||||
<?php endif ?>
|
'subtask' => $subtask,
|
||||||
|
)) ?>
|
||||||
<?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 ?>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
|
|||||||
13
app/Template/subtask/timer.php
Normal file
13
app/Template/subtask/timer.php
Normal file
@@ -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>
|
||||||
@@ -7,6 +7,12 @@
|
|||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</div>
|
</div>
|
||||||
<div class="table-list-header-menu">
|
<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)) ?>
|
<?= $this->render('task_list/sort_menu', array('paginator' => $paginator)) ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -5,7 +5,11 @@
|
|||||||
<p class="alert"><?= t('No tasks found.') ?></p>
|
<p class="alert"><?= t('No tasks found.') ?></p>
|
||||||
<?php elseif (! $paginator->isEmpty()): ?>
|
<?php elseif (! $paginator->isEmpty()): ?>
|
||||||
<div class="table-list">
|
<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): ?>
|
<?php foreach ($paginator->getCollection() as $task): ?>
|
||||||
<div class="table-list-row color-<?= $task['color_id'] ?>">
|
<div class="table-list-row color-<?= $task['color_id'] ?>">
|
||||||
<?= $this->render('task_list/task_title', array(
|
<?= $this->render('task_list/task_title', array(
|
||||||
@@ -21,8 +25,11 @@
|
|||||||
)) ?>
|
)) ?>
|
||||||
|
|
||||||
<?= $this->render('task_list/task_icons', array(
|
<?= $this->render('task_list/task_icons', array(
|
||||||
'project' => $project,
|
'task' => $task,
|
||||||
'task' => $task,
|
)) ?>
|
||||||
|
|
||||||
|
<?= $this->render('task_list/task_subtasks', array(
|
||||||
|
'task' => $task,
|
||||||
)) ?>
|
)) ?>
|
||||||
</div>
|
</div>
|
||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
|
|||||||
22
app/Template/task_list/task_subtasks.php
Normal file
22
app/Template/task_list/task_subtasks.php
Normal file
@@ -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 ?>
|
||||||
2
assets/css/app.min.css
vendored
2
assets/css/app.min.css
vendored
File diff suppressed because one or more lines are too long
4
assets/js/app.min.js
vendored
4
assets/js/app.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -6,7 +6,7 @@ Kanboard.Subtask.prototype.listen = function() {
|
|||||||
var self = this;
|
var self = this;
|
||||||
this.dragAndDrop();
|
this.dragAndDrop();
|
||||||
|
|
||||||
$(document).on("click", ".subtask-toggle-status", function(e) {
|
$(document).on("click", ".js-subtask-toggle-status", function(e) {
|
||||||
var el = $(this);
|
var el = $(this);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
@@ -14,18 +14,12 @@ Kanboard.Subtask.prototype.listen = function() {
|
|||||||
cache: false,
|
cache: false,
|
||||||
url: el.attr("href"),
|
url: el.attr("href"),
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
if (el.hasClass("subtask-refresh-table")) {
|
$(el).closest('.subtask-title').replaceWith(data);
|
||||||
$(".subtasks-table").replaceWith(data);
|
|
||||||
} else {
|
|
||||||
el.replaceWith(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.dragAndDrop();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).on("click", ".subtask-toggle-timer", function(e) {
|
$(document).on("click", ".js-subtask-toggle-timer", function(e) {
|
||||||
var el = $(this);
|
var el = $(this);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
@@ -33,8 +27,7 @@ Kanboard.Subtask.prototype.listen = function() {
|
|||||||
cache: false,
|
cache: false,
|
||||||
url: el.attr("href"),
|
url: el.attr("href"),
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
$(".subtasks-table").replaceWith(data);
|
$(el).closest('.subtask-time-tracking').replaceWith(data);
|
||||||
self.dragAndDrop();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,35 @@
|
|||||||
@import variables
|
@import variables
|
||||||
|
@import mixins
|
||||||
|
|
||||||
.subtasks-table
|
.subtask-cell
|
||||||
td
|
padding: 4px 10px
|
||||||
vertical-align: middle
|
border-top: 1px dotted #dedede
|
||||||
|
border-left: 1px dotted #dedede
|
||||||
|
display: table-cell
|
||||||
|
vertical-align: middle
|
||||||
|
a
|
||||||
|
color: color('primary')
|
||||||
|
text-decoration: none
|
||||||
|
&:hover, &:focus
|
||||||
|
color: link-color('primary')
|
||||||
|
&:first-child
|
||||||
|
border-left: none
|
||||||
|
@include sm-device
|
||||||
|
width: 90%
|
||||||
|
display: block
|
||||||
|
border-left: none
|
||||||
|
|
||||||
|
.task-list-subtasks
|
||||||
|
display: table
|
||||||
|
width: 100%
|
||||||
|
@include sm-device
|
||||||
|
display: block
|
||||||
|
|
||||||
|
.task-list-subtask
|
||||||
|
display: table-row
|
||||||
|
@include sm-device
|
||||||
|
display: block
|
||||||
|
|
||||||
|
.subtask-assignee, .subtask-time-tracking-cell
|
||||||
|
@include sm-device
|
||||||
|
display: none
|
||||||
|
|||||||
@@ -11,6 +11,13 @@
|
|||||||
line-height: 35px
|
line-height: 35px
|
||||||
padding-left: 3px
|
padding-left: 3px
|
||||||
padding-right: 3px
|
padding-right: 3px
|
||||||
|
a
|
||||||
|
color: color('primary')
|
||||||
|
font-weight: 500
|
||||||
|
text-decoration: none
|
||||||
|
margin-right: 10px
|
||||||
|
&:hover, &:focus
|
||||||
|
color: #767676
|
||||||
.table-list-header-count
|
.table-list-header-count
|
||||||
color: #767676
|
color: #767676
|
||||||
display: inline-block
|
display: inline-block
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ class TaskTagModelTest extends Base
|
|||||||
$this->assertTrue($taskTagModel->save(1, 1, array('My tag 1', 'My tag 2', 'My tag 3')));
|
$this->assertTrue($taskTagModel->save(1, 1, array('My tag 1', 'My tag 2', 'My tag 3')));
|
||||||
$this->assertTrue($taskTagModel->save(1, 2, array('My tag 3')));
|
$this->assertTrue($taskTagModel->save(1, 2, array('My tag 3')));
|
||||||
|
|
||||||
$tags = $taskTagModel->getTagsByTasks(array(1, 2, 3));
|
$tags = $taskTagModel->getTagsByTaskIds(array(1, 2, 3));
|
||||||
|
|
||||||
$expected = array(
|
$expected = array(
|
||||||
1 => array(
|
1 => array(
|
||||||
@@ -121,7 +121,7 @@ class TaskTagModelTest extends Base
|
|||||||
$this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test1')));
|
$this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test1')));
|
||||||
$this->assertTrue($taskTagModel->save(1, 1, array('My tag 1', 'My tag 2', 'My tag 3')));
|
$this->assertTrue($taskTagModel->save(1, 1, array('My tag 1', 'My tag 2', 'My tag 3')));
|
||||||
|
|
||||||
$tags = $taskTagModel->getTagsByTasks(array());
|
$tags = $taskTagModel->getTagsByTaskIds(array());
|
||||||
$this->assertEquals(array(), $tags);
|
$this->assertEquals(array(), $tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user