Move recurrence computation to a dedicated class

This commit is contained in:
Thomas 2011-10-14 11:58:42 +02:00
parent 93388585c4
commit e4917cf0aa
3 changed files with 100 additions and 30 deletions

View file

@ -224,7 +224,7 @@ class database_driver extends calendar_driver
$event['calendar'],
strval($event['uid']),
intval($event['all_day']),
$event['recurrence'],
$event['_recurrence'],
strval($event['title']),
strval($event['description']),
strval($event['location']),
@ -365,8 +365,7 @@ class database_driver extends calendar_driver
{
// compose vcalendar-style recurrencue rule from structured data
$rrule = $event['recurrence'] ? calendar::to_rrule($event['recurrence']) : '';
$event['_exdates'] = (array)$event['recurrence']['EXDATE'];
$event['recurrence'] = rtrim($rrule, ';');
$event['_recurrence'] = rtrim($rrule, ';');
$event['free_busy'] = intval($this->free_busy_map[strtolower($event['free_busy'])]);
if (isset($event['allday'])) {
@ -435,12 +434,15 @@ class database_driver extends calendar_driver
{
$event = $this->_save_preprocess($event);
$sql_set = array();
$set_cols = array('all_day', 'recurrence', 'recurrence_id', 'title', 'description', 'location', 'categories', 'free_busy', 'priority', 'sensitivity', 'attendees', 'alarms', 'notifyat');
$set_cols = array('all_day', 'recurrence_id', 'title', 'description', 'location', 'categories', 'free_busy', 'priority', 'sensitivity', 'attendees', 'alarms', 'notifyat');
foreach ($set_cols as $col) {
if (isset($event[$col]))
$sql_set[] = $this->rc->db->quote_identifier($col) . '=' . $this->rc->db->quote($event[$col]);
}
if ($event['_recurrence'])
$sql_set[] = $this->rc->db->quote_identifier('recurrence') . '=' . $this->rc->db->quote($event['_recurrence']);
if ($event['_fromcalendar'] && $event['_fromcalendar'] != $event['calendar'])
$sql_set[] = 'calendar_id=' . $this->rc->db->quote($event['calendar']);
@ -501,17 +503,13 @@ class database_driver extends calendar_driver
// create new fake entries
if ($event['recurrence']) {
// TODO: replace Horde classes with something that has less than 6'000 lines of code
$recurrence = new Horde_Date_Recurrence($event['start']);
$recurrence->fromRRule20($event['recurrence']);
// include library class
require_once($this->cal->home . '/lib/calendar_recurrence.php');
foreach ((array)$event['_exdates'] as $exdate)
$recurrence->addException(date('Y', $exdate), date('n', $exdate), date('j', $exdate));
$recurrence = new calendar_recurrence($this->cal, $event);
$duration = $event['end'] - $event['start'];
$next = new Horde_Date($event['start']);
while ($next = $recurrence->nextActiveRecurrence(array('year' => $next->year, 'month' => $next->month, 'mday' => $next->mday + 1, 'hour' => $next->hour, 'min' => $next->min, 'sec' => $next->sec))) {
$next_ts = $next->timestamp();
while ($next_ts = $recurrence->next_start()) {
$notify_at = $this->_get_notification(array('alarms' => $event['alarms'], 'start' => $next_ts, 'end' => $next_ts + $duration));
$query = $this->rc->db->query(sprintf(
"INSERT INTO " . $this->db_events . "

View file

@ -434,29 +434,15 @@ class kolab_calendar
*/
public function _get_recurring_events($event, $start, $end, $event_id = null)
{
// use Horde classes to compute recurring instances
require_once($this->cal->home . '/lib/Horde_Date_Recurrence.php');
// include library class
require_once($this->cal->home . '/lib/calendar_recurrence.php');
$recurrence = new Horde_Date_Recurrence($event['start']);
$recurrence->fromRRule20(calendar::to_rrule($event['recurrence']));
foreach ((array)$event['recurrence']['EXDATE'] as $exdate)
$recurrence->addException(date('Y', $exdate), date('n', $exdate), date('j', $exdate));
$recurrence = new calendar_recurrence($this->cal, $event);
$events = array();
$duration = $event['end'] - $event['start'];
$tz_offset = $event['allday'] ? $this->cal->gmt_offset - date('Z') : 0;
$next = new Horde_Date($event['start'] + $tz_offset); # shift all-day times to server timezone because computation operates in local TZ
$dst_start = $next->format('I');
$hour = $next->hour;
$i = 0;
while ($next = $recurrence->nextActiveRecurrence(array('year' => $next->year, 'month' => $next->month, 'mday' => $next->mday + 1, 'hour' => $next->hour, 'min' => $next->min, 'sec' => $next->sec))) {
if ($event['allday']) {
$next->hour = $hour; # fix time for all-day events
$next->min = 0;
}
$dst_diff = ($dst_start - $next->format('I')) * 3600; # consider difference in daylight saving between base event and recurring instance
$rec_start = $next->timestamp() - $tz_offset - $dst_diff;
while ($rec_start = $recurrence->next_start()) {
$rec_end = $rec_start + $duration;
$rec_id = $event['id'] . '-' . ++$i;

View file

@ -0,0 +1,86 @@
<?php
/**
* Recurrence computation class for the Calendar plugin
*
* Uitility class to compute instances of recurring events.
*
* @version 0.7-beta
* @author Thomas Bruederli <roundcube@gmail.com>
* @package calendar
*
* Copyright (C) 2011, Kolab Systems AG
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
class calendar_recurrence
{
private $cal;
private $event;
private $engine;
private $tz_offset = 0;
private $dst_start = false;
private $hour = 0;
/**
* Default constructor
*
* @param object calendar The calendar plugin instance
* @param array The event object to operate on
*/
function __construct($cal, $event)
{
$this->cal = $cal;
// use Horde classes to compute recurring instances
// TODO: replace with something that has less than 6'000 lines of code
require_once($this->cal->home . '/lib/Horde_Date_Recurrence.php');
$this->event = $event;
$this->engine = new Horde_Date_Recurrence($event['start']);
$this->engine->fromRRule20(calendar::to_rrule($event['recurrence']));
if (is_array($event['recurrence']['EXDATE'])) {
foreach ($event['recurrence']['EXDATE'] as $exdate)
$this->engine->addException(date('Y', $exdate), date('n', $exdate), date('j', $exdate));
}
$this->tz_offset = $event['allday'] ? $this->cal->gmt_offset - date('Z') : 0;
$this->next = new Horde_Date($event['start'] + $this->tz_offset); # shift all-day times to server timezone because computation operates in local TZ
$this->dst_start = $this->next->format('I');
$this->hour = $this->next->hour;
}
/**
* Get timestamp of the next occurence of this event
*
* @return mixed Unix timestamp or False if recurrence ended
*/
public function next_start()
{
$time = false;
if ($this->next && ($next = $this->engine->nextActiveRecurrence(array('year' => $this->next->year, 'month' => $this->next->month, 'mday' => $this->next->mday + 1, 'hour' => $this->next->hour, 'min' => $this->next->min, 'sec' => $this->next->sec)))) {
if ($this->event['allday']) {
$next->hour = $this->hour; # fix time for all-day events
$next->min = 0;
}
# $dst_diff = ($this->dst_start - $next->format('I')) * 3600; # consider difference in daylight saving between base event and recurring instance
$time = $next->timestamp() - $this->tz_offset;
$this->next = $next;
}
return $time;
}
}