CalDavBackend.search does not handle VTODO's properly
Nobody has claimed this yet.
- Dominant language
- PHP
- Stars
- 36.9k
- Forks
- 5.2k
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 713
Description
Follow-up for https://github.com/nextcloud/server/pull/44752 and https://github.com/nextcloud/server/pull/45222
The "Upcoming events" widget (provided by the calendar app) is using ICalendar.search / CalDavBackend.search to obtain a list of calendar objects within a given time range since calendar 4.6.0. Former versions sent one xhr request per calendar to the CalDAV api to obtain the calendar objects.
A user also mentioned that tasks (e.g. from the tasks app) were shown in the "Upcoming events" widget with older version and now are missing^1.
I had a brief look with the tasks app and found a couple of things:
"Upcoming events" use a search with a time range. That's done by filtering on oc_calendarobjects.firstoccurence and oc_calendarobjects.lastoccurence and additional processing. However, event's from the tasks app with a start and end date still ended up in oc_calendarobjects with firstoccurrence = null and lastoccurence = null.
- The processing for DTSTART in CalDavBackend.getDenormalizedData is only done for VEVENTS^2.
- VTODO don't have DTEND but DUE or DURATION^3. We can handle DURATION, but DUE is missing^4.
- VTODO can omit DTSTART if DUE is given^3.
- CalDavBackend.search uses a comp-filter to check if the given event is within the time range. The filter for component type "name" is mandatory and set to VEVENT and will remove VTODO or VJOURNAL. The component type is already known (from the db result) and can be reused or drop the filter and ask the components directly with isInTimeRange^5.
- CalDavBackend.search uses VCalendar.expand to return the event for the given date and without recurrences. The implemention does not take VTODO in account and therefore they are removed / not added to the expanded result^6.
- https://github.com/nextcloud/server/pull/45222/commits/458b1cbf6f0691b8f41f6d1b5efa2800a478e0f0 will sort the objects by the start date, for VTODOs without start date have to figure something out, likely the start date should be today then.
Proof of concept to bring back tasks in the "Upcoming events" widget.
- Diff based on https://github.com/nextcloud/server/pull/45222
- The parsing for VTODO is still incomplete.
Index: apps/dav/lib/CalDAV/CalDavBackend.php
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/apps/dav/lib/CalDAV/CalDavBackend.php b/apps/dav/lib/CalDAV/CalDavBackend.php
--- a/apps/dav/lib/CalDAV/CalDavBackend.php (revision d8189bf775459ca87b654e5eda6eafc86bcdd0a8)
+++ b/apps/dav/lib/CalDAV/CalDavBackend.php (date 1715795614049)
@@ -2011,6 +2011,7 @@
// Expand recurrences if an explicit time range is requested
if ($calendarData instanceof VCalendar
+ && isset($calendarData->VEVENT)
&& isset($options['timerange']['start'], $options['timerange']['end'])) {
$calendarData = $calendarData->expand(
$options['timerange']['start'],
@@ -2072,7 +2073,7 @@
'name' => 'VCALENDAR',
'comp-filters' => [
[
- 'name' => 'VEVENT',
+ 'name' => $row['componenttype'],
'comp-filters' => [],
'prop-filters' => [],
'is-not-defined' => false,
@@ -2946,7 +2947,7 @@
foreach ($vObject->getComponents() as $component) {
if ($component->name !== 'VTIMEZONE') {
// Finding all VEVENTs, and track them
- if ($component->name === 'VEVENT') {
+ if ($component->name === 'VEVENT' || $component->name === 'VTODO') {
$vEvents[] = $component;
if ($component->DTSTART) {
$hasDTSTART = true;
@@ -2975,6 +2976,8 @@
$endDate = clone $component->DTSTART->getDateTime();
$endDate->add(DateTimeParser::parse($component->DURATION->getValue()));
$lastOccurrence = $endDate->getTimeStamp();
+ } elseif (isset($component->DUE)) {
+ $lastOccurrence = $component->DUE->getDateTime()->getTimeStamp();
} elseif (!$component->DTSTART->hasTime()) {
$endDate = clone $component->DTSTART->getDateTime();
$endDate->modify('+1 day');
Alternative for $isValid = $this->validateFilterForObject
$vObject = Reader::read($row['calendardata']);
$isValid = false;
if (isset($vObject->VEVENT)) {
$isValid = $vObject->VEVENT->isInTimeRange($start, $end);
} else if (isset($vObject->VTODO)) {
$isValid = $vObject->VTODO->isInTimeRange($start, $end);
}
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in apps/dav/lib/CalDAV/CalDavBackend.php, especially search, getDenormalizedData, and validateFilterForObject, then review the referenced VCalendar expansion behavior. Trace VTODO handling for DTSTART, DUE, and DURATION, and verify that time-range searches include applicable VTODO objects in the Upcoming events results without removing VEVENT behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- php
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100