diff --git a/plugins/calendar/lib/calendar_recurrence.php b/plugins/calendar/lib/calendar_recurrence.php index 151f5c75..b31026a8 100644 --- a/plugins/calendar/lib/calendar_recurrence.php +++ b/plugins/calendar/lib/calendar_recurrence.php @@ -1,15 +1,15 @@ - * @package @package_name@ * - * Copyright (C) 2012, Kolab Systems AG + * Copyright (C) 2012-2014, Kolab Systems AG * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -24,14 +24,10 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -class calendar_recurrence +class calendar_recurrence extends libcalendaring_recurrence { - private $cal; private $event; - private $next; - private $engine; private $duration; - private $hour = 0; /** * Default constructor @@ -41,62 +37,15 @@ class calendar_recurrence */ function __construct($cal, $event) { - // use Horde classes to compute recurring instances - // TODO: replace with something that has less than 6'000 lines of code - require_once(__DIR__ . '/Horde_Date_Recurrence.php'); + parent::__construct($cal->lib); - $this->cal = $cal; $this->event = $event; - $this->next = new Horde_Date($event['start'], $cal->timezone->getName()); - $this->hour = $this->next->hour; if (is_object($event['start']) && is_object($event['end'])) $this->duration = $event['start']->diff($event['end']); - $this->engine = new Horde_Date_Recurrence($event['start']); - $this->engine->fromRRule20(libcalendaring::to_rrule($event['recurrence'])); - - if (is_array($event['recurrence']['EXDATE'])) { - foreach ($event['recurrence']['EXDATE'] as $exdate) { - if (is_a($exdate, 'DateTime')) { - $this->engine->addException($exdate->format('Y'), $exdate->format('n'), $exdate->format('j')); - } - } - } - if (is_array($event['recurrence']['RDATE'])) { - foreach ($event['recurrence']['RDATE'] as $rdate) { - if (is_a($rdate, 'DateTime')) { - $this->engine->addRDate($rdate->format('Y'), $rdate->format('n'), $rdate->format('j')); - } - } - } - } - - /** - * Get date/time of the next occurence of this event - * - * @return mixed DateTime object or False if recurrence ended - */ - public function next_start() - { - $time = false; - $after = clone $this->next; - $after->mday = $after->mday + 1; - if ($this->next && ($next = $this->engine->nextActiveRecurrence($after))) { - if (!$next->after($this->next)) { - // avoid endless loops if recurrence computation fails - return false; - } - if ($this->event['allday']) { - $next->hour = $this->hour; # fix time for all-day events - $next->min = 0; - } - - $time = $next->toDateTime(); - $this->next = $next; - } - - return $time; + $event['start']->_dateonly |= $event['allday']; + $this->init($event['recurrence'], $event['start']); } /** @@ -107,13 +56,15 @@ class calendar_recurrence public function next_instance() { if ($next_start = $this->next_start()) { - $next_end = clone $next_start; - $next_end->add($this->duration); - $next = $this->event; $next['recurrence_id'] = $next_start->format('Y-m-d'); $next['start'] = $next_start; - $next['end'] = $next_end; + + if ($this->duration) { + $next['end'] = clone $next_start; + $next['end']->add($this->duration); + } + unset($next['_formatobj']); return $next; diff --git a/plugins/calendar/package.xml b/plugins/calendar/package.xml index d8f1e628..5501ac75 100644 --- a/plugins/calendar/package.xml +++ b/plugins/calendar/package.xml @@ -56,17 +56,10 @@ - - - - - - - @@ -75,10 +68,6 @@ - - - - diff --git a/plugins/calendar/lib/Horde_Date.php b/plugins/libcalendaring/lib/Horde_Date.php similarity index 100% rename from plugins/calendar/lib/Horde_Date.php rename to plugins/libcalendaring/lib/Horde_Date.php diff --git a/plugins/calendar/lib/Horde_Date_Recurrence.php b/plugins/libcalendaring/lib/Horde_Date_Recurrence.php similarity index 100% rename from plugins/calendar/lib/Horde_Date_Recurrence.php rename to plugins/libcalendaring/lib/Horde_Date_Recurrence.php diff --git a/plugins/libcalendaring/lib/libcalendaring_recurrence.php b/plugins/libcalendaring/lib/libcalendaring_recurrence.php new file mode 100644 index 00000000..3423ae75 --- /dev/null +++ b/plugins/libcalendaring/lib/libcalendaring_recurrence.php @@ -0,0 +1,143 @@ + + * + * Copyright (C) 2012-2014, Kolab Systems AG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +class libcalendaring_recurrence +{ + protected $lib; + protected $start; + protected $next; + protected $engine; + protected $recurrence; + protected $dateonly = false; + protected $hour = 0; + + /** + * Default constructor + * + * @param object calendar The calendar plugin instance + */ + function __construct($lib) + { + // use Horde classes to compute recurring instances + // TODO: replace with something that has less than 6'000 lines of code + require_once(__DIR__ . '/Horde_Date_Recurrence.php'); + + $this->lib = $lib; + } + + /** + * Initialize recurrence engine + * + * @param array The recurrence properties + * @param object DateTime The recurrence start date + */ + public function init($recurrence, $start) + { + $this->start = $start; + $this->recurrence = $recurrence; + $this->dateonly = $start->_dateonly; + $this->next = new Horde_Date($start, $this->lib->timezone->getName()); + $this->hour = $this->next->hour; + + $this->engine = new Horde_Date_Recurrence($start); + $this->engine->fromRRule20(libcalendaring::to_rrule($recurrence)); + + if (is_array($recurrence['EXDATE'])) { + foreach ($recurrence['EXDATE'] as $exdate) { + if (is_a($exdate, 'DateTime')) { + $this->engine->addException($exdate->format('Y'), $exdate->format('n'), $exdate->format('j')); + } + } + } + if (is_array($recurrence['RDATE'])) { + foreach ($recurrence['RDATE'] as $rdate) { + if (is_a($rdate, 'DateTime')) { + $this->engine->addRDate($rdate->format('Y'), $rdate->format('n'), $rdate->format('j')); + } + } + } + } + + /** + * Get date/time of the next occurence of this event + * + * @return mixed DateTime object or False if recurrence ended + */ + public function next_start() + { + $time = false; + $after = clone $this->next; + $after->mday = $after->mday + 1; + if ($this->next && ($next = $this->engine->nextActiveRecurrence($after))) { + // avoid endless loops if recurrence computation fails + if (!$next->after($this->next)) { + return false; + } + // fix time for all-day events + if ($this->dateonly) { + $next->hour = $this->hour; + $next->min = 0; + } + + $time = $next->toDateTime(); + $this->next = $next; + } + + return $time; + } + + /** + * Get the end date of the occurence of this recurrence cycle + * + * @return DateTime|bool End datetime of the last occurence or False if recurrence exceeds limit + */ + public function end() + { + // recurrence end date is given + if ($this->recurrence['UNTIL'] instanceof DateTime) { + return $this->recurrence['UNTIL']; + } + + // take the last RDATE entry if set + if (is_array($this->recurrence['RDATE']) && !empty($this->recurrence['RDATE'])) { + $last = end($this->recurrence['RDATE']); + if ($last instanceof DateTime) { + return $last; + } + } + + // run through all items till we reach the end + if ($this->recurrence['COUNT']) { + $last = $this->start; + $this->next = new Horde_Date($this->start, $this->lib->timezone->getName()); + while (($next = $this->next_start()) && $c < 1000) { + $last = $next; + $c++; + } + } + + return $last; + } + +} diff --git a/plugins/libcalendaring/libcalendaring.php b/plugins/libcalendaring/libcalendaring.php index 627fbefd..1b680a56 100644 --- a/plugins/libcalendaring/libcalendaring.php +++ b/plugins/libcalendaring/libcalendaring.php @@ -124,6 +124,16 @@ class libcalendaring extends rcube_plugin return new libcalendaring_itip($self, $domain); } + /** + * Load recurrence computation engine + */ + public static function get_recurrence() + { + $self = self::get_instance(); + require_once($self->home . '/lib/libcalendaring_recurrence.php'); + return new libcalendaring_recurrence($self); + } + /** * Shift dates into user's current timezone *