Fixes for importing iCalendar files with RECURRENCE-ID set.
authorMichael J. Rubinsky <mrubinsk@horde.org>
Sun, 11 Jul 2010 14:56:58 +0000 (10:56 -0400)
committerMichael J. Rubinsky <mrubinsk@horde.org>
Sun, 11 Jul 2010 14:56:58 +0000 (10:56 -0400)
The original recurring event must be imported before any instances of
RECURRENCE-ID exceptions or the original event will not be found to modify.

kronolith/data.php
kronolith/lib/Api.php

index 8d7fefd..4bda27d 100644 (file)
@@ -244,6 +244,7 @@ if (is_array($next_step)) {
         }
     }
 
+    $recurrences = array();
     foreach ($next_step as $row) {
         if ($max_events !== true && $num_events >= $max_events) {
             try {
@@ -264,7 +265,15 @@ if (is_array($next_step)) {
             break;
         }
         if ($row instanceof Horde_iCalendar_vevent) {
-            $event->fromiCalendar($row);
+            // RECURRENCE-ID entries must be imported after the original
+            // recurring event is imported.
+            $recurrence = $row->getAttribute('RECURRENCE-ID');
+            if (!($recurrence instanceof PEAR_Error)) {
+                $recurrences[] = $row;
+                continue;
+            } else {
+                $event->fromiCalendar($row);
+            }
         } elseif ($row instanceof Horde_iCalendar) {
             // Skip other iCalendar components for now.
             continue;
@@ -290,6 +299,20 @@ if (is_array($next_step)) {
             $num_events++;
         }
     }
+
+    // Any RECURRENCE-ID entries?
+    foreach ($recurrences as $recurrence) {
+        $event = $kronolith_driver->getEvent();
+        $event->fromiCalendar($row);
+        try {
+            $event->save();
+        } catch (Exception $e) {
+            $notification->push($e, 'horde.error');
+            $error = true;
+            break;
+        }
+    }
+
     if (!$error) {
         $notification->push(sprintf(_("%s file successfully imported"),
                                     $file_types[$_SESSION['import_data']['format']]), 'horde.success');
index 24f3e09..acb5ea9 100644 (file)
@@ -642,42 +642,32 @@ class Kronolith_Api extends Horde_Registry_Api
             }
 
             $ids = array();
+            $recurrences = array();
             foreach ($components as $content) {
                 if ($content instanceof Horde_iCalendar_vevent) {
-                    $event = $kronolith_driver->getEvent();
-                    $event->fromiCalendar($content);
-                    // Check if the entry already exists in the data source,
-                    // first by UID.
-                    $uid = $event->uid;
-                    try {
-                        $existing_event = $kronolith_driver->getByUID($uid, array($calendar));
-                        throw new Kronolith_Exception(_("Already Exists"),
-                                                      'horde.message', null, null, $uid);
-                    } catch (Horde_Exception $e) {
-                    }
-                    $result = $kronolith_driver->search($event);
-                    // Check if the match really is an exact match:
-                    if (is_array($result) && count($result) > 0) {
-                        foreach($result as $match) {
-                            if ($match->start == $event->start &&
-                                $match->end == $event->end &&
-                                $match->title == $event->title &&
-                                $match->location == $event->location &&
-                                $match->hasPermission(Horde_Perms::EDIT)) {
-                                    throw new Kronolith_Exception(_("Already Exists"), 'horde.message', null, null, $match->uid);
-                                }
-                        }
+                    // Need to ensure that the original recurring event is
+                    // added before any of the instance exceptions. Easiest way
+                    // to do that is just add all the recurrence-id entries last
+                    $recurrenceId = $content->getAttribute('RECURRENCE-ID');
+                    if (!($recurrenceId instanceof PEAR_Error)) {
+                        $recurrences[] = $content;
+                    } else {
+                        $ids[] = $this->_addiCalEvent($content, $kronolith_driver);
                     }
-
-                    $eventId = $event->save();
-                    $ids[] = $event->uid;
                 }
             }
+            
             if (count($ids) == 0) {
                 throw new Kronolith_Exception(_("No iCalendar data was found."));
             } else if (count($ids) == 1) {
                 return $ids[0];
             }
+
+            // Now add all the exception instances
+            foreach ($recurrences as $recurrence) {
+                $ids[] = $this->_addiCalEvent($recurrence, $kronolith_driver);
+            }
+
             return $ids;
 
             case 'activesync':
@@ -691,6 +681,43 @@ class Kronolith_Api extends Horde_Registry_Api
     }
 
     /**
+     * Imports a single vEvent part to storage.
+     *
+     * @param Horde_iCalendar_vEvent $content  The vEvent part
+     * @param Kronolith_Driver $driver         The kronolith driver
+     *
+     * @return string  The new event's uid
+     */
+    protected function _addiCalEvent($content, $driver)
+    {
+        $event = $driver->getEvent();
+        $event->fromiCalendar($content);
+        // Check if the entry already exists in the data source,
+        // first by UID.
+        $uid = $event->uid;
+        try {
+            $existing_event = $driver->getByUID($uid, array($driver->calendar));
+            throw new Kronolith_Exception(_("Already Exists"), 'horde.message', null, null, $uid);
+        } catch (Horde_Exception $e) {}
+        $result = $driver->search($event);
+        // Check if the match really is an exact match:
+        if (is_array($result) && count($result) > 0) {
+            foreach($result as $match) {
+                if ($match->start == $event->start &&
+                    $match->end == $event->end &&
+                    $match->title == $event->title &&
+                    $match->location == $event->location &&
+                    $match->hasPermission(Horde_Perms::EDIT)) {
+                        throw new Kronolith_Exception(_("Already Exists"), 'horde.message', null, null, $match->uid);
+                    }
+            }
+        }
+        $event->save();
+
+        return $event->uid;
+    }
+
+    /**
      * Imports an event parsed from a string.
      *
      * @param string $text      The text to parse into an event