From 83d3d016bd0d3299261bf0886538f982ad707f3a Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Mon, 9 Mar 2015 12:30:53 +0100 Subject: [PATCH] Parse multiple vevent components with the same UID into one object with exceptions (#4733) --- plugins/libcalendaring/libvcalendar.php | 45 ++++++++++++------- plugins/libcalendaring/tests/libvcalendar.php | 8 ++-- .../tests/resources/recurrence-id.ics | 10 ++++- 3 files changed, 43 insertions(+), 20 deletions(-) diff --git a/plugins/libcalendaring/libvcalendar.php b/plugins/libcalendaring/libvcalendar.php index 37f62241..195f99da 100644 --- a/plugins/libcalendaring/libvcalendar.php +++ b/plugins/libcalendaring/libvcalendar.php @@ -305,6 +305,7 @@ class libvcalendar implements Iterator public function import_from_vobject($vobject) { $seen = array(); + $exceptions = array(); if ($vobject->name == 'VCALENDAR') { $this->method = strval($vobject->METHOD); @@ -315,22 +316,11 @@ class libvcalendar implements Iterator // convert to hash array representation $object = $this->_to_array($ve); - if (!$seen[$object['uid']]++) { - // parse recurrence exceptions - if ($object['recurrence']) { - $object['recurrence']['EXCEPTIONS'] = array(); - foreach ($vobject->children as $component) { - if ($component->name == 'VEVENT' && isset($component->{'RECURRENCE-ID'})) { - try { - $object['recurrence']['EXCEPTIONS'][] = $this->_to_array($component); - } - catch (Exception $e) { - console("iCal data parse error: " . $e->getMessage(), $component->serialize()); - } - } - } - } - + // temporarily store this as exception + if ($object['recurrence_date']) { + $exceptions[] = $object; + } + else if (!$seen[$object['uid']]++) { $this->objects[] = $object; } } @@ -338,6 +328,29 @@ class libvcalendar implements Iterator $this->objects[] = $this->_parse_freebusy($ve); } } + + // add exceptions to the according master events + foreach ($exceptions as $exception) { + $uid = $exception['uid']; + + // make this exception the master + if (!$seen[$uid]++) { + $this->objects[] = $exception; + } + else { + foreach ($this->objects as $i => $object) { + // add as exception to existing entry with a matching UID + if ($object['uid'] == $uid) { + $this->objects[$i]['exceptions'][] = $exception; + + if (!empty($object['recurrence'])) { + $this->objects[$i]['recurrence']['EXCEPTIONS'] = &$this->objects[$i]['exceptions']; + } + break; + } + } + } + } } return $this->objects; diff --git a/plugins/libcalendaring/tests/libvcalendar.php b/plugins/libcalendaring/tests/libvcalendar.php index 9180e915..e0e87636 100644 --- a/plugins/libcalendaring/tests/libvcalendar.php +++ b/plugins/libcalendaring/tests/libvcalendar.php @@ -112,8 +112,6 @@ class libvcalendar_test extends PHPUnit_Framework_TestCase /** * Test some extended ical properties such as attendees, recurrence rules, alarms and attachments - * - * @depends test_import_from_file */ function test_extended() { @@ -160,11 +158,15 @@ class libvcalendar_test extends PHPUnit_Framework_TestCase $this->assertEquals('libcalendaring tests', join(',', (array)$event['categories']), "Event categories"); $this->assertEquals('confidential', $event['sensitivity'], "Class/sensitivity = confidential"); - // parse a reccuence chain instance + // parse a recurrence chain instance $events = $ical->import_from_file(__DIR__ . '/resources/recurrence-id.ics', 'UTF-8'); $this->assertEquals(1, count($events), "Fall back to Component::getComponents() when getBaseComponents() is empty"); $this->assertInstanceOf('DateTime', $events[0]['recurrence_date'], "Recurrence-ID as date"); $this->assertTrue($events[0]['thisandfuture'], "Range=THISANDFUTURE"); + + $this->assertEquals(count($events[0]['exceptions']), 1, "Second VEVENT as exception"); + $this->assertEquals($events[0]['exceptions'][0]['uid'], $events[0]['uid'], "Exception UID match"); + $this->assertEquals($events[0]['exceptions'][0]['sequence'], '2', "Exception sequence"); } /** diff --git a/plugins/libcalendaring/tests/resources/recurrence-id.ics b/plugins/libcalendaring/tests/resources/recurrence-id.ics index 8229da28..af880934 100644 --- a/plugins/libcalendaring/tests/resources/recurrence-id.ics +++ b/plugins/libcalendaring/tests/resources/recurrence-id.ics @@ -21,11 +21,19 @@ BEGIN:VEVENT DTSTART;TZID="W. Europe":20140230T150000 DTEND;TZID="W. Europe":20140230T163000 TRANSP:OPAQUE -RDATE;TZID="W. Europe";VALUE=PERIOD:20140227T140000/20140227T153000 RECURRENCE-ID;RANGE=THISANDFUTURE:20140227T130000Z SEQUENCE:0 UID:7e93e8e8eef16f28aa33b78cd73613ebff DTSTAMP:20140120T105609Z SUMMARY:Invitation with Recurrence-ID END:VEVENT +BEGIN:VEVENT +DTSTART;TZID="W. Europe":20140305T150000 +DTEND;TZID="W. Europe":20140305T163000 +RECURRENCE-ID;TZID="W. Europe":20140305T150000 +SEQUENCE:2 +UID:7e93e8e8eef16f28aa33b78cd73613ebff +DTSTAMP:20140120T105609Z +SUMMARY:Invitation with Recurrence-ID #2 +END:VEVENT END:VCALENDAR