summaryrefslogtreecommitdiff
path: root/plugins/calendar
diff options
context:
space:
mode:
authorThomas Bruederli <bruederli@kolabsys.com>2015-02-26 13:48:14 (GMT)
committerThomas Bruederli <bruederli@kolabsys.com>2015-02-26 14:05:55 (GMT)
commit899646afd824cc9a57cba6f5c9a4971eab395d07 (patch)
treeb4773d5243dedb0567891d2dd0cb18cb616f1045 /plugins/calendar
parentef017d4eb059907ce7581f5e1b7842271eeb8efd (diff)
downloadroundcubemail-plugins-kolab-899646afd824cc9a57cba6f5c9a4971eab395d07.tar.gz
Handle single event occurrences with the same UID (#4722)
Diffstat (limited to 'plugins/calendar')
-rw-r--r--plugins/calendar/drivers/kolab/kolab_calendar.php37
-rw-r--r--plugins/calendar/drivers/kolab/kolab_driver.php122
2 files changed, 125 insertions, 34 deletions
diff --git a/plugins/calendar/drivers/kolab/kolab_calendar.php b/plugins/calendar/drivers/kolab/kolab_calendar.php
index faa92d2..bfdaf7b 100644
--- a/plugins/calendar/drivers/kolab/kolab_calendar.php
+++ b/plugins/calendar/drivers/kolab/kolab_calendar.php
@@ -192,14 +192,22 @@ class kolab_calendar extends kolab_storage_folder_api
// event not found, maybe a recurring instance is requested
if (!$this->events[$id]) {
$master_id = preg_replace('/-\d+(T\d{6})?$/', '', $id);
- if ($master_id != $id && ($record = $this->storage->get_object($master_id)))
- $this->events[$master_id] = $this->_to_rcube_event($record);
+ $instance_id = substr($id, strlen($master_id) + 1);
+
+ if ($master_id != $id && ($record = $this->storage->get_object($master_id))) {
+ $master = $this->events[$master_id] = $this->_to_rcube_event($record);
+ }
// check for match on the first instance already
- if (($_instance = $this->events[$master_id]['_instance']) && $id == $master_id . '-' . $_instance) {
+ if ($master['_instance'] && $master['_instance'] == $instance_id) {
$this->events[$id] = $this->events[$master_id];
}
- else if (($master = $this->events[$master_id]) && $master['recurrence']) {
+ // check for match in top-level exceptions (aka loose single occurrences)
+ else if ($master && $master['_formatobj'] && ($instance = $master['_formatobj']->get_instance($instance_id))) {
+ $instance = $this->_to_rcube_event($instance);
+ $this->events[$instance['id']] = $instance;
+ }
+ else if ($master && is_array($master['recurrence'])) {
$this->get_recurring_events($record, $master['start'], null, $id);
}
}
@@ -318,6 +326,15 @@ class kolab_calendar extends kolab_storage_folder_api
if ($record['recurrence'] && $virtual == 1) {
$events = array_merge($events, $this->get_recurring_events($record, $start, $end));
}
+ // add top-level exceptions (aka loose single occurrences)
+ else if (is_array($record['exceptions'])) {
+ foreach ($record['exceptions'] as $ex) {
+ $component = $this->_to_rcube_event($ex);
+ if ($component['start'] <= $end && $component['end'] >= $start) {
+ $events[] = $component;
+ }
+ }
+ }
}
// post-filter all events by fulltext search and partstat values
@@ -447,7 +464,7 @@ class kolab_calendar extends kolab_storage_folder_api
public function update_event($event, $exception_id = null)
{
$updated = false;
- $old = $this->storage->get_object($event['id']);
+ $old = $this->storage->get_object($event['uid'] ?: $event['id']);
if (!$old || PEAR::isError($old))
return false;
@@ -456,7 +473,7 @@ class kolab_calendar extends kolab_storage_folder_api
unset($event['links']);
$object = $this->_from_rcube_event($event, $old);
- $saved = $this->storage->save($object, 'event', $event['id']);
+ $saved = $this->storage->save($object, 'event', $old['uid']);
if (!$saved) {
rcube::raise_error(array(
@@ -751,14 +768,6 @@ class kolab_calendar extends kolab_storage_folder_api
}
}
- // search in recurrence exceptions
- if (!$hits && $recursive && !empty($event['recurrence']['EXCEPTIONS'])) {
- foreach ($event['recurrence']['EXCEPTIONS'] as $exception) {
- $hits = $this->fulltext_match($exception, $word, false);
- if ($hits) break;
- }
- }
-
return $hits;
}
diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php
index 6549381..cfdcb4c 100644
--- a/plugins/calendar/drivers/kolab/kolab_driver.php
+++ b/plugins/calendar/drivers/kolab/kolab_driver.php
@@ -583,8 +583,15 @@ class kolab_driver extends calendar_driver
$cid = $event['calendar'] ? $event['calendar'] : reset(array_keys($this->calendars));
if ($storage = $this->get_calendar($cid)) {
- $success = $storage->insert_event($event);
-
+ // if this is a recurrence instance, append as exception to an already existing object for this UID
+ if (!empty($event['recurrence_date']) && ($master = $this->get_event($event['uid']))) {
+ self::add_exception($master, $event);
+ $success = $storage->update_event($master);
+ }
+ else {
+ $success = $storage->insert_event($event);
+ }
+
if ($success && $this->freebusy_trigger) {
$this->rc->output->command('plugin.ping_url', array('action' => 'calendar/push-freebusy', 'source' => $storage->id));
$this->freebusy_trigger = false; // disable after first execution (#2355)
@@ -676,6 +683,9 @@ class kolab_driver extends calendar_driver
$master['recurrence']['EXCEPTIONS'][] = $event;
}
+ // set link to top-level exceptions
+ $master['exceptions'] = &$master['recurrence']['EXCEPTIONS'];
+
return $this->update_event($master);
}
}
@@ -741,22 +751,30 @@ class kolab_driver extends calendar_driver
$this->rc->session->remove('calendar_restore_event_data');
// read master if deleting a recurring event
- if ($event['recurrence'] || $event['recurrence_id']) {
- $master = $event['recurrence_id'] ? $storage->get_event($event['recurrence_id']) : $event;
- $savemode = $event['_savemode'] ?: ($event['_instance'] ? 'current' : 'all');
+ if ($event['recurrence'] || $event['recurrence_id'] || $event['isexception']) {
+ $master = $event['recurrence_id'] || $event['isexception'] ? $storage->get_event($event['uid']) : $event;
+ $savemode = $event['_savemode'] ?: ($event['_instance'] || $event['isexception'] ? 'current' : 'all');
+
+ // force 'current' mode for single occurrences stored as exception
+ if (!$event['recurrence'] && !$event['recurrence_id'] && $event['isexception'])
+ $savemode = 'current';
}
// removing an exception instance
- if ($event['recurrence_id'] && $master['recurrence'] && is_array($master['recurrence']['EXCEPTIONS'])) {
- foreach ($master['recurrence']['EXCEPTIONS'] as $i => $exception) {
+ if (($event['recurrence_id'] || $event['isexception']) && is_array($master['exceptions'])) {
+ foreach ($master['exceptions'] as $i => $exception) {
if ($exception['_instance'] == $event['_instance']) {
- unset($master['recurrence']['EXCEPTIONS'][$i]);
+ unset($master['exceptions'][$i]);
// set event date back to the actual occurrence
if ($exception['recurrence_date'])
$event['start'] = $exception['recurrence_date'];
break;
}
}
+
+ if (is_array($master['recurrence'])) {
+ $master['recurrence']['EXCEPTIONS'] = &$master['exceptions'];
+ }
}
switch ($savemode) {
@@ -764,7 +782,7 @@ class kolab_driver extends calendar_driver
$_SESSION['calendar_restore_event_data'] = $master;
// removing the first instance => just move to next occurence
- if ($master['id'] == $event['id']) {
+ if ($master['id'] == $event['id'] && $master['recurrence']) {
$recurring = reset($storage->get_recurring_events($event, $event['start'], null, $event['id'].'-1'));
// no future instances found: delete the master event (bug #1677)
@@ -821,7 +839,18 @@ class kolab_driver extends calendar_driver
}
default: // 'all' is default
- if ($decline && $this->rc->config->get('kolab_invitation_calendars')) {
+ // removing the master event with loose exceptions (not recurring though)
+ if (!empty($event['recurrence_date']) && !empty($master['exceptions'])) {
+ // make the first exception the new master
+ $newmaster = array_shift($master['exceptions']);
+ $newmaster['exceptions'] = $master['exceptions'];
+ $newmaster['_attachments'] = $master['_attachments'];
+ $newmaster['_mailbox'] = $master['_mailbox'];
+ $newmaster['_msguid'] = $master['_msguid'];
+
+ $success = $storage->update_event($newmaster);
+ }
+ else if ($decline && $this->rc->config->get('kolab_invitation_calendars')) {
// don't delete but set PARTSTAT=DECLINED
if ($this->cal->lib->set_partstat($master, 'DECLINED')) {
$success = $storage->update_event($master);
@@ -934,13 +963,16 @@ class kolab_driver extends calendar_driver
$event['attachments'] = array_merge((array)$old['attachments'], $attachments);
// modify a recurring event, check submitted savemode to do the right things
- if ($old['recurrence'] || $old['recurrence_id']) {
- $master = $old['recurrence_id'] ? $fromcalendar->get_event($old['recurrence_id']) : $old;
- $savemode = $event['_savemode'] ?: ($old['recurrence_id'] ? 'current' : 'all');
+ if ($old['recurrence'] || $old['recurrence_id'] || $old['isexception']) {
+ $master = $old['recurrence_id'] || $old['isexception'] ? $fromcalendar->get_event($old['uid']) : $old;
+ $savemode = $event['_savemode'] ?: ($old['recurrence_id'] || $old['isexception'] ? 'current' : 'all');
// this-and-future on the first instance equals to 'all'
if (!$old['recurrence_id'] && $savemode == 'future')
$savemode = 'all';
+ // force 'current' mode for single occurrences stored as exception
+ else if (!$old['recurrence'] && !$old['recurrence_id'] && $old['isexception'])
+ $savemode = 'current';
}
// check if update affects scheduling and update attendee status accordingly
@@ -953,6 +985,8 @@ class kolab_driver extends calendar_driver
$with_exceptions = true; // exceptions already provided (e.g. from iCal import)
else if ($old['recurrence']['EXCEPTIONS'])
$event['recurrence']['EXCEPTIONS'] = $old['recurrence']['EXCEPTIONS'];
+ else if ($old['exceptions'])
+ $event['exceptions'] = $old['exceptions'];
// remove some internal properties which should not be saved
unset($event['_savemode'], $event['_fromcalendar'], $event['_identity'], $event['_owner'],
@@ -1039,7 +1073,7 @@ class kolab_driver extends calendar_driver
// update master event (no rescheduling!)
self::clear_attandee_noreply($master);
- $udated = $storage->update_event($master);
+ $storage->update_event($master);
}
break;
@@ -1054,7 +1088,7 @@ class kolab_driver extends calendar_driver
$event['sequence'] = max($old['sequence'], $master['sequence']) + 1;
}
else if (!isset($event['sequence'])) {
- $event['sequence'] = $master['sequence'];
+ $event['sequence'] = $old['sequence'] ?: $master['sequence'];
}
// save properties to a recurrence exception instance
@@ -1081,10 +1115,9 @@ class kolab_driver extends calendar_driver
// save as new exception to master event
if ($add_exception) {
- $event['_instance'] = $old['_instance'];
- $event['recurrence_date'] = $old['recurrence_date'] ?: $old['start'];
- $master['recurrence']['EXCEPTIONS'][] = $event;
+ self::add_exception($master, $event, $old);
}
+
$success = $storage->update_event($master);
break;
@@ -1128,6 +1161,10 @@ class kolab_driver extends calendar_driver
if ($old['recurrence_id']) {
$event['recurrence']['EXCEPTIONS'] = $master['recurrence']['EXCEPTIONS'];
}
+ else if ($master['_instance']) {
+ $event['_instance'] = $master['_instance'];
+ $event['recurrence_date'] = $master['recurrence_date'];
+ }
// TODO: forward changes to exceptions (which do not yet have differing values stored)
if (is_array($event['recurrence']) && is_array($event['recurrence']['EXCEPTIONS']) && !$with_exceptions) {
@@ -1286,11 +1323,52 @@ class kolab_driver extends calendar_driver
}
*/
+ // set link to top-level exceptions
+ $master['exceptions'] = &$master['recurrence']['EXCEPTIONS'];
+
// returning false here will add a new exception
return $saved;
}
/**
+ * Add or update the given event as an exception to $master
+ */
+ public static function add_exception(&$master, $event, $old = null)
+ {
+ if ($old) {
+ $event['_instance'] = $old['_instance'];
+ if (!$event['recurrence_date'])
+ $event['recurrence_date'] = $old['recurrence_date'] ?: $old['start'];
+ }
+ else if (!$event['recurrence_date']) {
+ $event['recurrence_date'] = $event['start'];
+ }
+
+ if (!$event['_instance'] && is_a($event['recurrence_date'], 'DateTime')) {
+ $recurrence_id_format = $event['allday'] ? 'Ymd' : 'Ymd\THis';
+ $event['_instance'] = $event['recurrence_date']->format($recurrence_id_format);
+ }
+
+ if (!is_array($master['exceptions']) && is_array($master['recurrence']['EXCEPTIONS'])) {
+ $master['exceptions'] = &$master['recurrence']['EXCEPTIONS'];
+ }
+
+ $existing = false;
+ foreach ((array)$master['exceptions'] as $i => $exception) {
+ if ($exception['_instance'] == $event['_instance']) {
+ $master['exceptions'][$i] = $event;
+ $existing = true;
+ }
+ }
+
+ if (!$existing) {
+ $master['exceptions'][] = $event;
+ }
+
+ return true;
+ }
+
+ /**
* Remove the noreply flags from attendees
*/
public static function clear_attandee_noreply(&$event)
@@ -1856,10 +1934,14 @@ class kolab_driver extends calendar_driver
// add instance identifier to first occurrence (master event)
// do not add 'recurrence_date' though in order to keep the master even being exported as such
+ $recurrence_id_format = $record['allday'] ? 'Ymd' : 'Ymd\THis';
if ($record['recurrence'] && !$record['recurrence_id'] && !$record['_instance']) {
- $recurrence_id_format = $record['allday'] ? 'Ymd' : 'Ymd\THis';
$record['_instance'] = $record['start']->format($recurrence_id_format);
}
+ else if (is_a($record['recurrence_date'], 'DateTime')) {
+ $record['_instance'] = $record['recurrence_date']->format($recurrence_id_format);
+ $record['id'] = $record['uid'] . '-' . $record['_instance'];
+ }
// clean up exception data
if (is_array($record['recurrence']['EXCEPTIONS'])) {
@@ -1877,7 +1959,7 @@ class kolab_driver extends calendar_driver
public static function clean_rcube_event_out(&$record)
{
unset($record['_mailbox'], $record['_msguid'], $record['_type'], $record['_size'],
- $record['_formatobj'], $record['_attachments'], $record['x-custom']);
+ $record['_formatobj'], $record['_attachments'], $record['exceptions'], $record['x-custom']);
}
/**