4 commits - plugins/calendar plugins/libcalendaring plugins/libkolab

Thomas Brüderli bruederli at kolabsys.com
Thu Feb 26 15:05:58 CET 2015


 plugins/calendar/calendar.php                      |    4 
 plugins/calendar/drivers/kolab/kolab_calendar.php  |   37 +++---
 plugins/calendar/drivers/kolab/kolab_driver.php    |  122 +++++++++++++++++----
 plugins/libcalendaring/lib/libcalendaring_itip.php |    2 
 plugins/libkolab/lib/kolab_format_event.php        |   69 ++++++++++-
 plugins/libkolab/lib/kolab_format_xcal.php         |    8 -
 plugins/libkolab/lib/kolab_storage_cache_event.php |   18 +++
 7 files changed, 211 insertions(+), 49 deletions(-)

New commits:
commit 899646afd824cc9a57cba6f5c9a4971eab395d07
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Feb 26 14:48:14 2015 +0100

    Handle single event occurrences with the same UID (#4722)

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']);
   }
 
   /**


commit ef017d4eb059907ce7581f5e1b7842271eeb8efd
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Feb 26 14:46:51 2015 +0100

    Also request RSVP when creating a new event with sequence=0

diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php
index d247dd8..92f8340 100644
--- a/plugins/calendar/calendar.php
+++ b/plugins/calendar/calendar.php
@@ -2009,7 +2009,7 @@ class calendar extends rcube_plugin
 
     // compose multipart message using PEAR:Mail_Mime
     $method = $action == 'remove' ? 'CANCEL' : 'REQUEST';
-    $message = $itip->compose_itip_message($event, $method, $event['sequence'] > $old['sequence']);
+    $message = $itip->compose_itip_message($event, $method, !$old || $event['sequence'] > $old['sequence']);
 
     // list existing attendees from $old event
     $old_attendees = array();
diff --git a/plugins/libcalendaring/lib/libcalendaring_itip.php b/plugins/libcalendaring/lib/libcalendaring_itip.php
index 14dacf4..a67a380 100644
--- a/plugins/libcalendaring/lib/libcalendaring_itip.php
+++ b/plugins/libcalendaring/lib/libcalendaring_itip.php
@@ -250,7 +250,7 @@ class libcalendaring_itip
         // set RSVP for every attendee
         else if ($method == 'REQUEST') {
             foreach ($event['attendees'] as $i => $attendee) {
-                if ($attendee['status'] != 'DELEGATED') {
+                if (($rsvp || !isset($attendee['rsvp'])) && ($attendee['status'] != 'DELEGATED' && $attendee['role'] != 'NON-PARTICIPANT')) {
                     $event['attendees'][$i]['rsvp']= (bool)$rsvp;
                 }
             }


commit 31ad93a62e93cf12fbe7dcc885e077026d7655f4
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Feb 26 11:25:29 2015 +0100

    Provide access to exception instances outside of a recurring event context (#4722)

diff --git a/plugins/libkolab/lib/kolab_format_event.php b/plugins/libkolab/lib/kolab_format_event.php
index 91efb26..3207927 100644
--- a/plugins/libkolab/lib/kolab_format_event.php
+++ b/plugins/libkolab/lib/kolab_format_event.php
@@ -96,11 +96,15 @@ class kolab_format_event extends kolab_format_xcal
             $status = $this->status_map[$object['status']];
         $this->obj->setStatus($status);
 
-        // save recurrence exceptions
-        if (is_array($object['recurrence']) && is_array($object['recurrence']['EXCEPTIONS'])) {
+        // save (recurrence) exceptions
+        if (is_array($object['recurrence']) && is_array($object['recurrence']['EXCEPTIONS']) && !isset($object['exceptions'])) {
+            $object['exceptions'] = $object['recurrence']['EXCEPTIONS'];
+        }
+
+        if (is_array($object['exceptions'])) {
             $recurrence_id_format = $object['allday'] ? 'Ymd' : 'Ymd\THis';
             $vexceptions = new vectorevent;
-            foreach((array)$object['recurrence']['EXCEPTIONS'] as $i => $exception) {
+            foreach ($object['exceptions'] as $i => $exception) {
                 $exevent = new kolab_format_event;
                 $exevent->set(($compacted = $this->compact_exception($exception, $object)));  // only save differing values
 
@@ -118,11 +122,17 @@ class kolab_format_event extends kolab_format_xcal
                 $vexceptions->push($exevent->obj);
 
                 // write cleaned-up exception data back to memory/cache
-                $object['recurrence']['EXCEPTIONS'][$i] = $this->expand_exception($exevent->data, $object);
+                $object['exceptions'][$i] = $this->expand_exception($exevent->data, $object);
             }
             $this->obj->setExceptions($vexceptions);
+
+            // link with recurrence.EXCEPTIONS for compatibility
+            if (is_array($object['recurrence'])) {
+                $object['recurrence']['EXCEPTIONS'] = &$object['exceptions'];
+            }
         }
-        else if ($object['recurrence_date'] && $object['recurrence_date'] instanceof DateTime) {
+
+        if ($object['recurrence_date'] && $object['recurrence_date'] instanceof DateTime) {
             $this->obj->setRecurrenceID(self::get_datetime($object['recurrence_date'], null, $object['allday']), (bool)$object['thisandfuture']);
         }
 
@@ -194,7 +204,7 @@ class kolab_format_event extends kolab_format_xcal
             $object['recurrence_date'] = self::php_datetime($this->obj->recurrenceID());
         }
         // read exception event objects
-        else if (($exceptions = $this->obj->exceptions()) && is_object($exceptions) && $exceptions->size()) {
+        if (($exceptions = $this->obj->exceptions()) && is_object($exceptions) && $exceptions->size()) {
             $recurrence_exceptions = array();
             $recurrence_id_format = $object['allday'] ? 'Ymd' : 'Ymd\THis';
             for ($i=0; $i < $exceptions->size(); $i++) {
@@ -215,13 +225,54 @@ class kolab_format_event extends kolab_format_xcal
                     }
                 }
             }
-            $object['recurrence']['EXCEPTIONS'] = $recurrence_exceptions;
+            $object['exceptions'] = $recurrence_exceptions;
+
+            // also link with recurrence.EXCEPTIONS for compatibility
+            if (is_array($object['recurrence'])) {
+                $object['recurrence']['EXCEPTIONS'] = &$object['exceptions'];
+            }
         }
 
         return $this->data = $object;
     }
 
     /**
+     * Getter for a single instance from a recurrence series or stored subcomponents
+     *
+     * @param mixed The recurrence-id of the requested instance, either as string or a DateTime object
+     * @return array Event data as hash array or null if not found
+     */
+    public function get_instance($recurrence_id)
+    {
+        $result = null;
+        $object = $this->to_array();
+
+        $recurrence_id_format = $object['allday'] ? 'Ymd' : 'Ymd\THis';
+        $instance_id = $recurrence_id instanceof DateTime ? $recurrence_id->format($recurrence_id_format) : strval($recurrence_id);
+
+        if ($object['recurrence_date'] instanceof DateTime) {
+            if ($object['recurrence_date']->format($recurrence_id_format) == $instance_id) {
+                $result = $object;
+            }
+        }
+
+        if (!$result && is_array($object['exceptions'])) {
+            foreach ($object['exceptions'] as $exception) {
+                if ($exception['_instance'] == $instance_id) {
+                    $result = $exception;
+                    $result['isexception'] = 1;
+                    break;
+                }
+            }
+        }
+
+        // TODO: compute instances from recurrence rule and return the matching instance
+        // clone from plugins/calendar/drivers/kolab/kolab_calendar::get_recurring_events()
+
+        return $result;
+    }
+
+    /**
      * Callback for kolab_storage_cache to get object specific tags to cache
      *
      * @return array List of tags to save in cache
@@ -262,8 +313,10 @@ class kolab_format_event extends kolab_format_xcal
      */
     private function expand_exception($exception, $master)
     {
+        $is_recurring = !empty($master['recurrence']);
+
         foreach ($master as $prop => $value) {
-            if (empty($exception[$prop]) && !empty($value)) {
+            if (empty($exception[$prop]) && !empty($value) && ($is_recurring || in_array($prop, array('uid','organizer','_attachments')))) {
                 $exception[$prop] = $value;
                 if ($prop == 'recurrence') {
                     unset($exception[$prop]['EXCEPTIONS']);
diff --git a/plugins/libkolab/lib/kolab_format_xcal.php b/plugins/libkolab/lib/kolab_format_xcal.php
index 3a9ad1f..605d557 100644
--- a/plugins/libkolab/lib/kolab_format_xcal.php
+++ b/plugins/libkolab/lib/kolab_format_xcal.php
@@ -597,8 +597,8 @@ abstract class kolab_format_xcal extends kolab_format
         $words = rcube_utils::normalize_string($data, true);
 
         // collect words from recurrence exceptions
-        if (is_array($object['recurrence']) && $object['recurrence']['EXCEPTIONS']) {
-            foreach((array)$object['recurrence']['EXCEPTIONS'] as $exception) {
+        if (is_array($object['exceptions'])) {
+            foreach ($object['exceptions'] as $exception) {
                 $words = array_merge($words, $this->get_words($exception));
             }
         }
@@ -629,8 +629,8 @@ abstract class kolab_format_xcal extends kolab_format
         }
 
         // collect tags from recurrence exceptions
-        if (is_array($object['recurrence']) && $object['recurrence']['EXCEPTIONS']) {
-            foreach((array)$object['recurrence']['EXCEPTIONS'] as $exception) {
+        if (is_array($object['exceptions'])) {
+            foreach ($object['exceptions'] as $exception) {
                 $tags = array_merge($tags, $this->get_tags($exception));
             }
         }
diff --git a/plugins/libkolab/lib/kolab_storage_cache_event.php b/plugins/libkolab/lib/kolab_storage_cache_event.php
index 5fc44cd..e25bd89 100644
--- a/plugins/libkolab/lib/kolab_storage_cache_event.php
+++ b/plugins/libkolab/lib/kolab_storage_cache_event.php
@@ -44,6 +44,24 @@ class kolab_storage_cache_event extends kolab_storage_cache
             $sql_data['dtend'] = $dtend->format(self::DB_DATE_FORMAT);
         }
 
+        // extend start/end dates to spawn all exceptions
+        if (is_array($object['exceptions'])) {
+            foreach ($object['exceptions'] as $exception) {
+                if (is_a($exception['start'], 'DateTime')) {
+                    $exstart = $exception['start']->format(self::DB_DATE_FORMAT);
+                    if ($exstart < $sql_data['dtstart']) {
+                        $sql_data['dtstart'] = $exstart;
+                    }
+                }
+                if (is_a($exception['end'], 'DateTime')) {
+                    $exend = $exception['end']->format(self::DB_DATE_FORMAT);
+                    if ($exend > $sql_data['dtend']) {
+                        $sql_data['dtend'] = $exend;
+                    }
+                }
+            }
+        }
+
         return $sql_data;
     }
 }
\ No newline at end of file


commit 1c590d3969f95eec73ea9c30207780e8f0b46a1c
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Feb 25 17:25:47 2015 +0100

    Fix display of all-day events in iTip invitation mini-calendar

diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php
index 8febb40..d247dd8 100644
--- a/plugins/calendar/calendar.php
+++ b/plugins/calendar/calendar.php
@@ -2573,7 +2573,7 @@ class calendar extends rcube_plugin
    */
   private function mail_agenda_event_row($event, $class = '')
   {
-    $time = $event['all-day'] ? $this->gettext('allday') :
+    $time = $event['allday'] ? $this->gettext('all-day') :
       $this->rc->format_date($event['start'], $this->rc->config->get('time_format')) . ' - ' .
         $this->rc->format_date($event['end'], $this->rc->config->get('time_format'));
 




More information about the commits mailing list