Merge pull request #58697 from nextcloud/feat/add-calendar-name-to-search-result
Some checks are pending
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
Integration sqlite / changes (push) Waiting to run
Integration sqlite / integration-sqlite (master, 8.4, main, --tags ~@large files_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, capabilities_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, collaboration_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, comments_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, dav_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, federation_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, file_conversions) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, files_reminders) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, filesdrop_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, ldap_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, openldap_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, openldap_numerical_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, remoteapi_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, routing_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, setup_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, sharees_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, sharing_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, theming_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, videoverification_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite-summary (push) Blocked by required conditions
Psalm static code analysis / static-code-analysis (push) Waiting to run
Psalm static code analysis / static-code-analysis-security (push) Waiting to run
Psalm static code analysis / static-code-analysis-ocp (push) Waiting to run
Psalm static code analysis / static-code-analysis-ncu (push) Waiting to run
Psalm static code analysis / static-code-analysis-strict (push) Waiting to run

feat: add calendar name to search entries
This commit is contained in:
Sebastian Krupinski 2026-03-04 14:11:54 -05:00 committed by GitHub
commit 142ddaf146
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 60 additions and 49 deletions

View file

@ -150,13 +150,13 @@ class EventsSearchProvider extends ACalendarSearchProvider implements IFiltering
$formattedResults = \array_map(function (array $eventRow) use ($calendarsById, $subscriptionsById): SearchResultEntry {
$component = $this->getPrimaryComponent($eventRow['calendardata'], self::$componentType);
$title = (string)($component->SUMMARY ?? $this->l10n->t('Untitled event'));
$subline = $this->generateSubline($component);
if ($eventRow['calendartype'] === CalDavBackend::CALENDAR_TYPE_CALENDAR) {
$calendar = $calendarsById[$eventRow['calendarid']];
} else {
$calendar = $subscriptionsById[$eventRow['calendarid']];
}
$subline = $this->generateSubline($component, $calendar);
$resourceUrl = $this->getDeepLinkToCalendarApp($calendar['principaluri'], $calendar['uri'], $eventRow['uri']);
$result = new SearchResultEntry('', $title, $subline, $resourceUrl, 'icon-calendar-dark', false);
@ -204,7 +204,7 @@ class EventsSearchProvider extends ACalendarSearchProvider implements IFiltering
. $calendarObjectUri;
}
protected function generateSubline(Component $eventComponent): string {
protected function generateSubline(Component $eventComponent, array $calendarInfo): string {
$dtStart = $eventComponent->DTSTART;
$dtEnd = $this->getDTEndForEvent($eventComponent);
$isAllDayEvent = $dtStart instanceof Property\ICalendar\Date;
@ -214,24 +214,31 @@ class EventsSearchProvider extends ACalendarSearchProvider implements IFiltering
if ($isAllDayEvent) {
$endDateTime->modify('-1 day');
if ($this->isDayEqual($startDateTime, $endDateTime)) {
return $this->l10n->l('date', $startDateTime, ['width' => 'medium']);
$formattedSubline = $this->l10n->l('date', $startDateTime, ['width' => 'medium']);
} else {
$formattedStart = $this->l10n->l('date', $startDateTime, ['width' => 'medium']);
$formattedEnd = $this->l10n->l('date', $endDateTime, ['width' => 'medium']);
$formattedSubline = "$formattedStart - $formattedEnd";
}
} else {
$formattedStartDate = $this->l10n->l('date', $startDateTime, ['width' => 'medium']);
$formattedEndDate = $this->l10n->l('date', $endDateTime, ['width' => 'medium']);
$formattedStartTime = $this->l10n->l('time', $startDateTime, ['width' => 'short']);
$formattedEndTime = $this->l10n->l('time', $endDateTime, ['width' => 'short']);
$formattedStart = $this->l10n->l('date', $startDateTime, ['width' => 'medium']);
$formattedEnd = $this->l10n->l('date', $endDateTime, ['width' => 'medium']);
return "$formattedStart - $formattedEnd";
if ($this->isDayEqual($startDateTime, $endDateTime)) {
$formattedSubline = "$formattedStartDate $formattedStartTime - $formattedEndTime";
} else {
$formattedSubline = "$formattedStartDate $formattedStartTime - $formattedEndDate $formattedEndTime";
}
}
$formattedStartDate = $this->l10n->l('date', $startDateTime, ['width' => 'medium']);
$formattedEndDate = $this->l10n->l('date', $endDateTime, ['width' => 'medium']);
$formattedStartTime = $this->l10n->l('time', $startDateTime, ['width' => 'short']);
$formattedEndTime = $this->l10n->l('time', $endDateTime, ['width' => 'short']);
if ($this->isDayEqual($startDateTime, $endDateTime)) {
return "$formattedStartDate $formattedStartTime - $formattedEndTime";
if (isset($calendarInfo['{DAV:}displayname']) && !empty($calendarInfo['{DAV:}displayname'])) {
$formattedSubline = $formattedSubline . " ({$calendarInfo['{DAV:}displayname']})";
}
return "$formattedStartDate $formattedStartTime - $formattedEndDate $formattedEndTime";
// string cast is just to make psalm happy
return (string)$formattedSubline;
}
protected function getDTEndForEvent(Component $eventComponent):Property {

View file

@ -96,13 +96,13 @@ class TasksSearchProvider extends ACalendarSearchProvider {
$formattedResults = \array_map(function (array $taskRow) use ($calendarsById, $subscriptionsById):SearchResultEntry {
$component = $this->getPrimaryComponent($taskRow['calendardata'], self::$componentType);
$title = (string)($component->SUMMARY ?? $this->l10n->t('Untitled task'));
$subline = $this->generateSubline($component);
if ($taskRow['calendartype'] === CalDavBackend::CALENDAR_TYPE_CALENDAR) {
$calendar = $calendarsById[$taskRow['calendarid']];
} else {
$calendar = $subscriptionsById[$taskRow['calendarid']];
}
$subline = $this->generateSubline($component, $calendar);
$resourceUrl = $this->getDeepLinkToTasksApp($calendar['uri'], $taskRow['uri']);
return new SearchResultEntry('', $title, $subline, $resourceUrl, 'icon-checkmark', false);
@ -128,25 +128,29 @@ class TasksSearchProvider extends ACalendarSearchProvider {
);
}
protected function generateSubline(Component $taskComponent): string {
protected function generateSubline(Component $taskComponent, array $calendarInfo): string {
if ($taskComponent->COMPLETED) {
$completedDateTime = new \DateTime($taskComponent->COMPLETED->getDateTime()->format(\DateTimeInterface::ATOM));
$formattedDate = $this->l10n->l('date', $completedDateTime, ['width' => 'medium']);
return $this->l10n->t('Completed on %s', [$formattedDate]);
}
if ($taskComponent->DUE) {
$formattedSubline = $this->l10n->t('Completed on %s', [$formattedDate]);
} elseif ($taskComponent->DUE) {
$dueDateTime = new \DateTime($taskComponent->DUE->getDateTime()->format(\DateTimeInterface::ATOM));
$formattedDate = $this->l10n->l('date', $dueDateTime, ['width' => 'medium']);
if ($taskComponent->DUE->hasTime()) {
$formattedTime = $this->l10n->l('time', $dueDateTime, ['width' => 'short']);
return $this->l10n->t('Due on %s by %s', [$formattedDate, $formattedTime]);
$formattedSubline = $this->l10n->t('Due on %s by %s', [$formattedDate, $formattedTime]);
} else {
$formattedSubline = $this->l10n->t('Due on %s', [$formattedDate]);
}
return $this->l10n->t('Due on %s', [$formattedDate]);
} else {
$formattedSubline = '';
}
return '';
if (isset($calendarInfo['{DAV:}displayname']) && !empty($calendarInfo['{DAV:}displayname'])) {
$formattedSubline = $formattedSubline . (!empty($formattedSubline) ? ' ' : '') . "({$calendarInfo['{DAV:}displayname']})";
}
return $formattedSubline;
}
}

View file

@ -436,7 +436,7 @@ class EventsSearchProviderTest extends TestCase {
}
#[\PHPUnit\Framework\Attributes\DataProvider(methodName: 'generateSublineDataProvider')]
public function testGenerateSubline(string $ics, string $expectedSubline): void {
public function testGenerateSubline(string $ics, string $expectedSubline, array $calendarInfo = []): void {
$vCalendar = Reader::read($ics, Reader::OPTION_FORGIVING);
$eventComponent = $vCalendar->VEVENT;
@ -449,19 +449,23 @@ class EventsSearchProviderTest extends TestCase {
return $date->format('m-d');
});
$actual = self::invokePrivate($this->provider, 'generateSubline', [$eventComponent]);
$actual = self::invokePrivate($this->provider, 'generateSubline', [$eventComponent, $calendarInfo]);
$this->assertEquals($expectedSubline, $actual);
}
public static function generateSublineDataProvider(): array {
return [
[self::$vEvent1, '08-16 09:00 - 10:00'],
[self::$vEvent2, '08-16 09:00 - 08-17 10:00'],
[self::$vEvent3, '10-05'],
[self::$vEvent4, '10-05 - 10-07'],
[self::$vEvent5, '10-05 - 10-09'],
[self::$vEvent6, '10-05'],
[self::$vEvent7, '08-16 09:00 - 09:00'],
[self::$vEvent1, '08-16 09:00 - 10:00', []],
[self::$vEvent2, '08-16 09:00 - 08-17 10:00', []],
[self::$vEvent3, '10-05', []],
[self::$vEvent4, '10-05 - 10-07', []],
[self::$vEvent5, '10-05 - 10-09', []],
[self::$vEvent6, '10-05', []],
[self::$vEvent7, '08-16 09:00 - 09:00', []],
[self::$vEvent1, '08-16 09:00 - 10:00 (My Calendar)', ['{DAV:}displayname' => 'My Calendar']],
[self::$vEvent3, '10-05 (My Calendar)', ['{DAV:}displayname' => 'My Calendar']],
[self::$vEvent2, '08-16 09:00 - 08-17 10:00 (My Calendar)', ['{DAV:}displayname' => 'My Calendar']],
[self::$vEvent1, '08-16 09:00 - 10:00', ['{DAV:}displayname' => '']],
];
}
}

View file

@ -290,24 +290,29 @@ class TasksSearchProviderTest extends TestCase {
}
#[\PHPUnit\Framework\Attributes\DataProvider(methodName: 'generateSublineDataProvider')]
public function testGenerateSubline(string $ics, string $expectedSubline): void {
public function testGenerateSubline(string $ics, string $expectedSubline, array $calendarInfo = []): void {
$vCalendar = Reader::read($ics, Reader::OPTION_FORGIVING);
$taskComponent = $vCalendar->VTODO;
$this->l10n->method('t')->willReturnArgument(0);
$this->l10n->method('l')->willReturnArgument(0);
$actual = self::invokePrivate($this->provider, 'generateSubline', [$taskComponent]);
$actual = self::invokePrivate($this->provider, 'generateSubline', [$taskComponent, $calendarInfo]);
$this->assertEquals($expectedSubline, $actual);
}
public static function generateSublineDataProvider(): array {
return [
[self::$vTodo0, ''],
[self::$vTodo1, 'Completed on %s'],
[self::$vTodo2, 'Completed on %s'],
[self::$vTodo3, 'Due on %s'],
[self::$vTodo4, 'Due on %s by %s'],
[self::$vTodo0, '', []],
[self::$vTodo1, 'Completed on %s', []],
[self::$vTodo2, 'Completed on %s', []],
[self::$vTodo3, 'Due on %s', []],
[self::$vTodo4, 'Due on %s by %s', []],
[self::$vTodo0, '(My Tasks)', ['{DAV:}displayname' => 'My Tasks']],
[self::$vTodo1, 'Completed on %s (My Tasks)', ['{DAV:}displayname' => 'My Tasks']],
[self::$vTodo3, 'Due on %s (My Tasks)', ['{DAV:}displayname' => 'My Tasks']],
[self::$vTodo4, 'Due on %s by %s (My Tasks)', ['{DAV:}displayname' => 'My Tasks']],
[self::$vTodo1, 'Completed on %s', ['{DAV:}displayname' => '']],
];
}
}

View file

@ -839,9 +839,6 @@
</InvalidOperand>
</file>
<file src="apps/dav/lib/Search/EventsSearchProvider.php">
<FalsableReturnStatement>
<code><![CDATA[$this->l10n->l('date', $startDateTime, ['width' => 'medium'])]]></code>
</FalsableReturnStatement>
<InvalidOperand>
<code><![CDATA[$query->getCursor()]]></code>
</InvalidOperand>
@ -851,12 +848,6 @@
'ORGANIZER' => ['CN'],
]]]></code>
</InvalidPropertyAssignmentValue>
<InvalidReturnStatement>
<code><![CDATA[$this->l10n->l('date', $startDateTime, ['width' => 'medium'])]]></code>
</InvalidReturnStatement>
<InvalidReturnType>
<code><![CDATA[string]]></code>
</InvalidReturnType>
<UndefinedMethod>
<code><![CDATA[getDateTime]]></code>
<code><![CDATA[getDateTime]]></code>