From 31ad93a62e93cf12fbe7dcc885e077026d7655f4 Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Thu, 26 Feb 2015 11:25:29 +0100 Subject: [PATCH] Provide access to exception instances outside of a recurring event context (#4722) --- plugins/libkolab/lib/kolab_format_event.php | 69 ++++++++++++++++--- plugins/libkolab/lib/kolab_format_xcal.php | 8 +-- .../lib/kolab_storage_cache_event.php | 18 +++++ 3 files changed, 83 insertions(+), 12 deletions(-) diff --git a/plugins/libkolab/lib/kolab_format_event.php b/plugins/libkolab/lib/kolab_format_event.php index 91efb26f..3207927b 100644 --- a/plugins/libkolab/lib/kolab_format_event.php +++ b/plugins/libkolab/lib/kolab_format_event.php @@ -96,11 +96,15 @@ class kolab_format_event extends kolab_format_xcal $status = $this->status_map[$object['status']]; $this->obj->setStatus($status); - // save recurrence exceptions - if (is_array($object['recurrence']) && is_array($object['recurrence']['EXCEPTIONS'])) { + // save (recurrence) exceptions + if (is_array($object['recurrence']) && is_array($object['recurrence']['EXCEPTIONS']) && !isset($object['exceptions'])) { + $object['exceptions'] = $object['recurrence']['EXCEPTIONS']; + } + + if (is_array($object['exceptions'])) { $recurrence_id_format = $object['allday'] ? 'Ymd' : 'Ymd\THis'; $vexceptions = new vectorevent; - foreach((array)$object['recurrence']['EXCEPTIONS'] as $i => $exception) { + foreach ($object['exceptions'] as $i => $exception) { $exevent = new kolab_format_event; $exevent->set(($compacted = $this->compact_exception($exception, $object))); // only save differing values @@ -118,11 +122,17 @@ class kolab_format_event extends kolab_format_xcal $vexceptions->push($exevent->obj); // write cleaned-up exception data back to memory/cache - $object['recurrence']['EXCEPTIONS'][$i] = $this->expand_exception($exevent->data, $object); + $object['exceptions'][$i] = $this->expand_exception($exevent->data, $object); } $this->obj->setExceptions($vexceptions); + + // link with recurrence.EXCEPTIONS for compatibility + if (is_array($object['recurrence'])) { + $object['recurrence']['EXCEPTIONS'] = &$object['exceptions']; + } } - else if ($object['recurrence_date'] && $object['recurrence_date'] instanceof DateTime) { + + if ($object['recurrence_date'] && $object['recurrence_date'] instanceof DateTime) { $this->obj->setRecurrenceID(self::get_datetime($object['recurrence_date'], null, $object['allday']), (bool)$object['thisandfuture']); } @@ -194,7 +204,7 @@ class kolab_format_event extends kolab_format_xcal $object['recurrence_date'] = self::php_datetime($this->obj->recurrenceID()); } // read exception event objects - else if (($exceptions = $this->obj->exceptions()) && is_object($exceptions) && $exceptions->size()) { + if (($exceptions = $this->obj->exceptions()) && is_object($exceptions) && $exceptions->size()) { $recurrence_exceptions = array(); $recurrence_id_format = $object['allday'] ? 'Ymd' : 'Ymd\THis'; for ($i=0; $i < $exceptions->size(); $i++) { @@ -215,12 +225,53 @@ class kolab_format_event extends kolab_format_xcal } } } - $object['recurrence']['EXCEPTIONS'] = $recurrence_exceptions; + $object['exceptions'] = $recurrence_exceptions; + + // also link with recurrence.EXCEPTIONS for compatibility + if (is_array($object['recurrence'])) { + $object['recurrence']['EXCEPTIONS'] = &$object['exceptions']; + } } return $this->data = $object; } + /** + * Getter for a single instance from a recurrence series or stored subcomponents + * + * @param mixed The recurrence-id of the requested instance, either as string or a DateTime object + * @return array Event data as hash array or null if not found + */ + public function get_instance($recurrence_id) + { + $result = null; + $object = $this->to_array(); + + $recurrence_id_format = $object['allday'] ? 'Ymd' : 'Ymd\THis'; + $instance_id = $recurrence_id instanceof DateTime ? $recurrence_id->format($recurrence_id_format) : strval($recurrence_id); + + if ($object['recurrence_date'] instanceof DateTime) { + if ($object['recurrence_date']->format($recurrence_id_format) == $instance_id) { + $result = $object; + } + } + + if (!$result && is_array($object['exceptions'])) { + foreach ($object['exceptions'] as $exception) { + if ($exception['_instance'] == $instance_id) { + $result = $exception; + $result['isexception'] = 1; + break; + } + } + } + + // TODO: compute instances from recurrence rule and return the matching instance + // clone from plugins/calendar/drivers/kolab/kolab_calendar::get_recurring_events() + + return $result; + } + /** * Callback for kolab_storage_cache to get object specific tags to cache * @@ -262,8 +313,10 @@ class kolab_format_event extends kolab_format_xcal */ private function expand_exception($exception, $master) { + $is_recurring = !empty($master['recurrence']); + foreach ($master as $prop => $value) { - if (empty($exception[$prop]) && !empty($value)) { + if (empty($exception[$prop]) && !empty($value) && ($is_recurring || in_array($prop, array('uid','organizer','_attachments')))) { $exception[$prop] = $value; if ($prop == 'recurrence') { unset($exception[$prop]['EXCEPTIONS']); diff --git a/plugins/libkolab/lib/kolab_format_xcal.php b/plugins/libkolab/lib/kolab_format_xcal.php index 3a9ad1f6..605d557a 100644 --- a/plugins/libkolab/lib/kolab_format_xcal.php +++ b/plugins/libkolab/lib/kolab_format_xcal.php @@ -597,8 +597,8 @@ abstract class kolab_format_xcal extends kolab_format $words = rcube_utils::normalize_string($data, true); // collect words from recurrence exceptions - if (is_array($object['recurrence']) && $object['recurrence']['EXCEPTIONS']) { - foreach((array)$object['recurrence']['EXCEPTIONS'] as $exception) { + if (is_array($object['exceptions'])) { + foreach ($object['exceptions'] as $exception) { $words = array_merge($words, $this->get_words($exception)); } } @@ -629,8 +629,8 @@ abstract class kolab_format_xcal extends kolab_format } // collect tags from recurrence exceptions - if (is_array($object['recurrence']) && $object['recurrence']['EXCEPTIONS']) { - foreach((array)$object['recurrence']['EXCEPTIONS'] as $exception) { + if (is_array($object['exceptions'])) { + foreach ($object['exceptions'] as $exception) { $tags = array_merge($tags, $this->get_tags($exception)); } } diff --git a/plugins/libkolab/lib/kolab_storage_cache_event.php b/plugins/libkolab/lib/kolab_storage_cache_event.php index 5fc44cd7..e25bd899 100644 --- a/plugins/libkolab/lib/kolab_storage_cache_event.php +++ b/plugins/libkolab/lib/kolab_storage_cache_event.php @@ -44,6 +44,24 @@ class kolab_storage_cache_event extends kolab_storage_cache $sql_data['dtend'] = $dtend->format(self::DB_DATE_FORMAT); } + // extend start/end dates to spawn all exceptions + if (is_array($object['exceptions'])) { + foreach ($object['exceptions'] as $exception) { + if (is_a($exception['start'], 'DateTime')) { + $exstart = $exception['start']->format(self::DB_DATE_FORMAT); + if ($exstart < $sql_data['dtstart']) { + $sql_data['dtstart'] = $exstart; + } + } + if (is_a($exception['end'], 'DateTime')) { + $exend = $exception['end']->format(self::DB_DATE_FORMAT); + if ($exend > $sql_data['dtend']) { + $sql_data['dtend'] = $exend; + } + } + } + } + return $sql_data; } } \ No newline at end of file