diff options
41 files changed, 297 insertions, 79 deletions
diff --git a/app/Controller/Config.php b/app/Controller/Config.php index fbd374ab..19bc2767 100644 --- a/app/Controller/Config.php +++ b/app/Controller/Config.php @@ -42,7 +42,7 @@ class Config extends Base switch ($redirect) { case 'project': - $values += array('subtask_restriction' => 0, 'subtask_time_tracking' => 0); + $values += array('subtask_restriction' => 0); break; case 'integrations': $values += array('integration_slack_webhook' => 0, 'integration_hipchat' => 0, 'integration_gravatar' => 0, 'integration_jabber' => 0); diff --git a/app/Controller/Subtask.php b/app/Controller/Subtask.php index 5baa6004..6ee94333 100644 --- a/app/Controller/Subtask.php +++ b/app/Controller/Subtask.php @@ -256,7 +256,7 @@ class Subtask extends Base case 'dashboard': $this->response->redirect($this->helper->url->to('app', 'index')); default: - $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))); + $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])).'#subtasks'); } } diff --git a/app/Controller/Timer.php b/app/Controller/Timer.php new file mode 100644 index 00000000..2a4531ba --- /dev/null +++ b/app/Controller/Timer.php @@ -0,0 +1,35 @@ +<?php + +namespace Controller; + +/** + * Time Tracking controller + * + * @package controller + * @author Frederic Guillot + */ +class Timer extends Base +{ + /** + * Start/stop timer for subtasks + * + * @access public + */ + public function subtask() + { + $project_id = $this->request->getIntegerParam('project_id'); + $task_id = $this->request->getIntegerParam('task_id'); + $subtask_id = $this->request->getIntegerParam('subtask_id'); + $timer = $this->request->getStringParam('timer'); + + if ($timer === 'start') { + $this->subtaskTimeTracking->logStartTime($subtask_id, $this->userSession->getId()); + } + else if ($timer === 'stop') { + $this->subtaskTimeTracking->logEndTime($subtask_id, $this->userSession->getId()); + $this->subtaskTimeTracking->updateTaskTimeTracking($task_id); + } + + $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $project_id, 'task_id' => $task_id)).'#subtasks'); + } +} diff --git a/app/Helper/Datetime.php b/app/Helper/Datetime.php index 3a9c4c48..74ea9bdd 100644 --- a/app/Helper/Datetime.php +++ b/app/Helper/Datetime.php @@ -11,6 +11,39 @@ namespace Helper; class Datetime extends \Core\Base { /** + * Get the age of an item in quasi human readable format. + * It's in this format: <1h , NNh, NNd + * + * @access public + * @param integer $timestamp Unix timestamp of the artifact for which age will be calculated + * @param integer $now Compare with this timestamp (Default value is the current unix timestamp) + * @return string + */ + public function age($timestamp, $now = null) + { + if ($now === null) { + $now = time(); + } + + $diff = $now - $timestamp; + + if ($diff < 900) { + return t('<15m'); + } + if ($diff < 1200) { + return t('<30m'); + } + else if ($diff < 3600) { + return t('<1h'); + } + else if ($diff < 86400) { + return '~'.t('%dh', $diff / 3600); + } + + return t('%dd', ($now - $timestamp) / 86400); + } + + /** * Get all hours for day * * @access public diff --git a/app/Helper/Task.php b/app/Helper/Task.php index 13bdb07a..79c412e1 100644 --- a/app/Helper/Task.php +++ b/app/Helper/Task.php @@ -10,33 +10,6 @@ namespace Helper; */ class Task extends \Core\Base { - /** - * Get the age of an item in quasi human readable format. - * It's in this format: <1h , NNh, NNd - * - * @access public - * @param integer $timestamp Unix timestamp of the artifact for which age will be calculated - * @param integer $now Compare with this timestamp (Default value is the current unix timestamp) - * @return string - */ - public function age($timestamp, $now = null) - { - if ($now === null) { - $now = time(); - } - - $diff = $now - $timestamp; - - if ($diff < 3600) { - return t('<1h'); - } - else if ($diff < 86400) { - return t('%dh', $diff / 3600); - } - - return t('%dd', ($now - $timestamp) / 86400); - } - public function getColors() { return $this->color->getList(); diff --git a/app/Locale/da_DK/translations.php b/app/Locale/da_DK/translations.php index 82f9534c..3adf63d8 100644 --- a/app/Locale/da_DK/translations.php +++ b/app/Locale/da_DK/translations.php @@ -614,7 +614,6 @@ return array( // 'User dashboard' => '', // 'Allow only one subtask in progress at the same time for a user' => '', // 'Edit column "%s"' => '', - // 'Enable time tracking for subtasks' => '', // 'Select the new status of the subtask: "%s"' => '', // 'Subtask timesheet' => '', // 'There is nothing to show.' => '', @@ -946,4 +945,8 @@ return array( // '%%Y-%%m-%%d' => '', // 'Total for all columns' => '', // 'You need at least 2 days of data to show the chart.' => '', + // '<15m' => '', + // '<30m' => '', + // 'Stop timer' => '', + // 'Start timer' => '', ); diff --git a/app/Locale/de_DE/translations.php b/app/Locale/de_DE/translations.php index b004905b..20f30de0 100644 --- a/app/Locale/de_DE/translations.php +++ b/app/Locale/de_DE/translations.php @@ -614,7 +614,6 @@ return array( 'User dashboard' => 'Benutzer Dashboard', 'Allow only one subtask in progress at the same time for a user' => 'Erlaube nur eine Teilaufgabe pro Benutzer zu bearbeiten', 'Edit column "%s"' => 'Spalte "%s" bearbeiten', - 'Enable time tracking for subtasks' => 'Aktiviere Zeiterfassung für Teilaufgaben', 'Select the new status of the subtask: "%s"' => 'Wähle einen neuen Status für Teilaufgabe: "%s"', 'Subtask timesheet' => 'Teilaufgaben Zeiterfassung', 'There is nothing to show.' => 'Es ist nichts zum Anzeigen vorhanden.', @@ -946,4 +945,8 @@ return array( // '%%Y-%%m-%%d' => '', // 'Total for all columns' => '', // 'You need at least 2 days of data to show the chart.' => '', + // '<15m' => '', + // '<30m' => '', + // 'Stop timer' => '', + // 'Start timer' => '', ); diff --git a/app/Locale/es_ES/translations.php b/app/Locale/es_ES/translations.php index 8aa56f6c..cfb50ba8 100644 --- a/app/Locale/es_ES/translations.php +++ b/app/Locale/es_ES/translations.php @@ -614,7 +614,6 @@ return array( 'User dashboard' => 'Tablero de usuario', 'Allow only one subtask in progress at the same time for a user' => 'Permitir sólo una subtarea en progreso a la vez para cada usuario', 'Edit column "%s"' => 'Editar columna %s', - 'Enable time tracking for subtasks' => 'Activar seguimiento temporal para subtareas', 'Select the new status of the subtask: "%s"' => 'Seleccionar el nuevo estado de la subtarea: "%s"', 'Subtask timesheet' => 'Hoja temporal de subtarea', 'There is nothing to show.' => 'Nada que mostrar', @@ -946,4 +945,8 @@ return array( // '%%Y-%%m-%%d' => '', // 'Total for all columns' => '', // 'You need at least 2 days of data to show the chart.' => '', + // '<15m' => '', + // '<30m' => '', + // 'Stop timer' => '', + // 'Start timer' => '', ); diff --git a/app/Locale/fi_FI/translations.php b/app/Locale/fi_FI/translations.php index 601ab9fd..ae699514 100644 --- a/app/Locale/fi_FI/translations.php +++ b/app/Locale/fi_FI/translations.php @@ -614,7 +614,6 @@ return array( // 'User dashboard' => '', // 'Allow only one subtask in progress at the same time for a user' => '', // 'Edit column "%s"' => '', - // 'Enable time tracking for subtasks' => '', // 'Select the new status of the subtask: "%s"' => '', // 'Subtask timesheet' => '', // 'There is nothing to show.' => '', @@ -946,4 +945,8 @@ return array( // '%%Y-%%m-%%d' => '', // 'Total for all columns' => '', // 'You need at least 2 days of data to show the chart.' => '', + // '<15m' => '', + // '<30m' => '', + // 'Stop timer' => '', + // 'Start timer' => '', ); diff --git a/app/Locale/fr_FR/translations.php b/app/Locale/fr_FR/translations.php index 246c2471..8d5ed670 100644 --- a/app/Locale/fr_FR/translations.php +++ b/app/Locale/fr_FR/translations.php @@ -616,7 +616,6 @@ return array( 'User dashboard' => 'Tableau de bord de l\'utilisateur', 'Allow only one subtask in progress at the same time for a user' => 'Autoriser une seule sous-tâche en progrès en même temps pour un utilisateur', 'Edit column "%s"' => 'Modifier la colonne « %s »', - 'Enable time tracking for subtasks' => 'Activer la feuille de temps pour les sous-tâches', 'Select the new status of the subtask: "%s"' => 'Selectionnez le nouveau statut de la sous-tâche : « %s »', 'Subtask timesheet' => 'Feuille de temps des sous-tâches', 'There is nothing to show.' => 'Il n\'y a rien à montrer.', @@ -948,4 +947,8 @@ return array( '%%Y-%%m-%%d' => '%%d/%%m/%%Y', 'Total for all columns' => 'Total pour toutes les colonnes', 'You need at least 2 days of data to show the chart.' => 'Vous avez besoin d\'au minimum 2 jours de données pour afficher le graphique.', + '<15m' => '<15m', + '<30m' => '<30m', + 'Stop timer' => 'Stopper le chrono', + 'Start timer' => 'Démarrer le chrono', ); diff --git a/app/Locale/hu_HU/translations.php b/app/Locale/hu_HU/translations.php index 6c1861eb..98c8e793 100644 --- a/app/Locale/hu_HU/translations.php +++ b/app/Locale/hu_HU/translations.php @@ -614,7 +614,6 @@ return array( 'User dashboard' => 'Felhasználói vezérlőpult', 'Allow only one subtask in progress at the same time for a user' => 'Egyszerre csak egy folyamatban levő részfeladat engedélyezése a felhasználóknak', 'Edit column "%s"' => 'Oszlop szerkesztés: %s', - 'Enable time tracking for subtasks' => 'Idő követés engedélyezése a részfeladatokhoz', 'Select the new status of the subtask: "%s"' => 'Részfeladat állapot változtatás: %s', 'Subtask timesheet' => 'Részfeladat idővonal', 'There is nothing to show.' => 'Nincs megjelenítendő adat.', @@ -946,4 +945,8 @@ return array( // '%%Y-%%m-%%d' => '', // 'Total for all columns' => '', // 'You need at least 2 days of data to show the chart.' => '', + // '<15m' => '', + // '<30m' => '', + // 'Stop timer' => '', + // 'Start timer' => '', ); diff --git a/app/Locale/it_IT/translations.php b/app/Locale/it_IT/translations.php index 7bfa506a..f870a162 100644 --- a/app/Locale/it_IT/translations.php +++ b/app/Locale/it_IT/translations.php @@ -614,7 +614,6 @@ return array( 'User dashboard' => 'Bacheca utente', 'Allow only one subtask in progress at the same time for a user' => 'Permetti un solo sotto-compito in progresso per utente nello stesso tempo', 'Edit column "%s"' => 'Modifica la colonna "%s"', - 'Enable time tracking for subtasks' => 'Abilita la gestione del tempo per i sotto-compiti', 'Select the new status of the subtask: "%s"' => 'Selziona il nuovo status per il sotto-compito: "%s"', 'Subtask timesheet' => 'Timesheet del sotto-compito', 'There is nothing to show.' => 'Nulla da mostrare.', @@ -946,4 +945,8 @@ return array( // '%%Y-%%m-%%d' => '', // 'Total for all columns' => '', // 'You need at least 2 days of data to show the chart.' => '', + // '<15m' => '', + // '<30m' => '', + // 'Stop timer' => '', + // 'Start timer' => '', ); diff --git a/app/Locale/ja_JP/translations.php b/app/Locale/ja_JP/translations.php index 210621d0..19c2bf82 100644 --- a/app/Locale/ja_JP/translations.php +++ b/app/Locale/ja_JP/translations.php @@ -614,7 +614,6 @@ return array( 'User dashboard' => 'ユーザダッシュボード', 'Allow only one subtask in progress at the same time for a user' => '一人のユーザにつき一つのタスクのみ進行中にできます', 'Edit column "%s"' => 'カラム「%s」の編集', - 'Enable time tracking for subtasks' => 'サブタスクのタイムトラッキングを有効', 'Select the new status of the subtask: "%s"' => 'サブタスク「%s」のステータスを選択', 'Subtask timesheet' => 'サブタスクタイムシート', 'There is nothing to show.' => '何も表示するものがありません。', @@ -946,4 +945,8 @@ return array( // '%%Y-%%m-%%d' => '', // 'Total for all columns' => '', // 'You need at least 2 days of data to show the chart.' => '', + // '<15m' => '', + // '<30m' => '', + // 'Stop timer' => '', + // 'Start timer' => '', ); diff --git a/app/Locale/nl_NL/translations.php b/app/Locale/nl_NL/translations.php index 9e0cf6fe..7d59f5ec 100644 --- a/app/Locale/nl_NL/translations.php +++ b/app/Locale/nl_NL/translations.php @@ -614,7 +614,6 @@ return array( 'User dashboard' => 'Gebruiker dashboard', 'Allow only one subtask in progress at the same time for a user' => 'Sta maximaal één subtaak in behandeling toe per gebruiker', 'Edit column "%s"' => 'Kolom « %s » aanpassen', - 'Enable time tracking for subtasks' => 'Activeer tijdschrijven voor subtaken', 'Select the new status of the subtask: "%s"' => 'Selecteer nieuwe status voor subtaak : « %s »', 'Subtask timesheet' => 'Subtaak timesheet', 'There is nothing to show.' => 'Er is niets om te laten zijn.', @@ -946,4 +945,8 @@ return array( // '%%Y-%%m-%%d' => '', // 'Total for all columns' => '', // 'You need at least 2 days of data to show the chart.' => '', + // '<15m' => '', + // '<30m' => '', + // 'Stop timer' => '', + // 'Start timer' => '', ); diff --git a/app/Locale/pl_PL/translations.php b/app/Locale/pl_PL/translations.php index d2650a89..10d0c88a 100644 --- a/app/Locale/pl_PL/translations.php +++ b/app/Locale/pl_PL/translations.php @@ -614,7 +614,6 @@ return array( 'User dashboard' => 'Panel użytkownika', 'Allow only one subtask in progress at the same time for a user' => 'Zezwalaj na tylko jedno pod-zadanie o statusie "w trakcie" jednocześnie', 'Edit column "%s"' => 'Zmień kolumnę "%s"', - 'Enable time tracking for subtasks' => 'Włącz śledzenie czasu dla pod-zadań', 'Select the new status of the subtask: "%s"' => 'Wybierz nowy status dla pod-zadania: "%s"', 'Subtask timesheet' => 'Oś czasu pod-zadania', 'There is nothing to show.' => 'Nie nic do wyświetlenia', @@ -946,4 +945,8 @@ return array( // '%%Y-%%m-%%d' => '', // 'Total for all columns' => '', // 'You need at least 2 days of data to show the chart.' => '', + // '<15m' => '', + // '<30m' => '', + // 'Stop timer' => '', + // 'Start timer' => '', ); diff --git a/app/Locale/pt_BR/translations.php b/app/Locale/pt_BR/translations.php index 041434a2..1fecc08c 100644 --- a/app/Locale/pt_BR/translations.php +++ b/app/Locale/pt_BR/translations.php @@ -614,7 +614,6 @@ return array( 'User dashboard' => 'Painel de Controle do usuário', 'Allow only one subtask in progress at the same time for a user' => 'Permitir apenas uma subtarefa em andamento ao mesmo tempo para um usuário', 'Edit column "%s"' => 'Editar a coluna "%s"', - 'Enable time tracking for subtasks' => 'Ativar a gestão de tempo par a subtarefa', 'Select the new status of the subtask: "%s"' => 'Selecionar um novo status para a subtarefa: "%s"', 'Subtask timesheet' => 'Gestão de tempo das subtarefas', 'There is nothing to show.' => 'Não há nada para mostrar', @@ -946,4 +945,8 @@ return array( // '%%Y-%%m-%%d' => '', // 'Total for all columns' => '', // 'You need at least 2 days of data to show the chart.' => '', + // '<15m' => '', + // '<30m' => '', + // 'Stop timer' => '', + // 'Start timer' => '', ); diff --git a/app/Locale/ru_RU/translations.php b/app/Locale/ru_RU/translations.php index 7e94ecbb..0bfb33da 100644 --- a/app/Locale/ru_RU/translations.php +++ b/app/Locale/ru_RU/translations.php @@ -614,7 +614,6 @@ return array( 'User dashboard' => 'Пользователь панели мониторинга', 'Allow only one subtask in progress at the same time for a user' => 'Разрешена только одна подзадача в разработке одновременно для одного пользователя', 'Edit column "%s"' => 'Редактировать колонку "%s"', - 'Enable time tracking for subtasks' => 'Включить учет времени для подзадач', 'Select the new status of the subtask: "%s"' => 'Выбрать новый статус для подзадачи: "%s"', 'Subtask timesheet' => 'Табель времени подзадач', 'There is nothing to show.' => 'Здесь ничего нет.', @@ -946,4 +945,8 @@ return array( // '%%Y-%%m-%%d' => '', // 'Total for all columns' => '', // 'You need at least 2 days of data to show the chart.' => '', + // '<15m' => '', + // '<30m' => '', + // 'Stop timer' => '', + // 'Start timer' => '', ); diff --git a/app/Locale/sr_Latn_RS/translations.php b/app/Locale/sr_Latn_RS/translations.php index 8ac8ea31..0eefa97c 100644 --- a/app/Locale/sr_Latn_RS/translations.php +++ b/app/Locale/sr_Latn_RS/translations.php @@ -614,7 +614,6 @@ return array( 'User dashboard' => 'Korisnički panel', // 'Allow only one subtask in progress at the same time for a user' => '', // 'Edit column "%s"' => '', - // 'Enable time tracking for subtasks' => '', // 'Select the new status of the subtask: "%s"' => '', // 'Subtask timesheet' => '', 'There is nothing to show.' => 'Nema podataka', @@ -946,4 +945,8 @@ return array( // '%%Y-%%m-%%d' => '', // 'Total for all columns' => '', // 'You need at least 2 days of data to show the chart.' => '', + // '<15m' => '', + // '<30m' => '', + // 'Stop timer' => '', + // 'Start timer' => '', ); diff --git a/app/Locale/sv_SE/translations.php b/app/Locale/sv_SE/translations.php index 770a68d4..df721590 100644 --- a/app/Locale/sv_SE/translations.php +++ b/app/Locale/sv_SE/translations.php @@ -614,7 +614,6 @@ return array( 'User dashboard' => 'Användardashboard', 'Allow only one subtask in progress at the same time for a user' => 'Tillåt endast en deluppgift igång samtidigt för en användare', 'Edit column "%s"' => 'Ändra kolumn "%s"', - 'Enable time tracking for subtasks' => 'Aktivera tidsbevakning för deluppgifter', 'Select the new status of the subtask: "%s"' => 'Välj ny status för deluppgiften: "%s"', 'Subtask timesheet' => 'Tidrapport för deluppgiften', 'There is nothing to show.' => 'Det finns inget att visa', @@ -946,4 +945,8 @@ return array( // '%%Y-%%m-%%d' => '', // 'Total for all columns' => '', // 'You need at least 2 days of data to show the chart.' => '', + // '<15m' => '', + // '<30m' => '', + // 'Stop timer' => '', + // 'Start timer' => '', ); diff --git a/app/Locale/th_TH/translations.php b/app/Locale/th_TH/translations.php index b1fb0417..0eeaff11 100644 --- a/app/Locale/th_TH/translations.php +++ b/app/Locale/th_TH/translations.php @@ -614,7 +614,6 @@ return array( 'User dashboard' => 'ผู้ใช้แดชบอร์ด', 'Allow only one subtask in progress at the same time for a user' => 'อนุญาตให้ทำงานย่อยได้เพียงงานเดียวต่อหนึ่งคนในเวลาเดียวกัน', 'Edit column "%s"' => 'แก้ไขคอลัมน์ "%s"', - 'Enable time tracking for subtasks' => 'สามารถติดตามเวลาของงานย่อย', 'Select the new status of the subtask: "%s"' => 'เลือกสถานะใหม่ของงานย่อย: "%s"', 'Subtask timesheet' => 'เวลางานย่อย', 'There is nothing to show.' => 'ไม่มีที่ต้องแสดง', @@ -946,4 +945,8 @@ return array( // '%%Y-%%m-%%d' => '', // 'Total for all columns' => '', // 'You need at least 2 days of data to show the chart.' => '', + // '<15m' => '', + // '<30m' => '', + // 'Stop timer' => '', + // 'Start timer' => '', ); diff --git a/app/Locale/tr_TR/translations.php b/app/Locale/tr_TR/translations.php index cdd4d8cf..cb1249e7 100644 --- a/app/Locale/tr_TR/translations.php +++ b/app/Locale/tr_TR/translations.php @@ -614,7 +614,6 @@ return array( 'User dashboard' => 'Kullanıcı Anasayfası', 'Allow only one subtask in progress at the same time for a user' => 'Bir kullanıcı için aynı anda yalnızca bir alt göreve izin ver', 'Edit column "%s"' => '"%s" sütununu değiştir', - 'Enable time tracking for subtasks' => 'Alt görevler için zaman takibini etkinleştir', 'Select the new status of the subtask: "%s"' => '"%s" alt görevi için yeni durum seçin.', 'Subtask timesheet' => 'Alt görev için zaman takip tablosu', 'There is nothing to show.' => 'Gösterilecek hiçbir şey yok.', @@ -946,4 +945,8 @@ return array( // '%%Y-%%m-%%d' => '', // 'Total for all columns' => '', // 'You need at least 2 days of data to show the chart.' => '', + // '<15m' => '', + // '<30m' => '', + // 'Stop timer' => '', + // 'Start timer' => '', ); diff --git a/app/Locale/zh_CN/translations.php b/app/Locale/zh_CN/translations.php index 60634686..e0d57494 100644 --- a/app/Locale/zh_CN/translations.php +++ b/app/Locale/zh_CN/translations.php @@ -614,7 +614,6 @@ return array( 'User dashboard' => '用户仪表板', 'Allow only one subtask in progress at the same time for a user' => '每用户同时仅有一个活动子任务', 'Edit column "%s"' => '编辑栏目"%s"', - 'Enable time tracking for subtasks' => '启用子任务的时间记录', 'Select the new status of the subtask: "%s"' => '选择子任务的新状态:"%s"', 'Subtask timesheet' => '子任务时间', 'There is nothing to show.' => '无内容。', @@ -946,4 +945,8 @@ return array( // '%%Y-%%m-%%d' => '', // 'Total for all columns' => '', // 'You need at least 2 days of data to show the chart.' => '', + // '<15m' => '', + // '<30m' => '', + // 'Stop timer' => '', + // 'Start timer' => '', ); diff --git a/app/Model/Acl.php b/app/Model/Acl.php index c9e155ed..684fae13 100644 --- a/app/Model/Acl.php +++ b/app/Model/Acl.php @@ -41,6 +41,7 @@ class Acl extends Base 'subtask' => '*', 'task' => '*', 'tasklink' => '*', + 'timer' => '*', 'calendar' => array('show', 'project'), ); diff --git a/app/Model/Budget.php b/app/Model/Budget.php index d74dd870..76c42ca9 100644 --- a/app/Model/Budget.php +++ b/app/Model/Budget.php @@ -75,7 +75,7 @@ class Budget extends Base ->join(Task::TABLE, 'id', 'task_id', Subtask::TABLE) ->join(User::TABLE, 'id', 'user_id') ->eq(Task::TABLE.'.project_id', $project_id) - ->filter(array($this, 'applyUserRate')); + ->callback(array($this, 'applyUserRate')); } /** diff --git a/app/Model/Project.php b/app/Model/Project.php index 71c660b9..ef7340ee 100644 --- a/app/Model/Project.php +++ b/app/Model/Project.php @@ -276,7 +276,7 @@ class Project extends Base return $this->db ->table(Project::TABLE) ->in('id', $project_ids) - ->filter(array($this, 'applyColumnStats')); + ->callback(array($this, 'applyColumnStats')); } /** diff --git a/app/Model/Subtask.php b/app/Model/Subtask.php index ee000e32..b9cc6ded 100644 --- a/app/Model/Subtask.php +++ b/app/Model/Subtask.php @@ -78,6 +78,8 @@ class Subtask extends Base foreach ($subtasks as &$subtask) { $subtask['status_name'] = $status[$subtask['status']]; + $subtask['timer_start_date'] = isset($subtask['timer_start_date']) ? $subtask['timer_start_date'] : 0; + $subtask['is_timer_started'] = ! empty($subtask['timer_start_date']); } return $subtasks; @@ -101,12 +103,13 @@ class Subtask extends Base Task::TABLE.'.title AS task_name', Project::TABLE.'.name AS project_name' ) + ->subquery($this->subtaskTimeTracking->getTimerQuery($user_id), 'timer_start_date') ->eq('user_id', $user_id) ->eq(Project::TABLE.'.is_active', Project::ACTIVE) ->in(Subtask::TABLE.'.status', $status) ->join(Task::TABLE, 'id', 'task_id') ->join(Project::TABLE, 'id', 'project_id', Task::TABLE) - ->filter(array($this, 'addStatusName')); + ->callback(array($this, 'addStatusName')); } /** @@ -121,10 +124,15 @@ class Subtask extends Base return $this->db ->table(self::TABLE) ->eq('task_id', $task_id) - ->columns(self::TABLE.'.*', User::TABLE.'.username', User::TABLE.'.name') + ->columns( + self::TABLE.'.*', + User::TABLE.'.username', + User::TABLE.'.name' + ) + ->subquery($this->subtaskTimeTracking->getTimerQuery($this->userSession->getId()), 'timer_start_date') ->join(User::TABLE, 'id', 'user_id') ->asc(self::TABLE.'.position') - ->filter(array($this, 'addStatusName')) + ->callback(array($this, 'addStatusName')) ->findAll(); } @@ -144,8 +152,9 @@ class Subtask extends Base ->table(self::TABLE) ->eq(self::TABLE.'.id', $subtask_id) ->columns(self::TABLE.'.*', User::TABLE.'.username', User::TABLE.'.name') + ->subquery($this->subtaskTimeTracking->getTimerQuery($this->userSession->getId()), 'timer_start_date') ->join(User::TABLE, 'id', 'user_id') - ->filter(array($this, 'addStatusName')) + ->callback(array($this, 'addStatusName')) ->findOne(); } diff --git a/app/Model/SubtaskTimeTracking.php b/app/Model/SubtaskTimeTracking.php index 93a698b6..51743198 100644 --- a/app/Model/SubtaskTimeTracking.php +++ b/app/Model/SubtaskTimeTracking.php @@ -20,6 +20,27 @@ class SubtaskTimeTracking extends Base const TABLE = 'subtask_time_tracking'; /** + * Get query to check if a timer is started for the given user and subtask + * + * @access public + * @param integer $user_id User id + * @return string + */ + public function getTimerQuery($user_id) + { + return sprintf( + "SELECT %s FROM %s WHERE %s='%d' AND %s='0' AND %s=%s", + $this->db->escapeIdentifier('start'), + $this->db->escapeIdentifier(self::TABLE), + $this->db->escapeIdentifier('user_id'), + $user_id, + $this->db->escapeIdentifier('end'), + $this->db->escapeIdentifier('subtask_id'), + Subtask::TABLE.'.id' + ); + } + + /** * Get query for user timesheet (pagination) * * @access public @@ -217,7 +238,7 @@ class SubtaskTimeTracking extends Base { return $this->db ->table(self::TABLE) - ->insert(array('subtask_id' => $subtask_id, 'user_id' => $user_id, 'start' => time())); + ->insert(array('subtask_id' => $subtask_id, 'user_id' => $user_id, 'start' => time(), 'end' => 0)); } /** diff --git a/app/Model/TaskFilter.php b/app/Model/TaskFilter.php index 3d58c060..e99f36fb 100644 --- a/app/Model/TaskFilter.php +++ b/app/Model/TaskFilter.php @@ -301,7 +301,7 @@ class TaskFilter extends Base */ public function toAutoCompletion() { - return $this->query->columns('id', 'title')->filter(function(array $results) { + return $this->query->columns('id', 'title')->callback(function(array $results) { foreach ($results as &$result) { $result['value'] = $result['title']; diff --git a/app/Model/TaskFinder.php b/app/Model/TaskFinder.php index 234101ec..ead60b4b 100644 --- a/app/Model/TaskFinder.php +++ b/app/Model/TaskFinder.php @@ -316,7 +316,7 @@ class TaskFinder extends Base ->table(Task::TABLE) ->eq('project_id', $project_id) ->eq('column_id', $column_id) - ->in('is_active', 1) + ->eq('is_active', 1) ->count(); } @@ -336,7 +336,7 @@ class TaskFinder extends Base ->eq('project_id', $project_id) ->eq('column_id', $column_id) ->eq('swimlane_id', $swimlane_id) - ->in('is_active', 1) + ->eq('is_active', 1) ->count(); } diff --git a/app/Schema/Mysql.php b/app/Schema/Mysql.php index 34a609cf..dd6af5a2 100644 --- a/app/Schema/Mysql.php +++ b/app/Schema/Mysql.php @@ -6,7 +6,12 @@ use PDO; use Core\Security; use Model\Link; -const VERSION = 75; +const VERSION = 76; + +function version_76($pdo) +{ + $pdo->exec("DELETE FROM `settings` WHERE `option`='subtask_time_tracking'"); +} function version_75($pdo) { diff --git a/app/Schema/Postgres.php b/app/Schema/Postgres.php index e32b0b9b..ac6ebf6f 100644 --- a/app/Schema/Postgres.php +++ b/app/Schema/Postgres.php @@ -6,7 +6,12 @@ use PDO; use Core\Security; use Model\Link; -const VERSION = 55; +const VERSION = 56; + +function version_56($pdo) +{ + $pdo->exec('DELETE FROM "settings" WHERE "option"=\'subtask_time_tracking\''); +} function version_55($pdo) { diff --git a/app/Schema/Sqlite.php b/app/Schema/Sqlite.php index ebc3b064..feda8ec2 100644 --- a/app/Schema/Sqlite.php +++ b/app/Schema/Sqlite.php @@ -6,7 +6,12 @@ use Core\Security; use PDO; use Model\Link; -const VERSION = 72; +const VERSION = 73; + +function version_73($pdo) +{ + $pdo->exec("DELETE FROM settings WHERE option='subtask_time_tracking'"); +} function version_72($pdo) { diff --git a/app/ServiceProvider/EventDispatcherProvider.php b/app/ServiceProvider/EventDispatcherProvider.php index f566ede8..aa71d61b 100644 --- a/app/ServiceProvider/EventDispatcherProvider.php +++ b/app/ServiceProvider/EventDispatcherProvider.php @@ -12,7 +12,7 @@ use Subscriber\ProjectActivitySubscriber; use Subscriber\ProjectDailySummarySubscriber; use Subscriber\ProjectModificationDateSubscriber; use Subscriber\WebhookSubscriber; -use Subscriber\SubtaskTimesheetSubscriber; +use Subscriber\SubtaskTimeTrackingSubscriber; use Subscriber\TaskMovedDateSubscriber; use Subscriber\TransitionSubscriber; use Subscriber\RecurringTaskSubscriber; @@ -29,7 +29,7 @@ class EventDispatcherProvider implements ServiceProviderInterface $container['dispatcher']->addSubscriber(new ProjectModificationDateSubscriber($container)); $container['dispatcher']->addSubscriber(new WebhookSubscriber($container)); $container['dispatcher']->addSubscriber(new NotificationSubscriber($container)); - $container['dispatcher']->addSubscriber(new SubtaskTimesheetSubscriber($container)); + $container['dispatcher']->addSubscriber(new SubtaskTimeTrackingSubscriber($container)); $container['dispatcher']->addSubscriber(new TaskMovedDateSubscriber($container)); $container['dispatcher']->addSubscriber(new TransitionSubscriber($container)); $container['dispatcher']->addSubscriber(new RecurringTaskSubscriber($container)); diff --git a/app/Subscriber/SubtaskTimesheetSubscriber.php b/app/Subscriber/SubtaskTimeTrackingSubscriber.php index fdaf442f..02f14c40 100644 --- a/app/Subscriber/SubtaskTimesheetSubscriber.php +++ b/app/Subscriber/SubtaskTimeTrackingSubscriber.php @@ -6,7 +6,7 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Model\Subtask; use Event\SubtaskEvent; -class SubtaskTimesheetSubscriber extends \Core\Base implements EventSubscriberInterface +class SubtaskTimeTrackingSubscriber extends \Core\Base implements EventSubscriberInterface { public static function getSubscribedEvents() { @@ -28,7 +28,7 @@ class SubtaskTimesheetSubscriber extends \Core\Base implements EventSubscriberIn public function logStartEnd(SubtaskEvent $event) { - if ($this->config->get('subtask_time_tracking') == 1 && isset($event['status'])) { + if (isset($event['status'])) { $subtask = $this->subtask->getById($event['id']); diff --git a/app/Template/board/task_private.php b/app/Template/board/task_private.php index 088f47bc..77f3b958 100644 --- a/app/Template/board/task_private.php +++ b/app/Template/board/task_private.php @@ -32,8 +32,8 @@ </span> <div class="task-board-days"> - <span title="<?= t('Task age in days')?>" class="task-days-age"><?= $this->task->age($task['date_creation']) ?></span> - <span title="<?= t('Days in this column')?>" class="task-days-incolumn"><?= $this->task->age($task['date_moved']) ?></span> + <span title="<?= t('Task age in days')?>" class="task-days-age"><?= $this->datetime->age($task['date_creation']) ?></span> + <span title="<?= t('Days in this column')?>" class="task-days-incolumn"><?= $this->datetime->age($task['date_moved']) ?></span> </div> <div class="task-board-title"> diff --git a/app/Template/config/project.php b/app/Template/config/project.php index 90dd9c8e..1ab69e26 100644 --- a/app/Template/config/project.php +++ b/app/Template/config/project.php @@ -15,7 +15,6 @@ <p class="form-help"><?= t('Example: "Bug, Feature Request, Improvement"') ?></p> <?= $this->form->checkbox('subtask_restriction', t('Allow only one subtask in progress at the same time for a user'), 1, $values['subtask_restriction'] == 1) ?> - <?= $this->form->checkbox('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"/> diff --git a/app/Template/subtask/show.php b/app/Template/subtask/show.php index c9690f08..b91e830f 100644 --- a/app/Template/subtask/show.php +++ b/app/Template/subtask/show.php @@ -33,13 +33,29 @@ <?php endif ?> </td> <td> - <?php if (! empty($subtask['time_spent'])): ?> - <strong><?= $this->e($subtask['time_spent']).'h' ?></strong> <?= t('spent') ?> - <?php endif ?> + <ul class="no-bullet"> + <li> + <?php if (! empty($subtask['time_spent'])): ?> + <strong><?= $this->e($subtask['time_spent']).'h' ?></strong> <?= t('spent') ?> + <?php endif ?> - <?php if (! empty($subtask['time_estimated'])): ?> - <strong><?= $this->e($subtask['time_estimated']).'h' ?></strong> <?= t('estimated') ?> - <?php endif ?> + <?php if (! empty($subtask['time_estimated'])): ?> + <strong><?= $this->e($subtask['time_estimated']).'h' ?></strong> <?= t('estimated') ?> + <?php endif ?> + </li> + <?php if (! isset($not_editable) && $subtask['user_id'] == $this->user->getId()): ?> + <li> + <?php if ($subtask['is_timer_started']): ?> + <i class="fa fa-pause"></i> + <?= $this->url->link(t('Stop timer'), 'timer', 'subtask', array('timer' => 'stop', 'project_id' => $task['project_id'], 'task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id'])) ?> + (<?= $this->datetime->age($subtask['timer_start_date']) ?>) + <?php else: ?> + <i class="fa fa-play-circle-o"></i> + <?= $this->url->link(t('Start timer'), 'timer', 'subtask', array('timer' => 'start', 'project_id' => $task['project_id'], 'task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id'])) ?> + <?php endif ?> + </li> + <?php endif ?> + </ul> </td> <?php if (! isset($not_editable)): ?> <td> diff --git a/composer.json b/composer.json index 39585052..d04c8861 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,7 @@ "erusev/parsedown" : "1.5.1", "fabiang/xmpp" : "0.6.1", "fguillot/json-rpc" : "0.0.3", - "fguillot/picodb" : "0.0.3", + "fguillot/picodb" : "dev-master", "fguillot/simpleLogger" : "0.0.2", "fguillot/simple-validator" : "0.0.3", "lusitanian/oauth" : "0.3.5", diff --git a/composer.lock b/composer.lock index 35709f5b..a665b9ab 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "4aed9321378ab6e96c0fd0170b379c38", + "hash": "c5913f9f57295aae111f3a29b385dafb", "packages": [ { "name": "christian-riesen/base32", @@ -297,16 +297,16 @@ }, { "name": "fguillot/picodb", - "version": "v0.0.3", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/fguillot/picoDb.git", - "reference": "f65d11cb52de34e0fd236a34184ca1a310da244a" + "reference": "dd08649713c9d8330b3c5fa23220a11cb5da3e79" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fguillot/picoDb/zipball/f65d11cb52de34e0fd236a34184ca1a310da244a", - "reference": "f65d11cb52de34e0fd236a34184ca1a310da244a", + "url": "https://api.github.com/repos/fguillot/picoDb/zipball/dd08649713c9d8330b3c5fa23220a11cb5da3e79", + "reference": "dd08649713c9d8330b3c5fa23220a11cb5da3e79", "shasum": "" }, "require": { @@ -330,7 +330,7 @@ ], "description": "Minimalist database query builder", "homepage": "https://github.com/fguillot/picoDb", - "time": "2015-05-17 23:57:05" + "time": "2015-06-24 21:58:15" }, { "name": "fguillot/simple-validator", @@ -820,6 +820,7 @@ "aliases": [], "minimum-stability": "stable", "stability-flags": { + "fguillot/picodb": 20, "swiftmailer/swiftmailer": 0, "symfony/console": 0 }, diff --git a/tests/units/DatetimeHelperTest.php b/tests/units/DatetimeHelperTest.php index 2746beed..216cf34c 100644 --- a/tests/units/DatetimeHelperTest.php +++ b/tests/units/DatetimeHelperTest.php @@ -6,6 +6,18 @@ use Helper\Datetime; class DatetimeHelperTest extends Base { + public function testAge() + { + $h = new Datetime($this->container); + + $this->assertEquals('<15m', $h->age(0, 30)); + $this->assertEquals('<30m', $h->age(0, 1000)); + $this->assertEquals('<1h', $h->age(0, 3000)); + $this->assertEquals('~2h', $h->age(0, 2*3600)); + $this->assertEquals('1d', $h->age(0, 30*3600)); + $this->assertEquals('2d', $h->age(0, 65*3600)); + } + public function testGetDayHours() { $h = new Datetime($this->container); diff --git a/tests/units/SubtaskTimeTrackingTest.php b/tests/units/SubtaskTimeTrackingTest.php index e15e60da..94359159 100644 --- a/tests/units/SubtaskTimeTrackingTest.php +++ b/tests/units/SubtaskTimeTrackingTest.php @@ -9,9 +9,61 @@ use Model\SubtaskTimeTracking; use Model\Project; use Model\Category; use Model\User; +use Core\Session; class SubtaskTimeTrackingTest extends Base { + public function testGetTimerStatus() + { + $tc = new TaskCreation($this->container); + $s = new Subtask($this->container); + $st = new SubtaskTimeTracking($this->container); + $p = new Project($this->container); + $ss = new Session; + + $ss['user'] = array('id' => 1); + + $this->assertEquals(1, $p->create(array('name' => 'test1'))); + $this->assertEquals(1, $tc->create(array('title' => 'test 1', 'project_id' => 1, 'column_id' => 1, 'owner_id' => 1))); + $this->assertEquals(1, $s->create(array('title' => 'subtask #1', 'task_id' => 1, 'user_id' => 1))); + + // Nothing started + $subtasks = $s->getAll(1); + $this->assertNotEmpty($subtasks); + $this->assertEquals(0, $subtasks[0]['timer_start_date']); + $this->assertFalse($subtasks[0]['is_timer_started']); + + $subtask = $s->getbyId(1, true); + $this->assertNotEmpty($subtask); + $this->assertEquals(0, $subtask['timer_start_date']); + $this->assertFalse($subtask['is_timer_started']); + + // Start the clock + $this->assertTrue($st->logStartTime(1, 1)); + + $subtasks = $s->getAll(1); + $this->assertNotEmpty($subtasks); + $this->assertEquals(time(), $subtasks[0]['timer_start_date'], '', 3); + $this->assertTrue($subtasks[0]['is_timer_started']); + + $subtask = $s->getbyId(1, true); + $this->assertNotEmpty($subtask); + $this->assertEquals(time(), $subtask['timer_start_date'], '', 3); + $this->assertTrue($subtask['is_timer_started']); + + // Stop the clock + $this->assertTrue($st->logEndTime(1, 1)); + $subtasks = $s->getAll(1); + $this->assertNotEmpty($subtasks); + $this->assertEquals(0, $subtasks[0]['timer_start_date']); + $this->assertFalse($subtasks[0]['is_timer_started']); + + $subtask = $s->getbyId(1, true); + $this->assertNotEmpty($subtask); + $this->assertEquals(0, $subtask['timer_start_date']); + $this->assertFalse($subtask['is_timer_started']); + } + public function testLogStartTime() { $tc = new TaskCreation($this->container); |