2 commits - plugins/libcalendaring plugins/libkolab

Thomas Brüderli bruederli at kolabsys.com
Thu Sep 19 11:01:24 CEST 2013


 plugins/libcalendaring/libcalendaring.php                |    2 
 plugins/libcalendaring/libvcalendar.php                  |   28 ++++++-----
 plugins/libcalendaring/tests/libvcalendar.php            |   20 +++++++
 plugins/libcalendaring/tests/resources/invalid-event.ics |   14 +++++
 plugins/libcalendaring/tests/resources/vtodo.ics         |   38 +++++++++++++++
 plugins/libkolab/lib/kolab_format.php                    |   12 +++-
 plugins/libkolab/lib/kolab_format_xcal.php               |    2 
 7 files changed, 101 insertions(+), 15 deletions(-)

New commits:
commit 4700a07a89190cab913d1807a0c07016b63c6e90
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Sep 19 11:01:13 2013 +0200

    Fix ical alarms handling (#2239) and object validation

diff --git a/plugins/libcalendaring/libcalendaring.php b/plugins/libcalendaring/libcalendaring.php
index 608a527..3016e51 100644
--- a/plugins/libcalendaring/libcalendaring.php
+++ b/plugins/libcalendaring/libcalendaring.php
@@ -308,7 +308,7 @@ class libcalendaring extends rcube_plugin
     {
         if ($val[0] == '@')
             return array(substr($val, 1));
-        else if (preg_match('/([+-])(\d+)([HMD])/', $val, $m))
+        else if (preg_match('/([+-])P?T?(\d+)([HMSDW])/', $val, $m))
             return array($m[2], $m[1].$m[3]);
 
         return false;
diff --git a/plugins/libcalendaring/libvcalendar.php b/plugins/libcalendaring/libvcalendar.php
index bf7c322..b4fdce3 100644
--- a/plugins/libcalendaring/libvcalendar.php
+++ b/plugins/libcalendaring/libvcalendar.php
@@ -105,9 +105,10 @@ class libvcalendar
     *
     * @param  string vCalendar input
     * @param  string Input charset (from envelope)
+    * @param  boolean True if parsing exceptions should be forwarded to the caller
     * @return array List of events extracted from the input
     */
-    public function import($vcal, $charset = 'UTF-8')
+    public function import($vcal, $charset = 'UTF-8', $forward_exceptions = false)
     {
         // TODO: convert charset to UTF-8 if other
 
@@ -122,6 +123,10 @@ class libvcalendar
                 'file' => __FILE__, 'line' => __LINE__,
                 'message' => "iCal data parse error: " . $e->getMessage()),
                 true, false);
+
+            if ($forward_exceptions) {
+                throw $e;
+            }
         }
 
         return array();
@@ -130,10 +135,12 @@ class libvcalendar
     /**
     * Read iCalendar events from a file
     *
-    * @param string File path to read from
+    * @param  string File path to read from
+    * @param  string Input charset (from envelope)
+    * @param  boolean True if parsing exceptions should be forwarded to the caller
     * @return array List of events extracted from the file
     */
-    public function import_from_file($filepath)
+    public function import_from_file($filepath, $charset = 'UTF-8', $forward_exceptions = false)
     {
         $this->objects = array();
         $fp = fopen($filepath, 'r');
@@ -145,7 +152,7 @@ class libvcalendar
         }
         fclose($fp);
 
-        return $this->import(file_get_contents($filepath));
+        return $this->import(file_get_contents($filepath), $charset, $forward_exceptions);
     }
 
     /**
@@ -229,6 +236,7 @@ class libvcalendar
             // set defaults
             'priority' => 0,
             'attendees' => array(),
+            'x-custom' => array(),
         );
 
         // Catch possible exceptions when date is invalid (Bug #2144)
@@ -407,10 +415,7 @@ class libvcalendar
             }
 
             // sanity-check and fix end date
-            if (empty($event['end'])) {
-                $event['end'] = clone $event['start'];
-            }
-            else if ($event['end'] < $event['start']) {
+            if (!empty($event['end']) && $event['end'] < $event['start']) {
                 $event['end'] = clone $event['start'];
             }
         }
@@ -435,7 +440,7 @@ class libvcalendar
                         }
                     }
                     if (!$trigger) {
-                        $trigger = preg_replace('/PT/', '', $prop->value);
+                        $trigger = preg_replace('/PT?/', '', $prop->value);
                     }
                     break;
 
@@ -467,7 +472,7 @@ class libvcalendar
         }
 
         // minimal validation
-        if (empty($event['uid']) || empty($event['start']) != empty($event['end'])) {
+        if (empty($event['uid']) || ($event['_type'] == 'event' && empty($event['start']) != empty($event['end']))) {
             throw new VObject\ParseException('Object validation failed: missing mandatory object properties');
         }
 
@@ -743,7 +748,8 @@ class libvcalendar
             $va = VObject\Component::create('VALARM');
             list($trigger, $va->action) = explode(':', $event['alarms']);
             $val = libcalendaring::parse_alaram_value($trigger);
-            if ($val[1]) $va->add('TRIGGER', preg_replace('/^([-+])(.+)/', '\\1PT\\2', $trigger));
+            $period = $val[1] && preg_match('/[HMS]$/', $val[1]) ? 'PT' : 'P';
+            if ($val[1]) $va->add('TRIGGER', preg_replace('/^([-+])P?T?(.+)/', "\\1$period\\2", $trigger));
             else         $va->add('TRIGGER', gmdate('Ymd\THis\Z', $val[0]), array('VALUE' => 'DATE-TIME'));
             $ve->add($va);
         }
diff --git a/plugins/libcalendaring/tests/libvcalendar.php b/plugins/libcalendaring/tests/libvcalendar.php
index 9567759..c891145 100644
--- a/plugins/libcalendaring/tests/libvcalendar.php
+++ b/plugins/libcalendaring/tests/libvcalendar.php
@@ -85,6 +85,14 @@ class libvcalendar_test extends PHPUnit_Framework_TestCase
         $this->assertFalse(array_key_exists('changed', $event), "No changed date field");
     }
 
+    function test_invalid_vevent()
+    {
+        $this->setExpectedException('\Sabre\VObject\ParseException');
+
+        $ical = new libvcalendar();
+        $events = $ical->import_from_file(__DIR__ . '/resources/invalid-event.ics', 'UTF-8', true);
+    }
+
     /**
      * Test some extended ical properties such as attendees, recurrence rules, alarms and attachments
      *
@@ -170,6 +178,18 @@ class libvcalendar_test extends PHPUnit_Framework_TestCase
         $this->assertContains('dummy', $freebusy['comment'], "Parse comment");
     }
 
+    function test_vtodo()
+    {
+        $ical = new libvcalendar();
+        $tasks = $ical->import_from_file(__DIR__ . '/resources/vtodo.ics', 'UTF-8', true);
+        $task = $tasks[0];
+
+        $this->assertInstanceOf('DateTime', $task['start'],   "'start' property is DateTime object");
+        $this->assertInstanceOf('DateTime', $task['due'],     "'due' property is DateTime object");
+        $this->assertEquals('-1D:DISPLAY',  $task['alarms'],  "Taks alarm value");
+        $this->assertEquals(1, count($task['x-custom']),      "Custom properties");
+    }
+
     /**
      * Test for iCal export from internal hash array representation
      *
diff --git a/plugins/libcalendaring/tests/resources/invalid-event.ics b/plugins/libcalendaring/tests/resources/invalid-event.ics
new file mode 100644
index 0000000..81602ae
--- /dev/null
+++ b/plugins/libcalendaring/tests/resources/invalid-event.ics
@@ -0,0 +1,14 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Apple Inc.//iCal 5.0.3//EN
+CALSCALE:GREGORIAN
+BEGIN:VEVENT
+CREATED:20130917T000000Z
+LAST-MODIFIED:20130755
+UID:C968B885-08FB-40E5-B89E-6FA05F26AACC
+TRANSP:TRANSPARENT
+SUMMARY:Event with no end date nor duration
+DTSTART;VALUE=DATE:20130801
+SEQUENCE:2
+END:VEVENT
+END:VCALENDAR
diff --git a/plugins/libcalendaring/tests/resources/vtodo.ics b/plugins/libcalendaring/tests/resources/vtodo.ics
new file mode 100644
index 0000000..9526142
--- /dev/null
+++ b/plugins/libcalendaring/tests/resources/vtodo.ics
@@ -0,0 +1,38 @@
+BEGIN:VCALENDAR
+PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN
+VERSION:2.0
+BEGIN:VTIMEZONE
+TZID:Europe/Zurich
+X-LIC-LOCATION:Europe/Zurich
+BEGIN:DAYLIGHT
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0200
+TZNAME:CEST
+DTSTART:19700329T020000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+TZNAME:CET
+DTSTART:19701025T030000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VTODO
+LAST-MODIFIED:20130919T075227Z
+DTSTAMP:20130919T075227Z
+UID:163A577B800E62BFFEF1CEC9DDDE4E11-FCBB6C4091F28CA0
+SUMMARY:My first task today
+STATUS:IN-PROCESS
+DTSTART;TZID=Europe/Zurich:20130921T000000
+DUE;VALUE=DATE:20130921
+SEQUENCE:2
+X-MOZ-GENERATION:1
+BEGIN:VALARM
+ACTION:DISPLAY
+TRIGGER;VALUE=DURATION:-P1D
+DESCRIPTION:Default Mozilla Description
+END:VALARM
+END:VTODO
+END:VCALENDAR
diff --git a/plugins/libkolab/lib/kolab_format_xcal.php b/plugins/libkolab/lib/kolab_format_xcal.php
index 085e577..284a068 100644
--- a/plugins/libkolab/lib/kolab_format_xcal.php
+++ b/plugins/libkolab/lib/kolab_format_xcal.php
@@ -385,7 +385,7 @@ abstract class kolab_format_xcal extends kolab_format
             if (preg_match('/^@(\d+)/', $offset, $d)) {
                 $alarm->setStart(self::get_datetime($d[1], new DateTimeZone('UTC')));
             }
-            else if (preg_match('/^([-+]?)(\d+)([SMHDW])/', $offset, $d)) {
+            else if (preg_match('/^([-+]?)P?T?(\d+)([SMHDW])/', $offset, $d)) {
                 $days = $hours = $minutes = $seconds = 0;
                 switch ($d[3]) {
                     case 'W': $days  = 7*intval($d[2]); break;


commit 20c0706d9d0cc9ec8104cc8062e98958a25ec659
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Sep 19 10:42:35 2013 +0200

    Preserve x-custom data in cache, even when object is saved by a client that doesn't support custom fields (#2238)

diff --git a/plugins/libkolab/lib/kolab_format.php b/plugins/libkolab/lib/kolab_format.php
index f59518c..dc95c76 100644
--- a/plugins/libkolab/lib/kolab_format.php
+++ b/plugins/libkolab/lib/kolab_format.php
@@ -403,14 +403,22 @@ abstract class kolab_format
         $this->obj->setLastModified(self::get_datetime($object['changed'], new DateTimeZone('UTC')));
 
         // Save custom properties of the given object
-        if (!empty($object['x-custom'])) {
+        if (isset($object['x-custom'])) {
             $vcustom = new vectorcs;
-            foreach ($object['x-custom'] as $cp) {
+            foreach ((array)$object['x-custom'] as $cp) {
                 if (is_array($cp))
                     $vcustom->push(new CustomProperty($cp[0], $cp[1]));
             }
             $this->obj->setCustomProperties($vcustom);
         }
+        else {  // load custom properties from XML for caching (#2238)
+            $object['x-custom'] = array();
+            $vcustom = $this->obj->customProperties();
+            for ($i=0; $i < $vcustom->size(); $i++) {
+                $cp = $vcustom->get($i);
+                $object['x-custom'][] = array($cp->identifier, $cp->value);
+            }
+        }
     }
 
     /**




More information about the commits mailing list