From 074056352de98fc567b4d13184c72887c75625d0 Mon Sep 17 00:00:00 2001 From: Frédéric Guillot Date: Sun, 12 Oct 2014 21:38:56 -0400 Subject: Project activity refactoring and listeners improvements --- app/Action/Base.php | 1 + app/Controller/Base.php | 4 +- app/Controller/Project.php | 4 +- app/Core/Event.php | 5 +- app/Event/Base.php | 79 +++++++++++ app/Event/BaseNotificationListener.php | 87 ------------ app/Event/CommentHistoryListener.php | 73 ---------- app/Event/CommentNotificationListener.php | 30 ---- app/Event/FileNotificationListener.php | 30 ---- app/Event/NotificationListener.php | 83 +++++++++++ app/Event/ProjectActivityListener.php | 61 +++++++++ app/Event/ProjectModificationDate.php | 63 --------- app/Event/ProjectModificationDateListener.php | 30 ++++ app/Event/SubTaskNotificationListener.php | 30 ---- app/Event/SubtaskHistoryListener.php | 73 ---------- app/Event/TaskHistoryListener.php | 63 --------- app/Event/TaskNotificationListener.php | 29 ---- app/Event/WebhookListener.php | 32 +---- app/Locales/de_DE/translations.php | 4 +- app/Locales/es_ES/translations.php | 4 +- app/Locales/fi_FI/translations.php | 4 +- app/Locales/fr_FR/translations.php | 4 +- app/Locales/it_IT/translations.php | 4 +- app/Locales/pl_PL/translations.php | 4 +- app/Locales/pt_BR/translations.php | 4 +- app/Locales/ru_RU/translations.php | 4 +- app/Locales/sv_SE/translations.php | 4 +- app/Locales/zh_CN/translations.php | 4 +- app/Model/BaseHistory.php | 70 ---------- app/Model/CommentHistory.php | 152 --------------------- app/Model/Notification.php | 60 ++++---- app/Model/Project.php | 35 +---- app/Model/ProjectActivity.php | 189 ++++++++++++++++++++++++++ app/Model/SubtaskHistory.php | 161 ---------------------- app/Model/TaskHistory.php | 160 ---------------------- app/Model/Webhook.php | 11 +- app/Schema/Mysql.php | 27 +++- app/Schema/Postgres.php | 24 +++- app/Schema/Sqlite.php | 24 +++- app/Templates/event_comment_create.php | 8 +- app/Templates/event_comment_update.php | 8 +- app/Templates/event_subtask_create.php | 25 ++-- app/Templates/event_subtask_update.php | 25 ++-- app/Templates/event_task_assignee_change.php | 10 +- app/Templates/event_task_close.php | 2 +- app/Templates/event_task_create.php | 2 +- app/Templates/event_task_move_column.php | 4 +- app/Templates/event_task_move_position.php | 4 +- app/Templates/event_task_open.php | 2 +- app/Templates/event_task_update.php | 2 +- app/Templates/project_activity.php | 16 +-- app/Templates/project_feed.php | 4 +- app/Templates/task_public.php | 2 +- assets/css/app.css | 19 ++- tests/units/ProjectActivityTest.php | 102 ++++++++++++++ tests/units/ProjectTest.php | 2 +- tests/units/TaskHistoryTest.php | 98 ------------- 57 files changed, 776 insertions(+), 1289 deletions(-) create mode 100644 app/Event/Base.php delete mode 100644 app/Event/BaseNotificationListener.php delete mode 100644 app/Event/CommentHistoryListener.php delete mode 100644 app/Event/CommentNotificationListener.php delete mode 100644 app/Event/FileNotificationListener.php create mode 100644 app/Event/NotificationListener.php create mode 100644 app/Event/ProjectActivityListener.php delete mode 100644 app/Event/ProjectModificationDate.php create mode 100644 app/Event/ProjectModificationDateListener.php delete mode 100644 app/Event/SubTaskNotificationListener.php delete mode 100644 app/Event/SubtaskHistoryListener.php delete mode 100644 app/Event/TaskHistoryListener.php delete mode 100644 app/Event/TaskNotificationListener.php delete mode 100644 app/Model/BaseHistory.php delete mode 100644 app/Model/CommentHistory.php create mode 100644 app/Model/ProjectActivity.php delete mode 100644 app/Model/SubtaskHistory.php delete mode 100644 app/Model/TaskHistory.php create mode 100644 tests/units/ProjectActivityTest.php delete mode 100644 tests/units/TaskHistoryTest.php diff --git a/app/Action/Base.php b/app/Action/Base.php index 339aeecc..80930a4c 100644 --- a/app/Action/Base.php +++ b/app/Action/Base.php @@ -14,6 +14,7 @@ use Core\Tool; * * @property \Model\Acl $acl * @property \Model\Task $task + * @property \Model\TaskFinder $taskFinder */ abstract class Base implements Listener { diff --git a/app/Controller/Base.php b/app/Controller/Base.php index ac88df04..a8e22fd8 100644 --- a/app/Controller/Base.php +++ b/app/Controller/Base.php @@ -153,13 +153,11 @@ abstract class Base private function attachEvents() { $models = array( + 'projectActivity', // Order is important 'action', 'project', 'webhook', 'notification', - 'taskHistory', - 'commentHistory', - 'subtaskHistory', ); foreach ($models as $model) { diff --git a/app/Controller/Project.php b/app/Controller/Project.php index 39d8d1b0..1fac3ffb 100644 --- a/app/Controller/Project.php +++ b/app/Controller/Project.php @@ -364,7 +364,7 @@ class Project extends Base } $this->response->xml($this->template->load('project_feed', array( - 'events' => $this->project->getActivity($project['id']), + 'events' => $this->projectActivity->getAll($project['id']), 'project' => $project, ))); } @@ -379,7 +379,7 @@ class Project extends Base $project = $this->getProject(); $this->response->html($this->template->layout('project_activity', array( - 'events' => $this->project->getActivity($project['id']), + 'events' => $this->projectActivity->getAll($project['id']), 'menu' => 'projects', 'project' => $project, 'title' => t('%s\'s activity', $project['name']) diff --git a/app/Core/Event.php b/app/Core/Event.php index 0e6df5e8..a32499d8 100644 --- a/app/Core/Event.php +++ b/app/Core/Event.php @@ -69,11 +69,14 @@ class Event { if (! $this->isEventTriggered($eventName)) { - $this->lastEvent = $eventName; $this->events[] = $eventName; if (isset($this->listeners[$eventName])) { + foreach ($this->listeners[$eventName] as $listener) { + + $this->lastEvent = $eventName; + if ($listener->execute($data)) { $this->lastListener = get_class($listener); } diff --git a/app/Event/Base.php b/app/Event/Base.php new file mode 100644 index 00000000..745871a5 --- /dev/null +++ b/app/Event/Base.php @@ -0,0 +1,79 @@ +registry = $registry; + } + + /** + * Return class information + * + * @access public + * @return string + */ + public function __toString() + { + return get_called_class(); + } + + /** + * Load automatically models + * + * @access public + * @param string $name Model name + * @return mixed + */ + public function __get($name) + { + return Tool::loadModel($this->registry, $name); + } + + /** + * Get event namespace + * + * Event = task.close | Namespace = task + * + * @access public + * @return string + */ + public function getEventNamespace() + { + $event_name = $this->registry->event->getLastTriggeredEvent(); + return substr($event_name, 0, strpos($event_name, '.')); + } +} diff --git a/app/Event/BaseNotificationListener.php b/app/Event/BaseNotificationListener.php deleted file mode 100644 index fdabaf57..00000000 --- a/app/Event/BaseNotificationListener.php +++ /dev/null @@ -1,87 +0,0 @@ -template = $template; - $this->notification = $notification; - } - - /** - * Return class information - * - * @access public - * @return string - */ - public function __toString() - { - return get_called_class(); - } - - /** - * Execute the action - * - * @access public - * @param array $data Event data dictionary - * @return bool True if the action was executed or false when not executed - */ - public function execute(array $data) - { - $values = $this->getTemplateData($data); - - // Get the list of users to be notified - $users = $this->notification->getUsersList($values['task']['project_id']); - - // Send notifications - if ($users) { - $this->notification->sendEmails($this->template, $users, $values); - return true; - } - - return false; - } -} diff --git a/app/Event/CommentHistoryListener.php b/app/Event/CommentHistoryListener.php deleted file mode 100644 index ec826615..00000000 --- a/app/Event/CommentHistoryListener.php +++ /dev/null @@ -1,73 +0,0 @@ -model = $model; - } - - /** - * Return class information - * - * @access public - * @return string - */ - public function __toString() - { - return get_called_class(); - } - - /** - * Execute the action - * - * @access public - * @param array $data Event data dictionary - * @return bool True if the action was executed or false when not executed - */ - public function execute(array $data) - { - $creator_id = $this->model->acl->getUserId(); - - if ($creator_id && isset($data['task_id']) && isset($data['id'])) { - - $task = $this->model->taskFinder->getById($data['task_id']); - - $this->model->create( - $task['project_id'], - $data['task_id'], - $data['id'], - $creator_id, - $this->model->event->getLastTriggeredEvent(), - $data['comment'] - ); - } - - return false; - } -} diff --git a/app/Event/CommentNotificationListener.php b/app/Event/CommentNotificationListener.php deleted file mode 100644 index 13803874..00000000 --- a/app/Event/CommentNotificationListener.php +++ /dev/null @@ -1,30 +0,0 @@ -notification->comment->getById($data['id']); - $values['task'] = $this->notification->taskFinder->getDetails($values['comment']['task_id']); - - return $values; - } -} diff --git a/app/Event/FileNotificationListener.php b/app/Event/FileNotificationListener.php deleted file mode 100644 index 2366ce5d..00000000 --- a/app/Event/FileNotificationListener.php +++ /dev/null @@ -1,30 +0,0 @@ -notification->taskFinder->getDetails($data['task_id']); - - return $values; - } -} diff --git a/app/Event/NotificationListener.php b/app/Event/NotificationListener.php new file mode 100644 index 00000000..3c049327 --- /dev/null +++ b/app/Event/NotificationListener.php @@ -0,0 +1,83 @@ +template = $template; + } + + /** + * Execute the action + * + * @access public + * @param array $data Event data dictionary + * @return bool True if the action was executed or false when not executed + */ + public function execute(array $data) + { + $values = $this->getTemplateData($data); + $users = $this->notification->getUsersList($values['task']['project_id']); + + if ($users) { + $this->notification->sendEmails($this->template, $users, $values); + return true; + } + + return false; + } + + /** + * Fetch data for the mail template + * + * @access public + * @param array $data Event data + * @return array + */ + public function getTemplateData(array $data) + { + $values = array(); + + switch ($this->getEventNamespace()) { + case 'task': + $values['task'] = $this->taskFinder->getDetails($data['task_id']); + break; + case 'subtask': + $values['subtask'] = $this->subtask->getById($data['id'], true); + $values['task'] = $this->taskFinder->getDetails($data['task_id']); + break; + case 'file': + $values['file'] = $data; + $values['task'] = $this->taskFinder->getDetails($data['task_id']); + break; + case 'comment': + $values['comment'] = $this->comment->getById($data['id']); + $values['task'] = $this->taskFinder->getDetails($values['comment']['task_id']); + break; + } + + return $values; + } +} diff --git a/app/Event/ProjectActivityListener.php b/app/Event/ProjectActivityListener.php new file mode 100644 index 00000000..8958bd2b --- /dev/null +++ b/app/Event/ProjectActivityListener.php @@ -0,0 +1,61 @@ +getValues($data); + + return $this->projectActivity->createEvent( + $values['task']['project_id'], + $values['task']['id'], + $this->acl->getUserId(), + $this->registry->event->getLastTriggeredEvent(), + $values + ); + } + + return false; + } + + /** + * Get event activity data + * + * @access private + * @param array $data Event data dictionary + * @return array + */ + private function getValues(array $data) + { + $values = array(); + $values['task'] = $this->taskFinder->getDetails($data['task_id']); + + switch ($this->getEventNamespace()) { + case 'subtask': + $values['subtask'] = $this->subTask->getById($data['id'], true); + break; + case 'comment': + $values['comment'] = $this->comment->getById($data['id']); + break; + } + + return $values; + } +} diff --git a/app/Event/ProjectModificationDate.php b/app/Event/ProjectModificationDate.php deleted file mode 100644 index 1b0b3736..00000000 --- a/app/Event/ProjectModificationDate.php +++ /dev/null @@ -1,63 +0,0 @@ -project = $project; - } - - /** - * Return class information - * - * @access public - * @return string - */ - public function __toString() - { - return get_called_class(); - } - - /** - * Execute the action - * - * @access public - * @param array $data Event data dictionary - * @return bool True if the action was executed or false when not executed - */ - public function execute(array $data) - { - if (isset($data['project_id'])) { - return $this->project->updateModificationDate($data['project_id']); - } - - return false; - } -} diff --git a/app/Event/ProjectModificationDateListener.php b/app/Event/ProjectModificationDateListener.php new file mode 100644 index 00000000..abc176b0 --- /dev/null +++ b/app/Event/ProjectModificationDateListener.php @@ -0,0 +1,30 @@ +project->updateModificationDate($data['project_id']); + } + + return false; + } +} diff --git a/app/Event/SubTaskNotificationListener.php b/app/Event/SubTaskNotificationListener.php deleted file mode 100644 index 2b35d757..00000000 --- a/app/Event/SubTaskNotificationListener.php +++ /dev/null @@ -1,30 +0,0 @@ -notification->subtask->getById($data['id'], true); - $values['task'] = $this->notification->taskFinder->getDetails($data['task_id']); - - return $values; - } -} diff --git a/app/Event/SubtaskHistoryListener.php b/app/Event/SubtaskHistoryListener.php deleted file mode 100644 index 6b4f1c2d..00000000 --- a/app/Event/SubtaskHistoryListener.php +++ /dev/null @@ -1,73 +0,0 @@ -model = $model; - } - - /** - * Return class information - * - * @access public - * @return string - */ - public function __toString() - { - return get_called_class(); - } - - /** - * Execute the action - * - * @access public - * @param array $data Event data dictionary - * @return bool True if the action was executed or false when not executed - */ - public function execute(array $data) - { - $creator_id = $this->model->acl->getUserId(); - - if ($creator_id && isset($data['task_id']) && isset($data['id'])) { - - $task = $this->model->taskFinder->getById($data['task_id']); - - $this->model->create( - $task['project_id'], - $data['task_id'], - $data['id'], - $creator_id, - $this->model->event->getLastTriggeredEvent(), - '' - ); - } - - return false; - } -} diff --git a/app/Event/TaskHistoryListener.php b/app/Event/TaskHistoryListener.php deleted file mode 100644 index d963fa71..00000000 --- a/app/Event/TaskHistoryListener.php +++ /dev/null @@ -1,63 +0,0 @@ -model = $model; - } - - /** - * Return class information - * - * @access public - * @return string - */ - public function __toString() - { - return get_called_class(); - } - - /** - * Execute the action - * - * @access public - * @param array $data Event data dictionary - * @return bool True if the action was executed or false when not executed - */ - public function execute(array $data) - { - $creator_id = $this->model->acl->getUserId(); - - if ($creator_id && isset($data['task_id']) && isset($data['project_id'])) { - $this->model->create($data['project_id'], $data['task_id'], $creator_id, $this->model->event->getLastTriggeredEvent()); - } - - return false; - } -} diff --git a/app/Event/TaskNotificationListener.php b/app/Event/TaskNotificationListener.php deleted file mode 100644 index 119e61e5..00000000 --- a/app/Event/TaskNotificationListener.php +++ /dev/null @@ -1,29 +0,0 @@ -notification->taskFinder->getDetails($data['task_id']); - - return $values; - } -} diff --git a/app/Event/WebhookListener.php b/app/Event/WebhookListener.php index c2f6d56a..f7e23e07 100644 --- a/app/Event/WebhookListener.php +++ b/app/Event/WebhookListener.php @@ -2,25 +2,14 @@ namespace Event; -use Core\Listener; -use Model\Webhook; - /** * Webhook task events * * @package event * @author Frederic Guillot */ -class WebhookListener implements Listener +class WebhookListener extends Base { - /** - * Webhook model - * - * @accesss private - * @var \Model\Webhook - */ - private $webhook; - /** * Url to call * @@ -30,27 +19,14 @@ class WebhookListener implements Listener private $url = ''; /** - * Constructor + * Set webhook url * * @access public - * @param string $url URL to call - * @param \Model\Webhook $webhook Webhook model instance + * @param string $url URL to call */ - public function __construct($url, Webhook $webhook) + public function setUrl($url) { $this->url = $url; - $this->webhook = $webhook; - } - - /** - * Return class information - * - * @access public - * @return string - */ - public function __toString() - { - return get_called_class(); } /** diff --git a/app/Locales/de_DE/translations.php b/app/Locales/de_DE/translations.php index fdf96638..4b901148 100644 --- a/app/Locales/de_DE/translations.php +++ b/app/Locales/de_DE/translations.php @@ -495,8 +495,8 @@ return array( // 'Default values are "%s"' => '', // 'Default columns for new projects (Comma-separated)' => '', // 'Task assignee change' => '', - // '%s change the assignee of the task #%d' => '', - // '%s change the assignee of the task #%d' => '', + // '%s change the assignee of the task #%d to %s' => '', + // '%s change the assignee of the task #%d to %s' => '', // '[%s][Column Change] %s (#%d)' => '', // '[%s][Position Change] %s (#%d)' => '', // '[%s][Assignee Change] %s (#%d)' => '', diff --git a/app/Locales/es_ES/translations.php b/app/Locales/es_ES/translations.php index 368a11ee..0cdd5f32 100644 --- a/app/Locales/es_ES/translations.php +++ b/app/Locales/es_ES/translations.php @@ -495,8 +495,8 @@ return array( 'Default values are "%s"' => 'Los valores por defecto son "%s"', 'Default columns for new projects (Comma-separated)' => 'Columnas por defecto de los nuevos proyectos (Separadas mediante comas)', 'Task assignee change' => 'Cambiar persona asignada a la tarea', - '%s change the assignee of the task #%d' => '%s cambia la persona asignada a la tarea #%d', - '%s change the assignee of the task #%d' => '%s cambia la persona asignada a la tarea #%d', + // '%s change the assignee of the task #%d to %s' => '', + // '%s change the assignee of the task #%d to %s' => '', '[%s][Column Change] %s (#%d)' => '[%s][Cambia Columna] %s (#%d)', '[%s][Position Change] %s (#%d)' => '[%s][Cambia Posición] %s (#%d)', '[%s][Assignee Change] %s (#%d)' => '[%s][Cambia Persona Asignada] %s (#%d)', diff --git a/app/Locales/fi_FI/translations.php b/app/Locales/fi_FI/translations.php index 2a2f3e96..5d05e92d 100644 --- a/app/Locales/fi_FI/translations.php +++ b/app/Locales/fi_FI/translations.php @@ -495,8 +495,8 @@ return array( // 'Default values are "%s"' => '', // 'Default columns for new projects (Comma-separated)' => '', // 'Task assignee change' => '', - // '%s change the assignee of the task #%d' => '', - // '%s change the assignee of the task #%d' => '', + // '%s change the assignee of the task #%d to %s' => '', + // '%s change the assignee of the task #%d to %s' => '', // '[%s][Column Change] %s (#%d)' => '', // '[%s][Position Change] %s (#%d)' => '', // '[%s][Assignee Change] %s (#%d)' => '', diff --git a/app/Locales/fr_FR/translations.php b/app/Locales/fr_FR/translations.php index 49e6bc2f..f4bffc3b 100644 --- a/app/Locales/fr_FR/translations.php +++ b/app/Locales/fr_FR/translations.php @@ -495,8 +495,8 @@ return array( 'Default values are "%s"' => 'Les valeurs par défaut sont « %s »', 'Default columns for new projects (Comma-separated)' => 'Colonnes par défaut pour les nouveaux projets (séparé par des virgules)', 'Task assignee change' => 'Modification de la personne assignée sur une tâche', - '%s change the assignee of the task #%d' => '%s a changé la personne assignée sur la tâche #%d', - '%s change the assignee of the task #%d' => '%s a changé la personne assignée sur la tâche n°%d', + '%s change the assignee of the task #%d to %s' => '%s a changé la personne assignée sur la tâche #%d pour %s', + '%s change the assignee of the task #%d to %s' => '%s a changé la personne assignée sur la tâche n°%d pour %s', '[%s][Column Change] %s (#%d)' => '[%s][Changement de colonne] %s (#%d)', '[%s][Position Change] %s (#%d)' => '[%s][Changement de position] %s (#%d)', '[%s][Assignee Change] %s (#%d)' => '[%s][Changement d\'assigné] %s (#%d)', diff --git a/app/Locales/it_IT/translations.php b/app/Locales/it_IT/translations.php index 9f532438..5854d033 100644 --- a/app/Locales/it_IT/translations.php +++ b/app/Locales/it_IT/translations.php @@ -495,8 +495,8 @@ return array( // 'Default values are "%s"' => '', // 'Default columns for new projects (Comma-separated)' => '', // 'Task assignee change' => '', - // '%s change the assignee of the task #%d' => '', - // '%s change the assignee of the task #%d' => '', + // '%s change the assignee of the task #%d to %s' => '', + // '%s change the assignee of the task #%d to %s' => '', // '[%s][Column Change] %s (#%d)' => '', // '[%s][Position Change] %s (#%d)' => '', // '[%s][Assignee Change] %s (#%d)' => '', diff --git a/app/Locales/pl_PL/translations.php b/app/Locales/pl_PL/translations.php index f2e96bb6..fe76b34c 100644 --- a/app/Locales/pl_PL/translations.php +++ b/app/Locales/pl_PL/translations.php @@ -495,8 +495,8 @@ return array( // 'Default values are "%s"' => '', // 'Default columns for new projects (Comma-separated)' => '', // 'Task assignee change' => '', - // '%s change the assignee of the task #%d' => '', - // '%s change the assignee of the task #%d' => '', + // '%s change the assignee of the task #%d to %s' => '', + // '%s change the assignee of the task #%d to %s' => '', // '[%s][Column Change] %s (#%d)' => '', // '[%s][Position Change] %s (#%d)' => '', // '[%s][Assignee Change] %s (#%d)' => '', diff --git a/app/Locales/pt_BR/translations.php b/app/Locales/pt_BR/translations.php index 3f208361..59fcc983 100644 --- a/app/Locales/pt_BR/translations.php +++ b/app/Locales/pt_BR/translations.php @@ -495,8 +495,8 @@ return array( // 'Default values are "%s"' => '', // 'Default columns for new projects (Comma-separated)' => '', // 'Task assignee change' => '', - // '%s change the assignee of the task #%d' => '', - // '%s change the assignee of the task #%d' => '', + // '%s change the assignee of the task #%d to %s' => '', + // '%s change the assignee of the task #%d to %s' => '', // '[%s][Column Change] %s (#%d)' => '', // '[%s][Position Change] %s (#%d)' => '', // '[%s][Assignee Change] %s (#%d)' => '', diff --git a/app/Locales/ru_RU/translations.php b/app/Locales/ru_RU/translations.php index 5c634184..1f5bfea2 100644 --- a/app/Locales/ru_RU/translations.php +++ b/app/Locales/ru_RU/translations.php @@ -495,8 +495,8 @@ return array( 'Default values are "%s"' => 'Колонки по умолчанию: "%s"', 'Default columns for new projects (Comma-separated)' => 'Колонки по умолчанию для новых проектов (разделять запятой)', // 'Task assignee change' => '', - // '%s change the assignee of the task #%d' => '', - // '%s change the assignee of the task #%d' => '', + // '%s change the assignee of the task #%d to %s' => '', + // '%s change the assignee of the task #%d to %s' => '', // '[%s][Column Change] %s (#%d)' => '', // '[%s][Position Change] %s (#%d)' => '', // '[%s][Assignee Change] %s (#%d)' => '', diff --git a/app/Locales/sv_SE/translations.php b/app/Locales/sv_SE/translations.php index a96b6c9a..20b685ef 100644 --- a/app/Locales/sv_SE/translations.php +++ b/app/Locales/sv_SE/translations.php @@ -495,8 +495,8 @@ return array( // 'Default values are "%s"' => '', // 'Default columns for new projects (Comma-separated)' => '', // 'Task assignee change' => '', - // '%s change the assignee of the task #%d' => '', - // '%s change the assignee of the task #%d' => '', + // '%s change the assignee of the task #%d to %s' => '', + // '%s change the assignee of the task #%d to %s' => '', // '[%s][Column Change] %s (#%d)' => '', // '[%s][Position Change] %s (#%d)' => '', // '[%s][Assignee Change] %s (#%d)' => '', diff --git a/app/Locales/zh_CN/translations.php b/app/Locales/zh_CN/translations.php index ea7183c7..92f46ef9 100644 --- a/app/Locales/zh_CN/translations.php +++ b/app/Locales/zh_CN/translations.php @@ -495,8 +495,8 @@ return array( // 'Default values are "%s"' => '', // 'Default columns for new projects (Comma-separated)' => '', // 'Task assignee change' => '', - // '%s change the assignee of the task #%d' => '', - // '%s change the assignee of the task #%d' => '', + // '%s change the assignee of the task #%d to %s' => '', + // '%s change the assignee of the task #%d to %s' => '', // '[%s][Column Change] %s (#%d)' => '', // '[%s][Position Change] %s (#%d)' => '', // '[%s][Assignee Change] %s (#%d)' => '', diff --git a/app/Model/BaseHistory.php b/app/Model/BaseHistory.php deleted file mode 100644 index 31578a3b..00000000 --- a/app/Model/BaseHistory.php +++ /dev/null @@ -1,70 +0,0 @@ -db->table($this->table)->count() > $max) { - - $this->db->execute(' - DELETE FROM '.$this->table.' - WHERE id <= ( - SELECT id FROM ( - SELECT id FROM '.$this->table.' ORDER BY id DESC LIMIT 1 OFFSET '.$max.' - ) foo - )'); - } - } - - /** - * Get all events for a given project - * - * @access public - * @return array - */ - public function getAllByProjectId($project_id) - { - return $this->db->table($this->table) - ->eq('project_id', $project_id) - ->desc('id') - ->findAll(); - } - - /** - * Get the event html content - * - * @access public - * @param array $params Event properties - * @return string - */ - public function getContent(array $params) - { - $tpl = new Template; - return $tpl->load('event_'.str_replace('.', '_', $params['event_name']), $params); - } -} diff --git a/app/Model/CommentHistory.php b/app/Model/CommentHistory.php deleted file mode 100644 index 5988c026..00000000 --- a/app/Model/CommentHistory.php +++ /dev/null @@ -1,152 +0,0 @@ -table = self::TABLE; - } - - /** - * Create a new event - * - * @access public - * @param integer $project_id Project id - * @param integer $task_id Task id - * @param integer $comment_id Comment id - * @param integer $creator_id Author of the event (user id) - * @param string $event_name Task event name - * @param string $data Current comment - * @return boolean - */ - public function create($project_id, $task_id, $comment_id, $creator_id, $event_name, $data) - { - $values = array( - 'project_id' => $project_id, - 'task_id' => $task_id, - 'comment_id' => $comment_id, - 'creator_id' => $creator_id, - 'event_name' => $event_name, - 'date_creation' => time(), - 'data' => $data, - ); - - $this->db->startTransaction(); - - $this->cleanup(self::MAX_EVENTS - 1); - $result = $this->db->table(self::TABLE)->insert($values); - - $this->db->closeTransaction(); - - return $result; - } - - /** - * Get all necessary content to display activity feed - * - * $author_name - * $author_username - * $task['id', 'title', 'position', 'column_name'] - */ - public function getAllContentByProjectId($project_id, $limit = 50) - { - $sql = ' - SELECT - comment_has_events.id, - comment_has_events.date_creation, - comment_has_events.event_name, - comment_has_events.data as comment, - comment_has_events.task_id, - tasks.title as task_title, - users.username as author_username, - users.name as author_name - FROM comment_has_events - LEFT JOIN users ON users.id=comment_has_events.creator_id - LEFT JOIN tasks ON tasks.id=comment_has_events.task_id - WHERE comment_has_events.project_id = ? - ORDER BY comment_has_events.id DESC - LIMIT '.$limit.' OFFSET 0 - '; - - $rq = $this->db->execute($sql, array($project_id)); - $events = $rq->fetchAll(PDO::FETCH_ASSOC); - - foreach ($events as &$event) { - $event['author'] = $event['author_name'] ?: $event['author_username']; - $event['event_title'] = $this->getTitle($event); - $event['event_content'] = $this->getContent($event); - $event['event_type'] = 'comment'; - } - - return $events; - } - - /** - * Get the event title (translated) - * - * @access public - * @param array $event Event properties - * @return string - */ - public function getTitle(array $event) - { - $titles = array( - Comment::EVENT_UPDATE => t('%s updated a comment on the task #%d', $event['author'], $event['task_id']), - Comment::EVENT_CREATE => t('%s commented on the task #%d', $event['author'], $event['task_id']), - ); - - return isset($titles[$event['event_name']]) ? $titles[$event['event_name']] : ''; - } - - /** - * Attach events to be able to record the history - * - * @access public - */ - public function attachEvents() - { - $events = array( - Comment::EVENT_UPDATE, - Comment::EVENT_CREATE, - ); - - $listener = new CommentHistoryListener($this); - - foreach ($events as $event_name) { - $this->event->attach($event_name, $listener); - } - } -} diff --git a/app/Model/Notification.php b/app/Model/Notification.php index 4b7ae50f..0a80e335 100644 --- a/app/Model/Notification.php +++ b/app/Model/Notification.php @@ -5,12 +5,10 @@ namespace Model; use Core\Session; use Core\Translator; use Core\Template; -use Event\TaskNotificationListener; -use Event\CommentNotificationListener; -use Event\FileNotificationListener; -use Event\SubTaskNotificationListener; +use Event\NotificationListener; use Swift_Message; use Swift_Mailer; +use Swift_TransportException; /** * Notification model @@ -91,21 +89,28 @@ class Notification extends Base */ public function attachEvents() { - $this->event->attach(File::EVENT_CREATE, new FileNotificationListener($this, 'notification_file_creation')); + $events = array( + Task::EVENT_CREATE => 'notification_task_creation', + Task::EVENT_UPDATE => 'notification_task_update', + Task::EVENT_CLOSE => 'notification_task_close', + Task::EVENT_OPEN => 'notification_task_open', + Task::EVENT_MOVE_COLUMN => 'notification_task_move_column', + Task::EVENT_MOVE_POSITION => 'notification_task_move_position', + Task::EVENT_ASSIGNEE_CHANGE => 'notification_task_assignee_change', + SubTask::EVENT_CREATE => 'notification_subtask_creation', + SubTask::EVENT_UPDATE => 'notification_subtask_update', + Comment::EVENT_CREATE => 'notification_comment_creation', + Comment::EVENT_UPDATE => 'notification_comment_update', + File::EVENT_CREATE => 'notification_file_creation', + ); - $this->event->attach(Comment::EVENT_CREATE, new CommentNotificationListener($this, 'notification_comment_creation')); - $this->event->attach(Comment::EVENT_UPDATE, new CommentNotificationListener($this, 'notification_comment_update')); + foreach ($events as $event_name => $template_name) { - $this->event->attach(SubTask::EVENT_CREATE, new SubTaskNotificationListener($this, 'notification_subtask_creation')); - $this->event->attach(SubTask::EVENT_UPDATE, new SubTaskNotificationListener($this, 'notification_subtask_update')); + $listener = new NotificationListener($this->registry); + $listener->setTemplate($template_name); - $this->event->attach(Task::EVENT_CREATE, new TaskNotificationListener($this, 'notification_task_creation')); - $this->event->attach(Task::EVENT_UPDATE, new TaskNotificationListener($this, 'notification_task_update')); - $this->event->attach(Task::EVENT_CLOSE, new TaskNotificationListener($this, 'notification_task_close')); - $this->event->attach(Task::EVENT_OPEN, new TaskNotificationListener($this, 'notification_task_open')); - $this->event->attach(Task::EVENT_MOVE_COLUMN, new TaskNotificationListener($this, 'notification_task_move_column')); - $this->event->attach(Task::EVENT_MOVE_POSITION, new TaskNotificationListener($this, 'notification_task_move_position')); - $this->event->attach(Task::EVENT_ASSIGNEE_CHANGE, new TaskNotificationListener($this, 'notification_task_assignee_change')); + $this->event->attach($event_name, $listener); + } } /** @@ -118,17 +123,22 @@ class Notification extends Base */ public function sendEmails($template, array $users, array $data) { - $transport = $this->registry->shared('mailer'); - $mailer = Swift_Mailer::newInstance($transport); + try { + $transport = $this->registry->shared('mailer'); + $mailer = Swift_Mailer::newInstance($transport); - $message = Swift_Message::newInstance() - ->setSubject($this->getMailSubject($template, $data)) - ->setFrom(array(MAIL_FROM => 'Kanboard')) - ->setBody($this->getMailContent($template, $data), 'text/html'); + $message = Swift_Message::newInstance() + ->setSubject($this->getMailSubject($template, $data)) + ->setFrom(array(MAIL_FROM => 'Kanboard')) + ->setBody($this->getMailContent($template, $data), 'text/html'); - foreach ($users as $user) { - $message->setTo(array($user['email'] => $user['name'] ?: $user['username'])); - $mailer->send($message); + foreach ($users as $user) { + $message->setTo(array($user['email'] => $user['name'] ?: $user['username'])); + $mailer->send($message); + } + } + catch (Swift_TransportException $e) { + debug($e->getMessage()); } } diff --git a/app/Model/Project.php b/app/Model/Project.php index ad0afa82..32b7fcbe 100644 --- a/app/Model/Project.php +++ b/app/Model/Project.php @@ -4,7 +4,7 @@ namespace Model; use SimpleValidator\Validator; use SimpleValidator\Validators; -use Event\ProjectModificationDate; +use Event\ProjectModificationDateListener; use Core\Security; /** @@ -512,41 +512,10 @@ class Project extends Base GithubWebhook::EVENT_COMMIT, ); - $listener = new ProjectModificationDate($this); + $listener = new ProjectModificationDateListener($this->registry); foreach ($events as $event_name) { $this->event->attach($event_name, $listener); } } - - /** - * Get project activity - * - * @access public - * @param integer $project_id Project id - * @return array - */ - public function getActivity($project_id) - { - $activity = array(); - $tasks = $this->taskHistory->getAllContentByProjectId($project_id, 25); - $comments = $this->commentHistory->getAllContentByProjectId($project_id, 25); - $subtasks = $this->subtaskHistory->getAllContentByProjectId($project_id, 25); - - foreach ($tasks as &$task) { - $activity[$task['date_creation'].'-'.$task['id']] = $task; - } - - foreach ($subtasks as &$subtask) { - $activity[$subtask['date_creation'].'-'.$subtask['id']] = $subtask; - } - - foreach ($comments as &$comment) { - $activity[$comment['date_creation'].'-'.$comment['id']] = $comment; - } - - krsort($activity); - - return $activity; - } } diff --git a/app/Model/ProjectActivity.php b/app/Model/ProjectActivity.php new file mode 100644 index 00000000..d2457609 --- /dev/null +++ b/app/Model/ProjectActivity.php @@ -0,0 +1,189 @@ + $project_id, + 'task_id' => $task_id, + 'creator_id' => $creator_id, + 'event_name' => $event_name, + 'date_creation' => time(), + 'data' => serialize($data), + ); + + $this->cleanup(self::MAX_EVENTS - 1); + return $this->db->table(self::TABLE)->insert($values); + } + + /** + * Get all events for the given project + * + * @access public + * @param integer $project_id Project id + * @param integer $limit Maximum events number + * @return array + */ + public function getAll($project_id, $limit = 50) + { + $events = $this->db->table(self::TABLE) + ->columns( + self::TABLE.'.*', + User::TABLE.'.username AS author_username', + User::TABLE.'.name AS author_name' + ) + ->eq('project_id', $project_id) + ->join(User::TABLE, 'id', 'creator_id') + ->desc('id') + ->limit($limit) + ->findAll(); + + foreach ($events as &$event) { + + $event += unserialize($event['data']); + unset($event['data']); + + $event['author'] = $event['author_name'] ?: $event['author_username']; + $event['event_title'] = $this->getTitle($event); + $event['event_content'] = $this->getContent($event); + } + + return $events; + } + + /** + * Remove old event entries to avoid large table + * + * @access public + * @param integer $max Maximum number of items to keep in the table + */ + public function cleanup($max) + { + if ($this->db->table(self::TABLE)->count() > $max) { + + $this->db->execute(' + DELETE FROM '.self::TABLE.' + WHERE id <= ( + SELECT id FROM ( + SELECT id FROM '.self::TABLE.' ORDER BY id DESC LIMIT 1 OFFSET '.$max.' + ) foo + )' + ); + } + } + + /** + * Attach events to be able to record the history + * + * @access public + */ + public function attachEvents() + { + $events = array( + Task::EVENT_ASSIGNEE_CHANGE, + Task::EVENT_UPDATE, + Task::EVENT_CREATE, + Task::EVENT_CLOSE, + Task::EVENT_OPEN, + Task::EVENT_MOVE_COLUMN, + Task::EVENT_MOVE_POSITION, + Comment::EVENT_UPDATE, + Comment::EVENT_CREATE, + SubTask::EVENT_UPDATE, + SubTask::EVENT_CREATE, + ); + + $listener = new ProjectActivityListener($this->registry); + + foreach ($events as $event_name) { + $this->event->attach($event_name, $listener); + } + } + + /** + * Get the event html content + * + * @access public + * @param array $params Event properties + * @return string + */ + public function getContent(array $params) + { + $tpl = new Template; + return $tpl->load('event_'.str_replace('.', '_', $params['event_name']), $params); + } + + /** + * Get the event title (translated) + * + * @access public + * @param array $event Event properties + * @return string + */ + public function getTitle(array $event) + { + switch ($event['event_name']) { + case Task::EVENT_ASSIGNEE_CHANGE: + return t('%s change the assignee of the task #%d to %s', $event['author'], $event['task']['id'], $event['task']['assignee_name'] ?: $event['task']['assignee_username']); + case Task::EVENT_UPDATE: + return t('%s updated the task #%d', $event['author'], $event['task']['id']); + case Task::EVENT_CREATE: + return t('%s created the task #%d', $event['author'], $event['task']['id']); + case Task::EVENT_CLOSE: + return t('%s closed the task #%d', $event['author'], $event['task']['id']); + case Task::EVENT_OPEN: + return t('%s open the task #%d', $event['author'], $event['task']['id']); + case Task::EVENT_MOVE_COLUMN: + return t('%s moved the task #%d to the column "%s"', $event['author'], $event['task']['id'], $event['task']['column_title']); + case Task::EVENT_MOVE_POSITION: + return t('%s moved the task #%d to the position %d in the column "%s"', $event['author'], $event['task']['id'], $event['task']['position'], $event['task']['column_title']); + case SubTask::EVENT_UPDATE: + return t('%s updated a subtask for the task #%d', $event['author'], $event['task']['id']); + case SubTask::EVENT_CREATE: + return t('%s created a subtask for the task #%d', $event['author'], $event['task']['id']); + case Comment::EVENT_UPDATE: + return t('%s updated a comment on the task #%d', $event['author'], $event['task']['id']); + case Comment::EVENT_CREATE: + return t('%s commented on the task #%d', $event['author'], $event['task']['id']); + default: + return ''; + } + } +} diff --git a/app/Model/SubtaskHistory.php b/app/Model/SubtaskHistory.php deleted file mode 100644 index 89076261..00000000 --- a/app/Model/SubtaskHistory.php +++ /dev/null @@ -1,161 +0,0 @@ -table = self::TABLE; - } - - /** - * Create a new event - * - * @access public - * @param integer $project_id Project id - * @param integer $task_id Task id - * @param integer $subtask_id Subtask id - * @param integer $creator_id Author of the event (user id) - * @param string $event_name Task event name - * @param string $data Current comment - * @return boolean - */ - public function create($project_id, $task_id, $subtask_id, $creator_id, $event_name, $data) - { - $values = array( - 'project_id' => $project_id, - 'task_id' => $task_id, - 'subtask_id' => $subtask_id, - 'creator_id' => $creator_id, - 'event_name' => $event_name, - 'date_creation' => time(), - 'data' => $data, - ); - - $this->db->startTransaction(); - - $this->cleanup(self::MAX_EVENTS - 1); - $result = $this->db->table(self::TABLE)->insert($values); - - $this->db->closeTransaction(); - - return $result; - } - - /** - * Get all necessary content to display activity feed - * - * $author_name - * $author_username - * $task['id', 'title', 'position', 'column_name'] - */ - public function getAllContentByProjectId($project_id, $limit = 50) - { - $sql = ' - SELECT - subtask_has_events.id, - subtask_has_events.date_creation, - subtask_has_events.event_name, - subtask_has_events.task_id, - tasks.title as task_title, - users.username as author_username, - users.name as author_name, - assignees.name as subtask_assignee_name, - assignees.username as subtask_assignee_username, - task_has_subtasks.title as subtask_title, - task_has_subtasks.status as subtask_status, - task_has_subtasks.time_spent as subtask_time_spent, - task_has_subtasks.time_estimated as subtask_time_estimated - FROM subtask_has_events - LEFT JOIN users ON users.id=subtask_has_events.creator_id - LEFT JOIN tasks ON tasks.id=subtask_has_events.task_id - LEFT JOIN task_has_subtasks ON task_has_subtasks.id=subtask_has_events.subtask_id - LEFT JOIN users AS assignees ON assignees.id=task_has_subtasks.user_id - WHERE subtask_has_events.project_id = ? - ORDER BY subtask_has_events.id DESC - LIMIT '.$limit.' OFFSET 0 - '; - - $rq = $this->db->execute($sql, array($project_id)); - $events = $rq->fetchAll(PDO::FETCH_ASSOC); - - foreach ($events as &$event) { - $event['author'] = $event['author_name'] ?: $event['author_username']; - $event['subtask_assignee'] = $event['subtask_assignee_name'] ?: $event['subtask_assignee_username']; - $event['subtask_status_list'] = $this->subTask->getStatusList(); - $event['event_title'] = $this->getTitle($event); - $event['event_content'] = $this->getContent($event); - $event['event_type'] = 'subtask'; - } - - return $events; - } - - /** - * Get the event title (translated) - * - * @access public - * @param array $event Event properties - * @return string - */ - public function getTitle(array $event) - { - $titles = array( - SubTask::EVENT_UPDATE => t('%s updated a subtask for the task #%d', $event['author'], $event['task_id']), - SubTask::EVENT_CREATE => t('%s created a subtask for the task #%d', $event['author'], $event['task_id']), - ); - - return isset($titles[$event['event_name']]) ? $titles[$event['event_name']] : ''; - } - - /** - * Attach events to be able to record the history - * - * @access public - */ - public function attachEvents() - { - $events = array( - SubTask::EVENT_UPDATE, - SubTask::EVENT_CREATE, - ); - - $listener = new SubtaskHistoryListener($this); - - foreach ($events as $event_name) { - $this->event->attach($event_name, $listener); - } - } -} diff --git a/app/Model/TaskHistory.php b/app/Model/TaskHistory.php deleted file mode 100644 index 0615cba0..00000000 --- a/app/Model/TaskHistory.php +++ /dev/null @@ -1,160 +0,0 @@ -table = self::TABLE; - } - - /** - * Create a new event - * - * @access public - * @param integer $project_id Project id - * @param integer $task_id Task id - * @param integer $creator_id Author of the event (user id) - * @param string $event_name Task event name - * @return boolean - */ - public function create($project_id, $task_id, $creator_id, $event_name) - { - $values = array( - 'project_id' => $project_id, - 'task_id' => $task_id, - 'creator_id' => $creator_id, - 'event_name' => $event_name, - 'date_creation' => time(), - ); - - $this->db->startTransaction(); - - $this->cleanup(self::MAX_EVENTS - 1); - $result = $this->db->table(self::TABLE)->insert($values); - - $this->db->closeTransaction(); - - return $result; - } - - /** - * Get all necessary content to display activity feed - * - * $author_name - * $author_username - * $task['id', 'title', 'position', 'column_name'] - */ - public function getAllContentByProjectId($project_id, $limit = 50) - { - $sql = ' - SELECT - task_has_events.id, - task_has_events.date_creation, - task_has_events.event_name, - task_has_events.task_id, - tasks.title as task_title, - tasks.position as task_position, - columns.title as task_column_name, - users.username as author_username, - users.name as author_name - FROM task_has_events - LEFT JOIN users ON users.id=task_has_events.creator_id - LEFT JOIN tasks ON tasks.id=task_has_events.task_id - LEFT JOIN columns ON columns.id=tasks.column_id - WHERE task_has_events.project_id = ? - ORDER BY task_has_events.id DESC - LIMIT '.$limit.' OFFSET 0 - '; - - $rq = $this->db->execute($sql, array($project_id)); - $events = $rq->fetchAll(PDO::FETCH_ASSOC); - - foreach ($events as &$event) { - $event['author'] = $event['author_name'] ?: $event['author_username']; - $event['event_title'] = $this->getTitle($event); - $event['event_content'] = $this->getContent($event); - $event['event_type'] = 'task'; - } - - return $events; - } - - /** - * Get the event title (translated) - * - * @access public - * @param array $event Event properties - * @return string - */ - public function getTitle(array $event) - { - $titles = array( - Task::EVENT_ASSIGNEE_CHANGE => t('%s change the assignee of the task #%d', $event['author'], $event['task_id']), - Task::EVENT_UPDATE => t('%s updated the task #%d', $event['author'], $event['task_id']), - Task::EVENT_CREATE => t('%s created the task #%d', $event['author'], $event['task_id']), - Task::EVENT_CLOSE => t('%s closed the task #%d', $event['author'], $event['task_id']), - Task::EVENT_OPEN => t('%s open the task #%d', $event['author'], $event['task_id']), - Task::EVENT_MOVE_COLUMN => t('%s moved the task #%d to the column "%s"', $event['author'], $event['task_id'], $event['task_column_name']), - Task::EVENT_MOVE_POSITION => t('%s moved the task #%d to the position %d in the column "%s"', $event['author'], $event['task_id'], $event['task_position'], $event['task_column_name']), - ); - - return isset($titles[$event['event_name']]) ? $titles[$event['event_name']] : ''; - } - - /** - * Attach events to be able to record the history - * - * @access public - */ - public function attachEvents() - { - $events = array( - Task::EVENT_ASSIGNEE_CHANGE, - Task::EVENT_UPDATE, - Task::EVENT_CREATE, - Task::EVENT_CLOSE, - Task::EVENT_OPEN, - Task::EVENT_MOVE_COLUMN, - Task::EVENT_MOVE_POSITION, - ); - - $listener = new TaskHistoryListener($this); - - foreach ($events as $event_name) { - $this->event->attach($event_name, $listener); - } - } -} diff --git a/app/Model/Webhook.php b/app/Model/Webhook.php index 241806ba..b84728cf 100644 --- a/app/Model/Webhook.php +++ b/app/Model/Webhook.php @@ -88,9 +88,13 @@ class Webhook extends Base Task::EVENT_UPDATE, Task::EVENT_CLOSE, Task::EVENT_OPEN, + Task::EVENT_MOVE_COLUMN, + Task::EVENT_MOVE_POSITION, + Task::EVENT_ASSIGNEE_CHANGE, ); - $listener = new WebhookListener($this->url_task_modification, $this); + $listener = new WebhookListener($this->registry); + $listener->setUrl($this->url_task_modification); foreach ($events as $event_name) { $this->event->attach($event_name, $listener); @@ -104,7 +108,10 @@ class Webhook extends Base */ public function attachCreateEvents() { - $this->event->attach(Task::EVENT_CREATE, new WebhookListener($this->url_task_creation, $this)); + $listener = new WebhookListener($this->registry); + $listener->setUrl($this->url_task_creation); + + $this->event->attach(Task::EVENT_CREATE, $listener); } /** diff --git a/app/Schema/Mysql.php b/app/Schema/Mysql.php index 7a0b2cd1..05bef8cd 100644 --- a/app/Schema/Mysql.php +++ b/app/Schema/Mysql.php @@ -5,7 +5,30 @@ namespace Schema; use PDO; use Core\Security; -const VERSION = 32; +const VERSION = 33; + +function version_33($pdo) +{ + $pdo->exec(" + CREATE TABLE project_activities ( + id INT NOT NULL AUTO_INCREMENT, + date_creation INT NOT NULL, + event_name VARCHAR(50) NOT NULL, + creator_id INT, + project_id INT, + task_id INT, + data TEXT, + PRIMARY KEY(id), + FOREIGN KEY(creator_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE + ) ENGINE=InnoDB CHARSET=utf8 + "); + + $pdo->exec('DROP TABLE task_has_events'); + $pdo->exec('DROP TABLE comment_has_events'); + $pdo->exec('DROP TABLE subtask_has_events'); +} function version_32($pdo) { @@ -355,7 +378,7 @@ function version_1($pdo) id INT NOT NULL AUTO_INCREMENT, task_id INT, user_id INT, - date INT, + `date` INT, comment TEXT, PRIMARY KEY (id), FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE, diff --git a/app/Schema/Postgres.php b/app/Schema/Postgres.php index e42e8e72..8aaff259 100644 --- a/app/Schema/Postgres.php +++ b/app/Schema/Postgres.php @@ -5,7 +5,29 @@ namespace Schema; use PDO; use Core\Security; -const VERSION = 13; +const VERSION = 14; + +function version_14($pdo) +{ + $pdo->exec(" + CREATE TABLE project_activities ( + id SERIAL PRIMARY KEY, + date_creation INTEGER NOT NULL, + event_name VARCHAR(50) NOT NULL, + creator_id INTEGER, + project_id INTEGER, + task_id INTEGER, + data TEXT, + FOREIGN KEY(creator_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE + ) + "); + + $pdo->exec('DROP TABLE task_has_events'); + $pdo->exec('DROP TABLE comment_has_events'); + $pdo->exec('DROP TABLE subtask_has_events'); +} function version_13($pdo) { diff --git a/app/Schema/Sqlite.php b/app/Schema/Sqlite.php index f290c824..3c220f8c 100644 --- a/app/Schema/Sqlite.php +++ b/app/Schema/Sqlite.php @@ -5,7 +5,29 @@ namespace Schema; use Core\Security; use PDO; -const VERSION = 32; +const VERSION = 33; + +function version_33($pdo) +{ + $pdo->exec(" + CREATE TABLE project_activities ( + id INTEGER PRIMARY KEY, + date_creation INTEGER NOT NULL, + event_name TEXT NOT NULL, + creator_id INTEGER, + project_id INTEGER, + task_id INTEGER, + data TEXT, + FOREIGN KEY(creator_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE + ) + "); + + $pdo->exec('DROP TABLE task_has_events'); + $pdo->exec('DROP TABLE comment_has_events'); + $pdo->exec('DROP TABLE subtask_has_events'); +} function version_32($pdo) { diff --git a/app/Templates/event_comment_create.php b/app/Templates/event_comment_create.php index 4af59614..d2f6f97b 100644 --- a/app/Templates/event_comment_create.php +++ b/app/Templates/event_comment_create.php @@ -1,7 +1,7 @@

#%d', Helper\escape($author), $task_id, $task_id) ?>

-

-
-

-

\ No newline at end of file +
+
+
+
\ No newline at end of file diff --git a/app/Templates/event_comment_update.php b/app/Templates/event_comment_update.php index fe93b691..27cc0be6 100644 --- a/app/Templates/event_comment_update.php +++ b/app/Templates/event_comment_update.php @@ -1,7 +1,7 @@

#%d', Helper\escape($author), $task_id, $task_id) ?>

-

-
-

-

\ No newline at end of file +
+
+
+
\ No newline at end of file diff --git a/app/Templates/event_subtask_create.php b/app/Templates/event_subtask_create.php index 9772d807..664e9da2 100644 --- a/app/Templates/event_subtask_create.php +++ b/app/Templates/event_subtask_create.php @@ -1,12 +1,19 @@

#%d', Helper\escape($author), $task_id, $task_id) ?>

-

-
-

()

- -

- -

- -

\ No newline at end of file +
+

+ + +
\ No newline at end of file diff --git a/app/Templates/event_subtask_update.php b/app/Templates/event_subtask_update.php index 70f9d76d..96a589dd 100644 --- a/app/Templates/event_subtask_update.php +++ b/app/Templates/event_subtask_update.php @@ -1,12 +1,19 @@

#%d', Helper\escape($author), $task_id, $task_id) ?>

-

-
-

()

- -

- -

- -

\ No newline at end of file +
+

+ + +
diff --git a/app/Templates/event_task_assignee_change.php b/app/Templates/event_task_assignee_change.php index ac18af2f..b346325e 100644 --- a/app/Templates/event_task_assignee_change.php +++ b/app/Templates/event_task_assignee_change.php @@ -1,6 +1,12 @@

- #%d', Helper\escape($author), $task_id, $task_id) ?> + #%d to %s', + Helper\escape($author), + $task_id, + $task_id, + Helper\escape($task['assignee_name'] ?: $task['assignee_username']) + ) ?>

- +

\ No newline at end of file diff --git a/app/Templates/event_task_close.php b/app/Templates/event_task_close.php index 9d92ea55..48d25678 100644 --- a/app/Templates/event_task_close.php +++ b/app/Templates/event_task_close.php @@ -2,5 +2,5 @@ #%d', Helper\escape($author), $task_id, $task_id) ?>

- +

\ No newline at end of file diff --git a/app/Templates/event_task_create.php b/app/Templates/event_task_create.php index 7ceab4bb..2515af05 100644 --- a/app/Templates/event_task_create.php +++ b/app/Templates/event_task_create.php @@ -2,5 +2,5 @@ #%d', Helper\escape($author), $task_id, $task_id) ?>

- +

\ No newline at end of file diff --git a/app/Templates/event_task_move_column.php b/app/Templates/event_task_move_column.php index a674f80a..f2aac8f7 100644 --- a/app/Templates/event_task_move_column.php +++ b/app/Templates/event_task_move_column.php @@ -1,6 +1,6 @@

- #%d to the column "%s"', Helper\escape($author), $task_id, $task_id, Helper\escape($task_column_name)) ?> + #%d to the column "%s"', Helper\escape($author), $task_id, $task_id, Helper\escape($task['column_title'])) ?>

- +

\ No newline at end of file diff --git a/app/Templates/event_task_move_position.php b/app/Templates/event_task_move_position.php index 0b7e9615..26cdeb13 100644 --- a/app/Templates/event_task_move_position.php +++ b/app/Templates/event_task_move_position.php @@ -1,6 +1,6 @@

- #%d to the position #%d in the column "%s"', Helper\escape($author), $task_id, $task_id, $task_position, Helper\escape($task_column_name)) ?> + #%d to the position #%d in the column "%s"', Helper\escape($author), $task_id, $task_id, $task['position'], Helper\escape($task['column_title'])) ?>

- +

\ No newline at end of file diff --git a/app/Templates/event_task_open.php b/app/Templates/event_task_open.php index 763b0dfc..9623be74 100644 --- a/app/Templates/event_task_open.php +++ b/app/Templates/event_task_open.php @@ -2,5 +2,5 @@ #%d', Helper\escape($author), $task_id, $task_id) ?>

- +

\ No newline at end of file diff --git a/app/Templates/event_task_update.php b/app/Templates/event_task_update.php index 313e79cb..a270b936 100644 --- a/app/Templates/event_task_update.php +++ b/app/Templates/event_task_update.php @@ -2,5 +2,5 @@ #%d', Helper\escape($author), $task_id, $task_id) ?>

- +

\ No newline at end of file diff --git a/app/Templates/project_activity.php b/app/Templates/project_activity.php index ad91f8b8..50743d68 100644 --- a/app/Templates/project_activity.php +++ b/app/Templates/project_activity.php @@ -2,10 +2,10 @@
@@ -14,17 +14,17 @@ -

+

$project['token'])) ?>

- + - + - +   diff --git a/app/Templates/project_feed.php b/app/Templates/project_feed.php index b47c87ad..9d10ecb1 100644 --- a/app/Templates/project_feed.php +++ b/app/Templates/project_feed.php @@ -2,7 +2,7 @@ <?= t('%s\'s activity', $project['name']) ?> - + assets/img/favicon.png @@ -10,7 +10,7 @@ <?= $e['event_title'] ?> - + diff --git a/app/Templates/task_public.php b/app/Templates/task_public.php index bc4608d1..3f44ceec 100644 --- a/app/Templates/task_public.php +++ b/app/Templates/task_public.php @@ -2,7 +2,7 @@ $task, 'project' => $project)) ?> -

$project['token'])) ?>

+

$project['token'])) ?>

$task)) ?> diff --git a/assets/css/app.css b/assets/css/app.css index 185c98ec..0bb25740 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -26,10 +26,6 @@ body { text-rendering: optimizeLegibility; } -.align-right { - text-align: right; -} - /* links */ a { color: #3366CC; @@ -1108,6 +1104,21 @@ tr td.task-orange, padding-top: 5px; } +.activity-description ul { + margin-top: 10px; +} + +.activity-description li { + margin-left: 40px; + list-style-type: circle; + color: #555; +} + +.activity-description .markdown { + margin-top: 10px; + color: #555; +} + /* confirmation box */ .confirm { max-width: 700px; diff --git a/tests/units/ProjectActivityTest.php b/tests/units/ProjectActivityTest.php new file mode 100644 index 00000000..2565b6e7 --- /dev/null +++ b/tests/units/ProjectActivityTest.php @@ -0,0 +1,102 @@ +registry); + $t = new Task($this->registry); + $tf = new TaskFinder($this->registry); + $p = new Project($this->registry); + + $this->assertEquals(1, $p->create(array('name' => 'Project #1'))); + $this->assertEquals(1, $t->create(array('title' => 'Task #1', 'project_id' => 1))); + $this->assertEquals(2, $t->create(array('title' => 'Task #2', 'project_id' => 1))); + + $this->assertTrue($e->createEvent(1, 1, 1, Task::EVENT_CLOSE, array('task' => $tf->getbyId(1)))); + $this->assertTrue($e->createEvent(1, 2, 1, Task::EVENT_UPDATE, array('task' => $tf->getById(2)))); + $this->assertFalse($e->createEvent(1, 1, 0, Task::EVENT_OPEN, array('task' => $tf->getbyId(1)))); + + $events = $e->getAll(1); + + $this->assertNotEmpty($events); + $this->assertTrue(is_array($events)); + $this->assertEquals(2, count($events)); + $this->assertEquals(time(), $events[0]['date_creation']); + $this->assertEquals(Task::EVENT_UPDATE, $events[0]['event_name']); + $this->assertEquals(Task::EVENT_CLOSE, $events[1]['event_name']); + } + + public function testFetchAllContent() + { + $e = new ProjectActivity($this->registry); + $t = new Task($this->registry); + $tf = new TaskFinder($this->registry); + $p = new Project($this->registry); + + $this->assertEquals(1, $p->create(array('name' => 'Project #1'))); + $this->assertEquals(1, $t->create(array('title' => 'Task #1', 'project_id' => 1))); + + $nb_events = 80; + + for ($i = 0; $i < $nb_events; $i++) { + $this->assertTrue($e->createEvent(1, 1, 1, Task::EVENT_UPDATE, array('task' => $tf->getbyId(1)))); + } + + $events = $e->getAll(1); + + $this->assertNotEmpty($events); + $this->assertTrue(is_array($events)); + $this->assertEquals(50, count($events)); + $this->assertEquals('admin', $events[0]['author']); + $this->assertNotEmpty($events[0]['event_title']); + $this->assertNotEmpty($events[0]['event_content']); + } + + public function testCleanup() + { + $e = new ProjectActivity($this->registry); + $t = new Task($this->registry); + $tf = new TaskFinder($this->registry); + $p = new Project($this->registry); + + $this->assertEquals(1, $p->create(array('name' => 'Project #1'))); + $this->assertEquals(1, $t->create(array('title' => 'Task #1', 'project_id' => 1))); + + $max = 15; + $nb_events = 100; + + for ($i = 0; $i < $nb_events; $i++) { + $this->assertTrue($e->createEvent(1, 1, 1, Task::EVENT_CLOSE, array('task' => $tf->getbyId(1)))); + } + + $this->assertEquals($nb_events, $this->registry->shared('db')->table('project_activities')->count()); + $e->cleanup($max); + + $events = $e->getAll(1); + + $this->assertNotEmpty($events); + $this->assertTrue(is_array($events)); + $this->assertEquals($max, count($events)); + $this->assertEquals(100, $events[0]['id']); + $this->assertEquals(99, $events[1]['id']); + $this->assertEquals(86, $events[14]['id']); + + // Cleanup during task creation + + $nb_events = ProjectActivity::MAX_EVENTS + 10; + + for ($i = 0; $i < $nb_events; $i++) { + $this->assertTrue($e->createEvent(1, 1, 1, Task::EVENT_CLOSE, array('task' => $tf->getbyId(1)))); + } + + $this->assertEquals(ProjectActivity::MAX_EVENTS, $this->registry->shared('db')->table('project_activities')->count()); + } +} diff --git a/tests/units/ProjectTest.php b/tests/units/ProjectTest.php index 0085711a..cec8d93d 100644 --- a/tests/units/ProjectTest.php +++ b/tests/units/ProjectTest.php @@ -63,7 +63,7 @@ class ProjectTest extends Base $this->assertEquals(1, $t->create(array('title' => 'Task #1', 'project_id' => 1))); $this->assertTrue($this->registry->shared('event')->isEventTriggered(Task::EVENT_CREATE)); - $this->assertEquals('Event\ProjectModificationDate', $this->registry->shared('event')->getLastListenerExecuted()); + $this->assertEquals('Event\ProjectModificationDateListener', $this->registry->shared('event')->getLastListenerExecuted()); $project = $p->getById(1); $this->assertNotEmpty($project); diff --git a/tests/units/TaskHistoryTest.php b/tests/units/TaskHistoryTest.php deleted file mode 100644 index 085162ea..00000000 --- a/tests/units/TaskHistoryTest.php +++ /dev/null @@ -1,98 +0,0 @@ -registry); - $t = new Task($this->registry); - $p = new Project($this->registry); - - $this->assertEquals(1, $p->create(array('name' => 'Project #1'))); - $this->assertEquals(1, $t->create(array('title' => 'Task #1', 'project_id' => 1))); - $this->assertEquals(2, $t->create(array('title' => 'Task #2', 'project_id' => 1))); - - $this->assertTrue($e->create(1, 1, 1, Task::EVENT_CLOSE)); - $this->assertTrue($e->create(1, 2, 1, Task::EVENT_UPDATE)); - $this->assertFalse($e->create(1, 1, 0, Task::EVENT_OPEN)); - - $events = $e->getAllByProjectId(1); - - $this->assertNotEmpty($events); - $this->assertTrue(is_array($events)); - $this->assertEquals(2, count($events)); - $this->assertEquals(time(), $events[0]['date_creation']); - $this->assertEquals(Task::EVENT_UPDATE, $events[0]['event_name']); - $this->assertEquals(Task::EVENT_CLOSE, $events[1]['event_name']); - } - - public function testFetchAllContent() - { - $e = new TaskHistory($this->registry); - $t = new Task($this->registry); - $p = new Project($this->registry); - - $this->assertEquals(1, $p->create(array('name' => 'Project #1'))); - $this->assertEquals(1, $t->create(array('title' => 'Task #1', 'project_id' => 1))); - - $nb_events = 80; - - for ($i = 0; $i < $nb_events; $i++) { - $this->assertTrue($e->create(1, 1, 1, Task::EVENT_UPDATE)); - } - - $events = $e->getAllContentByProjectId(1); - - $this->assertNotEmpty($events); - $this->assertTrue(is_array($events)); - $this->assertEquals(50, count($events)); - $this->assertEquals('admin', $events[0]['author']); - $this->assertNotEmpty($events[0]['event_title']); - $this->assertNotEmpty($events[0]['event_content']); - } - - public function testCleanup() - { - $e = new TaskHistory($this->registry); - $t = new Task($this->registry); - $p = new Project($this->registry); - - $this->assertEquals(1, $p->create(array('name' => 'Project #1'))); - $this->assertEquals(1, $t->create(array('title' => 'Task #1', 'project_id' => 1))); - - $max = 15; - $nb_events = 100; - - for ($i = 0; $i < $nb_events; $i++) { - $this->assertTrue($e->create(1, 1, 1, Task::EVENT_CLOSE)); - } - - $this->assertEquals($nb_events, $this->registry->shared('db')->table('task_has_events')->count()); - $e->cleanup($max); - - $events = $e->getAllByProjectId(1); - - $this->assertNotEmpty($events); - $this->assertTrue(is_array($events)); - $this->assertEquals($max, count($events)); - $this->assertEquals(100, $events[0]['id']); - $this->assertEquals(99, $events[1]['id']); - $this->assertEquals(86, $events[14]['id']); - - // Cleanup during task creation - - $nb_events = TaskHistory::MAX_EVENTS + 10; - - for ($i = 0; $i < $nb_events; $i++) { - $this->assertTrue($e->create(1, 1, 1, Task::EVENT_CLOSE)); - } - - $this->assertEquals(TaskHistory::MAX_EVENTS, $this->registry->shared('db')->table('task_has_events')->count()); - } -} -- cgit v1.2.3