Branch 'dev/recurrence-exceptions' - plugins/calendar plugins/libkolab

Thomas Brüderli bruederli at kolabsys.com
Thu Feb 14 16:17:38 CET 2013


 plugins/calendar/calendar_ui.js                   |    3 
 plugins/calendar/drivers/kolab/kolab_calendar.php |   76 ++++++++++++++++------
 plugins/calendar/drivers/kolab/kolab_driver.php   |   53 +++++----------
 plugins/libkolab/lib/kolab_format_event.php       |   27 ++-----
 4 files changed, 88 insertions(+), 71 deletions(-)

New commits:
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;
       }
 





More information about the commits mailing list