From 4fc6674d96486db3bb8d43609ca62a854710823a Mon Sep 17 00:00:00 2001 From: Dominik Tobschall Date: Wed, 26 Aug 2015 10:39:40 +0200 Subject: [PATCH 01/19] Added BirthdayCalendarGeneratorTest for empty BDAY properties --- .../VObject/BirthdayCalendarGeneratorTest.php | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/VObject/BirthdayCalendarGeneratorTest.php b/tests/VObject/BirthdayCalendarGeneratorTest.php index a28484a28..1fc11803f 100644 --- a/tests/VObject/BirthdayCalendarGeneratorTest.php +++ b/tests/VObject/BirthdayCalendarGeneratorTest.php @@ -414,6 +414,35 @@ function testVcardStringWithValidBirthdayLocalized() { } + function testVcardStringWithEmptyBirthdayProperty() { + + $generator = new BirthdayCalendarGenerator(); + $input = <<setObjects($input); + $output = $generator->getResult(); + + $this->assertVObjEquals( + $expected, + $output + ); + + } + /** * @expectedException \Sabre\VObject\ParseException */ From 0f9e3706b0464ab1b8cba632164535197f0bc896 Mon Sep 17 00:00:00 2001 From: Dominik Tobschall Date: Wed, 26 Aug 2015 10:41:45 +0200 Subject: [PATCH 02/19] BirthdayCalendarGenerator skips cards with empty BDAY props --- lib/BirthdayCalendarGenerator.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/BirthdayCalendarGenerator.php b/lib/BirthdayCalendarGenerator.php index 05df5091c..f93030e91 100644 --- a/lib/BirthdayCalendarGenerator.php +++ b/lib/BirthdayCalendarGenerator.php @@ -120,6 +120,13 @@ function getResult() { continue; } + // We've seen clients (ez-vcard) putting "BDAY:" properties + // without a value into vCards. If we come across those, we'll + // skip them. + if (empty($object->BDAY->getValue())) { + continue; + } + // We're always converting to vCard 4.0 so we can rely on the // VCardConverter handling the X-APPLE-OMIT-YEAR property for us. $object = $object->convert(Document::VCARD40); From 662fe4713b2a76e2d3ecd76b7ecb72bedd00b678 Mon Sep 17 00:00:00 2001 From: Dominik Tobschall Date: Wed, 26 Aug 2015 13:57:55 +0200 Subject: [PATCH 03/19] Added isInTimeRange test for NoInstancesException case --- tests/VObject/Component/VEventTest.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/VObject/Component/VEventTest.php b/tests/VObject/Component/VEventTest.php index 1bf1ceefc..2ef6ea1c6 100644 --- a/tests/VObject/Component/VEventTest.php +++ b/tests/VObject/Component/VEventTest.php @@ -71,6 +71,16 @@ function timeRangeTestData() { $tests[] = [$vevent7, new \DateTime('2012-02-01 15:00:00'), new \DateTime('2012-02-02'), true]; // The timezone of timerange in question should also be considered. $tests[] = [$vevent7, new \DateTime('2012-02-02 00:00:00', new \DateTimeZone('Europe/Berlin')), new \DateTime('2012-02-03 00:00:00', new \DateTimeZone('Europe/Berlin')), false]; + + // Added this test to check recurring events that have no instances. + $vevent8 = clone $vevent; + $vevent8->DTSTART = '20130329T140000'; + $vevent8->DTEND = '20130329T153000'; + $vevent8->RRULE = ['FREQ' => 'WEEKLY', 'BYDAY' => ['FR'], 'UNTIL' => '20130412T115959Z']; + $vevent8->add('EXDATE', '20130405T140000'); + $vevent8->add('EXDATE', '20130329T140000'); + $tests[] = [$vevent8, new \DateTime('2013-03-01'), new \DateTime('2013-04-01'), false]; + return $tests; } From 9a48c45f608c74be58fdd8dd9dadd79075153e1b Mon Sep 17 00:00:00 2001 From: Dominik Tobschall Date: Wed, 26 Aug 2015 14:05:32 +0200 Subject: [PATCH 04/19] Handle NoInstancesException in isInTimeRange() --- lib/Component/VEvent.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/Component/VEvent.php b/lib/Component/VEvent.php index 3b3c69bec..ddc4dab08 100644 --- a/lib/Component/VEvent.php +++ b/lib/Component/VEvent.php @@ -5,6 +5,7 @@ use DateTimeInterface; use Sabre\VObject; use Sabre\VObject\Recur\EventIterator; +use Sabre\VObject\Recur\NoInstancesException; /** * VEvent component. @@ -32,7 +33,19 @@ class VEvent extends VObject\Component { function isInTimeRange(DateTimeInterface $start, DateTimeInterface $end) { if ($this->RRULE) { - $it = new EventIterator($this, null, $start->getTimezone()); + + try { + + $it = new EventIterator($this, null, $start->getTimezone()); + + } catch (NoInstancesException $e) { + + // If we've catched this exception, there are no instances + // for the event that fall into the specified time-range. + return false; + + } + $it->fastForward($start); // We fast-forwarded to a spot where the end-time of the From 9704c2808e1b8bdf7ccc115d7dc12fecef299887 Mon Sep 17 00:00:00 2001 From: Dominik Tobschall Date: Wed, 26 Aug 2015 14:11:46 +0200 Subject: [PATCH 05/19] Updated changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a40710d2..b98c64638 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ ChangeLog * #250: `isInTimeRange()` now considers the timezone for floating dates and times. (@armin-hackmann) * Added a duplicate vcard merging tool for the command line. +* #253: `isInTimeRange()` now correctly handles events that throw the + `NoInstancesException` exception. (@migrax, @DominikTo) 4.0.0-alpha1 (2015-07-17) From 5a01188c2a60e603e88adc52e085d672a11e21fb Mon Sep 17 00:00:00 2001 From: Dominik Tobschall Date: Wed, 26 Aug 2015 13:57:55 +0200 Subject: [PATCH 06/19] Added isInTimeRange test for NoInstancesException case Conflicts: tests/VObject/Component/VEventTest.php --- tests/VObject/Component/VEventTest.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/VObject/Component/VEventTest.php b/tests/VObject/Component/VEventTest.php index 0a78140f5..47e764a51 100644 --- a/tests/VObject/Component/VEventTest.php +++ b/tests/VObject/Component/VEventTest.php @@ -71,6 +71,18 @@ public function timeRangeTestData() { $vevent7->DTSTART['VALUE'] = 'DATE'; $vevent7->RRULE = 'FREQ=MONTHLY'; $tests[] = array($vevent7, new \DateTime('2012-02-01 15:00:00'), new \DateTime('2012-02-02'), true); + // The timezone of timerange in question should also be considered. + $tests[] = array($vevent7, new \DateTime('2012-02-02 00:00:00', new \DateTimeZone('Europe/Berlin')), new \DateTime('2012-02-03 00:00:00', new \DateTimeZone('Europe/Berlin')), false); + + // Added this test to check recurring events that have no instances. + $vevent8 = clone $vevent; + $vevent8->DTSTART = '20130329T140000'; + $vevent8->DTEND = '20130329T153000'; + $vevent8->RRULE = array('FREQ' => 'WEEKLY', 'BYDAY' => array('FR'), 'UNTIL' => '20130412T115959Z'); + $vevent8->add('EXDATE', '20130405T140000'); + $vevent8->add('EXDATE', '20130329T140000'); + $tests[] = array($vevent8, new \DateTime('2013-03-01'), new \DateTime('2013-04-01'), false); + return $tests; } From f04cafb4eb098109b178c2ba6cb636c28e4de357 Mon Sep 17 00:00:00 2001 From: Dominik Tobschall Date: Wed, 26 Aug 2015 14:05:32 +0200 Subject: [PATCH 07/19] Handle NoInstancesException in isInTimeRange() Conflicts: lib/Component/VEvent.php --- lib/Component/VEvent.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/Component/VEvent.php b/lib/Component/VEvent.php index a1b4fe24e..d75691926 100644 --- a/lib/Component/VEvent.php +++ b/lib/Component/VEvent.php @@ -4,6 +4,7 @@ use Sabre\VObject; use Sabre\VObject\Recur\EventIterator; +use Sabre\VObject\Recur\NoInstancesException; /** * VEvent component @@ -30,7 +31,19 @@ class VEvent extends VObject\Component { public function isInTimeRange(\DateTime $start, \DateTime $end) { if ($this->RRULE) { - $it = new EventIterator($this); + + try { + + $it = new EventIterator($this, null, $start->getTimezone()); + + } catch (NoInstancesException $e) { + + // If we've catched this exception, there are no instances + // for the event that fall into the specified time-range. + return false; + + } + $it->fastForward($start); // We fast-forwarded to a spot where the end-time of the From 82b7378067ca41ad58f8e434a0f0c7cdb9f3a755 Mon Sep 17 00:00:00 2001 From: Evert Pot Date: Fri, 28 Aug 2015 22:59:30 +0200 Subject: [PATCH 08/19] Removed test that doesn't work in 3.4 branch --- tests/VObject/Component/VEventTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/VObject/Component/VEventTest.php b/tests/VObject/Component/VEventTest.php index 47e764a51..b3eec3ef9 100644 --- a/tests/VObject/Component/VEventTest.php +++ b/tests/VObject/Component/VEventTest.php @@ -72,7 +72,6 @@ public function timeRangeTestData() { $vevent7->RRULE = 'FREQ=MONTHLY'; $tests[] = array($vevent7, new \DateTime('2012-02-01 15:00:00'), new \DateTime('2012-02-02'), true); // The timezone of timerange in question should also be considered. - $tests[] = array($vevent7, new \DateTime('2012-02-02 00:00:00', new \DateTimeZone('Europe/Berlin')), new \DateTime('2012-02-03 00:00:00', new \DateTimeZone('Europe/Berlin')), false); // Added this test to check recurring events that have no instances. $vevent8 = clone $vevent; From 98162177fd8723179ee940b66944e6cfb688702f Mon Sep 17 00:00:00 2001 From: Evert Pot Date: Fri, 28 Aug 2015 23:01:09 +0200 Subject: [PATCH 09/19] Updated changelog, bumped version. Fixes #249, #253 --- ChangeLog.md | 7 +++++++ lib/Version.php | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/ChangeLog.md b/ChangeLog.md index 08de99d37..c594918e8 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,6 +1,13 @@ ChangeLog ========= +3.4.7 (????-??-??) +------------------ + +* #253: Handle `isInTimeRange` for recurring events that have 0 valid + instances. (@DominikTo, @migrax). + + 3.4.6 (2015-08-06) ------------------ diff --git a/lib/Version.php b/lib/Version.php index 8dd682eca..e2275551f 100644 --- a/lib/Version.php +++ b/lib/Version.php @@ -14,6 +14,6 @@ class Version { /** * Full version number */ - const VERSION = '3.4.6'; + const VERSION = '3.4.7'; } From a949a2303bd9b2f04ea9c955b0bdebac31f838a5 Mon Sep 17 00:00:00 2001 From: Dominik Tobschall Date: Thu, 23 Apr 2015 23:17:42 +0200 Subject: [PATCH 10/19] Test for undefined index issue --- tests/VObject/IssueUndefinedIndexTest.php | 27 +++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 tests/VObject/IssueUndefinedIndexTest.php diff --git a/tests/VObject/IssueUndefinedIndexTest.php b/tests/VObject/IssueUndefinedIndexTest.php new file mode 100644 index 000000000..950c604f0 --- /dev/null +++ b/tests/VObject/IssueUndefinedIndexTest.php @@ -0,0 +1,27 @@ +assertInstanceOf('Sabre\\VObject\\Component\\VCard', $vcard); + + } + +} From 55a781401f3f7b4a4894b8a473fea5d702ee7eb0 Mon Sep 17 00:00:00 2001 From: Evert Pot Date: Mon, 31 Aug 2015 11:57:44 +0200 Subject: [PATCH 11/19] Now throwing an exception for issue #254. --- CHANGELOG.md | 2 ++ lib/Parser/MimeDir.php | 3 +++ tests/VObject/IssueUndefinedIndexTest.php | 3 +++ 3 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d63aff98..0d64e2f47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ ChangeLog * Added a duplicate vcard merging tool for the command line. * #253: `isInTimeRange()` now correctly handles events that throw the `NoInstancesException` exception. (@migrax, @DominikTo) +* #254: The parser threw an `E_NOTICE` for certain invalid objects. It now + correctly throws a `ParseException`. 4.0.0-alpha1 (2015-07-17) diff --git a/lib/Parser/MimeDir.php b/lib/Parser/MimeDir.php index 986708f68..a98a0f059 100644 --- a/lib/Parser/MimeDir.php +++ b/lib/Parser/MimeDir.php @@ -344,6 +344,9 @@ protected function readProperty($line) { $value = $this->unescapeParam($value); + if (is_null($lastParam)) { + throw new ParseException('Invalid Mimedir file. Line starting at ' . $this->startLine . ' did not follow iCalendar/vCard conventions'); + } if (is_null($property['parameters'][$lastParam])) { $property['parameters'][$lastParam] = $value; } elseif (is_array($property['parameters'][$lastParam])) { diff --git a/tests/VObject/IssueUndefinedIndexTest.php b/tests/VObject/IssueUndefinedIndexTest.php index 950c604f0..9b6292e1d 100644 --- a/tests/VObject/IssueUndefinedIndexTest.php +++ b/tests/VObject/IssueUndefinedIndexTest.php @@ -4,6 +4,9 @@ class IssueUndefinedIndexTest extends \PHPUnit_Framework_TestCase { + /** + * @expectedException \Sabre\VObject\ParseException + */ function testRead() { $input = << Date: Fri, 4 Sep 2015 17:17:58 +0200 Subject: [PATCH 12/19] Releasing sabre/vobject 3.4.7 --- ChangeLog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog.md b/ChangeLog.md index c594918e8..90b883532 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,7 +1,7 @@ ChangeLog ========= -3.4.7 (????-??-??) +3.4.7 (2015-09-04) ------------------ * #253: Handle `isInTimeRange` for recurring events that have 0 valid From 3fb58fdfb0d4c870b696d1a240183f9c667f3455 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Pr=C3=A9vot?= Date: Wed, 29 Jul 2015 14:57:03 +0200 Subject: [PATCH 13/19] CS fix --- lib/FreeBusyData.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/FreeBusyData.php b/lib/FreeBusyData.php index 487a0b342..00e791218 100644 --- a/lib/FreeBusyData.php +++ b/lib/FreeBusyData.php @@ -92,7 +92,7 @@ function add($start, $end, $type) { if ($this->data[$insertStartIndex - 1]['start'] === $start) { // The old item starts at the exact same point as the new item. $insertStartIndex--; - } + } // Now we know where to insert the item, we need to know where it // starts overlapping with items on the tail end. We need to start From a8afbb0a935a2d1b57cc504866216d058ddb68e0 Mon Sep 17 00:00:00 2001 From: Jan Kantert Date: Wed, 14 Oct 2015 13:35:16 +0200 Subject: [PATCH 14/19] use correct timezone --- lib/Recur/RDateIterator.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Recur/RDateIterator.php b/lib/Recur/RDateIterator.php index 985c2d75c..18bda5a29 100644 --- a/lib/Recur/RDateIterator.php +++ b/lib/Recur/RDateIterator.php @@ -91,7 +91,8 @@ function next() { $this->currentDate = DateTimeParser::parse( - $this->dates[$this->counter - 1] + $this->dates[$this->counter - 1], + $this->startDate->getTimezone() ); } From 1a6e996cb00bddec6a3eed8221346a47cbf1d515 Mon Sep 17 00:00:00 2001 From: Jan Kantert Date: Wed, 14 Oct 2015 13:36:33 +0200 Subject: [PATCH 15/19] also iterate events with rdate in calendar --- lib/Component/VCalendar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Component/VCalendar.php b/lib/Component/VCalendar.php index d1cd30833..d39c31e52 100644 --- a/lib/Component/VCalendar.php +++ b/lib/Component/VCalendar.php @@ -310,7 +310,7 @@ function expand(DateTimeInterface $start, DateTimeInterface $end, DateTimeZone $ throw new \LogicException('Event did not have a UID!'); } - if (isset($vevent->{'RECURRENCE-ID'}) || isset($vevent->RRULE)) { + if (isset($vevent->{'RECURRENCE-ID'}) || isset($vevent->RRULE) || isset($vevent->{'RDATE'})) { if (isset($recurringEvents[$uid])) { $recurringEvents[$uid][] = $vevent; } else { From e6b3307d09760c18a5ed1d3608155dd1751807e6 Mon Sep 17 00:00:00 2001 From: Jan Kantert Date: Wed, 14 Oct 2015 21:03:55 +0200 Subject: [PATCH 16/19] add test for RDateIterator and timezones --- tests/VObject/Recur/RDateIteratorTest.php | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/VObject/Recur/RDateIteratorTest.php b/tests/VObject/Recur/RDateIteratorTest.php index 2c9c7f1bf..7abf0e004 100644 --- a/tests/VObject/Recur/RDateIteratorTest.php +++ b/tests/VObject/Recur/RDateIteratorTest.php @@ -27,6 +27,30 @@ function testSimple() { } + function testTimezone() { + + $tz = new DateTimeZone('Europe/Berlin'); + $it = new RDateIterator('20140901T000000,20141001T000000', new DateTimeImmutable('2014-08-01 00:00:00', $tz)); + + $expected = [ + new DateTimeImmutable('2014-08-01 00:00:00', $tz), + new DateTimeImmutable('2014-09-01 00:00:00', $tz), + new DateTimeImmutable('2014-10-01 00:00:00', $tz), + ]; + + $result = iterator_to_array($it); + + $this->assertEquals( + $expected, + iterator_to_array($it) + ); + + + $this->assertFalse($it->isInfinite()); + + } + + function testFastForward() { $utc = new DateTimeZone('UTC'); From 654f718feef997f2a3271a7b30b891ea43347e9d Mon Sep 17 00:00:00 2001 From: Jan Kantert Date: Wed, 14 Oct 2015 21:30:32 +0200 Subject: [PATCH 17/19] add test for VCalendar expand with RDate --- .../EventIterator/HandleRDateExpandTest.php | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 tests/VObject/Recur/EventIterator/HandleRDateExpandTest.php diff --git a/tests/VObject/Recur/EventIterator/HandleRDateExpandTest.php b/tests/VObject/Recur/EventIterator/HandleRDateExpandTest.php new file mode 100644 index 000000000..027fc5571 --- /dev/null +++ b/tests/VObject/Recur/EventIterator/HandleRDateExpandTest.php @@ -0,0 +1,59 @@ +assertInstanceOf('Sabre\\VObject\\Component\\VCalendar', $vcal); + + $vcal->expand(new DateTime('2015-01-01'), new DateTime('2015-12-01')); + + $result = iterator_to_array($vcal->vevent); + + $this->assertEquals(5, count($result)); + + $utc = new DateTimeZone('UTC'); + $expected = array( + new DateTimeImmutable("2015-10-12", $utc), + new DateTimeImmutable("2015-10-15", $utc), + new DateTimeImmutable("2015-10-17", $utc), + new DateTimeImmutable("2015-10-18", $utc), + new DateTimeImmutable("2015-10-20", $utc), + ); + + $result = array_map(function($ev){return $ev->dtstart->getDateTime();}, $result); + $this->assertEquals($expected, $result); + + } + +} From 5f675d5bb1f0669f955dd7cd4845bd7fb5dd5996 Mon Sep 17 00:00:00 2001 From: Evert Pot Date: Wed, 14 Oct 2015 15:54:46 -0400 Subject: [PATCH 18/19] Updated changelog, bumped version. --- CHANGELOG.md | 7 +++++++ lib/Property/ICalendar/Recur.php | 1 - lib/Version.php | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bbfe6839..975ab2f56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ ChangeLog ========= +4.0.0-alpha3 (????-??-??) +------------------------- + +* #258: Support for expanding events that use `RDATE`. (@jabdoa2) +* #258: Correctly support TZID for events that use `RDATE`. (@jabdoa2) + + 4.0.0-alpha2 (2015-09-04) ------------------------- diff --git a/lib/Property/ICalendar/Recur.php b/lib/Property/ICalendar/Recur.php index b75d78643..6356d8ec4 100644 --- a/lib/Property/ICalendar/Recur.php +++ b/lib/Property/ICalendar/Recur.php @@ -91,7 +91,6 @@ function getValue() { * Sets a multi-valued property. * * @param array $parts - * * @return void */ function setParts(array $parts) { diff --git a/lib/Version.php b/lib/Version.php index e14f5fb7f..ea5778271 100644 --- a/lib/Version.php +++ b/lib/Version.php @@ -14,6 +14,6 @@ class Version { /** * Full version number. */ - const VERSION = '4.0.0-alpha2'; + const VERSION = '4.0.0-alpha3'; } From 3d45bcbdebb3ea43172d52556ddea679a9b60df4 Mon Sep 17 00:00:00 2001 From: Graham Crosmarie Date: Wed, 21 Oct 2015 15:21:28 +0200 Subject: [PATCH 19/19] Fix issue #259: add missing date format change in Recur object --- lib/Property/ICalendar/Recur.php | 3 +++ tests/VObject/Issue259Test.php | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 tests/VObject/Issue259Test.php diff --git a/lib/Property/ICalendar/Recur.php b/lib/Property/ICalendar/Recur.php index 6356d8ec4..a802aa6a0 100644 --- a/lib/Property/ICalendar/Recur.php +++ b/lib/Property/ICalendar/Recur.php @@ -51,6 +51,9 @@ function setValue($value) { if (strpos($v, ',') !== false) { $v = explode(',', $v); } + if (strcmp($k, 'until') === 0) { + $v = strtr($v, [':' => '', '-' => '']); + } } else { $v = array_map('strtoupper', $v); } diff --git a/tests/VObject/Issue259Test.php b/tests/VObject/Issue259Test.php new file mode 100644 index 000000000..4a73be560 --- /dev/null +++ b/tests/VObject/Issue259Test.php @@ -0,0 +1,21 @@ +setInput($jcalWithUntil); + + $vcalendar = $parser->parse(); + $eventAsArray = $vcalendar->select('VEVENT'); + $event = reset($eventAsArray); + $rruleAsArray = $event->select('RRULE'); + $rrule = reset($rruleAsArray); + $this->assertNotNull($rrule); + $this->assertEquals($rrule->getValue(), 'FREQ=MONTHLY;UNTIL=20160101T220000Z'); + } + +}