Add timer for subtasks and remove settings for subtask time tracking

This commit is contained in:
Frederic Guillot 2015-06-24 20:39:06 -04:00
parent 58c96b8c4e
commit 6efac784fc
41 changed files with 297 additions and 79 deletions

View File

@ -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);

View File

@ -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');
}
}

35
app/Controller/Timer.php Normal file
View File

@ -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');
}
}

View File

@ -10,6 +10,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
*

View File

@ -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();

View File

@ -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' => '',
);

View File

@ -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' => '',
);

View File

@ -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' => '',
);

View File

@ -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' => '',
);

View File

@ -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',
);

View File

@ -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' => '',
);

View File

@ -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' => '',
);

View File

@ -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' => '',
);

View File

@ -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' => '',
);

View File

@ -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' => '',
);

View File

@ -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' => '',
);

View File

@ -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' => '',
);

View File

@ -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' => '',
);

View File

@ -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' => '',
);

View File

@ -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' => '',
);

View File

@ -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' => '',
);

View File

@ -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' => '',
);

View File

@ -41,6 +41,7 @@ class Acl extends Base
'subtask' => '*',
'task' => '*',
'tasklink' => '*',
'timer' => '*',
'calendar' => array('show', 'project'),
);

View File

@ -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'));
}
/**

View File

@ -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'));
}
/**

View File

@ -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();
}

View File

@ -19,6 +19,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)
*
@ -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));
}
/**

View File

@ -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'];

View File

@ -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();
}

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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));

View File

@ -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']);

View File

@ -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">

View File

@ -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"/>

View File

@ -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>

View File

@ -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",

13
composer.lock generated
View File

@ -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
},

View File

@ -6,6 +6,18 @@ use Helper\Datetime;
class DatetimeHelperTest extends Base
{
public function testAge()
{
$h = new Datetime($this->container);
$this->assertEquals('&lt;15m', $h->age(0, 30));
$this->assertEquals('&lt;30m', $h->age(0, 1000));
$this->assertEquals('&lt;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);

View File

@ -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);