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