Save changes in a recurring event as exception to the master event
This commit is contained in:
parent
53c77796dd
commit
91779df09a
7 changed files with 128 additions and 27 deletions
|
@ -1096,6 +1096,7 @@ class calendar extends rcube_plugin
|
||||||
$event['recurrence_text'] = $this->_recurrence_text($event['recurrence']);
|
$event['recurrence_text'] = $this->_recurrence_text($event['recurrence']);
|
||||||
if ($event['recurrence']['UNTIL'])
|
if ($event['recurrence']['UNTIL'])
|
||||||
$event['recurrence']['UNTIL'] = $this->lib->adjust_timezone($event['recurrence']['UNTIL'])->format('c');
|
$event['recurrence']['UNTIL'] = $this->lib->adjust_timezone($event['recurrence']['UNTIL'])->format('c');
|
||||||
|
unset($event['recurrence']['EXCEPTIONS']);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ((array)$event['attachments'] as $k => $attachment) {
|
foreach ((array)$event['attachments'] as $k => $attachment) {
|
||||||
|
@ -1109,7 +1110,7 @@ class calendar extends rcube_plugin
|
||||||
'title' => strval($event['title']),
|
'title' => strval($event['title']),
|
||||||
'description' => strval($event['description']),
|
'description' => strval($event['description']),
|
||||||
'location' => strval($event['location']),
|
'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),
|
'allDay' => ($event['allday'] == 1),
|
||||||
) + $event;
|
) + $event;
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
* 'COUNT' => 1..n, // number of times
|
* 'COUNT' => 1..n, // number of times
|
||||||
* // + more properties (see http://www.kanzaki.com/docs/ical/recur.html)
|
* // + more properties (see http://www.kanzaki.com/docs/ical/recur.html)
|
||||||
* 'EXDATE' => array(), // list of DateTime objects of exception Dates/Times
|
* '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
|
* 'recurrence_id' => 'ID of the recurrence group', // usually the ID of the starting event
|
||||||
* 'categories' => 'Event category',
|
* 'categories' => 'Event category',
|
||||||
|
|
|
@ -312,7 +312,7 @@ class kolab_calendar
|
||||||
* @return boolean True on success, False on error
|
* @return boolean True on success, False on error
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public function update_event($event)
|
public function update_event($event, $exception_id = null)
|
||||||
{
|
{
|
||||||
$updated = false;
|
$updated = false;
|
||||||
$old = $this->storage->get_object($event['id']);
|
$old = $this->storage->get_object($event['id']);
|
||||||
|
@ -333,6 +333,11 @@ class kolab_calendar
|
||||||
else {
|
else {
|
||||||
$updated = true;
|
$updated = true;
|
||||||
$this->events[$event['id']] = $this->_to_rcube_event($object);
|
$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;
|
return $updated;
|
||||||
|
@ -413,6 +418,24 @@ class kolab_calendar
|
||||||
$end->add(new DateInterval($intvl));
|
$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
|
// use libkolab to compute recurring events
|
||||||
if (class_exists('kolabcalendaring')) {
|
if (class_exists('kolabcalendaring')) {
|
||||||
$recurrence = new kolab_date_recurrence($object);
|
$recurrence = new kolab_date_recurrence($object);
|
||||||
|
@ -423,8 +446,6 @@ class kolab_calendar
|
||||||
$recurrence = new calendar_recurrence($this->cal, $event);
|
$recurrence = new calendar_recurrence($this->cal, $event);
|
||||||
}
|
}
|
||||||
|
|
||||||
$i = 0;
|
|
||||||
$events = array();
|
|
||||||
while ($next_event = $recurrence->next_instance()) {
|
while ($next_event = $recurrence->next_instance()) {
|
||||||
$rec_start = $next_event['start']->format('U');
|
$rec_start = $next_event['start']->format('U');
|
||||||
$rec_end = $next_event['end']->format('U');
|
$rec_end = $next_event['end']->format('U');
|
||||||
|
|
|
@ -565,6 +565,8 @@ class kolab_driver extends calendar_driver
|
||||||
// keep saved exceptions (not submitted by the client)
|
// keep saved exceptions (not submitted by the client)
|
||||||
if ($old['recurrence']['EXDATE'])
|
if ($old['recurrence']['EXDATE'])
|
||||||
$event['recurrence']['EXDATE'] = $old['recurrence']['EXDATE'];
|
$event['recurrence']['EXDATE'] = $old['recurrence']['EXDATE'];
|
||||||
|
if ($old['recurrence']['EXCEPTIONS'])
|
||||||
|
$event['recurrence']['EXCEPTIONS'] = $old['recurrence']['EXCEPTIONS'];
|
||||||
|
|
||||||
switch ($savemode) {
|
switch ($savemode) {
|
||||||
case 'new':
|
case 'new':
|
||||||
|
@ -582,26 +584,11 @@ class kolab_driver extends calendar_driver
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'current':
|
case 'current':
|
||||||
// modifying the first instance => just move to next occurence
|
// save as exception to master event
|
||||||
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;
|
|
||||||
$event['recurrence'] = array();
|
$event['recurrence'] = array();
|
||||||
unset($event['recurrence_id']);
|
$master['recurrence']['EXCEPTIONS'][] = $event;
|
||||||
$event['uid'] = $this->cal->generate_uid();
|
# $master['recurrence']['EXDATE'][] = $event['start'];
|
||||||
$success = $storage->insert_event($event);
|
$success = $storage->update_event($master);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'future':
|
case 'future':
|
||||||
|
@ -632,6 +619,17 @@ class kolab_driver extends calendar_driver
|
||||||
}
|
}
|
||||||
|
|
||||||
default: // 'all' is default
|
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['id'] = $master['id'];
|
||||||
$event['uid'] = $master['uid'];
|
$event['uid'] = $master['uid'];
|
||||||
|
|
||||||
|
|
|
@ -107,6 +107,8 @@ class libcalendaring extends rcube_plugin
|
||||||
$dt = new DateTime('@'.$td);
|
$dt = new DateTime('@'.$td);
|
||||||
else if (is_string($dt))
|
else if (is_string($dt))
|
||||||
$dt = new DateTime($dt);
|
$dt = new DateTime($dt);
|
||||||
|
else
|
||||||
|
return $dt;
|
||||||
|
|
||||||
$dt->setTimezone($this->timezone);
|
$dt->setTimezone($this->timezone);
|
||||||
return $dt;
|
return $dt;
|
||||||
|
|
|
@ -30,6 +30,19 @@ class kolab_format_event extends kolab_format_xcal
|
||||||
protected $read_func = 'readEvent';
|
protected $read_func = 'readEvent';
|
||||||
protected $write_func = 'writeEvent';
|
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
|
* 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);
|
$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
|
// cache this data
|
||||||
$this->data = $object;
|
$this->data = $object;
|
||||||
unset($this->data['_formatobj']);
|
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
|
// merge with additional data, e.g. attachments from the message
|
||||||
if ($data) {
|
if ($data) {
|
||||||
foreach ($data as $idx => $value) {
|
foreach ($data as $idx => $value) {
|
||||||
|
@ -195,4 +232,45 @@ class kolab_format_event extends kolab_format_xcal
|
||||||
return $tags;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,7 +113,7 @@ abstract class kolab_format_xcal extends kolab_format
|
||||||
);
|
);
|
||||||
|
|
||||||
// read organizer and attendees
|
// read organizer and attendees
|
||||||
if ($organizer = $this->obj->organizer()) {
|
if (($organizer = $this->obj->organizer()) && ($organizer->email() || $organizer->name())) {
|
||||||
$object['organizer'] = array(
|
$object['organizer'] = array(
|
||||||
'email' => $organizer->email(),
|
'email' => $organizer->email(),
|
||||||
'name' => $organizer->name(),
|
'name' => $organizer->name(),
|
||||||
|
@ -170,9 +170,9 @@ abstract class kolab_format_xcal extends kolab_format
|
||||||
$object['recurrence']['BYMONTH'] = join(',', self::vector2array($bymonth));
|
$object['recurrence']['BYMONTH'] = join(',', self::vector2array($bymonth));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($exceptions = $this->obj->exceptionDates()) {
|
if ($exdates = $this->obj->exceptionDates()) {
|
||||||
for ($i=0; $i < $exceptions->size(); $i++) {
|
for ($i=0; $i < $exdates->size(); $i++) {
|
||||||
if ($exdate = self::php_datetime($exceptions->get($i)))
|
if ($exdate = self::php_datetime($exdates->get($i)))
|
||||||
$object['recurrence']['EXDATE'][] = $exdate;
|
$object['recurrence']['EXDATE'][] = $exdate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue