diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php index f18c1d75..3a35e1ef 100644 --- a/plugins/calendar/calendar.php +++ b/plugins/calendar/calendar.php @@ -849,8 +849,8 @@ class calendar extends rcube_plugin if (($event['_notify'] || $event['_decline']) && $action != 'new') { $old = $this->driver->get_event($event); - // load main event when savemode is 'all' - if ($event['_savemode'] == 'all' && $old['recurrence_id']) { + // load main event if savemode is 'all' or if deleting 'future' events + if (($event['_savemode'] == 'all' || ($event['_savemode'] == 'future' && $action == 'remove' && !$event['_decline'])) && $old['recurrence_id']) { $old['id'] = $old['recurrence_id']; $old = $this->driver->get_event($old); } @@ -926,10 +926,15 @@ class calendar extends rcube_plugin } // send cancellation for the main event - if ($event['_savemode'] == 'all') + if ($event['_savemode'] == 'all') { unset($old['_instance'], $old['recurrence_date'], $old['recurrence_id']); - else if ($event['_savemode'] == 'future') - $old['thisandfuture'] = true; + } + // send an update for the main event's recurrence rule instead of a cancellation message + else if ($event['_savemode'] == 'future' && $success !== false && $success !== true) { + $event['_savemode'] = 'all'; // force event_save_success() to load master event + $action = 'edit'; + $success = true; + } // send iTIP reply that participant has declined the event if ($success && $event['_decline']) { @@ -943,6 +948,10 @@ class calendar extends rcube_plugin } } + if ($event['_savemode'] == 'future' && $event['id'] != $old['id']) { + $old['thisandfuture'] = true; + } + $itip = $this->load_itip(); $itip->set_sender_email($reply_sender); if ($organizer && $itip->send_itip_message($old, 'REPLY', $organizer, 'itipsubjectdeclined', 'itipmailbodydeclined')) diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js index 59e0f4af..6eac13eb 100644 --- a/plugins/calendar/calendar_ui.js +++ b/plugins/calendar/calendar_ui.js @@ -2606,9 +2606,9 @@ function rcube_calendar_ui(settings) if (event.recurrence) { var future_disabled = '', message_label = (action == 'remove' ? 'removerecurringeventwarning' : 'changerecurringeventwarning'); - // disable the 'future' savemode if attendees are involved - // reason: no calendaring system supports the thisandfuture range parameter - if (action == 'remove' && _has_attendees && is_organizer(event)) { + // disable the 'future' savemode if I'm an attendee + // reason: no calendaring system supports the thisandfuture range parameter in iTip REPLY + if (action == 'remove' && _has_attendees && !_is_organizer && is_attendee(event)) { future_disabled = ' disabled'; } diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php index 410dc7a7..cc08e6b4 100644 --- a/plugins/calendar/drivers/kolab/kolab_driver.php +++ b/plugins/calendar/drivers/kolab/kolab_driver.php @@ -748,6 +748,7 @@ class kolab_driver extends calendar_driver */ public function remove_event($event, $force = true) { + $ret = true; $success = false; $savemode = $event['_savemode']; $decline = $event['_decline']; @@ -821,7 +822,9 @@ class kolab_driver extends calendar_driver break; case 'future': - if ($master['id'] != $event['id']) { + $recurrence_id_format = $master['allday'] ? 'Ymd' : 'Ymd\THis'; + $master['_instance'] = $master['start']->format($recurrence_id_format); + if ($master['_instance'] != $event['_instance']) { $_SESSION['calendar_restore_event_data'] = $master; // set until-date on master event @@ -844,6 +847,7 @@ class kolab_driver extends calendar_driver } $success = $storage->update_event($master); + $ret = $master['uid']; break; } @@ -875,7 +879,7 @@ class kolab_driver extends calendar_driver if ($success && $this->freebusy_trigger) $this->rc->output->command('plugin.ping_url', array('action' => 'calendar/push-freebusy', 'source' => $storage->id)); - return $success; + return $success ? $ret : false; } /**