From 12591358e600dd3b0ad91a8f29345354e34000ac Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Sun, 15 Feb 2015 16:33:39 +0100 Subject: [PATCH] Consider a change in recurrence rule significant for rescheduling (#4366) --- plugins/calendar/calendar.php | 8 ++-- .../calendar/drivers/kolab/kolab_driver.php | 17 +------ plugins/libkolab/config.inc.php.dist | 2 +- plugins/libkolab/lib/kolab_format_event.php | 12 ++++- plugins/libkolab/lib/kolab_format_task.php | 10 ++++ plugins/libkolab/lib/kolab_format_xcal.php | 48 ++++++++++++++----- 6 files changed, 65 insertions(+), 32 deletions(-) diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php index f1e66ec8..880086de 100644 --- a/plugins/calendar/calendar.php +++ b/plugins/calendar/calendar.php @@ -993,7 +993,7 @@ class calendar extends rcube_plugin if ($success = $this->driver->edit_rsvp($event, $status)) { $noreply = rcube_utils::get_input_value('noreply', rcube_utils::INPUT_GPC); $noreply = intval($noreply) || $status == 'needs-action' || $itip_sending === 0; - $reload = $event['calendar'] != $ev['calendar'] ? 2 : 1; + $reload = $event['calendar'] != $ev['calendar'] || $event['recurrence'] ? 2 : 1; $organizer = null; $emails = $this->get_user_emails(); @@ -1131,8 +1131,6 @@ class calendar extends rcube_plugin // make sure we have the complete record $event = $action == 'remove' ? $old : $this->driver->get_event($event); - // TODO: on change of a recurring (main) event, also send updates to differing attendess of recurrence exceptions - // only notify if data really changed (TODO: do diff check on client already) if (!$old || $action == 'remove' || self::event_diff($event, $old)) { $sent = $this->notify_attendees($event, $old, $action, $event['_comment']); @@ -1990,6 +1988,8 @@ class calendar extends rcube_plugin $sent = -100; } + // TODO: on change of a recurring (main) event, also send updates to differing attendess of recurrence exceptions + // send CANCEL message to removed attendees foreach ((array)$old['attendees'] as $attendee) { if ($attendee['ROLE'] == 'ORGANIZER' || !$attendee['email'] || in_array(strtolower($attendee['email']), $current)) @@ -2215,7 +2215,7 @@ class calendar extends rcube_plugin public static function event_diff($a, $b) { $diff = array(); - $ignore = array('changed' => 1, 'attachments' => 1, 'recurrence' => 1, '_notify' => 1, '_owner' => 1); + $ignore = array('changed' => 1, 'attachments' => 1, '_notify' => 1, '_owner' => 1, '_savemode' => 1); foreach (array_unique(array_merge(array_keys($a), array_keys($b))) as $key) { if (!$ignore[$key] && $a[$key] != $b[$key]) $diff[] = $key; diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php index 4b450f87..815f51ee 100644 --- a/plugins/calendar/drivers/kolab/kolab_driver.php +++ b/plugins/calendar/drivers/kolab/kolab_driver.php @@ -1035,26 +1035,13 @@ class kolab_driver extends calendar_driver */ public function check_scheduling(&$event, $old, $update = true) { - $reschedule = false; - // skip this check when importing iCal/iTip events if (isset($event['sequence']) || !empty($event['_method'])) { - return $reschedule; + return false; } // iterate through the list of properties considered 'significant' for scheduling - foreach (kolab_format_event::$scheduling_properties as $prop) { - $a = $old[$prop]; - $b = $event[$prop]; - if ($event['allday'] && ($prop == 'start' || $prop == 'end') && $a instanceof DateTime && $b instanceof DateTime) { - $a = $a->format('Y-m-d'); - $b = $b->format('Y-m-d'); - } - if ($a != $b) { - $reschedule = true; - break; - } - } + $reschedule = kolab_format_event::check_rescheduling($event, $old); // reset all attendee status to needs-action (#4360) if ($update && $reschedule && is_array($event['attendees'])) { diff --git a/plugins/libkolab/config.inc.php.dist b/plugins/libkolab/config.inc.php.dist index 6e4b613e..3a8476c3 100644 --- a/plugins/libkolab/config.inc.php.dist +++ b/plugins/libkolab/config.inc.php.dist @@ -41,7 +41,7 @@ $config['kolab_messages_cache_bypass'] = 0; // These event properties contribute to a significant revision to the calendar component // and if changed will increment the sequence number relevant for scheduling according to RFC 5545 -$config['kolab_event_scheduling_properties'] = array('start', 'end', 'allday', 'location', 'status', 'cancelled'); +$config['kolab_event_scheduling_properties'] = array('start', 'end', 'allday', 'recurrence', 'location', 'status', 'cancelled'); // These task properties contribute to a significant revision to the calendar component // and if changed will increment the sequence number relevant for scheduling according to RFC 5545 diff --git a/plugins/libkolab/lib/kolab_format_event.php b/plugins/libkolab/lib/kolab_format_event.php index bf171496..f3c52df8 100644 --- a/plugins/libkolab/lib/kolab_format_event.php +++ b/plugins/libkolab/lib/kolab_format_event.php @@ -26,7 +26,7 @@ class kolab_format_event extends kolab_format_xcal { public $CTYPEv2 = 'application/x-vnd.kolab.event'; - public static $scheduling_properties = array('start', 'end', 'allday', 'location', 'status', 'cancelled'); + public static $scheduling_properties = array('start', 'end', 'allday', 'recurrence', 'location', 'status', 'cancelled'); protected $objclass = 'Event'; protected $read_func = 'readEvent'; @@ -100,6 +100,7 @@ class kolab_format_event extends kolab_format_xcal foreach((array)$object['recurrence']['EXCEPTIONS'] as $i => $exception) { $exevent = new kolab_format_event; $exevent->set(($compacted = $this->compact_exception($exception, $object))); // only save differing values + console('COMPACTED', $compacted); // get value for recurrence-id if (!empty($exception['recurrence_date']) && is_a($exception['recurrence_date'], 'DateTime')) { @@ -274,4 +275,13 @@ class kolab_format_event extends kolab_format_xcal return $exception; } + /** + * Identify changes considered relevant for scheduling + * + * @see kolab_format_xcal::check_rescheduling() + */ + public static function check_rescheduling($object, $old, $checks = null) + { + return parent::check_rescheduling($object, $old, $checks ?: self::$scheduling_properties); + } } diff --git a/plugins/libkolab/lib/kolab_format_task.php b/plugins/libkolab/lib/kolab_format_task.php index ee0ca6a9..2c0cda5e 100644 --- a/plugins/libkolab/lib/kolab_format_task.php +++ b/plugins/libkolab/lib/kolab_format_task.php @@ -126,4 +126,14 @@ class kolab_format_task extends kolab_format_xcal return $tags; } + + /** + * Identify changes considered relevant for scheduling + * + * @see kolab_format_xcal::check_rescheduling() + */ + public static function check_rescheduling($object, $old, $checks = null) + { + return parent::check_rescheduling($object, $old, $checks ?: self::$scheduling_properties); + } } diff --git a/plugins/libkolab/lib/kolab_format_xcal.php b/plugins/libkolab/lib/kolab_format_xcal.php index d0f89b63..8d751a6a 100644 --- a/plugins/libkolab/lib/kolab_format_xcal.php +++ b/plugins/libkolab/lib/kolab_format_xcal.php @@ -321,17 +321,8 @@ abstract class kolab_format_xcal extends kolab_format // increment sequence when updating properties relevant for scheduling. // RFC 5545: "It is incremented [...] each time the Organizer makes a significant revision to the calendar component." - foreach (self::$scheduling_properties as $prop) { - $a = $old[$prop]; - $b = $object[$prop]; - if ($object['allday'] && ($prop == 'start' || $prop == 'end') && $a instanceof DateTime && $b instanceof DateTime) { - $a = $a->format('Y-m-d'); - $b = $b->format('Y-m-d'); - } - if ($a != $b) { - $object['sequence']++; - break; - } + if (self::check_rescheduling($object, $old)) { + $object['sequence']++; } } } @@ -637,4 +628,39 @@ abstract class kolab_format_xcal extends kolab_format return $tags; } + + /** + * Identify changes considered relevant for scheduling + * + * @param array Hash array with NEW object properties + * @param array Hash array with OLD object properties + * @param array List of object properties to check for changes + * + * @return boolean True if changes affect scheduling, False otherwise + */ + public static function check_rescheduling($object, $old, $checks = null) + { + $reschedule = false; + + foreach ($checks ?: self::$scheduling_properties as $prop) { + $a = $old[$prop]; + $b = $object[$prop]; + if ($object['allday'] && ($prop == 'start' || $prop == 'end') && $a instanceof DateTime && $b instanceof DateTime) { + $a = $a->format('Y-m-d'); + $b = $b->format('Y-m-d'); + } + if ($prop == 'recurrence') { + unset($a['EXCEPTIONS']); + unset($b['EXCEPTIONS']); + $a = array_filter($a); + $b = array_filter($b); + } + if ($a != $b) { + $reschedule = true; + break; + } + } + + return $reschedule; + } } \ No newline at end of file