summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrederic Guillot <fred@kanboard.net>2015-09-20 22:18:56 -0400
committerFrederic Guillot <fred@kanboard.net>2015-09-20 22:18:56 -0400
commit689687dd4ee186cb9cf5d0230b4648e242c53b10 (patch)
tree3d26bc2079c6eb45790ba604b3a79997be4768ab
parentf579663adcbc0b202d9a068d734e8f9284dc3a37 (diff)
Add formatters
-rw-r--r--app/Controller/App.php4
-rw-r--r--app/Controller/Calendar.php16
-rw-r--r--app/Controller/Gantt.php6
-rw-r--r--app/Controller/Ical.php27
-rw-r--r--app/Formatter/FormatterInterface.php14
-rw-r--r--app/Formatter/ProjectGanttFormatter.php90
-rw-r--r--app/Formatter/TaskFilterAutoCompleteFormatter.php33
-rw-r--r--app/Formatter/TaskFilterCalendarEvent.php76
-rw-r--r--app/Formatter/TaskFilterCalendarFormatter.php52
-rw-r--r--app/Formatter/TaskFilterGanttFormatter.php78
-rw-r--r--app/Formatter/TaskFilterICalendarFormatter.php135
-rw-r--r--app/Model/Base.php20
-rw-r--r--app/Model/Project.php48
-rw-r--r--app/Model/TaskFilter.php224
-rw-r--r--app/ServiceProvider/ClassProvider.php7
-rw-r--r--index.php4
-rw-r--r--tests/units/Formatter/TaskFilterCalendarFormatterTest.php29
-rw-r--r--tests/units/Formatter/TaskFilterGanttFormatterTest.php24
-rw-r--r--tests/units/Formatter/TaskFilterICalendarFormatterTest.php77
-rw-r--r--tests/units/Model/TaskFilterTest.php64
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',
diff --git a/index.php b/index.php
index da11fd9f..2f44b746 100644
--- a/index.php
+++ b/index.php
@@ -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);