7 commits - plugins/calendar plugins/libcalendaring plugins/libkolab
Thomas Brüderli
bruederli at kolabsys.com
Thu Feb 28 14:20:55 CET 2013
plugins/calendar/calendar.php | 3
plugins/calendar/calendar_ui.js | 3
plugins/calendar/drivers/calendar_driver.php | 1
plugins/calendar/drivers/kolab/kolab_calendar.php | 85 +++++++++++++++++++---
plugins/calendar/drivers/kolab/kolab_driver.php | 73 ++++++------------
plugins/calendar/lib/calendar_ical.php | 57 +++++++++-----
plugins/libcalendaring/libcalendaring.php | 4 +
plugins/libkolab/lib/kolab_format_event.php | 71 ++++++++++++++++++
plugins/libkolab/lib/kolab_format_xcal.php | 8 +-
9 files changed, 224 insertions(+), 81 deletions(-)
New commits:
commit 01dcd96c7438aec72371beee0c683cf8d8368974
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Thu Feb 28 14:18:08 2013 +0100
Really skip this property
diff --git a/plugins/libcalendaring/libcalendaring.php b/plugins/libcalendaring/libcalendaring.php
index 5e30b73..0760277 100644
--- a/plugins/libcalendaring/libcalendaring.php
+++ b/plugins/libcalendaring/libcalendaring.php
@@ -729,7 +729,7 @@ class libcalendaring extends rcube_plugin
$val = join(',', (array)$val);
break;
case 'EXCEPTIONS':
- continue;
+ continue 2;
}
$rrule .= $k . '=' . $val . ';';
}
commit 354a18795f351670bf676eabdf2cfac7b5c45301
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Thu Feb 28 10:44:15 2013 +0100
Properly export recurrence exceptions to iCal
diff --git a/plugins/calendar/lib/calendar_ical.php b/plugins/calendar/lib/calendar_ical.php
index 6d7474e..3dfe4c1 100644
--- a/plugins/calendar/lib/calendar_ical.php
+++ b/plugins/calendar/lib/calendar_ical.php
@@ -347,27 +347,32 @@ class calendar_ical
* @param boolean Directly send data to stdout instead of returning
* @return string Events in iCalendar format (http://tools.ietf.org/html/rfc5545)
*/
- public function export($events, $method = null, $write = false)
+ public function export($events, $method = null, $write = false, $recurrence_id = null)
{
- $ical = "BEGIN:VCALENDAR" . self::EOL;
- $ical .= "VERSION:2.0" . self::EOL;
- $ical .= "PRODID:-//Roundcube Webmail " . RCMAIL_VERSION . "//NONSGML Calendar//EN" . self::EOL;
- $ical .= "CALSCALE:GREGORIAN" . self::EOL;
-
- if ($method)
- $ical .= "METHOD:" . strtoupper($method) . self::EOL;
-
- if ($write) {
- echo $ical;
- $ical = '';
+ if (!$recurrence_id) {
+ $ical = "BEGIN:VCALENDAR" . self::EOL;
+ $ical .= "VERSION:2.0" . self::EOL;
+ $ical .= "PRODID:-//Roundcube Webmail " . RCMAIL_VERSION . "//NONSGML Calendar//EN" . self::EOL;
+ $ical .= "CALSCALE:GREGORIAN" . self::EOL;
+
+ if ($method)
+ $ical .= "METHOD:" . strtoupper($method) . self::EOL;
+
+ if ($write) {
+ echo $ical;
+ $ical = '';
+ }
}
-
+
foreach ($events as $event) {
$vevent = "BEGIN:VEVENT" . self::EOL;
$vevent .= "UID:" . self::escpape($event['uid']) . self::EOL;
$vevent .= $this->format_datetime("DTSTAMP", $event['changed'] ?: new DateTime(), false, true) . self::EOL;
if ($event['sequence'])
- $vevent .= "SEQUENCE:" . intval($event['sequence']) . self::EOL;
+ $vevent .= "SEQUENCE:" . intval($event['sequence']) . self::EOL;
+ if ($recurrence_id)
+ $vevent .= $recurrence_id . self::EOL;
+
// correctly set all-day dates
if ($event['allday']) {
$event['end'] = clone $event['end'];
@@ -389,7 +394,7 @@ class calendar_ical
if (!empty($event['location'])) {
$vevent .= "LOCATION:" . self::escpape($event['location']) . self::EOL;
}
- if ($event['recurrence']) {
+ if ($event['recurrence'] && !$recurrence_id) {
$vevent .= "RRULE:" . libcalendaring::to_rrule($event['recurrence'], self::EOL) . self::EOL;
}
if(!empty($event['categories'])) {
@@ -426,18 +431,30 @@ class calendar_ical
// TODO: export attachments
$vevent .= "END:VEVENT" . self::EOL;
-
+
+ // append recurrence exceptions
+ if ($event['recurrence']['EXCEPTIONS'] && !$recurrence_id) {
+ foreach ($event['recurrence']['EXCEPTIONS'] as $ex) {
+ $exdate = clone $event['start'];
+ $exdate->setDate($ex['start']->format('Y'), $ex['start']->format('n'), $ex['start']->format('j'));
+ $vevent .= $this->export(array($ex), null, false,
+ $this->format_datetime('RECURRENCE-ID', $exdate, $event['allday']));
+ }
+ }
+
if ($write)
echo rcube_vcard::rfc2425_fold($vevent);
else
$ical .= $vevent;
}
- $ical .= "END:VCALENDAR" . self::EOL;
+ if (!$recurrence_id) {
+ $ical .= "END:VCALENDAR" . self::EOL;
- if ($write) {
- echo $ical;
- return true;
+ if ($write) {
+ echo $ical;
+ return true;
+ }
}
// fold lines to 75 chars
commit e588b7fe09138526d68c9ce3690fe575be0f7431
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Thu Feb 28 08:26:54 2013 +0100
Don't attempt to serialize recurrence EXCEPTIONS
diff --git a/plugins/libcalendaring/libcalendaring.php b/plugins/libcalendaring/libcalendaring.php
index 35255b9..5e30b73 100644
--- a/plugins/libcalendaring/libcalendaring.php
+++ b/plugins/libcalendaring/libcalendaring.php
@@ -728,6 +728,8 @@ class libcalendaring extends rcube_plugin
$val[$i] = $ex->format('Ymd\THis');
$val = join(',', (array)$val);
break;
+ case 'EXCEPTIONS':
+ continue;
}
$rrule .= $k . '=' . $val . ';';
}
commit f320a772b0688db85ce261d090c8613df4592500
Merge: 465465d 36bbcc6
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Thu Feb 28 08:25:52 2013 +0100
Merge branch 'dev/recurrence-exceptions'
diff --cc plugins/libkolab/lib/kolab_format_event.php
index cd357fc,4e2cccc..2b3f0c8
--- a/plugins/libkolab/lib/kolab_format_event.php
+++ b/plugins/libkolab/lib/kolab_format_event.php
@@@ -86,15 -101,20 +99,27 @@@ class kolab_format_event extends kolab_
$attach->setUri('cid:' . $cid, $attr['mimetype']);
$vattach->push($attach);
}
+
+ foreach ((array)$object['links'] as $link) {
+ $attach = new Attachment;
+ $attach->setUri($link, null);
+ $vattach->push($attach);
+ }
+
$this->obj->setAttachments($vattach);
+ // save recurrence exceptions
+ if ($object['recurrence']['EXCEPTIONS']) {
+ $vexceptions = new vectorevent;
+ foreach((array)$object['recurrence']['EXCEPTIONS'] as $exception) {
+ $exevent = new kolab_format_event;
+ $exevent->set($this->compact_exception($exception, $object)); // only save differing values
+ $exevent->obj->setRecurrenceID(self::get_datetime($exception['start'], null, true), (bool)$exception['thisandfuture']);
+ $vexceptions->push($exevent->obj);
+ }
+ $this->obj->setExceptions($vexceptions);
+ }
+
// cache this data
$this->data = $object;
unset($this->data['_formatobj']);
@@@ -161,11 -183,24 +186,27 @@@
'content' => $data,
);
}
+ else if (substr($attach->uri(), 0, 4) == 'http') {
+ $object['links'][] = $attach->uri();
+ }
}
+ // read exception event objects
+ if (($exceptions = $this->obj->exceptions()) && $exceptions->size()) {
+ for ($i=0; $i < $exceptions->size(); $i++) {
+ if (($exobj = $exceptions->get($i))) {
+ $exception = new kolab_format_event($exobj);
+ if ($exception->is_valid()) {
+ $object['recurrence']['EXCEPTIONS'][] = $this->expand_exception($exception->to_array(), $object);
+ }
+ }
+ }
+ }
+ // this is an exception object
+ else if ($this->obj->recurrenceID()->isValid()) {
+ $object['thisandfuture'] = $this->obj->thisAndFuture();
+ }
+
// merge with additional data, e.g. attachments from the message
if ($data) {
foreach ($data as $idx => $value) {
commit 36bbcc6499f8ead8b5dee0d29060a2c0dc627566
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Thu Feb 14 16:17:02 2013 +0100
Make this-and-future recurrence exceptions work
diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js
index 24d7bd6..f4c7749 100644
--- a/plugins/calendar/calendar_ui.js
+++ b/plugins/calendar/calendar_ui.js
@@ -507,8 +507,9 @@ function rcube_calendar_ui(settings)
// show warning if editing a recurring event
if (event.id && event.recurrence) {
+ var sel = event.thisandfuture ? 'future' : 'all';
$('#edit-recurring-warning').show();
- $('input.edit-recurring-savemode[value="all"]').prop('checked', true);
+ $('input.edit-recurring-savemode[value="'+sel+'"]').prop('checked', true);
}
else
$('#edit-recurring-warning').hide();
diff --git a/plugins/calendar/drivers/kolab/kolab_calendar.php b/plugins/calendar/drivers/kolab/kolab_calendar.php
index 53c47ad..764f619 100644
--- a/plugins/calendar/drivers/kolab/kolab_calendar.php
+++ b/plugins/calendar/drivers/kolab/kolab_calendar.php
@@ -422,23 +422,33 @@ class kolab_calendar
$i = 0;
$events = array();
$exdates = array();
+ $futuredata = array();
if (is_array($event['recurrence']['EXCEPTIONS'])) {
- foreach ($event['recurrence']['EXCEPTIONS'] as $exception) {
- $rec_event = $this->_to_rcube_event($exception);
- $rec_event['id'] = $event['uid'] . '-' . ++$i;
- $rec_event['recurrence_id'] = $event['uid'];
- $rec_event['_instance'] = $i;
- $events[] = $rec_event;
-
- // found the specifically requested instance, exiting...
- if ($rec_event['id'] == $event_id) {
- $this->events[$rec_event['id']] = $rec_event;
- return $events;
- }
+ // copy the recurrence rule from the master event (to be used in the UI)
+ $recurrence_rule = $event['recurrence'];
+ unset($recurrence_rule['EXCEPTIONS'], $recurrence_rule['EXDATE']);
+
+ foreach ($event['recurrence']['EXCEPTIONS'] as $exception) {
+ $rec_event = $this->_to_rcube_event($exception);
+ $rec_event['id'] = $event['uid'] . '-' . ++$i;
+ $rec_event['recurrence_id'] = $event['uid'];
+ $rec_event['recurrence'] = $recurrence_rule;
+ $rec_event['_instance'] = $i;
+ $events[] = $rec_event;
- // remember this exception's date
- $exdates[$rec_event['start']->format('Y-m-d')] = $rec_event['id'];
+ // found the specifically requested instance, exiting...
+ if ($rec_event['id'] == $event_id) {
+ $this->events[$rec_event['id']] = $rec_event;
+ return $events;
}
+
+ // remember this exception's date
+ $exdate = $rec_event['start']->format('Y-m-d');
+ $exdates[$exdate] = $rec_event['id'];
+ if ($rec_event['thisandfuture']) {
+ $futuredata[$exdate] = $rec_event;
+ }
+ }
}
// use libkolab to compute recurring events
@@ -446,20 +456,29 @@ class kolab_calendar
$recurrence = new kolab_date_recurrence($object);
}
else {
- // fallback to local recurrence implementation
- require_once($this->cal->home . '/lib/calendar_recurrence.php');
- $recurrence = new calendar_recurrence($this->cal, $event);
+ // fallback to local recurrence implementation
+ require_once($this->cal->home . '/lib/calendar_recurrence.php');
+ $recurrence = new calendar_recurrence($this->cal, $event);
}
while ($next_event = $recurrence->next_instance()) {
// skip if there's an exception at this date
- if ($exdates[$next_event['start']->format('Y-m-d')])
+ $datestr = $next_event['start']->format('Y-m-d');
+ if ($exdates[$datestr]) {
+ // use this event data for future recurring instances
+ if ($futuredata[$datestr])
+ $overlay_data = $futuredata[$datestr];
continue;
+ }
// add to output if in range
$rec_id = $event['uid'] . '-' . ++$i;
if (($next_event['start'] <= $end && $next_event['end'] >= $start) || ($event_id && $rec_id == $event_id)) {
$rec_event = $this->_to_rcube_event($next_event);
+
+ if ($overlay_data) // copy data from a 'this-and-future' exception
+ $this->_merge_event_data($rec_event, $overlay_data);
+
$rec_event['id'] = $rec_id;
$rec_event['recurrence_id'] = $event['uid'];
$rec_event['_instance'] = $i;
@@ -483,6 +502,27 @@ class kolab_calendar
}
/**
+ * Merge certain properties from the overlay event to the base event object
+ *
+ * @param array The event object to be altered
+ * @param array The overlay event object to be merged over $event
+ */
+ private function _merge_event_data(&$event, $overlay)
+ {
+ static $forbidden = array('id','uid','created','changed','recurrence','organizer','attendees','sequence');
+
+ foreach ($overlay as $prop => $value) {
+ // adjust time of the recurring event instance
+ if ($prop == 'start' || $prop == 'end') {
+ if (is_object($event[$prop]) && is_a($event[$prop], 'DateTime'))
+ $event[$prop]->setTime($value->format('G'), intval($value->format('i')), intval($value->format('s')));
+ }
+ else if ($prop[0] != '_' && !in_array($prop, $forbidden))
+ $event[$prop] = $value;
+ }
+ }
+
+ /**
* Convert from Kolab_Format to internal representation
*/
private function _to_rcube_event($record)
diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php
index 869837f..bd4a855 100644
--- a/plugins/calendar/drivers/kolab/kolab_driver.php
+++ b/plugins/calendar/drivers/kolab/kolab_driver.php
@@ -421,6 +421,14 @@ class kolab_driver extends calendar_driver
$savemode = $event['_savemode'];
}
+ // removing an exception instance
+ if ($event['recurrence_id']) {
+ $i = $event['_instance'] - 1;
+ if (!empty($master['recurrence']['EXCEPTIONS'][$i])) {
+ unset($master['recurrence']['EXCEPTIONS'][$i]);
+ }
+ }
+
switch ($savemode) {
case 'current':
$_SESSION['calendar_restore_event_data'] = $master;
@@ -582,43 +590,12 @@ class kolab_driver extends calendar_driver
$success = $storage->insert_event($event);
break;
-
+
+ case 'future':
case 'current':
- // save as exception to master event
+ // recurring instances shall not store recurrence rules
$event['recurrence'] = array();
- $master['recurrence']['EXCEPTIONS'][] = $event;
-# $master['recurrence']['EXDATE'][] = $event['start'];
- $success = $storage->update_event($master);
- break;
-
- case 'future':
- if ($master['id'] != $event['id']) {
- // set until-date on master event
- $master['recurrence']['UNTIL'] = clone $old['start'];
- $master['recurrence']['UNTIL']->sub(new DateInterval('P1D'));
- unset($master['recurrence']['COUNT']);
- $storage->update_event($master);
-
- // save this instance as new recurring event
- $event += $old;
- $event['uid'] = $this->cal->generate_uid();
-
- // if recurrence COUNT, update value to the correct number of future occurences
- if ($event['recurrence']['COUNT']) {
- $event['recurrence']['COUNT'] -= $old['_instance'];
- }
-
- // remove fixed weekday, will be re-set to the new weekday in kolab_calendar::insert_event()
- if (strlen($event['recurrence']['BYDAY']) == 2)
- unset($event['recurrence']['BYDAY']);
- if ($master['recurrence']['BYMONTH'] == $master['start']->format('n'))
- unset($event['recurrence']['BYMONTH']);
-
- $success = $storage->insert_event($event);
- break;
- }
-
- default: // 'all' is default
+ $event['thisandfuture'] = $savemode == 'future';
// save properties to a recurrence exception instance
if ($old['recurrence_id']) {
@@ -630,6 +607,12 @@ class kolab_driver extends calendar_driver
}
}
+ // save as new exception to master event
+ $master['recurrence']['EXCEPTIONS'][] = $event;
+ $success = $storage->update_event($master);
+ break;
+
+ default: // 'all' is default
$event['id'] = $master['id'];
$event['uid'] = $master['uid'];
diff --git a/plugins/libkolab/lib/kolab_format_event.php b/plugins/libkolab/lib/kolab_format_event.php
index 7efd06d..4e2cccc 100644
--- a/plugins/libkolab/lib/kolab_format_event.php
+++ b/plugins/libkolab/lib/kolab_format_event.php
@@ -109,7 +109,7 @@ class kolab_format_event extends kolab_format_xcal
foreach((array)$object['recurrence']['EXCEPTIONS'] as $exception) {
$exevent = new kolab_format_event;
$exevent->set($this->compact_exception($exception, $object)); // only save differing values
- $exevent->obj->setRecurrenceID(self::get_datetime($exception['start'], null, true), false);
+ $exevent->obj->setRecurrenceID(self::get_datetime($exception['start'], null, true), (bool)$exception['thisandfuture']);
$vexceptions->push($exevent->obj);
}
$this->obj->setExceptions($vexceptions);
@@ -186,7 +186,7 @@ class kolab_format_event extends kolab_format_xcal
}
// read exception event objects
- if ($exceptions = $this->obj->exceptions()) {
+ if (($exceptions = $this->obj->exceptions()) && $exceptions->size()) {
for ($i=0; $i < $exceptions->size(); $i++) {
if (($exobj = $exceptions->get($i))) {
$exception = new kolab_format_event($exobj);
@@ -196,6 +196,10 @@ class kolab_format_event extends kolab_format_xcal
}
}
}
+ // this is an exception object
+ else if ($this->obj->recurrenceID()->isValid()) {
+ $object['thisandfuture'] = $this->obj->thisAndFuture();
+ }
// merge with additional data, e.g. attachments from the message
if ($data) {
@@ -233,24 +237,15 @@ class kolab_format_event extends kolab_format_xcal
}
/**
- * Reduce the exception container to attributes which differ from the master event
+ * Remove some attributes from the exception container
*/
private function compact_exception($exception, $master)
{
- static $mandatory = array('uid','created','start');
- static $forbidden = array('recurrence','attendees','sequence');
+ static $forbidden = array('recurrence','organizer','attendees','sequence');
$out = $exception;
foreach ($exception as $prop => $val) {
- if (in_array($prop, $mandatory))
- continue;
-
- if (is_object($exception[$prop]) && is_a($exception[$prop], 'DateTime'))
- $equals = $exception[$prop] <> $master[$prop];
- else
- $equals = $exception[$prop] == $master[$prop];
-
- if ($equals || in_array($prop, $forbidden)) {
+ if (in_array($prop, $forbidden)) {
unset($out[$prop]);
}
}
@@ -263,10 +258,8 @@ class kolab_format_event extends kolab_format_xcal
*/
private function expand_exception($exception, $master)
{
- static $forbidden = array('recurrence');
-
foreach ($master as $prop => $value) {
- if (empty($exception[$prop]) && !in_array($prop, $forbidden))
+ if (empty($exception[$prop]) && !empty($value))
$exception[$prop] = $value;
}
commit 1b4b6bb9659c2525f53b29d0186b8c817588cdb8
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Wed Jan 23 17:17:05 2013 +0100
Skip regular recurrences on exception dates
diff --git a/plugins/calendar/drivers/kolab/kolab_calendar.php b/plugins/calendar/drivers/kolab/kolab_calendar.php
index cfe46f9..53c47ad 100644
--- a/plugins/calendar/drivers/kolab/kolab_calendar.php
+++ b/plugins/calendar/drivers/kolab/kolab_calendar.php
@@ -421,6 +421,7 @@ class kolab_calendar
// add recurrence exceptions to output
$i = 0;
$events = array();
+ $exdates = array();
if (is_array($event['recurrence']['EXCEPTIONS'])) {
foreach ($event['recurrence']['EXCEPTIONS'] as $exception) {
$rec_event = $this->_to_rcube_event($exception);
@@ -429,10 +430,14 @@ class kolab_calendar
$rec_event['_instance'] = $i;
$events[] = $rec_event;
+ // found the specifically requested instance, exiting...
if ($rec_event['id'] == $event_id) {
$this->events[$rec_event['id']] = $rec_event;
return $events;
}
+
+ // remember this exception's date
+ $exdates[$rec_event['start']->format('Y-m-d')] = $rec_event['id'];
}
}
@@ -447,11 +452,12 @@ class kolab_calendar
}
while ($next_event = $recurrence->next_instance()) {
- $rec_start = $next_event['start']->format('U');
- $rec_end = $next_event['end']->format('U');
- $rec_id = $event['uid'] . '-' . ++$i;
+ // skip if there's an exception at this date
+ if ($exdates[$next_event['start']->format('Y-m-d')])
+ continue;
// add to output if in range
+ $rec_id = $event['uid'] . '-' . ++$i;
if (($next_event['start'] <= $end && $next_event['end'] >= $start) || ($event_id && $rec_id == $event_id)) {
$rec_event = $this->_to_rcube_event($next_event);
$rec_event['id'] = $rec_id;
commit 91779df09a5a19d7a4a401d8f5c556b6cf6fa8d6
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Wed Jan 23 14:45:41 2013 +0100
Save changes in a recurring event as exception to the master event
diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php
index 5f4cc6b..058b801 100644
--- a/plugins/calendar/calendar.php
+++ b/plugins/calendar/calendar.php
@@ -1096,6 +1096,7 @@ class calendar extends rcube_plugin
$event['recurrence_text'] = $this->_recurrence_text($event['recurrence']);
if ($event['recurrence']['UNTIL'])
$event['recurrence']['UNTIL'] = $this->lib->adjust_timezone($event['recurrence']['UNTIL'])->format('c');
+ unset($event['recurrence']['EXCEPTIONS']);
}
foreach ((array)$event['attachments'] as $k => $attachment) {
@@ -1109,7 +1110,7 @@ class calendar extends rcube_plugin
'title' => strval($event['title']),
'description' => strval($event['description']),
'location' => strval($event['location']),
- 'className' => ($addcss ? 'fc-event-cal-'.asciiwords($event['calendar'], true).' ' : '') . 'fc-event-cat-' . asciiwords(strtolower($event['categories']), true),
+ 'className' => ($addcss ? 'fc-event-cal-'.asciiwords($event['calendar'], true).' ' : '') . 'fc-event-cat-' . asciiwords(strtolower(join('-', (array)$event['categories'])), true),
'allDay' => ($event['allday'] == 1),
) + $event;
}
diff --git a/plugins/calendar/drivers/calendar_driver.php b/plugins/calendar/drivers/calendar_driver.php
index a0d0c52..478a08c 100644
--- a/plugins/calendar/drivers/calendar_driver.php
+++ b/plugins/calendar/drivers/calendar_driver.php
@@ -46,6 +46,7 @@
* 'COUNT' => 1..n, // number of times
* // + more properties (see http://www.kanzaki.com/docs/ical/recur.html)
* 'EXDATE' => array(), // list of DateTime objects of exception Dates/Times
+ * 'EXCEPTIONS' => array(<event>), list of event objects which denote exceptions in the recurrence chain
* ),
* 'recurrence_id' => 'ID of the recurrence group', // usually the ID of the starting event
* 'categories' => 'Event category',
diff --git a/plugins/calendar/drivers/kolab/kolab_calendar.php b/plugins/calendar/drivers/kolab/kolab_calendar.php
index 45db638..cfe46f9 100644
--- a/plugins/calendar/drivers/kolab/kolab_calendar.php
+++ b/plugins/calendar/drivers/kolab/kolab_calendar.php
@@ -312,7 +312,7 @@ class kolab_calendar
* @return boolean True on success, False on error
*/
- public function update_event($event)
+ public function update_event($event, $exception_id = null)
{
$updated = false;
$old = $this->storage->get_object($event['id']);
@@ -333,6 +333,11 @@ class kolab_calendar
else {
$updated = true;
$this->events[$event['id']] = $this->_to_rcube_event($object);
+
+ // refresh local cache with recurring instances
+ if ($exception_id) {
+ $this->_get_recurring_events($object, $event['start'], $event['end'], $exception_id);
+ }
}
return $updated;
@@ -413,6 +418,24 @@ class kolab_calendar
$end->add(new DateInterval($intvl));
}
+ // add recurrence exceptions to output
+ $i = 0;
+ $events = array();
+ if (is_array($event['recurrence']['EXCEPTIONS'])) {
+ foreach ($event['recurrence']['EXCEPTIONS'] as $exception) {
+ $rec_event = $this->_to_rcube_event($exception);
+ $rec_event['id'] = $event['uid'] . '-' . ++$i;
+ $rec_event['recurrence_id'] = $event['uid'];
+ $rec_event['_instance'] = $i;
+ $events[] = $rec_event;
+
+ if ($rec_event['id'] == $event_id) {
+ $this->events[$rec_event['id']] = $rec_event;
+ return $events;
+ }
+ }
+ }
+
// use libkolab to compute recurring events
if (class_exists('kolabcalendaring')) {
$recurrence = new kolab_date_recurrence($object);
@@ -423,8 +446,6 @@ class kolab_calendar
$recurrence = new calendar_recurrence($this->cal, $event);
}
- $i = 0;
- $events = array();
while ($next_event = $recurrence->next_instance()) {
$rec_start = $next_event['start']->format('U');
$rec_end = $next_event['end']->format('U');
diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php
index 886f280..869837f 100644
--- a/plugins/calendar/drivers/kolab/kolab_driver.php
+++ b/plugins/calendar/drivers/kolab/kolab_driver.php
@@ -565,6 +565,8 @@ class kolab_driver extends calendar_driver
// keep saved exceptions (not submitted by the client)
if ($old['recurrence']['EXDATE'])
$event['recurrence']['EXDATE'] = $old['recurrence']['EXDATE'];
+ if ($old['recurrence']['EXCEPTIONS'])
+ $event['recurrence']['EXCEPTIONS'] = $old['recurrence']['EXCEPTIONS'];
switch ($savemode) {
case 'new':
@@ -582,26 +584,11 @@ class kolab_driver extends calendar_driver
break;
case 'current':
- // modifying the first instance => just move to next occurence
- if ($master['id'] == $event['id']) {
- $recurring = reset($storage->_get_recurring_events($event, $event['start'], null, $event['id'].'-1'));
- $master['start'] = $recurring['start'];
- $master['end'] = $recurring['end'];
- if ($master['recurrence']['COUNT'])
- $master['recurrence']['COUNT']--;
- }
- else { // add exception to master event
- $master['recurrence']['EXDATE'][] = $old['start'];
- }
-
- $storage->update_event($master);
-
- // insert new event for this occurence
- $event += $old;
+ // save as exception to master event
$event['recurrence'] = array();
- unset($event['recurrence_id']);
- $event['uid'] = $this->cal->generate_uid();
- $success = $storage->insert_event($event);
+ $master['recurrence']['EXCEPTIONS'][] = $event;
+# $master['recurrence']['EXDATE'][] = $event['start'];
+ $success = $storage->update_event($master);
break;
case 'future':
@@ -632,6 +619,17 @@ class kolab_driver extends calendar_driver
}
default: // 'all' is default
+
+ // save properties to a recurrence exception instance
+ if ($old['recurrence_id']) {
+ $i = $old['_instance'] - 1;
+ if (!empty($master['recurrence']['EXCEPTIONS'][$i])) {
+ $master['recurrence']['EXCEPTIONS'][$i] = $event;
+ $success = $storage->update_event($master, $old['id']);
+ break;
+ }
+ }
+
$event['id'] = $master['id'];
$event['uid'] = $master['uid'];
diff --git a/plugins/libcalendaring/libcalendaring.php b/plugins/libcalendaring/libcalendaring.php
index 24f98cb..aa4ec5e 100644
--- a/plugins/libcalendaring/libcalendaring.php
+++ b/plugins/libcalendaring/libcalendaring.php
@@ -107,6 +107,8 @@ class libcalendaring extends rcube_plugin
$dt = new DateTime('@'.$td);
else if (is_string($dt))
$dt = new DateTime($dt);
+ else
+ return $dt;
$dt->setTimezone($this->timezone);
return $dt;
diff --git a/plugins/libkolab/lib/kolab_format_event.php b/plugins/libkolab/lib/kolab_format_event.php
index d750f8e..7efd06d 100644
--- a/plugins/libkolab/lib/kolab_format_event.php
+++ b/plugins/libkolab/lib/kolab_format_event.php
@@ -30,6 +30,19 @@ class kolab_format_event extends kolab_format_xcal
protected $read_func = 'readEvent';
protected $write_func = 'writeEvent';
+ /**
+ * Default constructor
+ */
+ function __construct($data = null, $version = 3.0)
+ {
+ parent::__construct(is_string($data) ? $data : null, $version);
+
+ // got an Event object as argument
+ if (is_object($data) && is_a($data, $this->objclass)) {
+ $this->obj = $data;
+ $this->loaded = true;
+ }
+ }
/**
* Clones into an instance of libcalendaring's extended EventCal class
@@ -90,6 +103,18 @@ class kolab_format_event extends kolab_format_xcal
}
$this->obj->setAttachments($vattach);
+ // save recurrence exceptions
+ if ($object['recurrence']['EXCEPTIONS']) {
+ $vexceptions = new vectorevent;
+ foreach((array)$object['recurrence']['EXCEPTIONS'] as $exception) {
+ $exevent = new kolab_format_event;
+ $exevent->set($this->compact_exception($exception, $object)); // only save differing values
+ $exevent->obj->setRecurrenceID(self::get_datetime($exception['start'], null, true), false);
+ $vexceptions->push($exevent->obj);
+ }
+ $this->obj->setExceptions($vexceptions);
+ }
+
// cache this data
$this->data = $object;
unset($this->data['_formatobj']);
@@ -160,6 +185,18 @@ class kolab_format_event extends kolab_format_xcal
}
}
+ // read exception event objects
+ if ($exceptions = $this->obj->exceptions()) {
+ for ($i=0; $i < $exceptions->size(); $i++) {
+ if (($exobj = $exceptions->get($i))) {
+ $exception = new kolab_format_event($exobj);
+ if ($exception->is_valid()) {
+ $object['recurrence']['EXCEPTIONS'][] = $this->expand_exception($exception->to_array(), $object);
+ }
+ }
+ }
+ }
+
// merge with additional data, e.g. attachments from the message
if ($data) {
foreach ($data as $idx => $value) {
@@ -195,4 +232,45 @@ class kolab_format_event extends kolab_format_xcal
return $tags;
}
+ /**
+ * Reduce the exception container to attributes which differ from the master event
+ */
+ private function compact_exception($exception, $master)
+ {
+ static $mandatory = array('uid','created','start');
+ static $forbidden = array('recurrence','attendees','sequence');
+
+ $out = $exception;
+ foreach ($exception as $prop => $val) {
+ if (in_array($prop, $mandatory))
+ continue;
+
+ if (is_object($exception[$prop]) && is_a($exception[$prop], 'DateTime'))
+ $equals = $exception[$prop] <> $master[$prop];
+ else
+ $equals = $exception[$prop] == $master[$prop];
+
+ if ($equals || in_array($prop, $forbidden)) {
+ unset($out[$prop]);
+ }
+ }
+
+ return $out;
+ }
+
+ /**
+ * Copy attributes not specified by the exception from the master event
+ */
+ private function expand_exception($exception, $master)
+ {
+ static $forbidden = array('recurrence');
+
+ foreach ($master as $prop => $value) {
+ if (empty($exception[$prop]) && !in_array($prop, $forbidden))
+ $exception[$prop] = $value;
+ }
+
+ return $exception;
+ }
+
}
diff --git a/plugins/libkolab/lib/kolab_format_xcal.php b/plugins/libkolab/lib/kolab_format_xcal.php
index a6450af..bc50c4b 100644
--- a/plugins/libkolab/lib/kolab_format_xcal.php
+++ b/plugins/libkolab/lib/kolab_format_xcal.php
@@ -113,7 +113,7 @@ abstract class kolab_format_xcal extends kolab_format
);
// read organizer and attendees
- if ($organizer = $this->obj->organizer()) {
+ if (($organizer = $this->obj->organizer()) && ($organizer->email() || $organizer->name())) {
$object['organizer'] = array(
'email' => $organizer->email(),
'name' => $organizer->name(),
@@ -170,9 +170,9 @@ abstract class kolab_format_xcal extends kolab_format
$object['recurrence']['BYMONTH'] = join(',', self::vector2array($bymonth));
}
- if ($exceptions = $this->obj->exceptionDates()) {
- for ($i=0; $i < $exceptions->size(); $i++) {
- if ($exdate = self::php_datetime($exceptions->get($i)))
+ if ($exdates = $this->obj->exceptionDates()) {
+ for ($i=0; $i < $exdates->size(); $i++) {
+ if ($exdate = self::php_datetime($exdates->get($i)))
$object['recurrence']['EXDATE'][] = $exdate;
}
}
More information about the commits
mailing list