diff options
author | Frederic Guillot <fred@kanboard.net> | 2015-09-20 22:18:56 -0400 |
---|---|---|
committer | Frederic Guillot <fred@kanboard.net> | 2015-09-20 22:18:56 -0400 |
commit | 689687dd4ee186cb9cf5d0230b4648e242c53b10 (patch) | |
tree | 3d26bc2079c6eb45790ba604b3a79997be4768ab | |
parent | f579663adcbc0b202d9a068d734e8f9284dc3a37 (diff) |
Add formatters
-rw-r--r-- | app/Controller/App.php | 4 | ||||
-rw-r--r-- | app/Controller/Calendar.php | 16 | ||||
-rw-r--r-- | app/Controller/Gantt.php | 6 | ||||
-rw-r--r-- | app/Controller/Ical.php | 27 | ||||
-rw-r--r-- | app/Formatter/FormatterInterface.php | 14 | ||||
-rw-r--r-- | app/Formatter/ProjectGanttFormatter.php | 90 | ||||
-rw-r--r-- | app/Formatter/TaskFilterAutoCompleteFormatter.php | 33 | ||||
-rw-r--r-- | app/Formatter/TaskFilterCalendarEvent.php | 76 | ||||
-rw-r--r-- | app/Formatter/TaskFilterCalendarFormatter.php | 52 | ||||
-rw-r--r-- | app/Formatter/TaskFilterGanttFormatter.php | 78 | ||||
-rw-r--r-- | app/Formatter/TaskFilterICalendarFormatter.php | 135 | ||||
-rw-r--r-- | app/Model/Base.php | 20 | ||||
-rw-r--r-- | app/Model/Project.php | 48 | ||||
-rw-r--r-- | app/Model/TaskFilter.php | 224 | ||||
-rw-r--r-- | app/ServiceProvider/ClassProvider.php | 7 | ||||
-rw-r--r-- | index.php | 4 | ||||
-rw-r--r-- | tests/units/Formatter/TaskFilterCalendarFormatterTest.php | 29 | ||||
-rw-r--r-- | tests/units/Formatter/TaskFilterGanttFormatterTest.php | 24 | ||||
-rw-r--r-- | tests/units/Formatter/TaskFilterICalendarFormatterTest.php | 77 | ||||
-rw-r--r-- | tests/units/Model/TaskFilterTest.php | 64 |
20 files changed, 651 insertions, 377 deletions
diff --git a/app/Controller/App.php b/app/Controller/App.php index fe1e648c..41be4608 100644 --- a/app/Controller/App.php +++ b/app/Controller/App.php @@ -213,7 +213,7 @@ class App extends Base { $search = $this->request->getStringParam('term'); - $filter = $this->taskFilter + $filter = $this->taskFilterAutoCompleteFormatter ->create() ->filterByProjects($this->projectPermission->getActiveMemberProjectIds($this->userSession->getId())) ->excludeTasks(array($this->request->getIntegerParam('exclude_task_id'))); @@ -226,6 +226,6 @@ class App extends Base $filter->filterByTitle($search); } - $this->response->json($filter->toAutoCompletion()); + $this->response->json($filter->format()); } } diff --git a/app/Controller/Calendar.php b/app/Controller/Calendar.php index 5ac92622..7050e54b 100644 --- a/app/Controller/Calendar.php +++ b/app/Controller/Calendar.php @@ -37,20 +37,20 @@ class Calendar extends Base $end = $this->request->getStringParam('end'); // Common filter - $filter = $this->taskFilter + $filter = $this->taskFilterCalendarFormatter ->search($this->userSession->getFilters($project_id)) ->filterByProject($project_id); // Tasks if ($this->config->get('calendar_project_tasks', 'date_started') === 'date_creation') { - $events = $filter->copy()->filterByCreationDateRange($start, $end)->toDateTimeCalendarEvents('date_creation', 'date_completed'); + $events = $filter->copy()->filterByCreationDateRange($start, $end)->setColumns('date_creation', 'date_completed')->format(); } else { - $events = $filter->copy()->filterByStartDateRange($start, $end)->toDateTimeCalendarEvents('date_started', 'date_completed'); + $events = $filter->copy()->filterByStartDateRange($start, $end)->setColumns('date_started', 'date_completed')->format(); } // Tasks with due date - $events = array_merge($events, $filter->copy()->filterByDueDateRange($start, $end)->toAllDayCalendarEvents()); + $events = array_merge($events, $filter->copy()->filterByDueDateRange($start, $end)->setColumns('date_due')->setFullDay()->format()); $events = $this->hook->merge('controller:calendar:project:events', $events, array( 'project_id' => $project_id, @@ -71,17 +71,17 @@ class Calendar extends Base $user_id = $this->request->getIntegerParam('user_id'); $start = $this->request->getStringParam('start'); $end = $this->request->getStringParam('end'); - $filter = $this->taskFilter->create()->filterByOwner($user_id)->filterByStatus(TaskModel::STATUS_OPEN); + $filter = $this->taskFilterCalendarFormatter->create()->filterByOwner($user_id)->filterByStatus(TaskModel::STATUS_OPEN); // Task with due date - $events = $filter->copy()->filterByDueDateRange($start, $end)->toAllDayCalendarEvents(); + $events = $filter->copy()->filterByDueDateRange($start, $end)->setColumns('date_due')->setFullDay()->format(); // Tasks if ($this->config->get('calendar_user_tasks', 'date_started') === 'date_creation') { - $events = array_merge($events, $filter->copy()->filterByCreationDateRange($start, $end)->toDateTimeCalendarEvents('date_creation', 'date_completed')); + $events = array_merge($events, $filter->copy()->filterByCreationDateRange($start, $end)->setColumns('date_creation', 'date_completed')->format()); } else { - $events = array_merge($events, $filter->copy()->filterByStartDateRange($start, $end)->toDateTimeCalendarEvents('date_started', 'date_completed')); + $events = array_merge($events, $filter->copy()->filterByStartDateRange($start, $end)->setColumns('date_started', 'date_completed')->format()); } // Subtasks time tracking diff --git a/app/Controller/Gantt.php b/app/Controller/Gantt.php index a2d3f363..879ab377 100644 --- a/app/Controller/Gantt.php +++ b/app/Controller/Gantt.php @@ -25,7 +25,7 @@ class Gantt extends Base } $this->response->html($this->template->layout('gantt/projects', array( - 'projects' => $this->project->getGanttBars($project_ids), + 'projects' => $this->projectGanttFormatter->filter($project_ids)->format(), 'title' => t('Gantt chart for all projects'), 'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()), ))); @@ -57,7 +57,7 @@ class Gantt extends Base public function project() { $params = $this->getProjectFilters('gantt', 'project'); - $filter = $this->taskFilter->search($params['filters']['search'])->filterByProject($params['project']['id']); + $filter = $this->taskFilterGanttFormatter->search($params['filters']['search'])->filterByProject($params['project']['id']); $sorting = $this->request->getStringParam('sorting', 'board'); if ($sorting === 'date') { @@ -70,7 +70,7 @@ class Gantt extends Base $this->response->html($this->template->layout('gantt/project', $params + array( 'users_list' => $this->projectPermission->getMemberList($params['project']['id'], false), 'sorting' => $sorting, - 'tasks' => $filter->toGanttBars(), + 'tasks' => $filter->format(), ))); } diff --git a/app/Controller/Ical.php b/app/Controller/Ical.php index 0129915e..e89b7e38 100644 --- a/app/Controller/Ical.php +++ b/app/Controller/Ical.php @@ -29,7 +29,7 @@ class Ical extends Base } // Common filter - $filter = $this->taskFilter + $filter = $this->taskFilterICalendarFormatter ->create() ->filterByOwner($user['id']); @@ -58,7 +58,7 @@ class Ical extends Base } // Common filter - $filter = $this->taskFilter + $filter = $this->taskFilterICalendarFormatter ->create() ->filterByProject($project['id']); @@ -83,16 +83,31 @@ class Ical extends Base // Tasks if ($this->config->get('calendar_project_tasks', 'date_started') === 'date_creation') { - $filter->copy()->filterByCreationDateRange($start, $end)->addDateTimeIcalEvents('date_creation', 'date_completed', $calendar); + $filter + ->copy() + ->filterByCreationDateRange($start, $end) + ->setColumns('date_creation', 'date_completed') + ->setCalendar($calendar) + ->addDateTimeEvents(); } else { - $filter->copy()->filterByStartDateRange($start, $end)->addDateTimeIcalEvents('date_started', 'date_completed', $calendar); + $filter + ->copy() + ->filterByStartDateRange($start, $end) + ->setColumns('date_started', 'date_completed') + ->setCalendar($calendar) + ->addDateTimeEvents($calendar); } // Tasks with due date - $filter->copy()->filterByDueDateRange($start, $end)->addAllDayIcalEvents('date_due', $calendar); + $filter + ->copy() + ->filterByDueDateRange($start, $end) + ->setColumns('date_due') + ->setCalendar($calendar) + ->addFullDayEvents($calendar); $this->response->contentType('text/calendar; charset=utf-8'); - echo $calendar->render(); + echo $filter->setCalendar($calendar)->format(); } } diff --git a/app/Formatter/FormatterInterface.php b/app/Formatter/FormatterInterface.php new file mode 100644 index 00000000..4193bd4e --- /dev/null +++ b/app/Formatter/FormatterInterface.php @@ -0,0 +1,14 @@ +<?php + +namespace Formatter; + +/** + * Formatter Interface + * + * @package formatter + * @author Frederic Guillot + */ +interface FormatterInterface +{ + public function format(); +} diff --git a/app/Formatter/ProjectGanttFormatter.php b/app/Formatter/ProjectGanttFormatter.php new file mode 100644 index 00000000..652b947d --- /dev/null +++ b/app/Formatter/ProjectGanttFormatter.php @@ -0,0 +1,90 @@ +<?php + +namespace Formatter; + +use Model\Project; + +/** + * Gantt chart formatter for projects + * + * @package formatter + * @author Frederic Guillot + */ +class ProjectGanttFormatter extends Project implements FormatterInterface +{ + /** + * List of projects + * + * @access private + * @var array + */ + private $projects = array(); + + /** + * Filter projects to generate the Gantt chart + * + * @access public + * @param int[] $project_ids + * @return ProjectGanttFormatter + */ + public function filter(array $project_ids) + { + if (empty($project_ids)) { + $this->projects = array(); + } + else { + + $this->projects = $this->db + ->table(self::TABLE) + ->asc('start_date') + ->in('id', $project_ids) + ->eq('is_active', self::ACTIVE) + ->eq('is_private', 0) + ->findAll(); + } + + return $this; + } + + /** + * Format projects to be displayed in the Gantt chart + * + * @access public + * @return array + */ + public function format() + { + $colors = $this->color->getDefaultColors(); + $bars = array(); + + foreach ($this->projects as $project) { + $start = empty($project['start_date']) ? time() : strtotime($project['start_date']); + $end = empty($project['end_date']) ? $start : strtotime($project['end_date']); + $color = next($colors) ?: reset($colors); + + $bars[] = array( + 'type' => 'project', + 'id' => $project['id'], + 'title' => $project['name'], + 'start' => array( + (int) date('Y', $start), + (int) date('n', $start), + (int) date('j', $start), + ), + 'end' => array( + (int) date('Y', $end), + (int) date('n', $end), + (int) date('j', $end), + ), + 'link' => $this->helper->url->href('project', 'show', array('project_id' => $project['id'])), + 'board_link' => $this->helper->url->href('board', 'show', array('project_id' => $project['id'])), + 'gantt_link' => $this->helper->url->href('gantt', 'project', array('project_id' => $project['id'])), + 'color' => $color, + 'not_defined' => empty($project['start_date']) || empty($project['end_date']), + 'users' => $this->projectPermission->getProjectUsers($project['id']), + ); + } + + return $bars; + } +} diff --git a/app/Formatter/TaskFilterAutoCompleteFormatter.php b/app/Formatter/TaskFilterAutoCompleteFormatter.php new file mode 100644 index 00000000..999a8949 --- /dev/null +++ b/app/Formatter/TaskFilterAutoCompleteFormatter.php @@ -0,0 +1,33 @@ +<?php + +namespace Formatter; + +use Model\Task; +use Model\TaskFilter; + +/** + * Autocomplete formatter for task filter + * + * @package formatter + * @author Frederic Guillot + */ +class TaskFilterAutoCompleteFormatter extends TaskFilter implements FormatterInterface +{ + /** + * Format the tasks for the ajax autocompletion + * + * @access public + * @return array + */ + public function format() + { + $tasks = $this->query->columns(Task::TABLE.'.id', Task::TABLE.'.title')->findAll(); + + foreach ($tasks as &$task) { + $task['value'] = $task['title']; + $task['label'] = '#'.$task['id'].' - '.$task['title']; + } + + return $tasks; + } +} diff --git a/app/Formatter/TaskFilterCalendarEvent.php b/app/Formatter/TaskFilterCalendarEvent.php new file mode 100644 index 00000000..8733ee83 --- /dev/null +++ b/app/Formatter/TaskFilterCalendarEvent.php @@ -0,0 +1,76 @@ +<?php + +namespace Formatter; + +use Model\TaskFilter; + +/** + * Common class to handle calendar events + * + * @package formatter + * @author Frederic Guillot + */ +abstract class TaskFilterCalendarEvent extends TaskFilter +{ + /** + * Column used for event start date + * + * @access protected + * @var string + */ + protected $startColumn = 'date_started'; + + /** + * Column used for event end date + * + * @access protected + * @var string + */ + protected $endColumn = 'date_completed'; + + /** + * Full day event flag + * + * @access private + * @var boolean + */ + private $fullDay = false; + + /** + * Transform results to calendar events + * + * @access public + * @param string $start_column Column name for the start date + * @param string $end_column Column name for the end date + * @return TaskFilterCalendarEvent + */ + public function setColumns($start_column, $end_column = '') + { + $this->startColumn = $start_column; + $this->endColumn = $end_column ?: $start_column; + return $this; + } + + /** + * When called calendar events will be full day + * + * @access public + * @return TaskFilterCalendarEvent + */ + public function setFullDay() + { + $this->fullDay = true; + return $this; + } + + /** + * Return true if the events are full day + * + * @access public + * @return boolean + */ + public function isFullDay() + { + return $this->fullDay; + } +} diff --git a/app/Formatter/TaskFilterCalendarFormatter.php b/app/Formatter/TaskFilterCalendarFormatter.php new file mode 100644 index 00000000..f3f42b97 --- /dev/null +++ b/app/Formatter/TaskFilterCalendarFormatter.php @@ -0,0 +1,52 @@ +<?php + +namespace Formatter; + +/** + * Calendar event formatter for task filter + * + * @package formatter + * @author Frederic Guillot + */ +class TaskFilterCalendarFormatter extends TaskFilterCalendarEvent implements FormatterInterface +{ + /** + * Transform tasks to calendar events + * + * @access public + * @return array + */ + public function format() + { + $events = array(); + + foreach ($this->query->findAll() as $task) { + $events[] = array( + 'timezoneParam' => $this->config->getCurrentTimezone(), + 'id' => $task['id'], + 'title' => t('#%d', $task['id']).' '.$task['title'], + 'backgroundColor' => $this->color->getBackgroundColor($task['color_id']), + 'borderColor' => $this->color->getBorderColor($task['color_id']), + 'textColor' => 'black', + 'url' => $this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), + 'start' => date($this->getDateTimeFormat(), $task[$this->startColumn]), + 'end' => date($this->getDateTimeFormat(), $task[$this->endColumn] ?: time()), + 'editable' => $this->isFullDay(), + 'allday' => $this->isFullDay(), + ); + } + + return $events; + } + + /** + * Get DateTime format for event + * + * @access private + * @return string + */ + private function getDateTimeFormat() + { + return $this->isFullDay() ? 'Y-m-d' : 'Y-m-d\TH:i:s'; + } +} diff --git a/app/Formatter/TaskFilterGanttFormatter.php b/app/Formatter/TaskFilterGanttFormatter.php new file mode 100644 index 00000000..069daa3d --- /dev/null +++ b/app/Formatter/TaskFilterGanttFormatter.php @@ -0,0 +1,78 @@ +<?php + +namespace Formatter; + +use Model\TaskFilter; + +/** + * Gantt chart formatter for task filter + * + * @package formatter + * @author Frederic Guillot + */ +class TaskFilterGanttFormatter extends TaskFilter implements FormatterInterface +{ + /** + * Local cache for project columns + * + * @access private + * @var array + */ + private $columns = array(); + + /** + * Format tasks to be displayed in the Gantt chart + * + * @access public + * @return array + */ + public function format() + { + $bars = array(); + + foreach ($this->query->findAll() as $task) { + $bars[] = $this->formatTask($task); + } + + return $bars; + } + + /** + * Format a single task + * + * @access private + * @param array $task + * @return array + */ + private function formatTask(array $task) + { + if (! isset($this->columns[$task['project_id']])) { + $this->columns[$task['project_id']] = $this->board->getColumnsList($task['project_id']); + } + + $start = $task['date_started'] ?: time(); + $end = $task['date_due'] ?: $start; + + return array( + 'type' => 'task', + 'id' => $task['id'], + 'title' => $task['title'], + 'start' => array( + (int) date('Y', $start), + (int) date('n', $start), + (int) date('j', $start), + ), + 'end' => array( + (int) date('Y', $end), + (int) date('n', $end), + (int) date('j', $end), + ), + 'column_title' => $task['column_name'], + 'assignee' => $task['assignee_name'] ?: $task['assignee_username'], + 'progress' => $this->task->getProgress($task, $this->columns[$task['project_id']]).'%', + 'link' => $this->helper->url->href('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), + 'color' => $this->color->getColorProperties($task['color_id']), + 'not_defined' => empty($task['date_due']) || empty($task['date_started']), + ); + } +} diff --git a/app/Formatter/TaskFilterICalendarFormatter.php b/app/Formatter/TaskFilterICalendarFormatter.php new file mode 100644 index 00000000..8aed1e20 --- /dev/null +++ b/app/Formatter/TaskFilterICalendarFormatter.php @@ -0,0 +1,135 @@ +<?php + +namespace Formatter; + +use DateTime; +use Eluceo\iCal\Component\Calendar; +use Eluceo\iCal\Component\Event; +use Eluceo\iCal\Property\Event\Attendees; + +/** + * iCal event formatter for task filter + * + * @package formatter + * @author Frederic Guillot + */ +class TaskFilterICalendarFormatter extends TaskFilterCalendarEvent implements FormatterInterface +{ + /** + * Calendar object + * + * @access private + * @var \Eluceo\iCal\Component\Calendar + */ + private $vCalendar; + + /** + * Get Ical events + * + * @access public + * @return string + */ + public function format() + { + return $this->vCalendar->render(); + } + + /** + * Set calendar object + * + * @access public + * @param \Eluceo\iCal\Component\Calendar $vCalendar + * @return TaskFilterICalendarFormatter + */ + public function setCalendar(Calendar $vCalendar) + { + $this->vCalendar = $vCalendar; + return $this; + } + + /** + * Transform results to ical events + * + * @access public + * @return TaskFilterICalendarFormatter + */ + public function addDateTimeEvents() + { + foreach ($this->query->findAll() as $task) { + + $start = new DateTime; + $start->setTimestamp($task[$this->startColumn]); + + $end = new DateTime; + $end->setTimestamp($task[$this->endColumn] ?: time()); + + $vEvent = $this->getTaskIcalEvent($task, 'task-#'.$task['id'].'-'.$this->startColumn.'-'.$this->endColumn); + $vEvent->setDtStart($start); + $vEvent->setDtEnd($end); + + $this->vCalendar->addComponent($vEvent); + } + + return $this; + } + + /** + * Transform results to all day ical events + * + * @access public + * @return TaskFilterICalendarFormatter + */ + public function addFullDayEvents() + { + foreach ($this->query->findAll() as $task) { + + $date = new DateTime; + $date->setTimestamp($task[$this->startColumn]); + + $vEvent = $this->getTaskIcalEvent($task, 'task-#'.$task['id'].'-'.$this->startColumn); + $vEvent->setDtStart($date); + $vEvent->setDtEnd($date); + $vEvent->setNoTime(true); + + $this->vCalendar->addComponent($vEvent); + } + + return $this; + } + + /** + * Get common events for task ical events + * + * @access protected + * @param array $task + * @param string $uid + * @return Event + */ + protected function getTaskIcalEvent(array &$task, $uid) + { + $dateCreation = new DateTime; + $dateCreation->setTimestamp($task['date_creation']); + + $dateModif = new DateTime; + $dateModif->setTimestamp($task['date_modification']); + + $vEvent = new Event($uid); + $vEvent->setCreated($dateCreation); + $vEvent->setModified($dateModif); + $vEvent->setUseTimezone(true); + $vEvent->setSummary(t('#%d', $task['id']).' '.$task['title']); + $vEvent->setUrl($this->helper->url->base().$this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))); + + if (! empty($task['owner_id'])) { + $vEvent->setOrganizer($task['assignee_name'] ?: $task['assignee_username'], $task['assignee_email']); + } + + if (! empty($task['creator_id'])) { + $attendees = new Attendees; + $attendees->add('MAILTO:'.($task['creator_email'] ?: $task['creator_username'].'@kanboard.local')); + $vEvent->setAttendees($attendees); + } + + return $vEvent; + } +} diff --git a/app/Model/Base.php b/app/Model/Base.php index 3ee60844..a15f071c 100644 --- a/app/Model/Base.php +++ b/app/Model/Base.php @@ -121,26 +121,6 @@ abstract class Base extends \Core\Base } /** - * Get common properties for task calendar events - * - * @access protected - * @param array $task - * @return array - */ - protected function getTaskCalendarProperties(array &$task) - { - return array( - 'timezoneParam' => $this->config->getCurrentTimezone(), - 'id' => $task['id'], - 'title' => t('#%d', $task['id']).' '.$task['title'], - 'backgroundColor' => $this->color->getBackgroundColor($task['color_id']), - 'borderColor' => $this->color->getBorderColor($task['color_id']), - 'textColor' => 'black', - 'url' => $this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), - ); - } - - /** * Group a collection of records by a column * * @access public diff --git a/app/Model/Project.php b/app/Model/Project.php index 8ddcc443..1bd5b624 100644 --- a/app/Model/Project.php +++ b/app/Model/Project.php @@ -115,54 +115,6 @@ class Project extends Base } /** - * Get all projects to generate the Gantt chart - * - * @access public - * @param array $project_ids - * @return array - */ - public function getGanttBars(array $project_ids) - { - if (empty($project_ids)) { - return array(); - } - - $colors = $this->color->getDefaultColors(); - $projects = $this->db->table(self::TABLE)->asc('start_date')->in('id', $project_ids)->eq('is_active', self::ACTIVE)->eq('is_private', 0)->findAll(); - $bars = array(); - - foreach ($projects as $project) { - $start = empty($project['start_date']) ? time() : strtotime($project['start_date']); - $end = empty($project['end_date']) ? $start : strtotime($project['end_date']); - $color = next($colors) ?: reset($colors); - - $bars[] = array( - 'type' => 'project', - 'id' => $project['id'], - 'title' => $project['name'], - 'start' => array( - (int) date('Y', $start), - (int) date('n', $start), - (int) date('j', $start), - ), - 'end' => array( - (int) date('Y', $end), - (int) date('n', $end), - (int) date('j', $end), - ), - 'link' => $this->helper->url->href('project', 'show', array('project_id' => $project['id'])), - 'board_link' => $this->helper->url->href('board', 'show', array('project_id' => $project['id'])), - 'gantt_link' => $this->helper->url->href('gantt', 'project', array('project_id' => $project['id'])), - 'color' => $color, - 'not_defined' => empty($project['start_date']) || empty($project['end_date']), - 'users' => $this->projectPermission->getProjectUsers($project['id']), - ); - } - - return $bars; - } - - /** * Get all projects * * @access public diff --git a/app/Model/TaskFilter.php b/app/Model/TaskFilter.php index 89ad9aa4..95fb293e 100644 --- a/app/Model/TaskFilter.php +++ b/app/Model/TaskFilter.php @@ -2,11 +2,6 @@ namespace Model; -use DateTime; -use Eluceo\iCal\Component\Calendar; -use Eluceo\iCal\Component\Event; -use Eluceo\iCal\Property\Event\Attendees; - /** * Task Filter * @@ -137,7 +132,7 @@ class TaskFilter extends Base */ public function copy() { - $filter = clone($this); + $filter = new static($this->container); $filter->query = clone($this->query); $filter->query->condition = clone($this->query->condition); return $filter; @@ -675,223 +670,6 @@ class TaskFilter extends Base } /** - * Format tasks to be displayed in the Gantt chart - * - * @access public - * @return array - */ - public function toGanttBars() - { - $bars = array(); - $columns = array(); - - foreach ($this->query->findAll() as $task) { - if (! isset($column_count[$task['project_id']])) { - $columns[$task['project_id']] = $this->board->getColumnsList($task['project_id']); - } - - $start = $task['date_started'] ?: time(); - $end = $task['date_due'] ?: $start; - - $bars[] = array( - 'type' => 'task', - 'id' => $task['id'], - 'title' => $task['title'], - 'start' => array( - (int) date('Y', $start), - (int) date('n', $start), - (int) date('j', $start), - ), - 'end' => array( - (int) date('Y', $end), - (int) date('n', $end), - (int) date('j', $end), - ), - 'column_title' => $task['column_name'], - 'assignee' => $task['assignee_name'] ?: $task['assignee_username'], - 'progress' => $this->task->getProgress($task, $columns[$task['project_id']]).'%', - 'link' => $this->helper->url->href('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), - 'color' => $this->color->getColorProperties($task['color_id']), - 'not_defined' => empty($task['date_due']) || empty($task['date_started']), - ); - } - - return $bars; - } - - /** - * Format the results to the ajax autocompletion - * - * @access public - * @return array - */ - public function toAutoCompletion() - { - return $this->query->columns(Task::TABLE.'.id', Task::TABLE.'.title')->callback(function(array $results) { - - foreach ($results as &$result) { - $result['value'] = $result['title']; - $result['label'] = '#'.$result['id'].' - '.$result['title']; - } - - return $results; - - })->findAll(); - } - - /** - * Transform results to calendar events - * - * @access public - * @param string $start_column Column name for the start date - * @param string $end_column Column name for the end date - * @return array - */ - public function toDateTimeCalendarEvents($start_column, $end_column) - { - $events = array(); - - foreach ($this->query->findAll() as $task) { - - $events[] = array_merge( - $this->getTaskCalendarProperties($task), - array( - 'start' => date('Y-m-d\TH:i:s', $task[$start_column]), - 'end' => date('Y-m-d\TH:i:s', $task[$end_column] ?: time()), - 'editable' => false, - ) - ); - } - - return $events; - } - - /** - * Transform results to all day calendar events - * - * @access public - * @param string $column Column name for the date - * @return array - */ - public function toAllDayCalendarEvents($column = 'date_due') - { - $events = array(); - - foreach ($this->query->findAll() as $task) { - - $events[] = array_merge( - $this->getTaskCalendarProperties($task), - array( - 'start' => date('Y-m-d', $task[$column]), - 'end' => date('Y-m-d', $task[$column]), - 'allday' => true, - ) - ); - } - - return $events; - } - - /** - * Transform results to ical events - * - * @access public - * @param string $start_column Column name for the start date - * @param string $end_column Column name for the end date - * @param Calendar $vCalendar Calendar object - * @return Calendar - */ - public function addDateTimeIcalEvents($start_column, $end_column, Calendar $vCalendar = null) - { - if ($vCalendar === null) { - $vCalendar = new Calendar('Kanboard'); - } - - foreach ($this->query->findAll() as $task) { - - $start = new DateTime; - $start->setTimestamp($task[$start_column]); - - $end = new DateTime; - $end->setTimestamp($task[$end_column] ?: time()); - - $vEvent = $this->getTaskIcalEvent($task, 'task-#'.$task['id'].'-'.$start_column.'-'.$end_column); - $vEvent->setDtStart($start); - $vEvent->setDtEnd($end); - - $vCalendar->addComponent($vEvent); - } - - return $vCalendar; - } - - /** - * Transform results to all day ical events - * - * @access public - * @param string $column Column name for the date - * @param Calendar $vCalendar Calendar object - * @return Calendar - */ - public function addAllDayIcalEvents($column = 'date_due', Calendar $vCalendar = null) - { - if ($vCalendar === null) { - $vCalendar = new Calendar('Kanboard'); - } - - foreach ($this->query->findAll() as $task) { - - $date = new DateTime; - $date->setTimestamp($task[$column]); - - $vEvent = $this->getTaskIcalEvent($task, 'task-#'.$task['id'].'-'.$column); - $vEvent->setDtStart($date); - $vEvent->setDtEnd($date); - $vEvent->setNoTime(true); - - $vCalendar->addComponent($vEvent); - } - - return $vCalendar; - } - - /** - * Get common events for task ical events - * - * @access protected - * @param array $task - * @param string $uid - * @return Event - */ - protected function getTaskIcalEvent(array &$task, $uid) - { - $dateCreation = new DateTime; - $dateCreation->setTimestamp($task['date_creation']); - - $dateModif = new DateTime; - $dateModif->setTimestamp($task['date_modification']); - - $vEvent = new Event($uid); - $vEvent->setCreated($dateCreation); - $vEvent->setModified($dateModif); - $vEvent->setUseTimezone(true); - $vEvent->setSummary(t('#%d', $task['id']).' '.$task['title']); - $vEvent->setUrl($this->helper->url->base().$this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))); - - if (! empty($task['owner_id'])) { - $vEvent->setOrganizer($task['assignee_name'] ?: $task['assignee_username'], $task['assignee_email']); - } - - if (! empty($task['creator_id'])) { - $attendees = new Attendees; - $attendees->add('MAILTO:'.($task['creator_email'] ?: $task['creator_username'].'@kanboard.local')); - $vEvent->setAttendees($attendees); - } - - return $vEvent; - } - - /** * Filter with an operator * * @access public diff --git a/app/ServiceProvider/ClassProvider.php b/app/ServiceProvider/ClassProvider.php index 5a8eeea4..78a089ee 100644 --- a/app/ServiceProvider/ClassProvider.php +++ b/app/ServiceProvider/ClassProvider.php @@ -61,6 +61,13 @@ class ClassProvider implements ServiceProviderInterface 'UserSession', 'Webhook', ), + 'Formatter' => array( + 'TaskFilterGanttFormatter', + 'TaskFilterAutoCompleteFormatter', + 'TaskFilterCalendarFormatter', + 'TaskFilterICalendarFormatter', + 'ProjectGanttFormatter', + ), 'Core' => array( 'EmailClient', 'Helper', @@ -2,6 +2,4 @@ require __DIR__.'/app/common.php'; -if (! $container['router']->dispatch($_SERVER['REQUEST_URI'], isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : '')) { - die('Page not found!'); -} +$container['router']->dispatch($_SERVER['REQUEST_URI'], isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : ''); diff --git a/tests/units/Formatter/TaskFilterCalendarFormatterTest.php b/tests/units/Formatter/TaskFilterCalendarFormatterTest.php new file mode 100644 index 00000000..2ea327ff --- /dev/null +++ b/tests/units/Formatter/TaskFilterCalendarFormatterTest.php @@ -0,0 +1,29 @@ +<?php + +require_once __DIR__.'/../Base.php'; + +use Formatter\TaskFilterCalendarFormatter; +use Model\Project; +use Model\User; +use Model\TaskCreation; +use Model\DateParser; +use Model\Category; +use Model\Subtask; +use Model\Config; +use Model\Swimlane; + +class TaskFilterCalendarFormatterTest extends Base +{ + public function testCopy() + { + $tf = new TaskFilterCalendarFormatter($this->container); + $filter1 = $tf->create()->setFullDay(); + $filter2 = $tf->copy(); + + $this->assertTrue($filter1 !== $filter2); + $this->assertTrue($filter1->query !== $filter2->query); + $this->assertTrue($filter1->query->condition !== $filter2->query->condition); + $this->assertTrue($filter1->isFullDay()); + $this->assertFalse($filter2->isFullDay()); + } +} diff --git a/tests/units/Formatter/TaskFilterGanttFormatterTest.php b/tests/units/Formatter/TaskFilterGanttFormatterTest.php new file mode 100644 index 00000000..9006e164 --- /dev/null +++ b/tests/units/Formatter/TaskFilterGanttFormatterTest.php @@ -0,0 +1,24 @@ +<?php + +require_once __DIR__.'/../Base.php'; + +use Formatter\TaskFilterGanttFormatter; +use Model\Project; +use Model\TaskCreation; +use Model\DateParser; + +class TaskFilterGanttFormatterTest extends Base +{ + public function testFormat() + { + $dp = new DateParser($this->container); + $p = new Project($this->container); + $tc = new TaskCreation($this->container); + $tf = new TaskFilterGanttFormatter($this->container); + + $this->assertEquals(1, $p->create(array('name' => 'test'))); + $this->assertNotFalse($tc->create(array('project_id' => 1, 'title' => 'task1'))); + + $this->assertNotEmpty($tf->search('status:open')->format()); + } +} diff --git a/tests/units/Formatter/TaskFilterICalendarFormatterTest.php b/tests/units/Formatter/TaskFilterICalendarFormatterTest.php new file mode 100644 index 00000000..17602520 --- /dev/null +++ b/tests/units/Formatter/TaskFilterICalendarFormatterTest.php @@ -0,0 +1,77 @@ +<?php + +require_once __DIR__.'/../Base.php'; + +use Eluceo\iCal\Component\Calendar; +use Formatter\TaskFilterICalendarFormatter; +use Model\Project; +use Model\User; +use Model\TaskCreation; +use Model\DateParser; +use Model\Category; +use Model\Subtask; +use Model\Config; +use Model\Swimlane; + +class TaskFilterICalendarFormatterTest extends Base +{ + public function testIcalEventsWithCreatorAndDueDate() + { + $dp = new DateParser($this->container); + $p = new Project($this->container); + $tc = new TaskCreation($this->container); + $tf = new TaskFilterICalendarFormatter($this->container); + + $this->assertEquals(1, $p->create(array('name' => 'test'))); + $this->assertNotFalse($tc->create(array('project_id' => 1, 'title' => 'task1', 'creator_id' => 1, 'date_due' => $dp->getTimestampFromIsoFormat('-2 days')))); + + $ics = $tf->create() + ->filterByDueDateRange(strtotime('-1 month'), strtotime('+1 month')) + ->setFullDay() + ->setCalendar(new Calendar('Kanboard')) + ->setColumns('date_due') + ->addFullDayEvents() + ->format(); + + $this->assertContains('UID:task-#1-date_due', $ics); + $this->assertContains('DTSTART;TZID=UTC;VALUE=DATE:'.date('Ymd', strtotime('-2 days')), $ics); + $this->assertContains('DTEND;TZID=UTC;VALUE=DATE:'.date('Ymd', strtotime('-2 days')), $ics); + $this->assertContains('URL:http://localhost/?controller=task&action=show&task_id=1&project_id=1', $ics); + $this->assertContains('SUMMARY:#1 task1', $ics); + $this->assertContains('ATTENDEE:MAILTO:admin@kanboard.local', $ics); + $this->assertContains('X-MICROSOFT-CDO-ALLDAYEVENT:TRUE', $ics); + } + + public function testIcalEventsWithAssigneeAndDueDate() + { + $dp = new DateParser($this->container); + $p = new Project($this->container); + $tc = new TaskCreation($this->container); + $tf = new TaskFilterICalendarFormatter($this->container); + $u = new User($this->container); + $c = new Config($this->container); + + $this->assertNotFalse($c->save(array('application_url' => 'http://kb/'))); + $this->assertEquals('http://kb/', $c->get('application_url')); + + $this->assertNotFalse($u->update(array('id' => 1, 'email' => 'bob@localhost'))); + $this->assertEquals(1, $p->create(array('name' => 'test'))); + $this->assertNotFalse($tc->create(array('project_id' => 1, 'title' => 'task1', 'owner_id' => 1, 'date_due' => $dp->getTimestampFromIsoFormat('+5 days')))); + + $ics = $tf->create() + ->filterByDueDateRange(strtotime('-1 month'), strtotime('+1 month')) + ->setFullDay() + ->setCalendar(new Calendar('Kanboard')) + ->setColumns('date_due') + ->addFullDayEvents() + ->format(); + + $this->assertContains('UID:task-#1-date_due', $ics); + $this->assertContains('DTSTART;TZID=UTC;VALUE=DATE:'.date('Ymd', strtotime('+5 days')), $ics); + $this->assertContains('DTEND;TZID=UTC;VALUE=DATE:'.date('Ymd', strtotime('+5 days')), $ics); + $this->assertContains('URL:http://kb/?controller=task&action=show&task_id=1&project_id=1', $ics); + $this->assertContains('SUMMARY:#1 task1', $ics); + $this->assertContains('ORGANIZER;CN=admin:MAILTO:bob@localhost', $ics); + $this->assertContains('X-MICROSOFT-CDO-ALLDAYEVENT:TRUE', $ics); + } +} diff --git a/tests/units/Model/TaskFilterTest.php b/tests/units/Model/TaskFilterTest.php index 1987265d..28b5d24a 100644 --- a/tests/units/Model/TaskFilterTest.php +++ b/tests/units/Model/TaskFilterTest.php @@ -14,70 +14,6 @@ use Model\Swimlane; class TaskFilterTest extends Base { - public function testGetGanttbar() - { - $dp = new DateParser($this->container); - $p = new Project($this->container); - $tc = new TaskCreation($this->container); - $tf = new TaskFilter($this->container); - - $this->assertEquals(1, $p->create(array('name' => 'test'))); - $this->assertNotFalse($tc->create(array('project_id' => 1, 'title' => 'task1'))); - - $this->assertNotEmpty($tf->search('status:open')->toGanttBars()); - $this->assertNotEmpty($p->getGanttBars(array(1))); - } - - public function testIcalEventsWithCreatorAndDueDate() - { - $dp = new DateParser($this->container); - $p = new Project($this->container); - $tc = new TaskCreation($this->container); - $tf = new TaskFilter($this->container); - - $this->assertEquals(1, $p->create(array('name' => 'test'))); - $this->assertNotFalse($tc->create(array('project_id' => 1, 'title' => 'task1', 'creator_id' => 1, 'date_due' => $dp->getTimestampFromIsoFormat('-2 days')))); - - $events = $tf->create()->filterByDueDateRange(strtotime('-1 month'), strtotime('+1 month'))->addAllDayIcalEvents(); - $ics = $events->render(); - - $this->assertContains('UID:task-#1-date_due', $ics); - $this->assertContains('DTSTART;TZID=UTC;VALUE=DATE:'.date('Ymd', strtotime('-2 days')), $ics); - $this->assertContains('DTEND;TZID=UTC;VALUE=DATE:'.date('Ymd', strtotime('-2 days')), $ics); - $this->assertContains('URL:http://localhost/?controller=task&action=show&task_id=1&project_id=1', $ics); - $this->assertContains('SUMMARY:#1 task1', $ics); - $this->assertContains('ATTENDEE:MAILTO:admin@kanboard.local', $ics); - $this->assertContains('X-MICROSOFT-CDO-ALLDAYEVENT:TRUE', $ics); - } - - public function testIcalEventsWithAssigneeAndDueDate() - { - $dp = new DateParser($this->container); - $p = new Project($this->container); - $tc = new TaskCreation($this->container); - $tf = new TaskFilter($this->container); - $u = new User($this->container); - $c = new Config($this->container); - - $this->assertNotFalse($c->save(array('application_url' => 'http://kb/'))); - $this->assertEquals('http://kb/', $c->get('application_url')); - - $this->assertNotFalse($u->update(array('id' => 1, 'email' => 'bob@localhost'))); - $this->assertEquals(1, $p->create(array('name' => 'test'))); - $this->assertNotFalse($tc->create(array('project_id' => 1, 'title' => 'task1', 'owner_id' => 1, 'date_due' => $dp->getTimestampFromIsoFormat('+5 days')))); - - $events = $tf->create()->filterByDueDateRange(strtotime('-1 month'), strtotime('+1 month'))->addAllDayIcalEvents(); - $ics = $events->render(); - - $this->assertContains('UID:task-#1-date_due', $ics); - $this->assertContains('DTSTART;TZID=UTC;VALUE=DATE:'.date('Ymd', strtotime('+5 days')), $ics); - $this->assertContains('DTEND;TZID=UTC;VALUE=DATE:'.date('Ymd', strtotime('+5 days')), $ics); - $this->assertContains('URL:http://kb/?controller=task&action=show&task_id=1&project_id=1', $ics); - $this->assertContains('SUMMARY:#1 task1', $ics); - $this->assertContains('ORGANIZER;CN=admin:MAILTO:bob@localhost', $ics); - $this->assertContains('X-MICROSOFT-CDO-ALLDAYEVENT:TRUE', $ics); - } - public function testSearchWithEmptyResult() { $dp = new DateParser($this->container); |