Add iCalendar public access for projects

This commit is contained in:
Frederic Guillot
2015-05-17 22:09:44 -04:00
parent 16973bb222
commit ac6e7bdfbf
7 changed files with 236 additions and 14 deletions

52
app/Controller/Ical.php Normal file
View File

@@ -0,0 +1,52 @@
<?php
namespace Controller;
use Model\Task as TaskModel;
/**
* iCalendar controller
*
* @package controller
* @author Frederic Guillot
*/
class Ical extends Base
{
/**
* Get project iCalendar
*
* @access public
*/
public function project()
{
$token = $this->request->getStringParam('token');
$project = $this->project->getByToken($token);
// Token verification
if (empty($project)) {
$this->forbidden(true);
}
$start = $this->request->getStringParam('start', strtotime('-1 month'));
$end = $this->request->getStringParam('end', strtotime('+2 months'));
// Common filter
$filter = $this->taskFilter
->create()
->filterByProject($project['id']);
// Tasks
if ($this->config->get('calendar_project_tasks', 'date_started') === 'date_creation') {
$calendar = $filter->copy()->filterByCreationDateRange($start, $end)->addDateTimeIcalEvents('date_creation', 'date_completed');
}
else {
$calendar = $filter->copy()->filterByStartDateRange($start, $end)->addDateTimeIcalEvents('date_started', 'date_completed');
}
// Tasks with due date
$calendar = $filter->copy()->filterByDueDateRange($start, $end)->addAllDayIcalEvents('date_due', $calendar);
$this->response->contentType('text/calendar; charset=utf-8');
echo $calendar->render();
}
}

View File

@@ -24,6 +24,7 @@ class Acl extends Base
'project' => array('feed'),
'webhook' => '*',
'app' => array('colors'),
'ical' => '*',
);
/**

View File

@@ -148,7 +148,7 @@ class DateParser extends Base
*/
public function getTimestampFromIsoFormat($date)
{
return $this->removeTimeFromTimestamp(strtotime($date));
return $this->removeTimeFromTimestamp(ctype_digit($date) ? $date : strtotime($date));
}
/**

View File

@@ -2,6 +2,11 @@
namespace Model;
use DateTime;
use Eluceo\iCal\Component\Calendar;
use Eluceo\iCal\Component\Event;
use Eluceo\iCal\Property\Event\Attendees;
/**
* Task Filter
*
@@ -27,6 +32,17 @@ class TaskFilter extends Base
public function create()
{
$this->query = $this->db->table(Task::TABLE);
$this->query->left(User::TABLE, 'ua', 'id', Task::TABLE, 'owner_id');
$this->query->left(User::TABLE, 'uc', 'id', Task::TABLE, 'creator_id');
$this->query->columns(
Task::TABLE.'.*',
'ua.email AS assignee_email',
'ua.username AS assignee_username',
'uc.email AS creator_email',
'uc.username AS creator_username'
);
return $this;
}
@@ -214,8 +230,8 @@ class TaskFilter extends Base
* Filter by due date (range)
*
* @access public
* @param integer $start
* @param integer $end
* @param string $start
* @param string $end
* @return TaskFilter
*/
public function filterByDueDateRange($start, $end)
@@ -230,8 +246,8 @@ class TaskFilter extends Base
* Filter by start date (range)
*
* @access public
* @param integer $start
* @param integer $end
* @param string $start
* @param strings $end
* @return TaskFilter
*/
public function filterByStartDateRange($start, $end)
@@ -250,8 +266,8 @@ class TaskFilter extends Base
* Filter by creation date
*
* @access public
* @param integer $start
* @param integer $end
* @param string $start
* @param string $end
* @return TaskFilter
*/
public function filterByCreationDateRange($start, $end)
@@ -349,4 +365,103 @@ class TaskFilter extends Base
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 Eluceo\iCal\Component\Calendar $vCalendar Calendar object
* @return Eluceo\iCal\Component\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 Eluceo\iCal\Component\Calendar $vCalendar Calendar object
* @return Eluceo\iCal\Component\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 Eluceo\iCal\Component\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->getCurrentBaseUrl().$this->helper->url('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
if (! empty($task['creator_id'])) {
$vEvent->setOrganizer('MAILTO:'.($task['creator_email'] ?: $task['creator_username'].'@kanboard.local'));
}
if (! empty($task['owner_id'])) {
$attendees = new Attendees;
$attendees->add('MAILTO:'.($task['creator_email'] ?: $task['creator_username'].'@kanboard.local'));
$vEvent->setAttendees($attendees);
}
return $vEvent;
}
}

View File

@@ -8,8 +8,8 @@
<ul class="no-bullet">
<li><strong><i class="fa fa-share-alt"></i> <?= $this->a(t('Public link'), 'board', 'readonly', array('token' => $project['token']), false, '', '', true) ?></strong></li>
<li><strong><i class="fa fa-rss-square"></i> <?= $this->a(t('RSS feed'), 'project', 'feed', array('token' => $project['token']), false, '', '', true) ?></strong></li>
<li><strong><i class="fa fa-calendar"></i> <?= $this->a(t('iCalendar (iCal format, *.ics)'), 'ical', 'project', array('token' => $project['token']), false, '', '', true) ?></strong></li>
</ul>
<input type="text" class="auto-select" readonly="readonly" value="<?= $this->getCurrentBaseUrl().$this->u('board', 'readonly', array('token' => $project['token'])) ?>"/>
</div>
<?= $this->a(t('Disable public access'), 'project', 'share', array('project_id' => $project['id'], 'switch' => 'disable'), true, 'btn btn-red') ?>