diff options
author | Frédéric Guillot <fred@kanboard.net> | 2014-09-10 16:21:47 +0200 |
---|---|---|
committer | Frédéric Guillot <fred@kanboard.net> | 2014-09-10 16:21:47 +0200 |
commit | 28ff8dad91c9e3c25f6a3b5398ae15f2a1ef95cd (patch) | |
tree | 0cecc5cbb6e7e6795dd032cc6a5703cd88b8770e /app/Model | |
parent | 9bde377bbe85617dde280af985e033cf7de61803 (diff) |
Add subtasks and comments history
Diffstat (limited to 'app/Model')
-rw-r--r-- | app/Model/BaseHistory.php | 70 | ||||
-rw-r--r-- | app/Model/CommentHistory.php | 152 | ||||
-rw-r--r-- | app/Model/Notification.php | 26 | ||||
-rw-r--r-- | app/Model/Project.php | 22 | ||||
-rw-r--r-- | app/Model/SubtaskHistory.php | 161 | ||||
-rw-r--r-- | app/Model/Task.php | 32 | ||||
-rw-r--r-- | app/Model/TaskHistory.php | 64 |
7 files changed, 445 insertions, 82 deletions
diff --git a/app/Model/BaseHistory.php b/app/Model/BaseHistory.php new file mode 100644 index 00000000..31578a3b --- /dev/null +++ b/app/Model/BaseHistory.php @@ -0,0 +1,70 @@ +<?php + +namespace Model; + +use PDO; +use Core\Template; + +/** + * Task history model + * + * @package model + * @author Frederic Guillot + */ +abstract class BaseHistory extends Base +{ + /** + * SQL table name + * + * @access protected + * @var string + */ + protected $table = ''; + + /** + * Remove old event entries to avoid a large table + * + * @access public + * @param integer $max Maximum number of items to keep in the table + */ + public function cleanup($max) + { + if ($this->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 new file mode 100644 index 00000000..5b94729c --- /dev/null +++ b/app/Model/CommentHistory.php @@ -0,0 +1,152 @@ +<?php + +namespace Model; + +use PDO; +use Core\Registry; +use Event\CommentHistoryListener; + +/** + * Comment history model + * + * @package model + * @author Frederic Guillot + */ +class CommentHistory extends BaseHistory +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'comment_has_events'; + + /** + * Maximum number of events + * + * @var integer + */ + const MAX_EVENTS = 5000; + + /** + * Constructor + * + * @access public + * @param \Core\Registry $registry Registry instance + */ + public function __construct(Registry $registry) + { + parent::__construct($registry); + $this->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 0, '.$limit.' + '; + + $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 a92ce73e..0cb17076 100644 --- a/app/Model/Notification.php +++ b/app/Model/Notification.php @@ -115,45 +115,41 @@ class Notification extends Base */ public function getMailSubject($template, array $data) { - Translator::disableEscaping(); - switch ($template) { case 'notification_file_creation': - $subject = t('[%s][New attachment] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']); + $subject = e('[%s][New attachment] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']); break; case 'notification_comment_creation': - $subject = t('[%s][New comment] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']); + $subject = e('[%s][New comment] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']); break; case 'notification_comment_update': - $subject = t('[%s][Comment updated] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']); + $subject = e('[%s][Comment updated] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']); break; case 'notification_subtask_creation': - $subject = t('[%s][New subtask] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']); + $subject = e('[%s][New subtask] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']); break; case 'notification_subtask_update': - $subject = t('[%s][Subtask updated] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']); + $subject = e('[%s][Subtask updated] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']); break; case 'notification_task_creation': - $subject = t('[%s][New task] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']); + $subject = e('[%s][New task] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']); break; case 'notification_task_update': - $subject = t('[%s][Task updated] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']); + $subject = e('[%s][Task updated] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']); break; case 'notification_task_close': - $subject = t('[%s][Task closed] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']); + $subject = e('[%s][Task closed] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']); break; case 'notification_task_open': - $subject = t('[%s][Task opened] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']); + $subject = e('[%s][Task opened] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']); break; case 'notification_task_due': - $subject = t('[%s][Due tasks]', $data['project']); + $subject = e('[%s][Due tasks]', $data['project']); break; default: - $subject = t('[Kanboard] Notification'); + $subject = e('[Kanboard] Notification'); } - Translator::enableEscaping(); - return $subject; } diff --git a/app/Model/Project.php b/app/Model/Project.php index 1da5c164..9aef3c7e 100644 --- a/app/Model/Project.php +++ b/app/Model/Project.php @@ -720,7 +720,25 @@ class Project extends Base */ public function getActivity($project_id) { - // TODO: merge comments and subtasks activity - return $this->taskHistory->getAllContentByProjectId($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/SubtaskHistory.php b/app/Model/SubtaskHistory.php new file mode 100644 index 00000000..bbf6ce3d --- /dev/null +++ b/app/Model/SubtaskHistory.php @@ -0,0 +1,161 @@ +<?php + +namespace Model; + +use PDO; +use Core\Registry; +use Event\SubtaskHistoryListener; + +/** + * Comment history model + * + * @package model + * @author Frederic Guillot + */ +class SubtaskHistory extends BaseHistory +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'subtask_has_events'; + + /** + * Maximum number of events + * + * @var integer + */ + const MAX_EVENTS = 5000; + + /** + * Constructor + * + * @access public + * @param \Core\Registry $registry Registry instance + */ + public function __construct(Registry $registry) + { + parent::__construct($registry); + $this->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 0, '.$limit.' + '; + + $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/Task.php b/app/Model/Task.php index 7ebb4641..10d125d4 100644 --- a/app/Model/Task.php +++ b/app/Model/Task.php @@ -936,21 +936,21 @@ class Task extends Base $tasks = $rq->fetchAll(PDO::FETCH_ASSOC); $columns = array( - t('Task Id'), - t('Project'), - t('Status'), - t('Category'), - t('Column'), - t('Position'), - t('Color'), - t('Due date'), - t('Creator'), - t('Assignee'), - t('Complexity'), - t('Title'), - t('Creation date'), - t('Modification date'), - t('Completion date'), + e('Task Id'), + e('Project'), + e('Status'), + e('Category'), + e('Column'), + e('Position'), + e('Color'), + e('Due date'), + e('Creator'), + e('Assignee'), + e('Complexity'), + e('Title'), + e('Creation date'), + e('Modification date'), + e('Completion date'), ); $results = array($columns); @@ -973,7 +973,7 @@ class Task extends Base { $colors = $this->getColors(); $task['score'] = $task['score'] ?: ''; - $task['is_active'] = $task['is_active'] == self::STATUS_OPEN ? t('Open') : t('Closed'); + $task['is_active'] = $task['is_active'] == self::STATUS_OPEN ? e('Open') : e('Closed'); $task['color_id'] = $colors[$task['color_id']]; $task['date_creation'] = date('Y-m-d', $task['date_creation']); $task['date_due'] = $task['date_due'] ? date('Y-m-d', $task['date_due']) : ''; diff --git a/app/Model/TaskHistory.php b/app/Model/TaskHistory.php index 181e4181..35b7cb27 100644 --- a/app/Model/TaskHistory.php +++ b/app/Model/TaskHistory.php @@ -3,7 +3,7 @@ namespace Model; use PDO; -use Core\Template; +use Core\Registry; use Event\TaskHistoryListener; /** @@ -12,7 +12,7 @@ use Event\TaskHistoryListener; * @package model * @author Frederic Guillot */ -class TaskHistory extends Base +class TaskHistory extends BaseHistory { /** * SQL table name @@ -29,6 +29,18 @@ class TaskHistory extends Base const MAX_EVENTS = 5000; /** + * Constructor + * + * @access public + * @param \Core\Registry $registry Registry instance + */ + public function __construct(Registry $registry) + { + parent::__construct($registry); + $this->table = self::TABLE; + } + + /** * Create a new event * * @access public @@ -59,40 +71,6 @@ class TaskHistory extends Base } /** - * Remove old event entries to avoid a 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 - )'); - } - } - - /** - * Get all events for a given project - * - * @access public - * @return array - */ - public function getAllByProjectId($project_id) - { - return $this->db->table(self::TABLE) - ->eq('project_id', $project_id) - ->desc('id') - ->findAll(); - } - - /** * Get all necessary content to display activity feed * * $author_name @@ -103,6 +81,7 @@ class TaskHistory extends Base { $sql = ' SELECT + task_has_events.id, task_has_events.date_creation, task_has_events.event_name, task_has_events.task_id, @@ -155,19 +134,6 @@ class TaskHistory extends Base } /** - * 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); - } - - /** * Attach events to be able to record the history * * @access public |