From c4f4f52aa0bde9dcec7aec5d47b2a76d914c1e3b Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 30 Dec 2016 05:33:02 -0500 Subject: [PATCH] T1841: Support non-all-day exceptions to all-day events and vice-versa --- .../calendar/drivers/database/database_driver.php | 2 +- plugins/calendar/drivers/kolab/kolab_calendar.php | 14 +++++++------- plugins/calendar/drivers/kolab/kolab_driver.php | 2 +- plugins/calendar/lib/calendar_recurrence.php | 2 +- plugins/libcalendaring/libcalendaring.php | 15 ++++++++++----- plugins/libkolab/lib/kolab_format_event.php | 6 ++++-- 6 files changed, 24 insertions(+), 17 deletions(-) diff --git a/plugins/calendar/drivers/database/database_driver.php b/plugins/calendar/drivers/database/database_driver.php index 84b70f22..3930b96d 100644 --- a/plugins/calendar/drivers/database/database_driver.php +++ b/plugins/calendar/drivers/database/database_driver.php @@ -391,7 +391,7 @@ class database_driver extends calendar_driver if ($event['id'] == $master['id']) { $event += $old; $event['recurrence_id'] = $master['id']; - $event['_instance'] = libcalendaring::recurrence_instance_identifier($old); + $event['_instance'] = libcalendaring::recurrence_instance_identifier($old, $master['allday']); $event['isexception'] = 1; $event_id = $this->_insert_event($event); return $event_id; diff --git a/plugins/calendar/drivers/kolab/kolab_calendar.php b/plugins/calendar/drivers/kolab/kolab_calendar.php index fe2ede53..f8733d52 100644 --- a/plugins/calendar/drivers/kolab/kolab_calendar.php +++ b/plugins/calendar/drivers/kolab/kolab_calendar.php @@ -207,7 +207,7 @@ class kolab_calendar extends kolab_storage_folder_api if ($master) { // check for match in top-level exceptions (aka loose single occurrences) if ($master['_formatobj'] && ($instance = $master['_formatobj']->get_instance($instance_id))) { - $this->events[$id] = $this->_to_driver_event($instance); + $this->events[$id] = $this->_to_driver_event($instance, false, true, $master); } // check for match on the first instance already else if ($master['_instance'] && $master['_instance'] == $instance_id) { @@ -354,7 +354,7 @@ class kolab_calendar extends kolab_storage_folder_api // add top-level exceptions (aka loose single occurrences) else if (is_array($record['exceptions'])) { foreach ($record['exceptions'] as $ex) { - $component = $this->_to_driver_event($ex, false, false); + $component = $this->_to_driver_event($ex, false, false, $record); if ($component['start'] <= $end && $component['end'] >= $start) { $events[] = $component; } @@ -629,9 +629,9 @@ class kolab_calendar extends kolab_storage_folder_api if (is_array($event['recurrence']['EXCEPTIONS'])) { foreach ($event['recurrence']['EXCEPTIONS'] as $exception) { if (!$exception['_instance']) - $exception['_instance'] = libcalendaring::recurrence_instance_identifier($exception); + $exception['_instance'] = libcalendaring::recurrence_instance_identifier($exception, $event['allday']); - $rec_event = $this->_to_driver_event($exception, false, false); + $rec_event = $this->_to_driver_event($exception, false, false, $event); $rec_event['id'] = $event['uid'] . '-' . $exception['_instance']; $rec_event['isexception'] = 1; @@ -692,7 +692,7 @@ class kolab_calendar extends kolab_storage_folder_api // add to output if in range if (($event_start <= $end && $event_end >= $start) || ($event_id && $rec_id == $event_id)) { - $rec_event = $this->_to_driver_event($next_event, false, false); + $rec_event = $this->_to_driver_event($next_event, false, false, $event); $rec_event['_instance'] = $instance_id; $rec_event['_count'] = $i + 1; @@ -724,7 +724,7 @@ class kolab_calendar extends kolab_storage_folder_api /** * Convert from Kolab_Format to internal representation */ - private function _to_driver_event($record, $noinst = false, $links = true) + private function _to_driver_event($record, $noinst = false, $links = true, $master_event = null) { $record['calendar'] = $this->id; @@ -738,7 +738,7 @@ class kolab_calendar extends kolab_storage_folder_api } // add instance identifier to first occurrence (master event) - $recurrence_id_format = libcalendaring::recurrence_id_format($record); + $recurrence_id_format = libcalendaring::recurrence_id_format($master_event ? $master_event : $record); if (!$noinst && $record['recurrence'] && !$record['recurrence_id'] && !$record['_instance']) { $record['_instance'] = $record['start']->format($recurrence_id_format); } diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php index ec60b509..813ed036 100644 --- a/plugins/calendar/drivers/kolab/kolab_driver.php +++ b/plugins/calendar/drivers/kolab/kolab_driver.php @@ -1361,7 +1361,7 @@ class kolab_driver extends calendar_driver } if (!$event['_instance'] && is_a($event['recurrence_date'], 'DateTime')) { - $event['_instance'] = libcalendaring::recurrence_instance_identifier($event); + $event['_instance'] = libcalendaring::recurrence_instance_identifier($event, $master['allday']); } if (!is_array($master['exceptions']) && is_array($master['recurrence']['EXCEPTIONS'])) { diff --git a/plugins/calendar/lib/calendar_recurrence.php b/plugins/calendar/lib/calendar_recurrence.php index 44d5b08f..c0d7c793 100644 --- a/plugins/calendar/lib/calendar_recurrence.php +++ b/plugins/calendar/lib/calendar_recurrence.php @@ -75,7 +75,7 @@ class calendar_recurrence extends libcalendaring_recurrence } $next['recurrence_date'] = clone $next_start; - $next['_instance'] = libcalendaring::recurrence_instance_identifier($next); + $next['_instance'] = libcalendaring::recurrence_instance_identifier($next, $this->event['allday']); unset($next['_formatobj']); diff --git a/plugins/libcalendaring/libcalendaring.php b/plugins/libcalendaring/libcalendaring.php index 1efead86..92618b02 100644 --- a/plugins/libcalendaring/libcalendaring.php +++ b/plugins/libcalendaring/libcalendaring.php @@ -1492,18 +1492,23 @@ class libcalendaring extends rcube_plugin * Return the identifer for the given instance of a recurring event * * @param array Hash array with event properties + * @param bool All-day flag from the main event + * * @return mixed Format string or null if identifier cannot be generated */ - public static function recurrence_instance_identifier($event) + public static function recurrence_instance_identifier($event, $allday = null) { $instance_date = $event['recurrence_date'] ?: $event['start']; if ($instance_date && is_a($instance_date, 'DateTime')) { - $recurrence_id_format = $event['allday'] ? 'Ymd' : 'Ymd\THis'; - return $instance_date->format($recurrence_id_format); - } + // According to RFC5545 (3.8.4.4) RECURRENCE-ID format should + // be date/date-time depending on the main event type, not the exception + if ($allday === null) { + $allday = $event['allday']; + } - return null; + return $instance_date->format($allday ? 'Ymd' : 'Ymd\THis'); + } } diff --git a/plugins/libkolab/lib/kolab_format_event.php b/plugins/libkolab/lib/kolab_format_event.php index 32ae60b6..e8393a42 100644 --- a/plugins/libkolab/lib/kolab_format_event.php +++ b/plugins/libkolab/lib/kolab_format_event.php @@ -291,7 +291,9 @@ class kolab_format_event extends kolab_format_xcal } // preserve this property for date serialization - $exception['allday'] = $master['allday']; + if (!isset($exception['allday'])) { + $exception['allday'] = $master['allday']; + } return $exception; } @@ -304,7 +306,7 @@ class kolab_format_event extends kolab_format_xcal // Note: If an exception has no attendees it means there's "no attendees // for this occurrence", not "attendees are the same as in the event" (#5300) - $forbidden = array('exceptions', 'attendees'); + $forbidden = array('exceptions', 'attendees', 'allday'); $is_recurring = !empty($master['recurrence']); foreach ($master as $prop => $value) {