summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrederic Guillot <fred@kanboard.net>2017-11-08 14:50:02 -0800
committerFrederic Guillot <fred@kanboard.net>2017-11-08 14:50:02 -0800
commitcafbd1f5a7e60d931e4dbd700b12e5f50319340d (patch)
tree10379ec3d0ced6dbdc1da2b78071added257f101
parent5e98a66aeb108b84ca4f2ae824c0a191fbd514cd (diff)
Improve iCalendar feed to include tasks with start/end date and due date with a time
-rw-r--r--ChangeLog1
-rw-r--r--app/Controller/ICalendarController.php97
-rw-r--r--app/Core/Filter/QueryBuilder.php12
-rw-r--r--app/Core/Helper.php1
-rw-r--r--app/Filter/BaseFilter.php4
-rw-r--r--app/Filter/TaskDueDateRangeFilter.php5
-rw-r--r--app/Formatter/BaseTaskCalendarFormatter.php43
-rw-r--r--app/Formatter/TaskICalFormatter.php40
-rw-r--r--app/Helper/ICalHelper.php37
-rw-r--r--app/ServiceProvider/HelperProvider.php1
-rw-r--r--vendor/composer/autoload_classmap.php2
-rw-r--r--vendor/composer/autoload_static.php2
12 files changed, 101 insertions, 144 deletions
diff --git a/ChangeLog b/ChangeLog
index 6fadf561..4675ae51 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -12,6 +12,7 @@ New features:
Improvements:
+* Improve iCalendar feed to include tasks with start/end date and due date with a time
* You can get an archive of Kanboard by using the download button in Github or the command git archive
Version 1.0.48 (October 23, 2017)
diff --git a/app/Controller/ICalendarController.php b/app/Controller/ICalendarController.php
index 4fe8b78a..20e2a9d7 100644
--- a/app/Controller/ICalendarController.php
+++ b/app/Controller/ICalendarController.php
@@ -5,6 +5,7 @@ namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
use Kanboard\Core\Filter\QueryBuilder;
use Kanboard\Filter\TaskAssigneeFilter;
+use Kanboard\Filter\TaskDueDateRangeFilter;
use Kanboard\Filter\TaskProjectFilter;
use Kanboard\Filter\TaskStatusFilter;
use Kanboard\Model\TaskModel;
@@ -18,81 +19,97 @@ use Eluceo\iCal\Component\Calendar as iCalendar;
*/
class ICalendarController extends BaseController
{
- /**
- * Get user iCalendar
- *
- * @access public
- */
public function user()
{
$token = $this->request->getStringParam('token');
$user = $this->userModel->getByToken($token);
- // Token verification
if (empty($user)) {
throw AccessForbiddenException::getInstance()->withoutLayout();
}
- // Common filter
- $queryBuilder = new QueryBuilder();
- $queryBuilder
- ->withQuery($this->taskFinderModel->getICalQuery())
- ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN))
- ->withFilter(new TaskAssigneeFilter($user['id']));
+ $startRange = $this->request->getStringParam('start', strtotime('-2 months'));
+ $endRange = $this->request->getStringParam('end', strtotime('+6 months'));
+
+ $startColumn = $this->configModel->get('calendar_project_tasks', 'date_started');
- // Calendar properties
$calendar = new iCalendar('Kanboard');
$calendar->setName($user['name'] ?: $user['username']);
$calendar->setDescription($user['name'] ?: $user['username']);
$calendar->setPublishedTTL('PT1H');
- $this->renderCalendar($queryBuilder, $calendar);
+ $queryDueDateOnly = QueryBuilder::create()
+ ->withQuery($this->taskFinderModel->getICalQuery())
+ ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN))
+ ->withFilter(new TaskDueDateRangeFilter(array($startRange, $endRange)))
+ ->withFilter(new TaskAssigneeFilter($user['id']))
+ ->getQuery();
+
+ $queryStartAndDueDate = QueryBuilder::create()
+ ->withQuery($this->taskFinderModel->getICalQuery())
+ ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN))
+ ->withFilter(new TaskAssigneeFilter($user['id']))
+ ->getQuery()
+ ->addCondition($this->getConditionForTasksWithStartAndDueDate($startRange, $endRange, $startColumn, 'date_due'));
+
+ $this->response->ical($this->taskICalFormatter
+ ->setCalendar($calendar)
+ ->addTasksWithDueDateOnly($queryDueDateOnly)
+ ->addTasksWithStartAndDueDate($queryStartAndDueDate, $startColumn, 'date_due')
+ ->format());
}
- /**
- * Get project iCalendar
- *
- * @access public
- */
public function project()
{
$token = $this->request->getStringParam('token');
$project = $this->projectModel->getByToken($token);
- // Token verification
if (empty($project)) {
throw AccessForbiddenException::getInstance()->withoutLayout();
}
- // Common filter
- $queryBuilder = new QueryBuilder();
- $queryBuilder
- ->withQuery($this->taskFinderModel->getICalQuery())
- ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN))
- ->withFilter(new TaskProjectFilter($project['id']));
+ $startRange = $this->request->getStringParam('start', strtotime('-2 months'));
+ $endRange = $this->request->getStringParam('end', strtotime('+6 months'));
+
+ $startColumn = $this->configModel->get('calendar_project_tasks', 'date_started');
- // Calendar properties
$calendar = new iCalendar('Kanboard');
$calendar->setName($project['name']);
$calendar->setDescription($project['name']);
$calendar->setPublishedTTL('PT1H');
- $this->renderCalendar($queryBuilder, $calendar);
+ $queryDueDateOnly = QueryBuilder::create()
+ ->withQuery($this->taskFinderModel->getICalQuery())
+ ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN))
+ ->withFilter(new TaskProjectFilter($project['id']))
+ ->withFilter(new TaskDueDateRangeFilter(array($startRange, $endRange)))
+ ->getQuery();
+
+ $queryStartAndDueDate = QueryBuilder::create()
+ ->withQuery($this->taskFinderModel->getICalQuery())
+ ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN))
+ ->withFilter(new TaskProjectFilter($project['id']))
+ ->getQuery()
+ ->addCondition($this->getConditionForTasksWithStartAndDueDate($startRange, $endRange, $startColumn, 'date_due'));
+
+ $this->response->ical($this->taskICalFormatter
+ ->setCalendar($calendar)
+ ->addTasksWithDueDateOnly($queryDueDateOnly)
+ ->addTasksWithStartAndDueDate($queryStartAndDueDate, $startColumn, 'date_due')
+ ->format());
}
- /**
- * Common method to render iCal events
- *
- * @access private
- * @param QueryBuilder $queryBuilder
- * @param iCalendar $calendar
- */
- private function renderCalendar(QueryBuilder $queryBuilder, iCalendar $calendar)
+ protected function getConditionForTasksWithStartAndDueDate($start_time, $end_time, $start_column, $end_column)
{
- $start = $this->request->getStringParam('start', strtotime('-2 month'));
- $end = $this->request->getStringParam('end', strtotime('+6 months'));
+ $start_column = $this->db->escapeIdentifier($start_column);
+ $end_column = $this->db->escapeIdentifier($end_column);
+
+ $conditions = array(
+ "($start_column >= '$start_time' AND $start_column <= '$end_time')",
+ "($start_column <= '$start_time' AND $end_column >= '$start_time')",
+ "($start_column <= '$start_time' AND ($end_column = '0' OR $end_column IS NULL))",
+ );
- $this->helper->ical->addTaskDateDueEvents($queryBuilder, $calendar, $start, $end);
- $this->response->ical($this->taskICalFormatter->setCalendar($calendar)->format());
+ return $start_column.' IS NOT NULL AND '.$start_column.' > 0 AND ('.implode(' OR ', $conditions).')';
}
}
diff --git a/app/Core/Filter/QueryBuilder.php b/app/Core/Filter/QueryBuilder.php
index 3de82b63..bdd6b940 100644
--- a/app/Core/Filter/QueryBuilder.php
+++ b/app/Core/Filter/QueryBuilder.php
@@ -21,6 +21,18 @@ class QueryBuilder
protected $query;
/**
+ * Create a new class instance
+ *
+ * @static
+ * @access public
+ * @return static
+ */
+ public static function create()
+ {
+ return new static();
+ }
+
+ /**
* Set the query
*
* @access public
diff --git a/app/Core/Helper.php b/app/Core/Helper.php
index cb9c50f6..5f4772d5 100644
--- a/app/Core/Helper.php
+++ b/app/Core/Helper.php
@@ -19,7 +19,6 @@ use Pimple\Container;
* @property \Kanboard\Helper\FileHelper $file
* @property \Kanboard\Helper\FormHelper $form
* @property \Kanboard\Helper\HookHelper $hook
- * @property \Kanboard\Helper\ICalHelper $ical
* @property \Kanboard\Helper\ModalHelper $modal
* @property \Kanboard\Helper\ModelHelper $model
* @property \Kanboard\Helper\SubtaskHelper $subtask
diff --git a/app/Filter/BaseFilter.php b/app/Filter/BaseFilter.php
index e029f4e1..2687d903 100644
--- a/app/Filter/BaseFilter.php
+++ b/app/Filter/BaseFilter.php
@@ -51,7 +51,7 @@ abstract class BaseFilter
*
* @access public
* @param Table $query
- * @return \Kanboard\Core\Filter\FilterInterface
+ * @return $this
*/
public function withQuery(Table $query)
{
@@ -64,7 +64,7 @@ abstract class BaseFilter
*
* @access public
* @param string $value
- * @return \Kanboard\Core\Filter\FilterInterface
+ * @return $this
*/
public function withValue($value)
{
diff --git a/app/Filter/TaskDueDateRangeFilter.php b/app/Filter/TaskDueDateRangeFilter.php
index a6aefbe2..a4250f80 100644
--- a/app/Filter/TaskDueDateRangeFilter.php
+++ b/app/Filter/TaskDueDateRangeFilter.php
@@ -32,6 +32,11 @@ class TaskDueDateRangeFilter extends BaseFilter implements FilterInterface
*/
public function apply()
{
+ $this->query->beginOr();
+ $this->query->isNull(TaskModel::TABLE.'.date_started');
+ $this->query->eq(TaskModel::TABLE.'.date_started', 0);
+ $this->query->closeOr();
+
$this->query->gte(TaskModel::TABLE.'.date_due', is_numeric($this->value[0]) ? $this->value[0] : strtotime($this->value[0]));
$this->query->lte(TaskModel::TABLE.'.date_due', is_numeric($this->value[1]) ? $this->value[1] : strtotime($this->value[1]));
return $this;
diff --git a/app/Formatter/BaseTaskCalendarFormatter.php b/app/Formatter/BaseTaskCalendarFormatter.php
deleted file mode 100644
index 3d9ead4d..00000000
--- a/app/Formatter/BaseTaskCalendarFormatter.php
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php
-
-namespace Kanboard\Formatter;
-
-/**
- * Common class to handle calendar events
- *
- * @package formatter
- * @author Frederic Guillot
- */
-abstract class BaseTaskCalendarFormatter extends BaseFormatter
-{
- /**
- * 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';
-
- /**
- * 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 $this
- */
- public function setColumns($start_column, $end_column = '')
- {
- $this->startColumn = $start_column;
- $this->endColumn = $end_column ?: $start_column;
- return $this;
- }
-}
diff --git a/app/Formatter/TaskICalFormatter.php b/app/Formatter/TaskICalFormatter.php
index ad2a4449..387074e5 100644
--- a/app/Formatter/TaskICalFormatter.php
+++ b/app/Formatter/TaskICalFormatter.php
@@ -8,6 +8,7 @@ use Eluceo\iCal\Component\Event;
use Eluceo\iCal\Property\Event\Attendees;
use Eluceo\iCal\Property\Event\Organizer;
use Kanboard\Core\Filter\FormatterInterface;
+use PicoDb\Table;
/**
* iCal event formatter for tasks
@@ -15,15 +16,15 @@ use Kanboard\Core\Filter\FormatterInterface;
* @package formatter
* @author Frederic Guillot
*/
-class TaskICalFormatter extends BaseTaskCalendarFormatter implements FormatterInterface
+class TaskICalFormatter extends BaseFormatter implements FormatterInterface
{
/**
* Calendar object
*
- * @access private
+ * @access protected
* @var \Eluceo\iCal\Component\Calendar
*/
- private $vCalendar;
+ protected $vCalendar;
/**
* Get Ical events
@@ -41,7 +42,7 @@ class TaskICalFormatter extends BaseTaskCalendarFormatter implements FormatterIn
*
* @access public
* @param \Eluceo\iCal\Component\Calendar $vCalendar
- * @return FormatterInterface
+ * @return $this
*/
public function setCalendar(Calendar $vCalendar)
{
@@ -53,18 +54,21 @@ class TaskICalFormatter extends BaseTaskCalendarFormatter implements FormatterIn
* Transform results to iCal events
*
* @access public
- * @return FormatterInterface
+ * @param Table $query
+ * @param string $startColumn
+ * @param string $endColumn
+ * @return $this
*/
- public function addDateTimeEvents()
+ public function addTasksWithStartAndDueDate(Table $query, $startColumn, $endColumn)
{
- foreach ($this->query->findAll() as $task) {
+ foreach ($query->findAll() as $task) {
$start = new DateTime;
- $start->setTimestamp($task[$this->startColumn]);
+ $start->setTimestamp($task[$startColumn]);
$end = new DateTime;
- $end->setTimestamp($task[$this->endColumn] ?: time());
+ $end->setTimestamp($task[$endColumn] ?: time());
- $vEvent = $this->getTaskIcalEvent($task, 'task-#'.$task['id'].'-'.$this->startColumn.'-'.$this->endColumn);
+ $vEvent = $this->getTaskIcalEvent($task, 'task-#'.$task['id'].'-'.$startColumn.'-'.$endColumn);
$vEvent->setDtStart($start);
$vEvent->setDtEnd($end);
@@ -78,18 +82,22 @@ class TaskICalFormatter extends BaseTaskCalendarFormatter implements FormatterIn
* Transform results to all day iCal events
*
* @access public
- * @return FormatterInterface
+ * @param Table $query
+ * @return $this
*/
- public function addFullDayEvents()
+ public function addTasksWithDueDateOnly(Table $query)
{
- foreach ($this->query->findAll() as $task) {
+ foreach ($query->findAll() as $task) {
$date = new DateTime;
- $date->setTimestamp($task[$this->startColumn]);
+ $date->setTimestamp($task['date_due']);
- $vEvent = $this->getTaskIcalEvent($task, 'task-#'.$task['id'].'-'.$this->startColumn);
+ $vEvent = $this->getTaskIcalEvent($task, 'task-#'.$task['id'].'-date_due');
$vEvent->setDtStart($date);
$vEvent->setDtEnd($date);
- $vEvent->setNoTime(true);
+
+ if ($date->format('Hi') === '0000') {
+ $vEvent->setNoTime(true);
+ }
$this->vCalendar->addComponent($vEvent);
}
diff --git a/app/Helper/ICalHelper.php b/app/Helper/ICalHelper.php
deleted file mode 100644
index 95723417..00000000
--- a/app/Helper/ICalHelper.php
+++ /dev/null
@@ -1,37 +0,0 @@
-<?php
-
-namespace Kanboard\Helper;
-
-use Kanboard\Core\Base;
-use Kanboard\Core\Filter\QueryBuilder;
-use Kanboard\Filter\TaskDueDateRangeFilter;
-use Eluceo\iCal\Component\Calendar as iCalendar;
-
-/**
- * ICal Helper
- *
- * @package helper
- * @author Frederic Guillot
- */
-class ICalHelper extends Base
-{
- /**
- * Get formatted calendar task due events
- *
- * @access public
- * @param QueryBuilder $queryBuilder
- * @param iCalendar $calendar
- * @param string $start
- * @param string $end
- */
- public function addTaskDateDueEvents(QueryBuilder $queryBuilder, iCalendar $calendar, $start, $end)
- {
- $queryBuilder->withFilter(new TaskDueDateRangeFilter(array($start, $end)));
-
- $this->taskICalFormatter
- ->setColumns('date_due')
- ->setCalendar($calendar)
- ->withQuery($queryBuilder->getQuery())
- ->addFullDayEvents();
- }
-}
diff --git a/app/ServiceProvider/HelperProvider.php b/app/ServiceProvider/HelperProvider.php
index 054c4009..f0f7ac07 100644
--- a/app/ServiceProvider/HelperProvider.php
+++ b/app/ServiceProvider/HelperProvider.php
@@ -26,7 +26,6 @@ class HelperProvider implements ServiceProviderInterface
$container['helper']->register('file', '\Kanboard\Helper\FileHelper');
$container['helper']->register('form', '\Kanboard\Helper\FormHelper');
$container['helper']->register('hook', '\Kanboard\Helper\HookHelper');
- $container['helper']->register('ical', '\Kanboard\Helper\ICalHelper');
$container['helper']->register('layout', '\Kanboard\Helper\LayoutHelper');
$container['helper']->register('model', '\Kanboard\Helper\ModelHelper');
$container['helper']->register('subtask', '\Kanboard\Helper\SubtaskHelper');
diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php
index 8722d3ea..7e03fcd2 100644
--- a/vendor/composer/autoload_classmap.php
+++ b/vendor/composer/autoload_classmap.php
@@ -436,7 +436,6 @@ return array(
'Kanboard\\Filter\\TaskTitleFilter' => $baseDir . '/app/Filter/TaskTitleFilter.php',
'Kanboard\\Filter\\UserNameFilter' => $baseDir . '/app/Filter/UserNameFilter.php',
'Kanboard\\Formatter\\BaseFormatter' => $baseDir . '/app/Formatter/BaseFormatter.php',
- 'Kanboard\\Formatter\\BaseTaskCalendarFormatter' => $baseDir . '/app/Formatter/BaseTaskCalendarFormatter.php',
'Kanboard\\Formatter\\BoardColumnFormatter' => $baseDir . '/app/Formatter/BoardColumnFormatter.php',
'Kanboard\\Formatter\\BoardFormatter' => $baseDir . '/app/Formatter/BoardFormatter.php',
'Kanboard\\Formatter\\BoardSwimlaneFormatter' => $baseDir . '/app/Formatter/BoardSwimlaneFormatter.php',
@@ -470,7 +469,6 @@ return array(
'Kanboard\\Helper\\FileHelper' => $baseDir . '/app/Helper/FileHelper.php',
'Kanboard\\Helper\\FormHelper' => $baseDir . '/app/Helper/FormHelper.php',
'Kanboard\\Helper\\HookHelper' => $baseDir . '/app/Helper/HookHelper.php',
- 'Kanboard\\Helper\\ICalHelper' => $baseDir . '/app/Helper/ICalHelper.php',
'Kanboard\\Helper\\LayoutHelper' => $baseDir . '/app/Helper/LayoutHelper.php',
'Kanboard\\Helper\\MailHelper' => $baseDir . '/app/Helper/MailHelper.php',
'Kanboard\\Helper\\ModalHelper' => $baseDir . '/app/Helper/ModalHelper.php',
diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php
index 241e54d6..28559567 100644
--- a/vendor/composer/autoload_static.php
+++ b/vendor/composer/autoload_static.php
@@ -572,7 +572,6 @@ class ComposerStaticInit6edea6294a88689e3f5c56484bb70c9b
'Kanboard\\Filter\\TaskTitleFilter' => __DIR__ . '/../..' . '/app/Filter/TaskTitleFilter.php',
'Kanboard\\Filter\\UserNameFilter' => __DIR__ . '/../..' . '/app/Filter/UserNameFilter.php',
'Kanboard\\Formatter\\BaseFormatter' => __DIR__ . '/../..' . '/app/Formatter/BaseFormatter.php',
- 'Kanboard\\Formatter\\BaseTaskCalendarFormatter' => __DIR__ . '/../..' . '/app/Formatter/BaseTaskCalendarFormatter.php',
'Kanboard\\Formatter\\BoardColumnFormatter' => __DIR__ . '/../..' . '/app/Formatter/BoardColumnFormatter.php',
'Kanboard\\Formatter\\BoardFormatter' => __DIR__ . '/../..' . '/app/Formatter/BoardFormatter.php',
'Kanboard\\Formatter\\BoardSwimlaneFormatter' => __DIR__ . '/../..' . '/app/Formatter/BoardSwimlaneFormatter.php',
@@ -606,7 +605,6 @@ class ComposerStaticInit6edea6294a88689e3f5c56484bb70c9b
'Kanboard\\Helper\\FileHelper' => __DIR__ . '/../..' . '/app/Helper/FileHelper.php',
'Kanboard\\Helper\\FormHelper' => __DIR__ . '/../..' . '/app/Helper/FormHelper.php',
'Kanboard\\Helper\\HookHelper' => __DIR__ . '/../..' . '/app/Helper/HookHelper.php',
- 'Kanboard\\Helper\\ICalHelper' => __DIR__ . '/../..' . '/app/Helper/ICalHelper.php',
'Kanboard\\Helper\\LayoutHelper' => __DIR__ . '/../..' . '/app/Helper/LayoutHelper.php',
'Kanboard\\Helper\\MailHelper' => __DIR__ . '/../..' . '/app/Helper/MailHelper.php',
'Kanboard\\Helper\\ModalHelper' => __DIR__ . '/../..' . '/app/Helper/ModalHelper.php',