Display tasks in the calendar + improve settings

This commit is contained in:
Frederic Guillot 2015-05-16 21:04:46 -04:00
parent b028b3586c
commit e94a2f6a00
35 changed files with 706 additions and 151 deletions

View File

@ -99,7 +99,9 @@ Documentation
- [Keyboard shortcuts](docs/keyboard-shortcuts.markdown)
- [Application settings](docs/application-configuration.markdown)
- [Project settings](docs/project-configuration.markdown)
- [Board settings](docs/board-configuration.markdown)
- [Calendar settings](docs/calendar-configuration.markdown)
- [Link settings](docs/link-labels.markdown)
- [Currency rate](docs/currency-rate.markdown)
- [Config file](docs/config.markdown)

View File

@ -47,7 +47,8 @@ class Calendar extends Base
$start = $this->request->getStringParam('start');
$end = $this->request->getStringParam('end');
$due_tasks = $this->taskFilter
// Common filter
$filter = $this->taskFilter
->create()
->filterByProject($project_id)
->filterByCategory($this->request->getIntegerParam('category_id', -1))
@ -55,11 +56,20 @@ class Calendar extends Base
->filterByColumn($this->request->getIntegerParam('column_id', -1))
->filterBySwimlane($this->request->getIntegerParam('swimlane_id', -1))
->filterByColor($this->request->getStringParam('color_id'))
->filterByStatus($this->request->getIntegerParam('is_active', -1))
->filterByDueDateRange($start, $end)
->toCalendarEvents();
->filterByStatus($this->request->getIntegerParam('is_active', -1));
$this->response->json($due_tasks);
// Tasks
if ($this->config->get('calendar_project_tasks', 'date_started') === 'date_creation') {
$events = $filter->copy()->filterByCreationDateRange($start, $end)->toDateTimeCalendarEvents('date_creation', 'date_completed');
}
else {
$events = $filter->copy()->filterByStartDateRange($start, $end)->toDateTimeCalendarEvents('date_started', 'date_completed');
}
// Tasks with due date
$events = array_merge($events, $filter->copy()->filterByDueDateRange($start, $end)->toAllDayCalendarEvents());
$this->response->json($events);
}
/**
@ -72,19 +82,30 @@ class Calendar extends Base
$user_id = $this->request->getIntegerParam('user_id');
$start = $this->request->getStringParam('start');
$end = $this->request->getStringParam('end');
$filter = $this->taskFilter->create()->filterByOwner($user_id)->filterByStatus(TaskModel::STATUS_OPEN);
$due_tasks = $this->taskFilter
->create()
->filterByOwner($user_id)
->filterByStatus(TaskModel::STATUS_OPEN)
->filterByDueDateRange($start, $end)
->toCalendarEvents();
// Task with due date
$events = $filter->copy()->filterByDueDateRange($start, $end)->toAllDayCalendarEvents();
$subtask_timeslots = $this->subtaskTimeTracking->getUserCalendarEvents($user_id, $start, $end);
// Tasks
if ($this->config->get('calendar_user_tasks', 'date_started') === 'date_creation') {
$events = array_merge($events, $filter->copy()->filterByCreationDateRange($start, $end)->toDateTimeCalendarEvents('date_creation', 'date_completed'));
}
else {
$events = array_merge($events, $filter->copy()->filterByStartDateRange($start, $end)->toDateTimeCalendarEvents('date_started', 'date_completed'));
}
$subtask_forcast = $this->config->get('subtask_forecast') == 1 ? $this->subtaskForecast->getCalendarEvents($user_id, $end) : array();
// Subtasks time tracking
if ($this->config->get('calendar_user_subtasks_time_tracking') == 1) {
$events = array_merge($events, $this->subtaskTimeTracking->getUserCalendarEvents($user_id, $start, $end));
}
$this->response->json(array_merge($due_tasks, $subtask_timeslots, $subtask_forcast));
// Subtask estimates
if ($this->config->get('calendar_user_subtasks_forecast') == 1) {
$events = array_merge($events, $this->subtaskForecast->getCalendarEvents($user_id, $end));
}
$this->response->json($events);
}
/**

View File

@ -40,11 +40,16 @@ class Config extends Base
$values = $this->request->getValues();
if ($redirect === 'board') {
$values += array('subtask_restriction' => 0, 'subtask_time_tracking' => 0, 'subtask_forecast' => 0);
}
else if ($redirect === 'integrations') {
$values += array('integration_slack_webhook' => 0, 'integration_hipchat' => 0, 'integration_gravatar' => 0, 'integration_jabber' => 0);
switch ($redirect) {
case 'project':
$values += array('subtask_restriction' => 0, 'subtask_time_tracking' => 0);
break;
case 'integrations':
$values += array('integration_slack_webhook' => 0, 'integration_hipchat' => 0, 'integration_gravatar' => 0, 'integration_jabber' => 0);
break;
case 'calendar':
$values += array('calendar_user_subtasks_forecast' => 0, 'calendar_user_subtasks_time_tracking' => 0);
break;
}
if ($this->config->save($values)) {
@ -89,6 +94,21 @@ class Config extends Base
)));
}
/**
* Display the project settings page
*
* @access public
*/
public function project()
{
$this->common('project');
$this->response->html($this->layout('config/project', array(
'default_columns' => implode(', ', $this->board->getDefaultColumns()),
'title' => t('Settings').' > '.t('Project settings'),
)));
}
/**
* Display the board settings page
*
@ -99,11 +119,24 @@ class Config extends Base
$this->common('board');
$this->response->html($this->layout('config/board', array(
'default_columns' => implode(', ', $this->board->getDefaultColumns()),
'title' => t('Settings').' > '.t('Board settings'),
)));
}
/**
* Display the calendar settings page
*
* @access public
*/
public function calendar()
{
$this->common('calendar');
$this->response->html($this->layout('config/calendar', array(
'title' => t('Settings').' > '.t('Calendar settings'),
)));
}
/**
* Display the integration settings page
*

View File

@ -274,7 +274,7 @@ class Helper
*/
public function formRadio($name, $label, $value, $selected = false, $class = '')
{
return '<label><input type="radio" name="'.$name.'" class="'.$class.'" value="'.$this->e($value).'" '.($selected ? 'selected="selected"' : '').'> '.$this->e($label).'</label>';
return '<label><input type="radio" name="'.$name.'" class="'.$class.'" value="'.$this->e($value).'" '.($selected ? 'checked="checked"' : '').'> '.$this->e($label).'</label>';
}
/**

View File

@ -808,7 +808,7 @@ return array(
// 'Move the task to another column when assigned to a user' => '',
// 'Move the task to another column when assignee is cleared' => '',
// 'Source column' => '',
// 'Show subtask estimates in the user calendar' => '',
// 'Show subtask estimates (forecast of future work)' => '',
// 'Transitions' => '',
// 'Executer' => '',
// 'Time spent in the column' => '',
@ -908,4 +908,12 @@ return array(
// 'Multi-user chat room' => '',
// 'Help on Jabber integration' => '',
// 'The server address must use this format: "tcp://hostname:5222"' => '',
// 'Calendar settings' => '',
// 'Project calendar view' => '',
// 'Project settings' => '',
// 'Show subtasks based on the time tracking' => '',
// 'Show tasks based on the creation date' => '',
// 'Show tasks based on the start date' => '',
// 'Subtasks time tracking' => '',
// 'User calendar view' => '',
);

View File

@ -808,7 +808,7 @@ return array(
'Move the task to another column when assigned to a user' => 'Aufgabe in eine andere Spalte verschieben, wenn ein User zugeordnet wurde.',
'Move the task to another column when assignee is cleared' => 'Aufgabe in eine andere Spalte verschieben, wenn die Zuordnung gelöscht wurde.',
'Source column' => 'Quellspalte',
'Show subtask estimates in the user calendar' => 'Teilaufgabenschätzung in Benutzerkalender anzeigen.',
// 'Show subtask estimates (forecast of future work)' => '',
'Transitions' => 'Übergänge',
'Executer' => 'Ausführender',
'Time spent in the column' => 'Zeit in Spalte verbracht',
@ -908,4 +908,12 @@ return array(
// 'Multi-user chat room' => '',
// 'Help on Jabber integration' => '',
// 'The server address must use this format: "tcp://hostname:5222"' => '',
// 'Calendar settings' => '',
// 'Project calendar view' => '',
// 'Project settings' => '',
// 'Show subtasks based on the time tracking' => '',
// 'Show tasks based on the creation date' => '',
// 'Show tasks based on the start date' => '',
// 'Subtasks time tracking' => '',
// 'User calendar view' => '',
);

View File

@ -808,7 +808,7 @@ return array(
'Move the task to another column when assigned to a user' => 'Mover la tarea a otra columna al asignarse al usuario',
'Move the task to another column when assignee is cleared' => 'Mover la tarea a otra columna al quitar el asignado',
'Source column' => 'Columna fuente',
'Show subtask estimates in the user calendar' => 'Mostrar estimaciones de subtarea en calendario de usuario',
// 'Show subtask estimates (forecast of future work)' => '',
'Transitions' => 'Transiciones',
'Executer' => 'Ejecutor',
'Time spent in the column' => 'Tiempo transcurrido en la columna',
@ -908,4 +908,12 @@ return array(
// 'Multi-user chat room' => '',
// 'Help on Jabber integration' => '',
// 'The server address must use this format: "tcp://hostname:5222"' => '',
// 'Calendar settings' => '',
// 'Project calendar view' => '',
// 'Project settings' => '',
// 'Show subtasks based on the time tracking' => '',
// 'Show tasks based on the creation date' => '',
// 'Show tasks based on the start date' => '',
// 'Subtasks time tracking' => '',
// 'User calendar view' => '',
);

View File

@ -808,7 +808,7 @@ return array(
// 'Move the task to another column when assigned to a user' => '',
// 'Move the task to another column when assignee is cleared' => '',
// 'Source column' => '',
// 'Show subtask estimates in the user calendar' => '',
// 'Show subtask estimates (forecast of future work)' => '',
// 'Transitions' => '',
// 'Executer' => '',
// 'Time spent in the column' => '',
@ -908,4 +908,12 @@ return array(
// 'Multi-user chat room' => '',
// 'Help on Jabber integration' => '',
// 'The server address must use this format: "tcp://hostname:5222"' => '',
// 'Calendar settings' => '',
// 'Project calendar view' => '',
// 'Project settings' => '',
// 'Show subtasks based on the time tracking' => '',
// 'Show tasks based on the creation date' => '',
// 'Show tasks based on the start date' => '',
// 'Subtasks time tracking' => '',
// 'User calendar view' => '',
);

View File

@ -810,7 +810,7 @@ return array(
'Move the task to another column when assigned to a user' => 'Déplacer la tâche dans une autre colonne lorsque celle-ci est assignée à quelqu\'un',
'Move the task to another column when assignee is cleared' => 'Déplacer la tâche dans une autre colonne lorsque celle-ci n\'est plus assignée',
'Source column' => 'Colonne d\'origine',
'Show subtask estimates in the user calendar' => 'Afficher le temps estimé des sous-tâches dans le calendrier utilisateur',
'Show subtask estimates (forecast of future work)' => 'Afficher l\'estimation des sous-tâches (prévision du travail à venir)',
'Transitions' => 'Transitions',
'Executer' => 'Exécutant',
'Time spent in the column' => 'Temps passé dans la colonne',
@ -910,4 +910,12 @@ return array(
'Multi-user chat room' => 'Salon de discussion multi-utilisateurs',
'Help on Jabber integration' => 'Aide sur l\'intégration avec Jabber',
'The server address must use this format: "tcp://hostname:5222"' => 'L\'adresse du serveur doit utiliser le format suivant : « tcp://hostname:5222 »',
'Calendar settings' => 'Paramètres du calendrier',
'Project calendar view' => 'Vue en mode projet du calendrier',
'Project settings' => 'Paramètres des projets',
'Show subtasks based on the time tracking' => 'Afficher les sous-tâches basé sur le suivi du temps',
'Show tasks based on the creation date' => 'Afficher les tâches en fonction de la date de création',
'Show tasks based on the start date' => 'Afficher les tâches en fonction de la date de début',
'Subtasks time tracking' => 'Suivi du temps par rapport aux sous-tâches',
'User calendar view' => 'Vue en mode utilisateur du calendrier',
);

View File

@ -808,7 +808,7 @@ return array(
'Move the task to another column when assigned to a user' => 'Feladat másik oszlopba helyezése felhasználóhoz rendélés után',
'Move the task to another column when assignee is cleared' => 'Feladat másik oszlopba helyezése felhasználóhoz rendélés törlésekor',
'Source column' => 'Forrás oszlop',
// 'Show subtask estimates in the user calendar' => '',
// 'Show subtask estimates (forecast of future work)' => '',
// 'Transitions' => '',
// 'Executer' => '',
// 'Time spent in the column' => '',
@ -908,4 +908,12 @@ return array(
// 'Multi-user chat room' => '',
// 'Help on Jabber integration' => '',
// 'The server address must use this format: "tcp://hostname:5222"' => '',
// 'Calendar settings' => '',
// 'Project calendar view' => '',
// 'Project settings' => '',
// 'Show subtasks based on the time tracking' => '',
// 'Show tasks based on the creation date' => '',
// 'Show tasks based on the start date' => '',
// 'Subtasks time tracking' => '',
// 'User calendar view' => '',
);

View File

@ -808,7 +808,7 @@ return array(
'Move the task to another column when assigned to a user' => 'Sposta il compito in un\'altra colonna quando viene assegnato ad un utente',
'Move the task to another column when assignee is cleared' => 'Sposta il compito in un\'altra colonna quando l\'assegnatario cancellato',
'Source column' => 'Colonna sorgente',
'Show subtask estimates in the user calendar' => 'Mostra le stime dei sotto-compiti nel calendario utente',
// 'Show subtask estimates (forecast of future work)' => '',
'Transitions' => 'Transizioni',
'Executer' => 'Esecutore',
'Time spent in the column' => 'Tempo trascorso nella colonna',
@ -908,4 +908,12 @@ return array(
// 'Multi-user chat room' => '',
// 'Help on Jabber integration' => '',
// 'The server address must use this format: "tcp://hostname:5222"' => '',
// 'Calendar settings' => '',
// 'Project calendar view' => '',
// 'Project settings' => '',
// 'Show subtasks based on the time tracking' => '',
// 'Show tasks based on the creation date' => '',
// 'Show tasks based on the start date' => '',
// 'Subtasks time tracking' => '',
// 'User calendar view' => '',
);

View File

@ -808,7 +808,7 @@ return array(
'Move the task to another column when assigned to a user' => 'ユーザの割り当てをしたらタスクを他のカラムに移動',
'Move the task to another column when assignee is cleared' => 'ユーザの割り当てがなくなったらタスクを他のカラムに移動',
'Source column' => '移動元のカラム',
'Show subtask estimates in the user calendar' => 'カレンダーでサブタスクの見積もりを表示',
// 'Show subtask estimates (forecast of future work)' => '',
'Transitions' => '履歴',
'Executer' => '実行者',
'Time spent in the column' => 'カラムでの時間消費',
@ -908,4 +908,12 @@ return array(
// 'Multi-user chat room' => '',
// 'Help on Jabber integration' => '',
// 'The server address must use this format: "tcp://hostname:5222"' => '',
// 'Calendar settings' => '',
// 'Project calendar view' => '',
// 'Project settings' => '',
// 'Show subtasks based on the time tracking' => '',
// 'Show tasks based on the creation date' => '',
// 'Show tasks based on the start date' => '',
// 'Subtasks time tracking' => '',
// 'User calendar view' => '',
);

View File

@ -808,7 +808,7 @@ return array(
// 'Move the task to another column when assigned to a user' => '',
// 'Move the task to another column when assignee is cleared' => '',
// 'Source column' => '',
// 'Show subtask estimates in the user calendar' => '',
// 'Show subtask estimates (forecast of future work)' => '',
// 'Transitions' => '',
// 'Executer' => '',
// 'Time spent in the column' => '',
@ -908,4 +908,12 @@ return array(
// 'Multi-user chat room' => '',
// 'Help on Jabber integration' => '',
// 'The server address must use this format: "tcp://hostname:5222"' => '',
// 'Calendar settings' => '',
// 'Project calendar view' => '',
// 'Project settings' => '',
// 'Show subtasks based on the time tracking' => '',
// 'Show tasks based on the creation date' => '',
// 'Show tasks based on the start date' => '',
// 'Subtasks time tracking' => '',
// 'User calendar view' => '',
);

View File

@ -808,7 +808,7 @@ return array(
// 'Move the task to another column when assigned to a user' => '',
// 'Move the task to another column when assignee is cleared' => '',
// 'Source column' => '',
// 'Show subtask estimates in the user calendar' => '',
// 'Show subtask estimates (forecast of future work)' => '',
// 'Transitions' => '',
// 'Executer' => '',
// 'Time spent in the column' => '',
@ -908,4 +908,12 @@ return array(
// 'Multi-user chat room' => '',
// 'Help on Jabber integration' => '',
// 'The server address must use this format: "tcp://hostname:5222"' => '',
// 'Calendar settings' => '',
// 'Project calendar view' => '',
// 'Project settings' => '',
// 'Show subtasks based on the time tracking' => '',
// 'Show tasks based on the creation date' => '',
// 'Show tasks based on the start date' => '',
// 'Subtasks time tracking' => '',
// 'User calendar view' => '',
);

View File

@ -808,7 +808,7 @@ return array(
'Move the task to another column when assigned to a user' => 'Mover a tarefa para uma outra coluna quando esta está atribuída a um usuário',
'Move the task to another column when assignee is cleared' => 'Mover a tarefa para uma outra coluna quando esta não está atribuída',
'Source column' => 'Coluna de origem',
'Show subtask estimates in the user calendar' => 'Mostrar o tempo estimado das subtarefas no calendário do usuário',
// 'Show subtask estimates (forecast of future work)' => '',
'Transitions' => 'Transições',
'Executer' => 'Executor(a)',
'Time spent in the column' => 'Tempo gasto na coluna',
@ -908,4 +908,12 @@ return array(
// 'Multi-user chat room' => '',
// 'Help on Jabber integration' => '',
// 'The server address must use this format: "tcp://hostname:5222"' => '',
// 'Calendar settings' => '',
// 'Project calendar view' => '',
// 'Project settings' => '',
// 'Show subtasks based on the time tracking' => '',
// 'Show tasks based on the creation date' => '',
// 'Show tasks based on the start date' => '',
// 'Subtasks time tracking' => '',
// 'User calendar view' => '',
);

View File

@ -808,7 +808,7 @@ return array(
// 'Move the task to another column when assigned to a user' => '',
// 'Move the task to another column when assignee is cleared' => '',
// 'Source column' => '',
// 'Show subtask estimates in the user calendar' => '',
// 'Show subtask estimates (forecast of future work)' => '',
'Transitions' => 'Перемещения',
'Executer' => 'Исполнитель',
'Time spent in the column' => 'Время проведенное в колонке',
@ -908,4 +908,12 @@ return array(
// 'Multi-user chat room' => '',
// 'Help on Jabber integration' => '',
// 'The server address must use this format: "tcp://hostname:5222"' => '',
// 'Calendar settings' => '',
// 'Project calendar view' => '',
// 'Project settings' => '',
// 'Show subtasks based on the time tracking' => '',
// 'Show tasks based on the creation date' => '',
// 'Show tasks based on the start date' => '',
// 'Subtasks time tracking' => '',
// 'User calendar view' => '',
);

View File

@ -808,7 +808,7 @@ return array(
// 'Move the task to another column when assigned to a user' => '',
// 'Move the task to another column when assignee is cleared' => '',
// 'Source column' => '',
// 'Show subtask estimates in the user calendar' => '',
// 'Show subtask estimates (forecast of future work)' => '',
// 'Transitions' => '',
// 'Executer' => '',
// 'Time spent in the column' => '',
@ -908,4 +908,12 @@ return array(
// 'Multi-user chat room' => '',
// 'Help on Jabber integration' => '',
// 'The server address must use this format: "tcp://hostname:5222"' => '',
// 'Calendar settings' => '',
// 'Project calendar view' => '',
// 'Project settings' => '',
// 'Show subtasks based on the time tracking' => '',
// 'Show tasks based on the creation date' => '',
// 'Show tasks based on the start date' => '',
// 'Subtasks time tracking' => '',
// 'User calendar view' => '',
);

View File

@ -808,7 +808,7 @@ return array(
'Move the task to another column when assigned to a user' => 'Flytta uppgiften till en annan kolumn när den tilldelats en användare',
'Move the task to another column when assignee is cleared' => 'Flytta uppgiften till en annan kolumn när tilldelningen tas bort.',
'Source column' => 'Källkolumn',
'Show subtask estimates in the user calendar' => 'Visa deluppgiftsuppskattning i användarens kalender',
// 'Show subtask estimates (forecast of future work)' => '',
'Transitions' => 'Övergångar',
'Executer' => 'Verkställare',
'Time spent in the column' => 'Tid i kolumnen.',
@ -908,4 +908,12 @@ return array(
// 'Multi-user chat room' => '',
// 'Help on Jabber integration' => '',
// 'The server address must use this format: "tcp://hostname:5222"' => '',
// 'Calendar settings' => '',
// 'Project calendar view' => '',
// 'Project settings' => '',
// 'Show subtasks based on the time tracking' => '',
// 'Show tasks based on the creation date' => '',
// 'Show tasks based on the start date' => '',
// 'Subtasks time tracking' => '',
// 'User calendar view' => '',
);

View File

@ -808,7 +808,7 @@ return array(
// 'Move the task to another column when assigned to a user' => '',
// 'Move the task to another column when assignee is cleared' => '',
// 'Source column' => '',
// 'Show subtask estimates in the user calendar' => '',
// 'Show subtask estimates (forecast of future work)' => '',
// 'Transitions' => '',
// 'Executer' => '',
// 'Time spent in the column' => '',
@ -908,4 +908,12 @@ return array(
// 'Multi-user chat room' => '',
// 'Help on Jabber integration' => '',
// 'The server address must use this format: "tcp://hostname:5222"' => '',
// 'Calendar settings' => '',
// 'Project calendar view' => '',
// 'Project settings' => '',
// 'Show subtasks based on the time tracking' => '',
// 'Show tasks based on the creation date' => '',
// 'Show tasks based on the start date' => '',
// 'Subtasks time tracking' => '',
// 'User calendar view' => '',
);

View File

@ -808,7 +808,7 @@ return array(
// 'Move the task to another column when assigned to a user' => '',
// 'Move the task to another column when assignee is cleared' => '',
// 'Source column' => '',
// 'Show subtask estimates in the user calendar' => '',
// 'Show subtask estimates (forecast of future work)' => '',
// 'Transitions' => '',
// 'Executer' => '',
// 'Time spent in the column' => '',
@ -908,4 +908,12 @@ return array(
// 'Multi-user chat room' => '',
// 'Help on Jabber integration' => '',
// 'The server address must use this format: "tcp://hostname:5222"' => '',
// 'Calendar settings' => '',
// 'Project calendar view' => '',
// 'Project settings' => '',
// 'Show subtasks based on the time tracking' => '',
// 'Show tasks based on the creation date' => '',
// 'Show tasks based on the start date' => '',
// 'Subtasks time tracking' => '',
// 'User calendar view' => '',
);

View File

@ -808,7 +808,7 @@ return array(
'Move the task to another column when assigned to a user' => '指定负责人时移动到其它栏目',
'Move the task to another column when assignee is cleared' => '移除负责人时移动到其它栏目',
'Source column' => '原栏目',
'Show subtask estimates in the user calendar' => '在用户日历中显示子任务预估',
// 'Show subtask estimates (forecast of future work)' => '',
'Transitions' => '变更',
'Executer' => '执行者',
'Time spent in the column' => '栏目中的时间消耗',
@ -908,4 +908,12 @@ return array(
// 'Multi-user chat room' => '',
// 'Help on Jabber integration' => '',
// 'The server address must use this format: "tcp://hostname:5222"' => '',
// 'Calendar settings' => '',
// 'Project calendar view' => '',
// 'Project settings' => '',
// 'Show subtasks based on the time tracking' => '',
// 'Show tasks based on the creation date' => '',
// 'Show tasks based on the start date' => '',
// 'Subtasks time tracking' => '',
// 'User calendar view' => '',
);

View File

@ -162,4 +162,48 @@ abstract class Base
}
}
}
/**
* Build SQL condition for a given time range
*
* @access protected
* @param string $start_time Start timestamp
* @param string $end_time End timestamp
* @param string $start_column Start column name
* @param string $end_column End column name
* @return string
*/
protected function getCalendarCondition($start_time, $end_time, $start_column, $end_column)
{
$start_column = $this->db->escapeIdentifier($start_column);
$end_column = $this->db->escapeIdentifier($end_column);
$conditions = array(
"($start_column >= '$start_time' AND $start_column <= '$end_time')",
"($start_column <= '$start_time' AND $end_column >= '$start_time')",
"($start_column <= '$start_time' AND ($end_column = '0' OR $end_column IS NULL))",
);
return '('.implode(' OR ', $conditions).')';
}
/**
* Get common properties for task calendar events
*
* @access protected
* @param array $task
* @return array
*/
protected function getTaskCalendarProperties(array &$task)
{
return array(
'timezoneParam' => $this->config->getCurrentTimezone(),
'id' => $task['id'],
'title' => t('#%d', $task['id']).' '.$task['title'],
'backgroundColor' => $this->color->getBackgroundColor($task['color_id']),
'borderColor' => $this->color->getBorderColor($task['color_id']),
'textColor' => 'black',
'url' => $this->helper->url('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])),
);
}
}

View File

@ -135,8 +135,13 @@ class SubtaskTimeTracking extends Base
public function getUserCalendarEvents($user_id, $start, $end)
{
$result = $this->getUserQuery($user_id)
->addCondition($this->getCalendarCondition($start, $end))
->findAll();
->addCondition($this->getCalendarCondition(
$this->dateParser->getTimestampFromIsoFormat($start),
$this->dateParser->getTimestampFromIsoFormat($end),
'start',
'end'
))
->findAll();
$result = $this->timetable->calculateEventsIntersect($user_id, $result, $start, $end);
@ -154,37 +159,19 @@ class SubtaskTimeTracking extends Base
*/
public function getProjectCalendarEvents($project_id, $start, $end)
{
$result = $this->getProjectQuery($project_id)
->addCondition($this->getCalendarCondition($start, $end))
->findAll();
$result = $this
->getProjectQuery($project_id)
->addCondition($this->getCalendarCondition(
$this->dateParser->getTimestampFromIsoFormat($start),
$this->dateParser->getTimestampFromIsoFormat($end),
'start',
'end'
))
->findAll();
return $this->toCalendarEvents($result);
}
/**
* Get time slots that should be displayed in the calendar time range
*
* @access private
* @param string $start ISO8601 start date
* @param string $end ISO8601 end date
* @return string
*/
private function getCalendarCondition($start, $end)
{
$start_time = $this->dateParser->getTimestampFromIsoFormat($start);
$end_time = $this->dateParser->getTimestampFromIsoFormat($end);
$start_column = $this->db->escapeIdentifier('start');
$end_column = $this->db->escapeIdentifier('end');
$conditions = array(
"($start_column >= '$start_time' AND $start_column <= '$end_time')",
"($start_column <= '$start_time' AND $end_column >= '$start_time')",
"($start_column <= '$start_time' AND $end_column = '0')",
);
return '('.implode(' OR ', $conditions).')';
}
/**
* Convert a record set to calendar events
*

View File

@ -10,20 +10,59 @@ namespace Model;
*/
class TaskFilter extends Base
{
private $query;
/**
* Query
*
* @access public
* @var \PicoDb\Table
*/
public $query;
/**
* Create a new query
*
* @access public
* @return TaskFilter
*/
public function create()
{
$this->query = $this->db->table(Task::TABLE);
return $this;
}
/**
* Clone the filter
*
* @access public
* @return TaskFilter
*/
public function copy()
{
$filter = clone($this);
$filter->query = clone($this->query);
return $filter;
}
/**
* Exclude a list of task_id
*
* @access public
* @param array $task_ids
* @return TaskFilter
*/
public function excludeTasks(array $task_ids)
{
$this->query->notin('id', $task_ids);
return $this;
}
/**
* Filter by id
*
* @access public
* @param integer $task_id
* @return TaskFilter
*/
public function filterById($task_id)
{
if ($task_id > 0) {
@ -33,18 +72,39 @@ class TaskFilter extends Base
return $this;
}
/**
* Filter by title
*
* @access public
* @param string $title
* @return TaskFilter
*/
public function filterByTitle($title)
{
$this->query->ilike('title', '%'.$title.'%');
return $this;
}
/**
* Filter by a list of project id
*
* @access public
* @param array $project_ids
* @return TaskFilter
*/
public function filterByProjects(array $project_ids)
{
$this->query->in('project_id', $project_ids);
return $this;
}
/**
* Filter by project id
*
* @access public
* @param integer $project_id
* @return TaskFilter
*/
public function filterByProject($project_id)
{
if ($project_id > 0) {
@ -54,6 +114,13 @@ class TaskFilter extends Base
return $this;
}
/**
* Filter by category id
*
* @access public
* @param integer $category_id
* @return TaskFilter
*/
public function filterByCategory($category_id)
{
if ($category_id >= 0) {
@ -63,6 +130,13 @@ class TaskFilter extends Base
return $this;
}
/**
* Filter by assignee
*
* @access public
* @param integer $owner_id
* @return TaskFilter
*/
public function filterByOwner($owner_id)
{
if ($owner_id >= 0) {
@ -72,6 +146,13 @@ class TaskFilter extends Base
return $this;
}
/**
* Filter by color
*
* @access public
* @param string $color_id
* @return TaskFilter
*/
public function filterByColor($color_id)
{
if ($color_id !== '') {
@ -81,6 +162,13 @@ class TaskFilter extends Base
return $this;
}
/**
* Filter by column
*
* @access public
* @param integer $column_id
* @return TaskFilter
*/
public function filterByColumn($column_id)
{
if ($column_id >= 0) {
@ -90,6 +178,13 @@ class TaskFilter extends Base
return $this;
}
/**
* Filter by swimlane
*
* @access public
* @param integer $swimlane_id
* @return TaskFilter
*/
public function filterBySwimlane($swimlane_id)
{
if ($swimlane_id >= 0) {
@ -99,6 +194,13 @@ class TaskFilter extends Base
return $this;
}
/**
* Filter by status
*
* @access public
* @param integer $is_active
* @return TaskFilter
*/
public function filterByStatus($is_active)
{
if ($is_active >= 0) {
@ -108,6 +210,14 @@ class TaskFilter extends Base
return $this;
}
/**
* Filter by due date (range)
*
* @access public
* @param integer $start
* @param integer $end
* @return TaskFilter
*/
public function filterByDueDateRange($start, $end)
{
$this->query->gte('date_due', $this->dateParser->getTimestampFromIsoFormat($start));
@ -116,11 +226,63 @@ class TaskFilter extends Base
return $this;
}
/**
* Filter by start date (range)
*
* @access public
* @param integer $start
* @param integer $end
* @return TaskFilter
*/
public function filterByStartDateRange($start, $end)
{
$this->query->addCondition($this->getCalendarCondition(
$this->dateParser->getTimestampFromIsoFormat($start),
$this->dateParser->getTimestampFromIsoFormat($end),
'date_started',
'date_completed'
));
return $this;
}
/**
* Filter by creation date
*
* @access public
* @param integer $start
* @param integer $end
* @return TaskFilter
*/
public function filterByCreationDateRange($start, $end)
{
$this->query->addCondition($this->getCalendarCondition(
$this->dateParser->getTimestampFromIsoFormat($start),
$this->dateParser->getTimestampFromIsoFormat($end),
'date_creation',
'date_completed'
));
return $this;
}
/**
* Get all results of the filter
*
* @access public
* @return array
*/
public function findAll()
{
return $this->query->findAll();
}
/**
* Format the results to the ajax autocompletion
*
* @access public
* @return array
*/
public function toAutoCompletion()
{
return $this->query->columns('id', 'title')->filter(function(array $results) {
@ -135,22 +297,53 @@ class TaskFilter extends Base
})->findAll();
}
public function toCalendarEvents()
/**
* Transform results to calendar events
*
* @access public
* @param string $start_column Column name for the start date
* @param string $end_column Column name for the end date
* @return array
*/
public function toDateTimeCalendarEvents($start_column, $end_column)
{
$events = array();
foreach ($this->query->findAll() as $task) {
$events[] = array(
'timezoneParam' => $this->config->getCurrentTimezone(),
'id' => $task['id'],
'title' => t('#%d', $task['id']).' '.$task['title'],
'start' => date('Y-m-d', $task['date_due']),
'end' => date('Y-m-d', $task['date_due']),
'allday' => true,
'backgroundColor' => $this->color->getBackgroundColor($task['color_id']),
'borderColor' => $this->color->getBorderColor($task['color_id']),
'textColor' => 'black',
'url' => $this->helper->url('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])),
$events[] = array_merge(
$this->getTaskCalendarProperties($task),
array(
'start' => date('Y-m-d\TH:i:s', $task[$start_column]),
'end' => date('Y-m-d\TH:i:s', $task[$end_column] ?: time()),
'editable' => false,
)
);
}
return $events;
}
/**
* Transform results to all day calendar events
*
* @access public
* @param string $column Column name for the date
* @return array
*/
public function toAllDayCalendarEvents($column = 'date_due')
{
$events = array();
foreach ($this->query->findAll() as $task) {
$events[] = array_merge(
$this->getTaskCalendarProperties($task),
array(
'start' => date('Y-m-d', $task[$column]),
'end' => date('Y-m-d', $task[$column]),
'allday' => true,
)
);
}

View File

@ -6,7 +6,22 @@ use PDO;
use Core\Security;
use Model\Link;
const VERSION = 68;
const VERSION = 69;
function version_69($pdo)
{
$rq = $pdo->prepare("SELECT `value` FROM `settings` WHERE `option`='subtask_forecast'");
$rq->execute();
$result = $rq->fetch(PDO::FETCH_ASSOC);
$rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)');
$rq->execute(array('calendar_user_subtasks_forecast', isset($result['subtask_forecast']) && $result['subtask_forecast'] == 1 ? 1 : 0));
$rq->execute(array('calendar_user_subtasks_time_tracking', 0));
$rq->execute(array('calendar_user_tasks', 'date_started'));
$rq->execute(array('calendar_project_tasks', 'date_started'));
$pdo->exec("DELETE FROM `settings` WHERE `option`='subtask_forecast'");
}
function version_68($pdo)
{
@ -20,12 +35,12 @@ function version_68($pdo)
$rq->execute(array('integration_jabber_room', ''));
$pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber INTEGER DEFAULT '0'");
$pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_server TEXT DEFAULT ''");
$pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_domain TEXT DEFAULT ''");
$pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_username TEXT DEFAULT ''");
$pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_password TEXT DEFAULT ''");
$pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_nickname TEXT DEFAULT 'kanboard'");
$pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_room TEXT DEFAULT ''");
$pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_server VARCHAR(255) DEFAULT ''");
$pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_domain VARCHAR(255) DEFAULT ''");
$pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_username VARCHAR(255) DEFAULT ''");
$pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_password VARCHAR(255) DEFAULT ''");
$pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_nickname VARCHAR(255) DEFAULT 'kanboard'");
$pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_room VARCHAR(255) DEFAULT ''");
}
function version_67($pdo)

View File

@ -6,7 +6,22 @@ use PDO;
use Core\Security;
use Model\Link;
const VERSION = 49;
const VERSION = 50;
function version_50($pdo)
{
$rq = $pdo->prepare("SELECT value FROM settings WHERE option='subtask_forecast'");
$rq->execute();
$result = $rq->fetch(PDO::FETCH_ASSOC);
$rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)');
$rq->execute(array('calendar_user_subtasks_forecast', isset($result['subtask_forecast']) && $result['subtask_forecast'] == 1 ? 1 : 0));
$rq->execute(array('calendar_user_subtasks_time_tracking', 0));
$rq->execute(array('calendar_user_tasks', 'date_started'));
$rq->execute(array('calendar_project_tasks', 'date_started'));
$pdo->exec("DELETE FROM settings WHERE option='subtask_forecast'");
}
function version_49($pdo)
{
@ -20,12 +35,12 @@ function version_49($pdo)
$rq->execute(array('integration_jabber_room', ''));
$pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber INTEGER DEFAULT '0'");
$pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_server TEXT DEFAULT ''");
$pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_domain TEXT DEFAULT ''");
$pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_username TEXT DEFAULT ''");
$pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_password TEXT DEFAULT ''");
$pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_nickname TEXT DEFAULT 'kanboard'");
$pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_room TEXT DEFAULT ''");
$pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_server VARCHAR(255) DEFAULT ''");
$pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_domain VARCHAR(255) DEFAULT ''");
$pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_username VARCHAR(255) DEFAULT ''");
$pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_password VARCHAR(255) DEFAULT ''");
$pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_nickname VARCHAR(255) DEFAULT 'kanboard'");
$pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_room VARCHAR(255) DEFAULT ''");
}
function version_48($pdo)

View File

@ -6,7 +6,22 @@ use Core\Security;
use PDO;
use Model\Link;
const VERSION = 67;
const VERSION = 68;
function version_68($pdo)
{
$rq = $pdo->prepare("SELECT value FROM settings WHERE option='subtask_forecast'");
$rq->execute();
$result = $rq->fetch(PDO::FETCH_ASSOC);
$rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)');
$rq->execute(array('calendar_user_subtasks_forecast', isset($result['subtask_forecast']) && $result['subtask_forecast'] == 1 ? 1 : 0));
$rq->execute(array('calendar_user_subtasks_time_tracking', 0));
$rq->execute(array('calendar_user_tasks', 'date_started'));
$rq->execute(array('calendar_project_tasks', 'date_started'));
$pdo->exec("DELETE FROM settings WHERE option='subtask_forecast'");
}
function version_67($pdo)
{

View File

@ -18,18 +18,6 @@
<?= $this->formNumber('board_private_refresh_interval', $values, $errors) ?><br/>
<p class="form-help"><?= t('Frequency in second (0 to disable this feature, 10 seconds by default)') ?></p>
<?= $this->formLabel(t('Default columns for new projects (Comma-separated)'), 'board_columns') ?>
<?= $this->formText('board_columns', $values, $errors) ?><br/>
<p class="form-help"><?= t('Default values are "%s"', $default_columns) ?></p>
<?= $this->formLabel(t('Default categories for new projects (Comma-separated)'), 'project_categories') ?>
<?= $this->formText('project_categories', $values, $errors) ?><br/>
<p class="form-help"><?= t('Example: "Bug, Feature Request, Improvement"') ?></p>
<?= $this->formCheckbox('subtask_restriction', t('Allow only one subtask in progress at the same time for a user'), 1, $values['subtask_restriction'] == 1) ?>
<?= $this->formCheckbox('subtask_time_tracking', t('Enable time tracking for subtasks'), 1, $values['subtask_time_tracking'] == 1) ?>
<?= $this->formCheckbox('subtask_forecast', t('Show subtask estimates in the user calendar'), 1, $values['subtask_forecast'] == 1) ?>
<div class="form-actions">
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
</div>

View File

@ -0,0 +1,33 @@
<div class="page-header">
<h2><?= t('Calendar settings') ?></h2>
</div>
<section>
<form method="post" action="<?= $this->u('config', 'calendar') ?>" autocomplete="off">
<?= $this->formCsrf() ?>
<h3><?= t('Project calendar view') ?></h3>
<div class="listing">
<?= $this->formRadios('calendar_project_tasks', array(
'date_creation' => t('Show tasks based on the creation date'),
'date_started' => t('Show tasks based on the start date'),
), $values) ?>
</div>
<h3><?= t('User calendar view') ?></h3>
<div class="listing">
<?= $this->formRadios('calendar_user_tasks', array(
'date_creation' => t('Show tasks based on the creation date'),
'date_started' => t('Show tasks based on the start date'),
), $values) ?>
<h4><?= t('Subtasks time tracking') ?></h4>
<?= $this->formCheckbox('calendar_user_subtasks_time_tracking', t('Show subtasks based on the time tracking'), 1, $values['calendar_user_subtasks_time_tracking'] == 1) ?>
<?= $this->formCheckbox('calendar_user_subtasks_forecast', t('Show subtask estimates (forecast of future work)'), 1, $values['calendar_user_subtasks_forecast'] == 1) ?>
</div>
<div class="form-actions">
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
</div>
</form>
</section>

View File

@ -0,0 +1,24 @@
<div class="page-header">
<h2><?= t('Project settings') ?></h2>
</div>
<section>
<form method="post" action="<?= $this->u('config', 'project') ?>" autocomplete="off">
<?= $this->formCsrf() ?>
<?= $this->formLabel(t('Default columns for new projects (Comma-separated)'), 'board_columns') ?>
<?= $this->formText('board_columns', $values, $errors) ?><br/>
<p class="form-help"><?= t('Default values are "%s"', $default_columns) ?></p>
<?= $this->formLabel(t('Default categories for new projects (Comma-separated)'), 'project_categories') ?>
<?= $this->formText('project_categories', $values, $errors) ?><br/>
<p class="form-help"><?= t('Example: "Bug, Feature Request, Improvement"') ?></p>
<?= $this->formCheckbox('subtask_restriction', t('Allow only one subtask in progress at the same time for a user'), 1, $values['subtask_restriction'] == 1) ?>
<?= $this->formCheckbox('subtask_time_tracking', t('Enable time tracking for subtasks'), 1, $values['subtask_time_tracking'] == 1) ?>
<div class="form-actions">
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
</div>
</form>
</section>

View File

@ -7,9 +7,15 @@
<li>
<?= $this->a(t('Application settings'), 'config', 'application') ?>
</li>
<li>
<?= $this->a(t('Project settings'), 'config', 'project') ?>
</li>
<li>
<?= $this->a(t('Board settings'), 'config', 'board') ?>
</li>
<li>
<?= $this->a(t('Calendar settings'), 'config', 'calendar') ?>
</li>
<li>
<?= $this->a(t('Link settings'), 'link', 'index') ?>
</li>

View File

@ -1,8 +1,5 @@
Board settings
===============
Some parameters for boards can be changed on the settings page.
Only administrators can change those settings.
==============
Go to the menu **Settings**, then choose **Board settings** on the left.
@ -25,42 +22,3 @@ When you share a board, the page will refresh automatically every 60 seconds by
When your web browser is open on a board, Kanboard check every 10 seconds if something have been changed by someone else.
Technically this process is done by Ajax polling.
### Default columns for new projects
You can change the default column names here.
It's useful if you always create projects with the same columns.
Each column name must be separated by a comma.
By default, Kanboard use those column names: Backlog, Ready, Work in progress and Done.
### Default categories for new projects
Categories are not global to the application but attached to a project.
Each project can have different categories.
However, if you always create the same categories for all your projects, you can define here the list of categories to create automatically.
### Allow only one subtask in progress at the same time for a user
When this option is enabled, a user can work with only one subtask at the time.
If another subtask have the status "in progress", the user will see this dialog box:
![Subtask user restriction](http://kanboard.net/screenshots/documentation/subtask-user-restriction.png)
### Enable time tracking for subtasks
When this option is enabled, each time the status of a subtask is changed, the start time and the end time are recorded in the database for the assignee.
- When the status changes to "in progress" then the start time is saved
- When the status changes to "done" then the end time is saved
The time spent for the subtask and the task is also updated.
### Show subtask estimates in the user calendar
When enabled, assigned subtasks with the status "todo" and with a defined estimate value will be displayed on the user calendar.
The user calender is available on the dashboard or from the user profile.

View File

@ -0,0 +1,42 @@
Calendar settings
=================
Go to the menu **Settings**, then choose **Calendar settings** on the left.
![Calendar settings](http://kanboard.net/screenshots/documentation/calendar-settings.png)
There are two different calendars in Kanboard:
- Project calendar
- User calendar (available from the dashboard)
Project calendar
----------------
This calendar show tasks with defined due date and tasks based on the creation date or the start date.
### Show tasks based on the creation date
The start date of the calendar event is the creation date of the task.
The end date of the event is the date of completion.
### Show tasks based on the start date
The start date of the calendar event is the start date of the task.
This date can be defined manually.
The end date of the event is the date of completion.
If there is no start date the task will not appear on the calendar.
User calendar
-------------
This calendar show only tasks assigned to the user and optionally subtasks information.
### Show subtasks based on the time tracking
Display subtasks in the calendar from the information recorded in the time tracking table.
The intersection with the user timetable is also calculated.
### Show subtask estimates (forecast of future work)
Display the estimate of future work for subtasks in status "todo" and with a defined "estimate" value.

View File

@ -9,6 +9,10 @@ There are two different views for the calendar:
At this time the calendar is able to display these information:
- Tasks with a due date, displayed at the top. The date can be changed by moving the task to another day.
- Subtask time tracking, each recorded time slot will be shown in the calendar.
- Tasks based on the creation date or the start date. These events cannot be modified with the calendar.
- Subtask time tracking, all recorded time slot will be shown in the calendar.
- Subtask estimates, forecast of work left
![Calendar](http://kanboard.net/screenshots/documentation/calendar.png)
The calendar configuration can be changed in the settings page.

View File

@ -0,0 +1,39 @@
Project settings
================
Go to the menu **Settings**, then choose **Project settings** on the left.
![Project settings](http://kanboard.net/screenshots/documentation/project-settings.png)
### Default columns for new projects
You can change the default column names here.
It's useful if you always create projects with the same columns.
Each column name must be separated by a comma.
By default, Kanboard use those column names: Backlog, Ready, Work in progress and Done.
### Default categories for new projects
Categories are not global to the application but attached to a project.
Each project can have different categories.
However, if you always create the same categories for all your projects, you can define here the list of categories to create automatically.
### Allow only one subtask in progress at the same time for a user
When this option is enabled, a user can work with only one subtask at the time.
If another subtask have the status "in progress", the user will see this dialog box:
![Subtask user restriction](http://kanboard.net/screenshots/documentation/subtask-user-restriction.png)
### Enable time tracking for subtasks
When this option is enabled, each time the status of a subtask is changed, the start time and the end time are recorded in the database for the assignee.
- When the status changes to "in progress" then the start time is saved
- When the status changes to "done" then the end time is saved
The time spent for the subtask and the task is also updated.