From ace08819f98b346b713b7ae2639582a7b9d0261e Mon Sep 17 00:00:00 2001 From: Richard Steinmetz Date: Wed, 20 Sep 2023 17:45:54 +0200 Subject: [PATCH] fix(dav): expand recurrences when searching Signed-off-by: Richard Steinmetz --- apps/dav/lib/CalDAV/CalDavBackend.php | 13 +++- .../tests/unit/CalDAV/CalDavBackendTest.php | 69 +++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/apps/dav/lib/CalDAV/CalDavBackend.php b/apps/dav/lib/CalDAV/CalDavBackend.php index 915976d7dd3..5f2fe7e5dce 100644 --- a/apps/dav/lib/CalDAV/CalDavBackend.php +++ b/apps/dav/lib/CalDAV/CalDavBackend.php @@ -20,6 +20,7 @@ * @author Thomas Citharel * @author Thomas Müller * @author Vinicius Cubas Brand + * @author Richard Steinmetz * * @license AGPL-3.0 * @@ -1959,8 +1960,18 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription }); $result->closeCursor(); - return array_map(function ($o) { + return array_map(function ($o) use ($options) { $calendarData = Reader::read($o['calendardata']); + + // Expand recurrences if an explicit time range is requested + if ($calendarData instanceof VCalendar + && isset($options['timerange']['start'], $options['timerange']['end'])) { + $calendarData = $calendarData->expand( + $options['timerange']['start'], + $options['timerange']['end'], + ); + } + $comps = $calendarData->getComponents(); $objects = []; $timezones = []; diff --git a/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php b/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php index 3da6ca61b7e..b23091275ef 100644 --- a/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php +++ b/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php @@ -32,7 +32,9 @@ namespace OCA\DAV\Tests\unit\CalDAV; +use DateInterval; use DateTime; +use DateTimeImmutable; use DateTimeZone; use OCA\DAV\CalDAV\CalDavBackend; use OCA\DAV\CalDAV\Calendar; @@ -1420,4 +1422,71 @@ EOD; // Check that no crash occurs when prune is called without current changes $deleted = $this->backend->pruneOutdatedSyncTokens(1); } + + public function testSearchAndExpandRecurrences() { + $calendarId = $this->createTestCalendar(); + $calendarInfo = [ + 'id' => $calendarId, + 'principaluri' => 'user1', + '{http://owncloud.org/ns}owner-principal' => 'user1', + ]; + + $calData = <<<'EOD' +BEGIN:VCALENDAR +PRODID:-//IDN nextcloud.com//Calendar app 4.5.0-alpha.2//EN +CALSCALE:GREGORIAN +VERSION:2.0 +BEGIN:VEVENT +CREATED:20230921T133401Z +DTSTAMP:20230921T133448Z +LAST-MODIFIED:20230921T133448Z +SEQUENCE:2 +UID:7b7d5d12-683c-48ce-973a-b3e1cb0bae2a +DTSTART;VALUE=DATE:20230912 +DTEND;VALUE=DATE:20230913 +STATUS:CONFIRMED +SUMMARY:Daily Event +RRULE:FREQ=DAILY +END:VEVENT +END:VCALENDAR +EOD; + $uri = static::getUniqueID('calobj'); + $this->backend->createCalendarObject($calendarId, $uri, $calData); + + $start = new DateTimeImmutable('2023-09-20T00:00:00Z'); + $end = $start->add(new DateInterval('P14D')); + + $results = $this->backend->search( + $calendarInfo, + '', + [], + [ + 'timerange' => [ + 'start' => $start, + 'end' => $end, + ] + ], + null, + null, + ); + + $this->assertCount(1, $results); + $this->assertCount(14, $results[0]['objects']); + foreach ($results as $result) { + foreach ($result['objects'] as $object) { + $this->assertEquals($object['UID'][0], '7b7d5d12-683c-48ce-973a-b3e1cb0bae2a'); + $this->assertEquals($object['SUMMARY'][0], 'Daily Event'); + $this->assertGreaterThanOrEqual( + $start->getTimestamp(), + $object['DTSTART'][0]->getTimestamp(), + 'Recurrence starting before requested start', + ); + $this->assertLessThanOrEqual( + $end->getTimestamp(), + $object['DTSTART'][0]->getTimestamp(), + 'Recurrence starting after requested end', + ); + } + } + } } -- 2.39.5