diff options
author | Frederic Guillot <fred@kanboard.net> | 2015-05-10 13:45:09 -0400 |
---|---|---|
committer | Frederic Guillot <fred@kanboard.net> | 2015-05-10 13:45:09 -0400 |
commit | 94a5b12e68fc5e5f6aee7bf1d6d25427421aba7a (patch) | |
tree | aa1f86e9fd134ec60534446948dcd2d54e02e5f7 | |
parent | 98aab0d99465b40907bbc1f1108ea20db290e036 (diff) | |
parent | ec24efa2d9599eaf6cbc39da25cffeaff555ba3c (diff) |
Merge pull-request #847 (recurring tasks)
40 files changed, 1134 insertions, 60 deletions
diff --git a/Vagrantfile b/Vagrantfile index 89d950ee..924d4147 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -1,7 +1,7 @@ $script_sqlite = <<SCRIPT apt-get update -apt-get install -y apache2 php5 php5-sqlite php5-xdebug +apt-get install -y apache2 php5 php5-gd php5-curl php5-sqlite php5-xdebug apt-get clean -y echo "ServerName localhost" >> /etc/apache2/apache2.conf service apache2 restart @@ -22,7 +22,7 @@ SCRIPT $script_mysql = <<SCRIPT export DEBIAN_FRONTEND=noninteractive apt-get update -apt-get install -y apache2 php5 php5-mysql php5-xdebug mysql-server mysql-client +apt-get install -y apache2 php5 php5-gd php5-curl php5-mysql php5-xdebug mysql-server mysql-client apt-get clean -y echo "ServerName localhost" >> /etc/apache2/apache2.conf service apache2 restart @@ -45,7 +45,7 @@ SCRIPT $script_postgres = <<SCRIPT export DEBIAN_FRONTEND=noninteractive apt-get update -apt-get install -y apache2 php5 php5-pgsql php5-xdebug postgresql postgresql-contrib +apt-get install -y apache2 php5 php5-gd php5-curl php5-pgsql php5-xdebug postgresql postgresql-contrib apt-get clean -y echo "ServerName localhost" >> /etc/apache2/apache2.conf service apache2 restart diff --git a/app/Controller/Board.php b/app/Controller/Board.php index f539a77c..e92cfe37 100644 --- a/app/Controller/Board.php +++ b/app/Controller/Board.php @@ -350,4 +350,21 @@ class Board extends Base 'redirect' => 'board', ))); } + + /** + * Get recurrence information on mouseover + * + * @access public + */ + public function recurrence() + { + $task = $this->getTask(); + + $this->response->html($this->template->render('board/recurrence', array( + 'task' => $task, + 'recurrence_trigger_list' => $this->task->getRecurrenceTriggerList(), + 'recurrence_timeframe_list' => $this->task->getRecurrenceTimeframeList(), + 'recurrence_basedate_list' => $this->task->getRecurrenceBasedateList(), + ))); + } } diff --git a/app/Controller/Task.php b/app/Controller/Task.php index 866ef774..060a478c 100644 --- a/app/Controller/Task.php +++ b/app/Controller/Task.php @@ -81,6 +81,9 @@ class Task extends Base 'date_format' => $this->config->get('application_date_format'), 'date_formats' => $this->dateParser->getAvailableFormats(), 'title' => $task['project_name'].' > '.$task['title'], + 'recurrence_trigger_list' => $this->task->getRecurrenceTriggerList(), + 'recurrence_timeframe_list' => $this->task->getRecurrenceTimeframeList(), + 'recurrence_basedate_list' => $this->task->getRecurrenceBasedateList(), ))); } @@ -444,6 +447,63 @@ class Task extends Base } /** + * Edit recurrence form + * + * @access public + */ + public function recurrence() + { + $task = $this->getTask(); + $ajax = $this->request->isAjax() || $this->request->getIntegerParam('ajax'); + + if ($this->request->isPost()) { + + $values = $this->request->getValues(); + + list($valid, $errors) = $this->taskValidator->validateEditRecurrence($values); + + if ($valid) { + + if ($this->taskModification->update($values)) { + $this->session->flash(t('Task updated successfully.')); + } + else { + $this->session->flashError(t('Unable to update your task.')); + } + + if ($ajax) { + $this->response->redirect('?controller=board&action=show&project_id='.$task['project_id']); + } + else { + $this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id']); + } + } + } + else { + $values = $task; + $errors = array(); + } + + $params = array( + 'values' => $values, + 'errors' => $errors, + 'task' => $task, + 'ajax' => $ajax, + 'recurrence_status_list' => $this->task->getRecurrenceStatusList(), + 'recurrence_trigger_list' => $this->task->getRecurrenceTriggerList(), + 'recurrence_timeframe_list' => $this->task->getRecurrenceTimeframeList(), + 'recurrence_basedate_list' => $this->task->getRecurrenceBasedateList(), + ); + + if ($ajax) { + $this->response->html($this->template->render('task/edit_recurrence', $params)); + } + else { + $this->response->html($this->taskLayout('task/edit_recurrence', $params)); + } + } + + /** * Move a task to another project * * @access public diff --git a/app/Locale/da_DK/translations.php b/app/Locale/da_DK/translations.php index 663c2603..3d166f5d 100644 --- a/app/Locale/da_DK/translations.php +++ b/app/Locale/da_DK/translations.php @@ -872,4 +872,28 @@ return array( // 'Start to type task title...' => '', // 'A task cannot be linked to itself' => '', // 'The exact same link already exists' => '', + // 'Action date' => '', + // 'Base date to calculate new due date' => '', + // 'Base date to calculate new due date: %s' => '', + // 'Created recurrent task: %s' => '', + // 'Day(s)' => '', + // 'Edit recurrence' => '', + // 'Existing due date' => '', + // 'Factor to calculate new due date' => '', + // 'Factor to calculate new due date: %s' => '', + // 'Generate recurrent task' => '', + // 'Month(s)' => '', + // 'No recurrent task is scheduled to generate' => '', + // 'Recurrence' => '', + // 'Recurrent task created by: %s' => '', + // 'Recurrent task has been generated' => '', + // 'Recurrent task is scheduled to generate' => '', + // 'Timeframe to calculate new due date' => '', + // 'Timeframe to calculate new due date: %s' => '', + // 'Trigger to generate recurrent task' => '', + // 'Trigger to generate recurrent task: %s' => '', + // 'When task is closed' => '', + // 'When task is moved from first column' => '', + // 'When task is moved to last column' => '', + // 'Year(s)' => '', ); diff --git a/app/Locale/de_DE/translations.php b/app/Locale/de_DE/translations.php index 6efe6167..0aab9fde 100644 --- a/app/Locale/de_DE/translations.php +++ b/app/Locale/de_DE/translations.php @@ -872,4 +872,28 @@ return array( // 'Start to type task title...' => '', // 'A task cannot be linked to itself' => '', // 'The exact same link already exists' => '', + // 'Action date' => '', + // 'Base date to calculate new due date' => '', + // 'Base date to calculate new due date: %s' => '', + // 'Created recurrent task: %s' => '', + // 'Day(s)' => '', + // 'Edit recurrence' => '', + // 'Existing due date' => '', + // 'Factor to calculate new due date' => '', + // 'Factor to calculate new due date: %s' => '', + // 'Generate recurrent task' => '', + // 'Month(s)' => '', + // 'No recurrent task is scheduled to generate' => '', + // 'Recurrence' => '', + // 'Recurrent task created by: %s' => '', + // 'Recurrent task has been generated' => '', + // 'Recurrent task is scheduled to generate' => '', + // 'Timeframe to calculate new due date' => '', + // 'Timeframe to calculate new due date: %s' => '', + // 'Trigger to generate recurrent task' => '', + // 'Trigger to generate recurrent task: %s' => '', + // 'When task is closed' => '', + // 'When task is moved from first column' => '', + // 'When task is moved to last column' => '', + // 'Year(s)' => '', ); diff --git a/app/Locale/es_ES/translations.php b/app/Locale/es_ES/translations.php index 449fcd94..d89f7b1d 100644 --- a/app/Locale/es_ES/translations.php +++ b/app/Locale/es_ES/translations.php @@ -872,4 +872,28 @@ return array( // 'Start to type task title...' => '', // 'A task cannot be linked to itself' => '', // 'The exact same link already exists' => '', + // 'Action date' => '', + // 'Base date to calculate new due date' => '', + // 'Base date to calculate new due date: %s' => '', + // 'Created recurrent task: %s' => '', + // 'Day(s)' => '', + // 'Edit recurrence' => '', + // 'Existing due date' => '', + // 'Factor to calculate new due date' => '', + // 'Factor to calculate new due date: %s' => '', + // 'Generate recurrent task' => '', + // 'Month(s)' => '', + // 'No recurrent task is scheduled to generate' => '', + // 'Recurrence' => '', + // 'Recurrent task created by: %s' => '', + // 'Recurrent task has been generated' => '', + // 'Recurrent task is scheduled to generate' => '', + // 'Timeframe to calculate new due date' => '', + // 'Timeframe to calculate new due date: %s' => '', + // 'Trigger to generate recurrent task' => '', + // 'Trigger to generate recurrent task: %s' => '', + // 'When task is closed' => '', + // 'When task is moved from first column' => '', + // 'When task is moved to last column' => '', + // 'Year(s)' => '', ); diff --git a/app/Locale/fi_FI/translations.php b/app/Locale/fi_FI/translations.php index 15c919ed..ba01e7be 100644 --- a/app/Locale/fi_FI/translations.php +++ b/app/Locale/fi_FI/translations.php @@ -872,4 +872,28 @@ return array( // 'Start to type task title...' => '', // 'A task cannot be linked to itself' => '', // 'The exact same link already exists' => '', + // 'Action date' => '', + // 'Base date to calculate new due date' => '', + // 'Base date to calculate new due date: %s' => '', + // 'Created recurrent task: %s' => '', + // 'Day(s)' => '', + // 'Edit recurrence' => '', + // 'Existing due date' => '', + // 'Factor to calculate new due date' => '', + // 'Factor to calculate new due date: %s' => '', + // 'Generate recurrent task' => '', + // 'Month(s)' => '', + // 'No recurrent task is scheduled to generate' => '', + // 'Recurrence' => '', + // 'Recurrent task created by: %s' => '', + // 'Recurrent task has been generated' => '', + // 'Recurrent task is scheduled to generate' => '', + // 'Timeframe to calculate new due date' => '', + // 'Timeframe to calculate new due date: %s' => '', + // 'Trigger to generate recurrent task' => '', + // 'Trigger to generate recurrent task: %s' => '', + // 'When task is closed' => '', + // 'When task is moved from first column' => '', + // 'When task is moved to last column' => '', + // 'Year(s)' => '', ); diff --git a/app/Locale/fr_FR/translations.php b/app/Locale/fr_FR/translations.php index 84a28207..934762d0 100644 --- a/app/Locale/fr_FR/translations.php +++ b/app/Locale/fr_FR/translations.php @@ -874,4 +874,28 @@ return array( 'Start to type task title...' => 'Tappez le titre de la tâche...', 'A task cannot be linked to itself' => 'Une tâche ne peut être liée à elle-même', 'The exact same link already exists' => 'Un lien identique existe déjà', + // 'Action date' => '', + // 'Base date to calculate new due date' => '', + // 'Base date to calculate new due date: %s' => '', + // 'Created recurrent task: %s' => '', + // 'Day(s)' => '', + // 'Edit recurrence' => '', + // 'Existing due date' => '', + // 'Factor to calculate new due date' => '', + // 'Factor to calculate new due date: %s' => '', + // 'Generate recurrent task' => '', + // 'Month(s)' => '', + // 'No recurrent task is scheduled to generate' => '', + // 'Recurrence' => '', + // 'Recurrent task created by: %s' => '', + // 'Recurrent task has been generated' => '', + // 'Recurrent task is scheduled to generate' => '', + // 'Timeframe to calculate new due date' => '', + // 'Timeframe to calculate new due date: %s' => '', + // 'Trigger to generate recurrent task' => '', + // 'Trigger to generate recurrent task: %s' => '', + // 'When task is closed' => '', + // 'When task is moved from first column' => '', + // 'When task is moved to last column' => '', + // 'Year(s)' => '', ); diff --git a/app/Locale/hu_HU/translations.php b/app/Locale/hu_HU/translations.php index 26b1e132..3614f4cd 100644 --- a/app/Locale/hu_HU/translations.php +++ b/app/Locale/hu_HU/translations.php @@ -872,4 +872,28 @@ return array( // 'Start to type task title...' => '', // 'A task cannot be linked to itself' => '', // 'The exact same link already exists' => '', + // 'Action date' => '', + // 'Base date to calculate new due date' => '', + // 'Base date to calculate new due date: %s' => '', + // 'Created recurrent task: %s' => '', + // 'Day(s)' => '', + // 'Edit recurrence' => '', + // 'Existing due date' => '', + // 'Factor to calculate new due date' => '', + // 'Factor to calculate new due date: %s' => '', + // 'Generate recurrent task' => '', + // 'Month(s)' => '', + // 'No recurrent task is scheduled to generate' => '', + // 'Recurrence' => '', + // 'Recurrent task created by: %s' => '', + // 'Recurrent task has been generated' => '', + // 'Recurrent task is scheduled to generate' => '', + // 'Timeframe to calculate new due date' => '', + // 'Timeframe to calculate new due date: %s' => '', + // 'Trigger to generate recurrent task' => '', + // 'Trigger to generate recurrent task: %s' => '', + // 'When task is closed' => '', + // 'When task is moved from first column' => '', + // 'When task is moved to last column' => '', + // 'Year(s)' => '', ); diff --git a/app/Locale/it_IT/translations.php b/app/Locale/it_IT/translations.php index 8736572e..11f4f47d 100644 --- a/app/Locale/it_IT/translations.php +++ b/app/Locale/it_IT/translations.php @@ -872,4 +872,28 @@ return array( // 'Start to type task title...' => '', // 'A task cannot be linked to itself' => '', // 'The exact same link already exists' => '', + // 'Action date' => '', + // 'Base date to calculate new due date' => '', + // 'Base date to calculate new due date: %s' => '', + // 'Created recurrent task: %s' => '', + // 'Day(s)' => '', + // 'Edit recurrence' => '', + // 'Existing due date' => '', + // 'Factor to calculate new due date' => '', + // 'Factor to calculate new due date: %s' => '', + // 'Generate recurrent task' => '', + // 'Month(s)' => '', + // 'No recurrent task is scheduled to generate' => '', + // 'Recurrence' => '', + // 'Recurrent task created by: %s' => '', + // 'Recurrent task has been generated' => '', + // 'Recurrent task is scheduled to generate' => '', + // 'Timeframe to calculate new due date' => '', + // 'Timeframe to calculate new due date: %s' => '', + // 'Trigger to generate recurrent task' => '', + // 'Trigger to generate recurrent task: %s' => '', + // 'When task is closed' => '', + // 'When task is moved from first column' => '', + // 'When task is moved to last column' => '', + // 'Year(s)' => '', ); diff --git a/app/Locale/ja_JP/translations.php b/app/Locale/ja_JP/translations.php index fa047f69..3d54b0c2 100644 --- a/app/Locale/ja_JP/translations.php +++ b/app/Locale/ja_JP/translations.php @@ -872,4 +872,28 @@ return array( // 'Start to type task title...' => '', // 'A task cannot be linked to itself' => '', // 'The exact same link already exists' => '', + // 'Action date' => '', + // 'Base date to calculate new due date' => '', + // 'Base date to calculate new due date: %s' => '', + // 'Created recurrent task: %s' => '', + // 'Day(s)' => '', + // 'Edit recurrence' => '', + // 'Existing due date' => '', + // 'Factor to calculate new due date' => '', + // 'Factor to calculate new due date: %s' => '', + // 'Generate recurrent task' => '', + // 'Month(s)' => '', + // 'No recurrent task is scheduled to generate' => '', + // 'Recurrence' => '', + // 'Recurrent task created by: %s' => '', + // 'Recurrent task has been generated' => '', + // 'Recurrent task is scheduled to generate' => '', + // 'Timeframe to calculate new due date' => '', + // 'Timeframe to calculate new due date: %s' => '', + // 'Trigger to generate recurrent task' => '', + // 'Trigger to generate recurrent task: %s' => '', + // 'When task is closed' => '', + // 'When task is moved from first column' => '', + // 'When task is moved to last column' => '', + // 'Year(s)' => '', ); diff --git a/app/Locale/nl_NL/translations.php b/app/Locale/nl_NL/translations.php index eecdaa9f..2fb1637b 100644 --- a/app/Locale/nl_NL/translations.php +++ b/app/Locale/nl_NL/translations.php @@ -872,4 +872,28 @@ return array( // 'Start to type task title...' => '', // 'A task cannot be linked to itself' => '', // 'The exact same link already exists' => '', + // 'Action date' => '', + // 'Base date to calculate new due date' => '', + // 'Base date to calculate new due date: %s' => '', + // 'Created recurrent task: %s' => '', + // 'Day(s)' => '', + // 'Edit recurrence' => '', + // 'Existing due date' => '', + // 'Factor to calculate new due date' => '', + // 'Factor to calculate new due date: %s' => '', + // 'Generate recurrent task' => '', + // 'Month(s)' => '', + // 'No recurrent task is scheduled to generate' => '', + // 'Recurrence' => '', + // 'Recurrent task created by: %s' => '', + // 'Recurrent task has been generated' => '', + // 'Recurrent task is scheduled to generate' => '', + // 'Timeframe to calculate new due date' => '', + // 'Timeframe to calculate new due date: %s' => '', + // 'Trigger to generate recurrent task' => '', + // 'Trigger to generate recurrent task: %s' => '', + // 'When task is closed' => '', + // 'When task is moved from first column' => '', + // 'When task is moved to last column' => '', + // 'Year(s)' => '', ); diff --git a/app/Locale/pl_PL/translations.php b/app/Locale/pl_PL/translations.php index a3871723..5065ddbf 100644 --- a/app/Locale/pl_PL/translations.php +++ b/app/Locale/pl_PL/translations.php @@ -872,4 +872,28 @@ return array( // 'Start to type task title...' => '', // 'A task cannot be linked to itself' => '', // 'The exact same link already exists' => '', + // 'Action date' => '', + // 'Base date to calculate new due date' => '', + // 'Base date to calculate new due date: %s' => '', + // 'Created recurrent task: %s' => '', + // 'Day(s)' => '', + // 'Edit recurrence' => '', + // 'Existing due date' => '', + // 'Factor to calculate new due date' => '', + // 'Factor to calculate new due date: %s' => '', + // 'Generate recurrent task' => '', + // 'Month(s)' => '', + // 'No recurrent task is scheduled to generate' => '', + // 'Recurrence' => '', + // 'Recurrent task created by: %s' => '', + // 'Recurrent task has been generated' => '', + // 'Recurrent task is scheduled to generate' => '', + // 'Timeframe to calculate new due date' => '', + // 'Timeframe to calculate new due date: %s' => '', + // 'Trigger to generate recurrent task' => '', + // 'Trigger to generate recurrent task: %s' => '', + // 'When task is closed' => '', + // 'When task is moved from first column' => '', + // 'When task is moved to last column' => '', + // 'Year(s)' => '', ); diff --git a/app/Locale/pt_BR/translations.php b/app/Locale/pt_BR/translations.php index 3e1ddd13..576e1a8f 100644 --- a/app/Locale/pt_BR/translations.php +++ b/app/Locale/pt_BR/translations.php @@ -846,6 +846,7 @@ return array( 'Secret key: ' => 'Chave secreta:', 'Test your device' => 'Teste o seu dispositivo', 'Assign a color when the task is moved to a specific column' => 'Atribuir uma cor quando a tarefa é movida em uma coluna específica', +<<<<<<< HEAD '%s via Kanboard' => '%s via Kanboard', 'uploaded by: %s' => 'carregado por: %s', 'uploaded on: %s' => 'carregado em: %s', @@ -872,4 +873,50 @@ return array( // 'Start to type task title...' => '', // 'A task cannot be linked to itself' => '', // 'The exact same link already exists' => '', + // '%s via Kanboard' => '', + // 'uploaded by: %s' => '', + // 'uploaded on: %s' => '', + // 'size: %s' => '', + // 'Burndown chart for "%s"' => '', + // 'Burndown chart' => '', + // 'This chart show the task complexity over the time (Work Remaining).' => '', + // 'Screenshot taken %s' => '', + // 'Add a screenshot' => '', + // 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => '', + // 'Screenshot uploaded successfully.' => '', + // 'SEK - Swedish Krona' => '', + // 'The project identifier is an optional alphanumeric code used to identify your project.' => '', + // 'Identifier' => '', + // 'Postmark (incoming emails)' => '', + // 'Help on Postmark integration' => '', + // 'Mailgun (incoming emails)' => '', + // 'Help on Mailgun integration' => '', + // 'Sendgrid (incoming emails)' => '', + // 'Help on Sendgrid integration' => '', + // 'Disable two factor authentication' => '', + // 'Do you really want to disable the two factor authentication for this user: "%s"?' => '', + // 'Action date' => '', + // 'Base date to calculate new due date' => '', + // 'Base date to calculate new due date: %s' => '', + // 'Created recurrent task: %s' => '', + // 'Day(s)' => '', + // 'Edit recurrence' => '', + // 'Existing due date' => '', + // 'Factor to calculate new due date' => '', + // 'Factor to calculate new due date: %s' => '', + // 'Generate recurrent task' => '', + // 'Month(s)' => '', + // 'No recurrent task is scheduled to generate' => '', + // 'Recurrence' => '', + // 'Recurrent task created by: %s' => '', + // 'Recurrent task has been generated' => '', + // 'Recurrent task is scheduled to generate' => '', + // 'Timeframe to calculate new due date' => '', + // 'Timeframe to calculate new due date: %s' => '', + // 'Trigger to generate recurrent task' => '', + // 'Trigger to generate recurrent task: %s' => '', + // 'When task is closed' => '', + // 'When task is moved from first column' => '', + // 'When task is moved to last column' => '', + // 'Year(s)' => '', ); diff --git a/app/Locale/ru_RU/translations.php b/app/Locale/ru_RU/translations.php index 4787d2c3..14019773 100644 --- a/app/Locale/ru_RU/translations.php +++ b/app/Locale/ru_RU/translations.php @@ -872,4 +872,28 @@ return array( // 'Start to type task title...' => '', // 'A task cannot be linked to itself' => '', // 'The exact same link already exists' => '', + // 'Action date' => '', + // 'Base date to calculate new due date' => '', + // 'Base date to calculate new due date: %s' => '', + // 'Created recurrent task: %s' => '', + // 'Day(s)' => '', + // 'Edit recurrence' => '', + // 'Existing due date' => '', + // 'Factor to calculate new due date' => '', + // 'Factor to calculate new due date: %s' => '', + // 'Generate recurrent task' => '', + // 'Month(s)' => '', + // 'No recurrent task is scheduled to generate' => '', + // 'Recurrence' => '', + // 'Recurrent task created by: %s' => '', + // 'Recurrent task has been generated' => '', + // 'Recurrent task is scheduled to generate' => '', + // 'Timeframe to calculate new due date' => '', + // 'Timeframe to calculate new due date: %s' => '', + // 'Trigger to generate recurrent task' => '', + // 'Trigger to generate recurrent task: %s' => '', + // 'When task is closed' => '', + // 'When task is moved from first column' => '', + // 'When task is moved to last column' => '', + // 'Year(s)' => '', ); diff --git a/app/Locale/sr_Latn_RS/translations.php b/app/Locale/sr_Latn_RS/translations.php index b74be22f..521721b8 100644 --- a/app/Locale/sr_Latn_RS/translations.php +++ b/app/Locale/sr_Latn_RS/translations.php @@ -872,4 +872,28 @@ return array( // 'Start to type task title...' => '', // 'A task cannot be linked to itself' => '', // 'The exact same link already exists' => '', + // 'Action date' => '', + // 'Base date to calculate new due date' => '', + // 'Base date to calculate new due date: %s' => '', + // 'Created recurrent task: %s' => '', + // 'Day(s)' => '', + // 'Edit recurrence' => '', + // 'Existing due date' => '', + // 'Factor to calculate new due date' => '', + // 'Factor to calculate new due date: %s' => '', + // 'Generate recurrent task' => '', + // 'Month(s)' => '', + // 'No recurrent task is scheduled to generate' => '', + // 'Recurrence' => '', + // 'Recurrent task created by: %s' => '', + // 'Recurrent task has been generated' => '', + // 'Recurrent task is scheduled to generate' => '', + // 'Timeframe to calculate new due date' => '', + // 'Timeframe to calculate new due date: %s' => '', + // 'Trigger to generate recurrent task' => '', + // 'Trigger to generate recurrent task: %s' => '', + // 'When task is closed' => '', + // 'When task is moved from first column' => '', + // 'When task is moved to last column' => '', + // 'Year(s)' => '', ); diff --git a/app/Locale/sv_SE/translations.php b/app/Locale/sv_SE/translations.php index f5636925..3303cede 100644 --- a/app/Locale/sv_SE/translations.php +++ b/app/Locale/sv_SE/translations.php @@ -872,4 +872,28 @@ return array( // 'Start to type task title...' => '', // 'A task cannot be linked to itself' => '', // 'The exact same link already exists' => '', + // 'Action date' => '', + // 'Base date to calculate new due date' => '', + // 'Base date to calculate new due date: %s' => '', + // 'Created recurrent task: %s' => '', + // 'Day(s)' => '', + // 'Edit recurrence' => '', + // 'Existing due date' => '', + // 'Factor to calculate new due date' => '', + // 'Factor to calculate new due date: %s' => '', + // 'Generate recurrent task' => '', + // 'Month(s)' => '', + // 'No recurrent task is scheduled to generate' => '', + // 'Recurrence' => '', + // 'Recurrent task created by: %s' => '', + // 'Recurrent task has been generated' => '', + // 'Recurrent task is scheduled to generate' => '', + // 'Timeframe to calculate new due date' => '', + // 'Timeframe to calculate new due date: %s' => '', + // 'Trigger to generate recurrent task' => '', + // 'Trigger to generate recurrent task: %s' => '', + // 'When task is closed' => '', + // 'When task is moved from first column' => '', + // 'When task is moved to last column' => '', + // 'Year(s)' => '', ); diff --git a/app/Locale/th_TH/translations.php b/app/Locale/th_TH/translations.php index 1e1d81d9..045b1f66 100644 --- a/app/Locale/th_TH/translations.php +++ b/app/Locale/th_TH/translations.php @@ -872,4 +872,28 @@ return array( // 'Start to type task title...' => '', // 'A task cannot be linked to itself' => '', // 'The exact same link already exists' => '', + // 'Action date' => '', + // 'Base date to calculate new due date' => '', + // 'Base date to calculate new due date: %s' => '', + // 'Created recurrent task: %s' => '', + // 'Day(s)' => '', + // 'Edit recurrence' => '', + // 'Existing due date' => '', + // 'Factor to calculate new due date' => '', + // 'Factor to calculate new due date: %s' => '', + // 'Generate recurrent task' => '', + // 'Month(s)' => '', + // 'No recurrent task is scheduled to generate' => '', + // 'Recurrence' => '', + // 'Recurrent task created by: %s' => '', + // 'Recurrent task has been generated' => '', + // 'Recurrent task is scheduled to generate' => '', + // 'Timeframe to calculate new due date' => '', + // 'Timeframe to calculate new due date: %s' => '', + // 'Trigger to generate recurrent task' => '', + // 'Trigger to generate recurrent task: %s' => '', + // 'When task is closed' => '', + // 'When task is moved from first column' => '', + // 'When task is moved to last column' => '', + // 'Year(s)' => '', ); diff --git a/app/Locale/tr_TR/translations.php b/app/Locale/tr_TR/translations.php index 5bd66e4a..96e3e034 100644 --- a/app/Locale/tr_TR/translations.php +++ b/app/Locale/tr_TR/translations.php @@ -872,4 +872,28 @@ return array( // 'Start to type task title...' => '', // 'A task cannot be linked to itself' => '', // 'The exact same link already exists' => '', + // 'Action date' => '', + // 'Base date to calculate new due date' => '', + // 'Base date to calculate new due date: %s' => '', + // 'Created recurrent task: %s' => '', + // 'Day(s)' => '', + // 'Edit recurrence' => '', + // 'Existing due date' => '', + // 'Factor to calculate new due date' => '', + // 'Factor to calculate new due date: %s' => '', + // 'Generate recurrent task' => '', + // 'Month(s)' => '', + // 'No recurrent task is scheduled to generate' => '', + // 'Recurrence' => '', + // 'Recurrent task created by: %s' => '', + // 'Recurrent task has been generated' => '', + // 'Recurrent task is scheduled to generate' => '', + // 'Timeframe to calculate new due date' => '', + // 'Timeframe to calculate new due date: %s' => '', + // 'Trigger to generate recurrent task' => '', + // 'Trigger to generate recurrent task: %s' => '', + // 'When task is closed' => '', + // 'When task is moved from first column' => '', + // 'When task is moved to last column' => '', + // 'Year(s)' => '', ); diff --git a/app/Locale/zh_CN/translations.php b/app/Locale/zh_CN/translations.php index 8a7c5e42..5daa713a 100644 --- a/app/Locale/zh_CN/translations.php +++ b/app/Locale/zh_CN/translations.php @@ -872,4 +872,28 @@ return array( // 'Start to type task title...' => '', // 'A task cannot be linked to itself' => '', // 'The exact same link already exists' => '', + // 'Action date' => '', + // 'Base date to calculate new due date' => '', + // 'Base date to calculate new due date: %s' => '', + // 'Created recurrent task: %s' => '', + // 'Day(s)' => '', + // 'Edit recurrence' => '', + // 'Existing due date' => '', + // 'Factor to calculate new due date' => '', + // 'Factor to calculate new due date: %s' => '', + // 'Generate recurrent task' => '', + // 'Month(s)' => '', + // 'No recurrent task is scheduled to generate' => '', + // 'Recurrence' => '', + // 'Recurrent task created by: %s' => '', + // 'Recurrent task has been generated' => '', + // 'Recurrent task is scheduled to generate' => '', + // 'Timeframe to calculate new due date' => '', + // 'Timeframe to calculate new due date: %s' => '', + // 'Trigger to generate recurrent task' => '', + // 'Trigger to generate recurrent task: %s' => '', + // 'When task is closed' => '', + // 'When task is moved from first column' => '', + // 'When task is moved to last column' => '', + // 'Year(s)' => '', ); diff --git a/app/Model/Board.php b/app/Model/Board.php index 3650418f..eecbc91c 100644 --- a/app/Model/Board.php +++ b/app/Model/Board.php @@ -315,6 +315,18 @@ class Board extends Base } /** + * Get the last column id for a given project + * + * @access public + * @param integer $project_id Project id + * @return integer + */ + public function getLastColumn($project_id) + { + return $this->db->table(self::TABLE)->eq('project_id', $project_id)->desc('position')->findOneColumn('id'); + } + + /** * Get the list of columns sorted by position [ column_id => title ] * * @access public diff --git a/app/Model/Task.php b/app/Model/Task.php index bc2913ec..02469047 100644 --- a/app/Model/Task.php +++ b/app/Model/Task.php @@ -42,6 +42,41 @@ class Task extends Base const EVENT_ASSIGNEE_CHANGE = 'task.assignee_change'; /** + * Recurrence: status + * + * @var integer + */ + const RECURE_STATUS_NONE = 0; + const RECURE_STATUS_PENDING = 1; + const RECURE_STATUS_PROCESSED = 2; + + /** + * Recurrence: trigger + * + * @var integer + */ + const RECURE_TRIGGER_FIRST = 0; + const RECURE_TRIGGER_LAST = 1; + const RECURE_TRIGGER_CLOSE = 2; + + /** + * Recurrence: timeframe + * + * @var integer + */ + const RECURE_DAYS = 0; + const RECURE_MONTHS = 1; + const RECURE_YEARS = 2; + + /** + * Recurrence: base date used to calculate new due date + * + * @var integer + */ + const RECURE_BASEDATE_DUEDATE = 0; + const RECURE_BASEDATE_TRIGGERDATE = 1; + + /** * Remove a task * * @access public @@ -76,4 +111,62 @@ class Task extends Base return 0; } + + /** + * Return the list user selectable recurrence status + * + * @access public + * @return array + */ + public function getRecurrenceStatusList() + { + return array ( + Task::RECURE_STATUS_NONE => t('No'), + Task::RECURE_STATUS_PENDING => t('Yes'), + ); + } + + /** + * Return the list recurrence triggers + * + * @access public + * @return array + */ + public function getRecurrenceTriggerList() + { + return array ( + Task::RECURE_TRIGGER_FIRST => t('When task is moved from first column'), + Task::RECURE_TRIGGER_LAST => t('When task is moved to last column'), + Task::RECURE_TRIGGER_CLOSE => t('When task is closed'), + ); + } + + /** + * Return the list options to calculate recurrence due date + * + * @access public + * @return array + */ + public function getRecurrenceBasedateList() + { + return array ( + Task::RECURE_BASEDATE_DUEDATE => t('Existing due date'), + Task::RECURE_BASEDATE_TRIGGERDATE => t('Action date'), + ); + } + + /** + * Return the list recurrence timeframes + * + * @access public + * @return array + */ + public function getRecurrenceTimeframeList() + { + return array ( + Task::RECURE_DAYS => t('Day(s)'), + Task::RECURE_MONTHS => t('Month(s)'), + Task::RECURE_YEARS => t('Year(s)'), + ); + } } diff --git a/app/Model/TaskDuplication.php b/app/Model/TaskDuplication.php index bd593dc1..f3ce4f7b 100644..100755 --- a/app/Model/TaskDuplication.php +++ b/app/Model/TaskDuplication.php @@ -30,6 +30,11 @@ class TaskDuplication extends Base 'category_id', 'time_estimated', 'swimlane_id', + 'recurrence_status', + 'recurrence_trigger', + 'recurrence_factor', + 'recurrence_timeframe', + 'recurrence_basedate', ); /** @@ -45,6 +50,43 @@ class TaskDuplication extends Base } /** + * Create task recurrence to the same project + * + * @access public + * @param integer $task_id Task id + * @return boolean|integer Recurrence task id + */ + public function createRecurrence($task_id) + { + $values = $this->copyFields($task_id); + + if ($values['recurrence_status'] == Task::RECURE_STATUS_PENDING) + { + $values['recurrence_parent'] = $task_id; + $values['column_id'] = $this->board->getFirstColumn($values['project_id']); + $this->recurrenceDateDue($values); + $recuretask = $this->save($task_id, $values); + + if ($recuretask) + { + $recurrenceStatusUpdate = $this->db + ->table(Task::TABLE) + ->eq('id',$task_id) + ->update(array( + 'recurrence_status' => Task::RECURE_STATUS_PROCESSED, + 'recurrence_child' => $recuretask, + )); + + if($recurrenceStatusUpdate) + { + return $recuretask; + } + } + } + return false; + } + + /** * Duplicate a task to another project * * @access public @@ -127,6 +169,51 @@ class TaskDuplication extends Base } /** + * Calculate new due date for new recurrence task + * + * @access private + * @param array $values + */ + private function recurrenceDateDue(&$values) + { + if ($values['date_due'] && $values['recurrence_factor']) + { + if ($values['recurrence_basedate']) + { + $values['date_due'] = time(); + } + + $factor = abs($values['recurrence_factor']); + + if ($values['recurrence_factor'] < 0) + { + $subtract=TRUE; + } + + switch ($values['recurrence_timeframe']) + { + case Task::RECURE_MONTHS: + $interval = 'P' . $factor . 'M'; + break; + case Task::RECURE_YEARS: + $interval = 'P' . $factor . 'Y'; + break; + default: + $interval = 'P' . $factor . 'D'; + break; + } + + $date_due = new \DateTime(); + + $date_due->setTimestamp($values['date_due']); + + $subtract ? $date_due->sub(new \DateInterval($interval)) : $date_due->add(new \DateInterval($interval)); + + $values['date_due'] = $date_due->getTimestamp(); + } + } + + /** * Duplicate fields for the new task * * @access private diff --git a/app/Model/TaskFinder.php b/app/Model/TaskFinder.php index 54dd578a..6f53249a 100644 --- a/app/Model/TaskFinder.php +++ b/app/Model/TaskFinder.php @@ -104,6 +104,13 @@ class TaskFinder extends Base 'tasks.score', 'tasks.category_id', 'tasks.date_moved', + 'tasks.recurrence_status', + 'tasks.recurrence_trigger', + 'tasks.recurrence_factor', + 'tasks.recurrence_timeframe', + 'tasks.recurrence_basedate', + 'tasks.recurrence_parent', + 'tasks.recurrence_child', 'users.username AS assignee_username', 'users.name AS assignee_name' ) @@ -246,6 +253,13 @@ class TaskFinder extends Base tasks.category_id, tasks.swimlane_id, tasks.date_moved, + tasks.recurrence_status, + tasks.recurrence_trigger, + tasks.recurrence_factor, + tasks.recurrence_timeframe, + tasks.recurrence_basedate, + tasks.recurrence_parent, + tasks.recurrence_child, project_has_categories.name AS category_name, projects.name AS project_name, columns.title AS column_title, diff --git a/app/Model/TaskModification.php b/app/Model/TaskModification.php index dac52334..677fcd60 100644 --- a/app/Model/TaskModification.php +++ b/app/Model/TaskModification.php @@ -67,7 +67,7 @@ class TaskModification extends Base $this->dateParser->convert($values, array('date_due', 'date_started')); $this->removeFields($values, array('another_task', 'id')); $this->resetFields($values, array('date_due', 'date_started', 'score', 'category_id', 'time_estimated', 'time_spent')); - $this->convertIntegerFields($values, array('is_active')); + $this->convertIntegerFields($values, array('is_active', 'recurrence_status', 'recurrence_trigger', 'recurrence_factor', 'recurrence_timeframe', 'recurrence_basedate')); $values['date_modification'] = time(); } diff --git a/app/Model/TaskPosition.php b/app/Model/TaskPosition.php index ab5fe43b..37c1ace0 100644 --- a/app/Model/TaskPosition.php +++ b/app/Model/TaskPosition.php @@ -39,6 +39,19 @@ class TaskPosition extends Base if ($fire_events) { $this->fireEvents($original_task, $column_id, $position, $swimlane_id); } + + if ($original_task['recurrence_status'] == Task::RECURE_STATUS_PENDING + && $original_task['column_id'] != $column_id + && ( + ($original_task['column_id'] == $this->board->getFirstColumn($project_id) + && $original_task['recurrence_trigger'] == Task::RECURE_TRIGGER_FIRST) + || ($column_id == $this->board->getLastColumn($project_id) + && $original_task['recurrence_trigger'] == Task::RECURE_TRIGGER_LAST) + ) + ) + { + $this->taskDuplication->createRecurrence($task_id); + } } return $result; diff --git a/app/Model/TaskStatus.php b/app/Model/TaskStatus.php index 30a65e1e..1ae8bfeb 100644 --- a/app/Model/TaskStatus.php +++ b/app/Model/TaskStatus.php @@ -89,7 +89,9 @@ class TaskStatus extends Base */ private function changeStatus($task_id, $status, $date_completed, $event) { - if (! $this->taskFinder->exists($task_id)) { + $task = $this->taskFinder->getById($task_id); + + if (!$task['id']) { return false; } @@ -107,6 +109,13 @@ class TaskStatus extends Base $event, new TaskEvent(array('task_id' => $task_id) + $this->taskFinder->getById($task_id)) ); + + if ($status == Task::STATUS_CLOSED + && $task['recurrence_status'] == Task::RECURE_STATUS_PENDING + && $task['recurrence_trigger'] == Task::RECURE_TRIGGER_CLOSE) + { + $this->taskDuplication->createRecurrence($task_id); + } } return $result; diff --git a/app/Model/TaskValidator.php b/app/Model/TaskValidator.php index ae21ca28..ec1383ad 100644 --- a/app/Model/TaskValidator.php +++ b/app/Model/TaskValidator.php @@ -30,6 +30,13 @@ class TaskValidator extends Base new Validators\Integer('score', t('This value must be an integer')), new Validators\Integer('category_id', t('This value must be an integer')), new Validators\Integer('swimlane_id', t('This value must be an integer')), + new Validators\Integer('recurrence_child', t('This value must be an integer')), + new Validators\Integer('recurrence_parent', t('This value must be an integer')), + new Validators\Integer('recurrence_factor', t('This value must be an integer')), + new Validators\Integer('recurrence_timeframe', t('This value must be an integer')), + new Validators\Integer('recurrence_basedate', t('This value must be an integer')), + new Validators\Integer('recurrence_trigger', t('This value must be an integer')), + new Validators\Integer('recurrence_status', t('This value must be an integer')), new Validators\MaxLength('title', t('The maximum length is %d characters', 200), 200), new Validators\Date('date_due', t('Invalid date'), $this->dateParser->getDateFormats()), new Validators\Date('date_started', t('Invalid date'), $this->dateParser->getDateFormats()), @@ -82,6 +89,28 @@ class TaskValidator extends Base } /** + * Validate edit recurrence + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validateEditRecurrence(array $values) + { + $rules = array( + new Validators\Required('id', t('The id is required')), + ); + + $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); + + return array( + $v->execute(), + $v->getErrors() + ); + } + + + /** * Validate task modification (form) * * @access public diff --git a/app/Schema/Mysql.php b/app/Schema/Mysql.php index 22f8c1b0..9ed23ee0 100644 --- a/app/Schema/Mysql.php +++ b/app/Schema/Mysql.php @@ -6,7 +6,18 @@ use PDO; use Core\Security; use Model\Link; -const VERSION = 66; +const VERSION = 67; + +function version_67($pdo) +{ + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_status INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_trigger INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_factor INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_timeframe INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_basedate INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_parent INTEGER'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_child INTEGER'); +} function version_66($pdo) { diff --git a/app/Schema/Postgres.php b/app/Schema/Postgres.php index db30af67..f1262816 100644 --- a/app/Schema/Postgres.php +++ b/app/Schema/Postgres.php @@ -6,7 +6,18 @@ use PDO; use Core\Security; use Model\Link; -const VERSION = 47; +const VERSION = 48; + +function version_48($pdo) +{ + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_status INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_trigger INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_factor INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_timeframe INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_basedate INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_parent INTEGER'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_child INTEGER'); +} function version_47($pdo) { diff --git a/app/Schema/Sqlite.php b/app/Schema/Sqlite.php index 79c50458..714d0e2f 100644 --- a/app/Schema/Sqlite.php +++ b/app/Schema/Sqlite.php @@ -6,7 +6,18 @@ use Core\Security; use PDO; use Model\Link; -const VERSION = 65; +const VERSION = 66; + +function version_66($pdo) +{ + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_status INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_trigger INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_factor INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_timeframe INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_basedate INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_parent INTEGER'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_child INTEGER'); +} function version_65($pdo) { diff --git a/app/Template/board/recurrence.php b/app/Template/board/recurrence.php new file mode 100644 index 00000000..1b71bc34 --- /dev/null +++ b/app/Template/board/recurrence.php @@ -0,0 +1,18 @@ +<section class="tooltip-large"> +<?php if ($task['recurrence_status'] == \Model\Task::RECURE_STATUS_PENDING): ?> + <?= t('Recurrent task is scheduled to generate') ?><br/> +<?php endif ?> +<?php if ($task['recurrence_status'] == \Model\Task::RECURE_STATUS_PROCESSED): ?> + <?= t('Recurrent task has been generated') ?><br/> +<?php endif ?> + <?= t('Trigger to generate recurrent task: %s', $recurrence_trigger_list[$task['recurrence_trigger']]) ?><br/> + <?= t('Factor to calculate new due date: %s', $task['recurrence_factor']) ?><br/> + <?= t('Timeframe to calculate new due date: %s', $recurrence_timeframe_list[$task['recurrence_timeframe']]) ?><br/> + <?= t('Base date to calculate new due date: %s', $recurrence_basedate_list[$task['recurrence_basedate']]) ?><br/> +<?php if ($task['recurrence_parent']): ?> + <?= t('Recurrent task created by: %s', $task['recurrence_parent']) ?><br/> +<?php endif ?> +<?php if ($task['recurrence_child']): ?> + <?= t('Created recurrent task: %s', $task['recurrence_child']) ?><br/> +<?php endif ?> +</section> diff --git a/app/Template/board/task_footer.php b/app/Template/board/task_footer.php index 36ed2684..b8868f52 100644 --- a/app/Template/board/task_footer.php +++ b/app/Template/board/task_footer.php @@ -25,6 +25,14 @@ </span> <?php endif ?> + <?php if ($task['recurrence_status'] == \Model\Task::RECURE_STATUS_PENDING): ?> + <span title="<?= t('Recurrence') ?>" class="task-board-tooltip" data-href="<?= $this->u('board', 'recurrence', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"><i class="fa fa-refresh fa-rotate-90"></i></span> + <?php endif ?> + + <?php if ($task['recurrence_status'] == \Model\Task::RECURE_STATUS_PROCESSED): ?> + <span title="<?= t('Recurrence') ?>" class="task-board-tooltip" data-href="<?= $this->u('board', 'recurrence', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"><i class="fa fa-refresh fa-rotate-90 fa-inverse"></i></span> + <?php endif ?> + <?php if (! empty($task['nb_links'])): ?> <span title="<?= t('Links') ?>" class="task-board-tooltip" data-href="<?= $this->u('board', 'tasklinks', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"><i class="fa fa-code-fork"></i> <?= $task['nb_links'] ?></span> <?php endif ?> diff --git a/app/Template/board/task_menu.php b/app/Template/board/task_menu.php index f3ec3019..fba2d71d 100644 --- a/app/Template/board/task_menu.php +++ b/app/Template/board/task_menu.php @@ -5,6 +5,7 @@ <li><i class="fa fa-user"></i> <?= $this->a(t('Change assignee'), 'board', 'changeAssignee', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-popover') ?></li> <li><i class="fa fa-tag"></i> <?= $this->a(t('Change category'), 'board', 'changeCategory', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-popover') ?></li> <li><i class="fa fa-align-left"></i> <?= $this->a(t('Change description'), 'task', 'description', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-popover') ?></li> + <li><i class="fa fa-refresh fa-rotate-90"></i> <?= $this->a(t('Edit recurrence'), 'task', 'recurrence', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-popover') ?></li> <li><i class="fa fa-comment-o"></i> <?= $this->a(t('Add a comment'), 'comment', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-popover') ?></li> <li><i class="fa fa-code-fork"></i> <?= $this->a(t('Add a link'), 'tasklink', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-popover') ?></li> <li><i class="fa fa-pencil-square-o"></i> <?= $this->a(t('Edit this task'), 'task', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'task-board-popover') ?></li> @@ -12,4 +13,4 @@ <li><i class="fa fa-close"></i> <?= $this->a(t('Close this task'), 'task', 'close', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'redirect' => 'board'), false, 'task-board-popover') ?></li> </ul> </span> -</span>
\ No newline at end of file +</span> diff --git a/app/Template/task/details.php b/app/Template/task/details.php index 76241acf..331862b9 100644 --- a/app/Template/task/details.php +++ b/app/Template/task/details.php @@ -80,5 +80,44 @@ <?= $this->a(t('Public link'), 'task', 'readonly', array('task_id' => $task['id'], 'token' => $project['token']), false, '', '', true) ?> </li> <?php endif ?> + <?php if ($task['recurrence_status'] == \Model\Task::RECURE_STATUS_NONE): ?> + <li> + <?= t('No recurrent task is scheduled to generate') ?> + </li> + <?php endif ?> + <?php if ($task['recurrence_status'] != \Model\Task::RECURE_STATUS_NONE): ?> + <li> + <?php if ($task['recurrence_status'] == \Model\Task::RECURE_STATUS_PENDING): ?> + <?= t('Recurrent task is scheduled to generate') ?> + <?php endif ?> + <?php if ($task['recurrence_status'] == \Model\Task::RECURE_STATUS_PROCESSED): ?> + <?= t('Recurrent task has been generated') ?> + <?php endif ?> + <ul> + <li> + <?= t('Trigger to generate recurrent task: %s', $recurrence_trigger_list[$task['recurrence_trigger']]) ?></stong> + </li> + <li> + <?= t('Factor to calculate new due date: %s', $task['recurrence_factor']) ?></stong> + </li> + <li> + <?= t('Timeframe to calculate new due date: %s', $recurrence_timeframe_list[$task['recurrence_timeframe']]) ?></stong> + </li> + <li> + <?= t('Base date to calculate new due date: %s', $recurrence_basedate_list[$task['recurrence_basedate']]) ?></stong> + </li> + </ul> + </li> + <?php endif ?> + <?php if ($task['recurrence_parent']): ?> + <li> + <?= t('Recurrent task created by: %s', $task['recurrence_parent']) ?> + </li> + <?php endif ?> + <?php if ($task['recurrence_child']): ?> + <li> + <?= t('Created recurrent task: %s', $task['recurrence_child']) ?> + </li> + <?php endif ?> </ul> </div> diff --git a/app/Template/task/edit_recurrence.php b/app/Template/task/edit_recurrence.php new file mode 100644 index 00000000..bb86e429 --- /dev/null +++ b/app/Template/task/edit_recurrence.php @@ -0,0 +1,85 @@ +<div class="page-header"> + <h2><?= t('Edit recurrence') ?></h2> +</div> +<section id="task-section"> + + + +<form method="post" action="<?= $this->u('task', 'recurrence', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'ajax' => $ajax)) ?>" autocomplete="off"> + + <?= $this->formCsrf() ?> + + + <div class="form-column"> + <?php if ($task['recurrence_status'] == \Model\Task::RECURE_STATUS_PROCESSED): ?> + <ul> + <li><?= t('Recurrent task has been generated') ?> + <ul> + <li> + <?= t('Trigger to generate recurrent task: %s', $recurrence_trigger_list[$task['recurrence_trigger']]) ?></stong> + </li> + <li> + <?= t('Factor to calculate new due date: %s', $task['recurrence_factor']) ?></stong> + </li> + <li> + <?= t('Timeframe to calculate new due date: %s', $recurrence_timeframe_list[$task['recurrence_timeframe']]) ?></stong> + </li> + <li> + <?= t('Base date to calculate new due date: %s', $recurrence_basedate_list[$task['recurrence_basedate']]) ?></stong> + </li> + </ul> + </li> + </ul> + <?php endif ?> + <?php if ($task['recurrence_parent'] || $task['recurrence_child']): ?> + <ul> + <?php if ($task['recurrence_parent']): ?> + <li> + <?= t('Recurrent task created by: %s', $task['recurrence_parent']) ?> + </li> + <?php endif ?> + <?php if ($task['recurrence_child']): ?> + <li> + <?= t('Created recurrent task: %s', $task['recurrence_child']) ?> + </li> + <?php endif ?> + </ul> + <?php endif ?> + + <?php if ($task['recurrence_status'] != \Model\Task::RECURE_STATUS_PROCESSED): ?> + + <?= $this->formHidden('id', $values) ?> + <?= $this->formHidden('project_id', $values) ?> + + <?= $this->formLabel(t('Generate recurrent task'), 'recurrence_status') ?> + <?= $this->formSelect('recurrence_status', $recurrence_status_list, $values, $errors) ?> </br> + + <?= $this->formLabel(t('Trigger to generate recurrent task'), 'recurrence_trigger') ?> + <?= $this->formSelect('recurrence_trigger', $recurrence_trigger_list, $values, $errors) ?> </br> + + <?= $this->formLabel(t('Factor to calculate new due date'), 'recurrence_factor') ?> + <?= $this->formNumber('recurrence_factor', $values, $errors) ?> </br> + + <?= $this->formLabel(t('Timeframe to calculate new due date'), 'recurrence_timeframe') ?> + <?= $this->formSelect('recurrence_timeframe', $recurrence_timeframe_list, $values, $errors) ?> </br> + + <?= $this->formLabel(t('Base date to calculate new due date'), 'recurrence_basedate') ?> + <?= $this->formSelect('recurrence_basedate', $recurrence_basedate_list, $values, $errors) ?> </br> + + <?php endif ?> + + </div> + + <div class="form-actions"> + <?php if ($task['recurrence_status'] != \Model\Task::RECURE_STATUS_PROCESSED): ?> + <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> + <?= t('or') ?> + <?php endif ?> + <?php if ($ajax): ?> + <?= $this->a(t('cancel'), 'board', 'show', array('project_id' => $task['project_id'])) ?> + <?php else: ?> + <?= $this->a(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + <?php endif ?> + </div> +</form> +</section> diff --git a/app/Template/task/show.php b/app/Template/task/show.php index 50316c9f..9d16ab74 100644 --- a/app/Template/task/show.php +++ b/app/Template/task/show.php @@ -1,4 +1,4 @@ -<?= $this->render('task/details', array('task' => $task, 'project' => $project)) ?> +<?= $this->render('task/details', array('task' => $task, 'project' => $project, 'recurrence_trigger_list' => $this->task->getRecurrenceTriggerList(), 'recurrence_timeframe_list' => $this->task->getRecurrenceTimeframeList(), 'recurrence_basedate_list' => $this->task->getRecurrenceBasedateList())) ?> <?= $this->render('task/time', array('task' => $task, 'values' => $values, 'date_format' => $date_format, 'date_formats' => $date_formats)) ?> <?= $this->render('task/show_description', array('task' => $task)) ?> <?= $this->render('tasklink/show', array('task' => $task, 'links' => $links, 'link_label_list' => $link_label_list)) ?> diff --git a/app/Template/task/sidebar.php b/app/Template/task/sidebar.php index a740e939..7c82700a 100644 --- a/app/Template/task/sidebar.php +++ b/app/Template/task/sidebar.php @@ -25,6 +25,9 @@ <?= $this->a(t('Edit the description'), 'task', 'description', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> + <?= $this->a(t('Edit recurrence'), 'task', 'recurrence', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + </li> + <li> <?= $this->a(t('Add a sub-task'), 'subtask', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> </li> <li> @@ -61,4 +64,4 @@ </li> <?php endif ?> </ul> -</div>
\ No newline at end of file +</div> diff --git a/docs/api-json-rpc.markdown b/docs/api-json-rpc.markdown index e7f5a6b6..8250b1c4 100644 --- a/docs/api-json-rpc.markdown +++ b/docs/api-json-rpc.markdown @@ -710,8 +710,10 @@ Response example: "position": "1", "project_id": "1", "task_limit": "0", + "description": "", "tasks": [], - "nb_tasks": 0 + "nb_tasks": 0, + "score": 0 }, { "id": "2", @@ -719,8 +721,46 @@ Response example: "position": "2", "project_id": "1", "task_limit": "0", - "tasks": [], - "nb_tasks": 0 + "description": "", + "tasks": [ + { + "nb_comments":"0", + "nb_files":"0", + "nb_subtasks":"0", + "nb_completed_subtasks":"0", + "nb_links":"0", + "id":"2", + "reference":"", + "title":"Test", + "description":"", + "date_creation":"1430870507", + "date_modification":"1430870507", + "date_completed":null, + "date_due":"0", + "color_id":"yellow", + "project_id":"1", + "column_id":"2", + "swimlane_id":"0", + "owner_id":"0", + "creator_id":"1", + "position":"1", + "is_active":"1", + "score":"0", + "category_id":"0", + "date_moved":"1430870507", + "recurrence_status":"0", + "recurrence_trigger":"0", + "recurrence_factor":"0", + "recurrence_timeframe":"0", + "recurrence_basedate":"0", + "recurrence_parent":null, + "recurrence_child":null, + "assignee_username":null, + "assignee_name":null + } + ], + "nb_tasks": 1, + "score": 0 }, { "id": "3", @@ -728,8 +768,46 @@ Response example: "position": "3", "project_id": "1", "task_limit": "0", - "tasks": [], - "nb_tasks": 0 + "description": "", + "tasks": [ + { + "nb_comments":"0", + "nb_files":"0", + "nb_subtasks":"1", + "nb_completed_subtasks":"0", + "nb_links":"0", + "id":"1", + "reference":"", + "title":"Task with comment", + "description":"", + "date_creation":"1430783188", + "date_modification":"1430783188", + "date_completed":null, + "date_due":"0", + "color_id":"red", + "project_id":"1", + "column_id":"3", + "swimlane_id":"0", + "owner_id":"1", + "creator_id":"0", + "position":"1", + "is_active":"1", + "score":"0", + "category_id":"0", + "date_moved":"1430783191", + "recurrence_status":"0", + "recurrence_trigger":"0", + "recurrence_factor":"0", + "recurrence_timeframe":"0", + "recurrence_basedate":"0", + "recurrence_parent":null, + "recurrence_child":null, + "assignee_username":"admin", + "assignee_name":null + } + ], + "nb_tasks": 1, + "score": 0 }, { "id": "4", @@ -737,11 +815,14 @@ Response example: "position": "4", "project_id": "1", "task_limit": "0", + "description": "", "tasks": [], - "nb_tasks": 0 + "nb_tasks": 0, + "score": 0 } ], - "nb_columns": 4 + "nb_columns": 4, + "nb_tasks": 2 } ] } @@ -1609,13 +1690,18 @@ Response example: - **project_id** (integer, required) - **color_id** (string, optional) - **column_id** (integer, optional) - - **description** Markdown content (string, optional) - **owner_id** (integer, optional) - **creator_id** (integer, optional) - - **score** (integer, optional) - **date_due**: ISO8601 format (string, optional) + - **description** Markdown content (string, optional) - **category_id** (integer, optional) - - **swimelane_id** (integer, optional) + - **score** (integer, optional) + - **swimlane_id** (integer, optional) + - **recurrence_status** (integer, optional) + - **recurrence_trigger** (integer, optional) + - **recurrence_factor** (integer, optional) + - **recurrence_timeframe** (integer, optional) + - **recurrence_basedate** (integer, optional) - Result on success: **task_id** - Result on failure: **false** @@ -1636,7 +1722,12 @@ Request example: "title": "Test", "project_id": 1, "color_id": "green", - "column_id": 2 + "column_id": 2, + "recurrence_status": 0, + "recurrence_trigger": 0, + "recurrence_factor": 0, + "recurrence_timeframe": 0, + "recurrence_basedate": 0 } } ``` @@ -1695,8 +1786,20 @@ Response example: "category_id": "0", "creator_id": "0", "date_modification": "1409963206", - "swimlane_id": 0 - } + "reference": "", + "date_started": null, + "time_spent": "0", + "time_estimated": "0", + "swimlane_id": "0", + "date_moved": "1430875287", + "recurrence_status": "0", + "recurrence_trigger": "0", + "recurrence_factor": "0", + "recurrence_timeframe": "0", + "recurrence_basedate": "0", + "recurrence_parent": null, + "recurrence_child": null + } } ``` @@ -1747,7 +1850,19 @@ Response example: "category_id": "0", "creator_id": "0", "date_modification": "1409961789", - "swimlane_id": 0 + "reference": "", + "date_started": null, + "time_spent": "0", + "time_estimated": "0", + "swimlane_id": "0", + "date_moved": "1430783191", + "recurrence_status": "0", + "recurrence_trigger": "0", + "recurrence_factor": "0", + "recurrence_timeframe": "0", + "recurrence_basedate": "0", + "recurrence_parent": null, + "recurrence_child": null }, { "id": "2", @@ -1766,7 +1881,19 @@ Response example: "category_id": "0", "creator_id": "0", "date_modification": "1409962115", - "swimlane_id": 0 + "reference": "", + "date_started": null, + "time_spent": "0", + "time_estimated": "0", + "swimlane_id": "0", + "date_moved": "1430783191", + "recurrence_status": "0", + "recurrence_trigger": "0", + "recurrence_factor": "0", + "recurrence_timeframe": "0", + "recurrence_basedate": "0", + "recurrence_parent": null, + "recurrence_child": null }, ... ] @@ -1785,7 +1912,7 @@ Request example to fetch all tasks on the board: { "jsonrpc": "2.0", "method": "getOverdueTasks", - "id": 133280317, + "id": 133280317 } ``` @@ -1799,40 +1926,20 @@ Response example: { "id": "1", "title": "Task #1", - "description": "", - "date_creation": "1409961789", - "color_id": "blue", + "date_due": "1409961789", "project_id": "1", - "column_id": "2", - "owner_id": "1", - "position": "1", - "is_active": "1", - "date_completed": null, - "score": "0", - "date_due": "0", - "category_id": "0", - "creator_id": "0", - "date_modification": "1409961789", - "swimlane_id": 0 + "project_name": "Test", + "assignee_username":"admin", + "assignee_name": null }, { "id": "2", "title": "Test", - "description": "", - "date_creation": "1409962115", - "color_id": "green", + "date_due": "1409962115", "project_id": "1", - "column_id": "2", - "owner_id": "1", - "position": "2", - "is_active": "1", - "date_completed": null, - "score": "0", - "date_due": "0", - "category_id": "0", - "creator_id": "0", - "date_modification": "1409962115", - "swimlane_id": 0 + "project_name": "Test", + "assignee_username":"admin", + "assignee_name": null }, ... ] @@ -1845,16 +1952,21 @@ Response example: - Parameters: - **id** (integer, required) - **title** (string, optional) - - **color_id** (string, optional) - **project_id** (integer, optional) + - **color_id** (string, optional) - **column_id** (integer, optional) - - **description** Markdown content (string, optional) - **owner_id** (integer, optional) - **creator_id** (integer, optional) - - **score** (integer, optional) - **date_due**: ISO8601 format (string, optional) + - **description** Markdown content (string, optional) - **category_id** (integer, optional) + - **score** (integer, optional) - **swimlane_id** (integer, optional) + - **recurrence_status** (integer, optional) + - **recurrence_trigger** (integer, optional) + - **recurrence_factor** (integer, optional) + - **recurrence_timeframe** (integer, optional) + - **recurrence_basedate** (integer, optional) - Result on success: **true** - Result on failure: **false** diff --git a/jsonrpc.php b/jsonrpc.php index 9bf065f8..e5c000c0 100644 --- a/jsonrpc.php +++ b/jsonrpc.php @@ -169,7 +169,7 @@ $server->bind('closeTask', $container['taskStatus'], 'close'); $server->bind('removeTask', $container['task'], 'remove'); $server->bind('moveTaskPosition', $container['taskPosition'], 'movePosition'); -$server->register('createTask', function($title, $project_id, $color_id = '', $column_id = 0, $owner_id = 0, $creator_id = 0, $date_due = '', $description = '', $category_id = 0, $score = 0, $swimlane_id = 0) use ($container) { +$server->register('createTask', function($title, $project_id, $color_id = '', $column_id = 0, $owner_id = 0, $creator_id = 0, $date_due = '', $description = '', $category_id = 0, $score = 0, $swimlane_id = 0, $recurrence_status = 0, $recurrence_trigger = 0, $recurrence_factor = 0, $recurrence_timeframe = 0, $recurrence_basedate = 0) use ($container) { $values = array( 'title' => $title, @@ -183,6 +183,11 @@ $server->register('createTask', function($title, $project_id, $color_id = '', $c 'category_id' => $category_id, 'score' => $score, 'swimlane_id' => $swimlane_id, + 'recurrence_status' => $recurrence_status, + 'recurrence_trigger' => $recurrence_trigger, + 'recurrence_factor' => $recurrence_factor, + 'recurrence_timeframe' => $recurrence_timeframe, + 'recurrence_basedate' => $recurrence_basedate, ); list($valid,) = $container['taskValidator']->validateCreation($values); @@ -194,7 +199,7 @@ $server->register('createTask', function($title, $project_id, $color_id = '', $c return $container['taskCreation']->create($values); }); -$server->register('updateTask', function($id, $title = null, $project_id = null, $color_id = null, $column_id = null, $owner_id = null, $creator_id = null, $date_due = null, $description = null, $category_id = null, $score = null, $swimlane_id = null) use ($container) { +$server->register('updateTask', function($id, $title = null, $project_id = null, $color_id = null, $column_id = null, $owner_id = null, $creator_id = null, $date_due = null, $description = null, $category_id = null, $score = null, $swimlane_id = null, $recurrence_status = null, $recurrence_trigger = null, $recurrence_factor = null, $recurrence_timeframe = null, $recurrence_basedate = null) use ($container) { $values = array( 'id' => $id, @@ -209,6 +214,11 @@ $server->register('updateTask', function($id, $title = null, $project_id = null, 'category_id' => $category_id, 'score' => $score, 'swimlane_id' => $swimlane_id, + 'recurrence_status' => $recurrence_status, + 'recurrence_trigger' => $recurrence_trigger, + 'recurrence_factor' => $recurrence_factor, + 'recurrence_timeframe' => $recurrence_timeframe, + 'recurrence_basedate' => $recurrence_basedate, ); foreach ($values as $key => $value) { |