From b4bcf723e5fd1dbddd06f4861aa4ba43ec2cfc5b Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Mon, 2 Feb 2015 13:12:56 +0100 Subject: [PATCH] Display recurrence information from iTip invitations (#4446) --- plugins/calendar/calendar.php | 7 +++ plugins/calendar/drivers/calendar_driver.php | 55 +++++++++++++++++++ .../calendar/drivers/kolab/kolab_calendar.php | 8 +-- .../calendar/drivers/kolab/kolab_driver.php | 24 +++++++- .../lib/libcalendaring_itip.php | 23 +++++--- plugins/libcalendaring/localization/en_US.inc | 1 + 6 files changed, 104 insertions(+), 14 deletions(-) diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php index 64dc2ced..98bdac43 100644 --- a/plugins/calendar/calendar.php +++ b/plugins/calendar/calendar.php @@ -1227,6 +1227,13 @@ class calendar extends rcube_plugin $event['_part'] = $mime_id; $events[] = $this->_client_event($event, true); + + // add recurring instances + if (!empty($event['recurrence'])) { + foreach ($this->driver->get_recurring_events($event, $event['start']) as $recurring) { + $events[] = $this->_client_event($recurring, true); + } + } } return $events; diff --git a/plugins/calendar/drivers/calendar_driver.php b/plugins/calendar/drivers/calendar_driver.php index 4ec53c4b..24e7a2e8 100644 --- a/plugins/calendar/drivers/calendar_driver.php +++ b/plugins/calendar/drivers/calendar_driver.php @@ -431,6 +431,61 @@ abstract class calendar_driver return false; } + /** + * Create instances of a recurring event + * + * @param array Hash array with event properties + * @param object DateTime Start date of the recurrence window + * @param object DateTime End date of the recurrence window + * @return array List of recurring event instances + */ + public function get_recurring_events($event, $start, $end = null) + { + $events = array(); + + if ($event['recurrence']) { + // include library class + require_once(dirname(__FILE__) . '/../lib/calendar_recurrence.php'); + + $rcmail = rcmail::get_instance(); + $recurrence = new calendar_recurrence($rcmail->plugins->get_plugin('calendar'), $event); + + // determine a reasonable end date if none given + if (!$end) { + switch ($event['recurrence']['FREQ']) { + case 'YEARLY': $intvl = 'P100Y'; break; + case 'MONTHLY': $intvl = 'P20Y'; break; + default: $intvl = 'P10Y'; break; + } + + $end = clone $event['start']; + $end->add(new DateInterval($intvl)); + } + + $i = 0; + while ($next_event = $recurrence->next_instance()) { + $next_event['uid'] = $event['uid'] . '-' . ++$i; + // add to output if in range + if (($next_event['start'] <= $end && $next_event['end'] >= $start)) { + $next_event['id'] = $next_event['uid']; + $next_event['recurrence_id'] = $event['uid']; + $next_event['_instance'] = $i; + $events[] = $next_event; + } + else if ($next_event['start'] > $end) { // stop loop if out of range + break; + } + + // avoid endless recursion loops + if ($i > 1000) { + break; + } + } + } + + return $events; + } + /** * Provide a list of revisions for the given event * diff --git a/plugins/calendar/drivers/kolab/kolab_calendar.php b/plugins/calendar/drivers/kolab/kolab_calendar.php index 850a12f6..d10126a6 100644 --- a/plugins/calendar/drivers/kolab/kolab_calendar.php +++ b/plugins/calendar/drivers/kolab/kolab_calendar.php @@ -196,7 +196,7 @@ class kolab_calendar extends kolab_storage_folder_api $this->events[$master_id] = $this->_to_rcube_event($record); if (($master = $this->events[$master_id]) && $master['recurrence']) { - $this->_get_recurring_events($record, $master['start'], null, $id); + $this->get_recurring_events($record, $master['start'], null, $id); } } @@ -323,7 +323,7 @@ class kolab_calendar extends kolab_storage_folder_api // resolve recurring events if ($record['recurrence'] && $virtual == 1) { - $events = array_merge($events, $this->_get_recurring_events($record, $start, $end)); + $events = array_merge($events, $this->get_recurring_events($record, $start, $end)); } } @@ -455,7 +455,7 @@ class kolab_calendar extends kolab_storage_folder_api // refresh local cache with recurring instances if ($exception_id) { - $this->_get_recurring_events($object, $event['start'], $event['end'], $exception_id); + $this->get_recurring_events($object, $event['start'], $event['end'], $exception_id); } } @@ -538,7 +538,7 @@ class kolab_calendar extends kolab_storage_folder_api * @param string ID of a specific recurring event instance * @return array List of recurring event instances */ - public function _get_recurring_events($event, $start, $end = null, $event_id = null) + public function get_recurring_events($event, $start, $end = null, $event_id = null) { $object = $event['_formatobj']; if (!$object) { diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php index 8a484055..b53ffbf2 100644 --- a/plugins/calendar/drivers/kolab/kolab_driver.php +++ b/plugins/calendar/drivers/kolab/kolab_driver.php @@ -704,7 +704,7 @@ class kolab_driver extends calendar_driver // removing the first instance => just move to next occurence if ($master['id'] == $event['id']) { - $recurring = reset($storage->_get_recurring_events($event, $event['start'], null, $event['id'].'-1')); + $recurring = reset($storage->get_recurring_events($event, $event['start'], null, $event['id'].'-1')); // no future instances found: delete the master event (bug #1677) if (!$recurring['start']) { @@ -1245,6 +1245,28 @@ class kolab_driver extends calendar_driver return $this->rc->config->get('calendar_categories', $this->default_categories); } + /** + * Create instances of a recurring event + * + * @param array Hash array with event properties + * @param object DateTime Start date of the recurrence window + * @param object DateTime End date of the recurrence window + * @return array List of recurring event instances + */ + public function get_recurring_events($event, $start, $end = null) + { + // load the given event data into a libkolabxml container + if (!$event['_formatobj']) { + $event_xml = new kolab_format_event(); + $event_xml->set($event); + $event['_formatobj'] = $event_xml; + } + + $this->_read_calendars(); + $storage = reset($this->calendars); + return $storage->get_recurring_events($event, $start, $end); + } + /** * Fetch free/busy information from a person within the given range */ diff --git a/plugins/libcalendaring/lib/libcalendaring_itip.php b/plugins/libcalendaring/lib/libcalendaring_itip.php index 06853379..3eead6fa 100644 --- a/plugins/libcalendaring/lib/libcalendaring_itip.php +++ b/plugins/libcalendaring/lib/libcalendaring_itip.php @@ -125,7 +125,8 @@ class libcalendaring_itip 'name' => $bodytext, 'vars' => array( 'title' => $event['title'], - 'date' => $this->lib->event_date_text($event, true), + 'date' => $this->lib->event_date_text($event, true) . + (empty($event['recurrence']) ? '' : sprintf("\n%s: %s", $this->gettext('recurring'), $this->lib->recurrence_text($event['recurrence']))), 'attendees' => join(",\n ", $attendees_list), 'sender' => $this->sender['name'], 'organizer' => $this->sender['name'], @@ -697,27 +698,31 @@ class libcalendaring_itip $table->add('ititle', $title); $table->add('title', Q($event['title'])); if ($event['start'] && $event['end']) { - $table->add('label', $this->plugin->gettext('date'), $this->domain); + $table->add('label', $this->gettext('date')); $table->add('date', Q($this->lib->event_date_text($event))); } else if ($event['due'] && $event['_type'] == 'task') { - $table->add('label', $this->plugin->gettext('date'), $this->domain); + $table->add('label', $this->gettext('date')); $table->add('date', Q($this->lib->event_date_text($event))); } + if (!empty($event['recurrence'])) { + $table->add('label', $this->gettext('recurring')); + $table->add('recurrence', $this->lib->recurrence_text($event['recurrence'])); + } if ($event['location']) { - $table->add('label', $this->plugin->gettext('location'), $this->domain); + $table->add('label', $this->gettext('location')); $table->add('location', Q($event['location'])); } if ($event['sensitivity'] && $event['sensitivity'] != 'public') { - $table->add('label', $this->plugin->gettext('sensitivity'), $this->domain); - $table->add('sensitivity', ucfirst($this->plugin->gettext($event['sensitivity'], $this->domain)) . '!'); + $table->add('label', $this->gettext('sensitivity')); + $table->add('sensitivity', ucfirst($this->gettext($event['sensitivity'])) . '!'); } if ($event['status'] == 'COMPLETED' || $event['status'] == 'CANCELLED') { - $table->add('label', $this->plugin->gettext('status'), $this->domain); - $table->add('status', $this->plugin->gettext('status-' . strtolower($event['status']), $this->domain)); + $table->add('label', $this->gettext('status')); + $table->add('status', $this->gettext('status-' . strtolower($event['status']))); } if ($event['comment']) { - $table->add('label', $this->plugin->gettext('comment'), $this->domain); + $table->add('label', $this->gettext('comment')); $table->add('location', Q($event['comment'])); } diff --git a/plugins/libcalendaring/localization/en_US.inc b/plugins/libcalendaring/localization/en_US.inc index 2542c47c..3e49838b 100644 --- a/plugins/libcalendaring/localization/en_US.inc +++ b/plugins/libcalendaring/localization/en_US.inc @@ -46,6 +46,7 @@ $labels['repeatinweek'] = 'Repeat in a week'; $labels['showmore'] = 'Show more...'; // recurrence related labels +$labels['recurring'] = 'Repeats'; $labels['frequency'] = 'Repeat'; $labels['never'] = 'never'; $labels['daily'] = 'daily';