Skip to content

Commit 6374218

Browse files
committed
feat(caldav): Allow advanced search for events/tasks
Signed-off-by: Benjamin Gaussorgues <benjamin.gaussorgues@nextcloud.com>
1 parent 0261507 commit 6374218

File tree

4 files changed

+71
-128
lines changed

4 files changed

+71
-128
lines changed

apps/dav/lib/CalDAV/CalDavBackend.php

Lines changed: 24 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -208,36 +208,23 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
208208
*/
209209
protected array $userDisplayNames;
210210

211-
private IDBConnection $db;
212211
private Backend $calendarSharingBackend;
213-
private Principal $principalBackend;
214-
private IUserManager $userManager;
215-
private ISecureRandom $random;
216-
private LoggerInterface $logger;
217-
private IEventDispatcher $dispatcher;
218-
private IConfig $config;
219212
private bool $legacyEndpoint;
220213
private string $dbObjectPropertiesTable = 'calendarobjects_props';
221214
private array $cachedObjects = [];
222215

223-
public function __construct(IDBConnection $db,
224-
Principal $principalBackend,
225-
IUserManager $userManager,
226-
IGroupManager $groupManager,
227-
ISecureRandom $random,
228-
LoggerInterface $logger,
229-
IEventDispatcher $dispatcher,
230-
IConfig $config,
231-
bool $legacyEndpoint = false) {
232-
$this->db = $db;
233-
$this->principalBackend = $principalBackend;
234-
$this->userManager = $userManager;
216+
public function __construct(
217+
private IDBConnection $db,
218+
private Principal $principalBackend,
219+
private IUserManager $userManager,
220+
IGroupManager $groupManager,
221+
private ISecureRandom $random,
222+
private LoggerInterface $logger,
223+
private IEventDispatcher $dispatcher,
224+
private IConfig $config,
225+
bool $legacyEndpoint = false,
226+
) {
235227
$this->calendarSharingBackend = new Backend($this->db, $this->userManager, $groupManager, $principalBackend, 'calendar');
236-
$this->random = $random;
237-
$this->logger = $logger;
238-
$this->dispatcher = $dispatcher;
239-
$this->config = $config;
240-
$this->legacyEndpoint = $legacyEndpoint;
241228
}
242229

243230
/**
@@ -1855,7 +1842,7 @@ public function calendarSearch($principalUri, array $filters, $limit = null, $of
18551842
* @return array
18561843
*/
18571844
public function search(array $calendarInfo, $pattern, array $searchProperties,
1858-
array $options, $limit, $offset) {
1845+
array $options, $limit, $offset) {
18591846
$outerQuery = $this->db->getQueryBuilder();
18601847
$innerQuery = $this->db->getQueryBuilder();
18611848

@@ -2069,11 +2056,11 @@ private function transformSearchProperty(Property $prop) {
20692056
* @return array
20702057
*/
20712058
public function searchPrincipalUri(string $principalUri,
2072-
string $pattern,
2073-
array $componentTypes,
2074-
array $searchProperties,
2075-
array $searchParameters,
2076-
array $options = []): array {
2059+
string $pattern,
2060+
array $componentTypes,
2061+
array $searchProperties,
2062+
array $searchParameters,
2063+
array $options = []): array {
20772064
return $this->atomic(function () use ($principalUri, $pattern, $componentTypes, $searchProperties, $searchParameters, $options) {
20782065
$escapePattern = !\array_key_exists('escape_like_param', $options) || $options['escape_like_param'] !== false;
20792066

@@ -2155,6 +2142,12 @@ public function searchPrincipalUri(string $principalUri,
21552142
if (isset($options['offset'])) {
21562143
$calendarObjectIdQuery->setFirstResult($options['offset']);
21572144
}
2145+
if (isset($options['since'])) {
2146+
$calendarObjectIdQuery->andWhere($calendarObjectIdQuery->expr()->lte('co.lastoccurence', $calendarObjectIdQuery->createNamedParameter($options['since']->get()->getTimestamp())));
2147+
}
2148+
if (isset($options['until'])) {
2149+
$calendarObjectIdQuery->andWhere($calendarObjectIdQuery->expr()->gte('co.firstoccurence', $calendarObjectIdQuery->createNamedParameter($options['since']->get()->getTimestamp())));
2150+
}
21582151

21592152
$result = $calendarObjectIdQuery->executeQuery();
21602153
$matches = $result->fetchAll();
@@ -3183,7 +3176,7 @@ public function pruneOutdatedSyncTokens(int $keep = 10_000): int {
31833176
$maxId = (int) $result->fetchOne();
31843177
$result->closeCursor();
31853178
if (!$maxId || $maxId < $keep) {
3186-
return 0;
3179+
return 0;
31873180
}
31883181

31893182
$query = $this->db->getQueryBuilder();

apps/dav/lib/Search/ContactsSearchProvider.php

Lines changed: 18 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,6 @@
4141

4242
class ContactsSearchProvider implements IProvider {
4343

44-
/** @var IAppManager */
45-
private $appManager;
46-
47-
/** @var IL10N */
48-
private $l10n;
49-
50-
/** @var IURLGenerator */
51-
private $urlGenerator;
52-
53-
/** @var CardDavBackend */
54-
private $backend;
55-
5644
/**
5745
* @var string[]
5846
*/
@@ -68,22 +56,12 @@ class ContactsSearchProvider implements IProvider {
6856
'NOTE',
6957
];
7058

71-
/**
72-
* ContactsSearchProvider constructor.
73-
*
74-
* @param IAppManager $appManager
75-
* @param IL10N $l10n
76-
* @param IURLGenerator $urlGenerator
77-
* @param CardDavBackend $backend
78-
*/
79-
public function __construct(IAppManager $appManager,
80-
IL10N $l10n,
81-
IURLGenerator $urlGenerator,
82-
CardDavBackend $backend) {
83-
$this->appManager = $appManager;
84-
$this->l10n = $l10n;
85-
$this->urlGenerator = $urlGenerator;
86-
$this->backend = $backend;
59+
public function __construct(
60+
private IAppManager $appManager,
61+
private IL10N $l10n,
62+
private IURLGenerator $urlGenerator,
63+
private CardDavBackend $backend,
64+
) {
8765
}
8866

8967
/**
@@ -127,11 +105,13 @@ public function search(IUser $user, ISearchQuery $query): SearchResult {
127105

128106
$searchResults = $this->backend->searchPrincipalUri(
129107
$principalUri,
130-
$query->getTerm(),
108+
$query->getFilter('term')?->get() ?? '',
131109
self::$searchProperties,
132110
[
133111
'limit' => $query->getLimit(),
134112
'offset' => $query->getCursor(),
113+
'since' => $query->getFilter('since'),
114+
'until' => $query->getFilter('until'),
135115
]
136116
);
137117
$formattedResults = \array_map(function (array $contactRow) use ($addressBooksById):SearchResultEntry {
@@ -158,15 +138,11 @@ public function search(IUser $user, ISearchQuery $query): SearchResult {
158138
);
159139
}
160140

161-
/**
162-
* @param string $principalUri
163-
* @param string $addressBookUri
164-
* @param string $contactsUri
165-
* @return string
166-
*/
167-
protected function getDavUrlForContact(string $principalUri,
168-
string $addressBookUri,
169-
string $contactsUri): string {
141+
protected function getDavUrlForContact(
142+
string $principalUri,
143+
string $addressBookUri,
144+
string $contactsUri,
145+
): string {
170146
[, $principalType, $principalId] = explode('/', $principalUri, 3);
171147

172148
return $this->urlGenerator->getAbsoluteURL(
@@ -178,13 +154,10 @@ protected function getDavUrlForContact(string $principalUri,
178154
);
179155
}
180156

181-
/**
182-
* @param string $addressBookUri
183-
* @param string $contactUid
184-
* @return string
185-
*/
186-
protected function getDeepLinkToContactsApp(string $addressBookUri,
187-
string $contactUid): string {
157+
protected function getDeepLinkToContactsApp(
158+
string $addressBookUri,
159+
string $contactUid,
160+
): string {
188161
return $this->urlGenerator->getAbsoluteURL(
189162
$this->urlGenerator->linkToRoute('contacts.contacts.direct', [
190163
'contact' => $contactUid . '~' . $addressBookUri
@@ -194,7 +167,6 @@ protected function getDeepLinkToContactsApp(string $addressBookUri,
194167

195168
/**
196169
* @param VCard $vCard
197-
* @return string
198170
*/
199171
protected function generateSubline(VCard $vCard): string {
200172
$emailAddresses = $vCard->select('EMAIL');

apps/dav/lib/Search/EventsSearchProvider.php

Lines changed: 18 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242
* @package OCA\DAV\Search
4343
*/
4444
class EventsSearchProvider extends ACalendarSearchProvider {
45-
4645
/**
4746
* @var string[]
4847
*/
@@ -95,8 +94,10 @@ public function getOrder(string $route, array $routeParameters): int {
9594
/**
9695
* @inheritDoc
9796
*/
98-
public function search(IUser $user,
99-
ISearchQuery $query): SearchResult {
97+
public function search(
98+
IUser $user,
99+
ISearchQuery $query,
100+
): SearchResult {
100101
if (!$this->appManager->isEnabledForUser('calendar', $user)) {
101102
return SearchResult::complete($this->getName(), []);
102103
}
@@ -107,13 +108,15 @@ public function search(IUser $user,
107108

108109
$searchResults = $this->backend->searchPrincipalUri(
109110
$principalUri,
110-
$query->getTerm(),
111+
$query->getFilter('term')?->get() ?? '',
111112
[self::$componentType],
112113
self::$searchProperties,
113114
self::$searchParameters,
114115
[
115116
'limit' => $query->getLimit(),
116117
'offset' => $query->getCursor(),
118+
'since' => $query->getFilter('since'),
119+
'until' => $query->getFilter('until'),
117120
]
118121
);
119122
$formattedResults = \array_map(function (array $eventRow) use ($calendarsById, $subscriptionsById):SearchResultEntry {
@@ -138,15 +141,11 @@ public function search(IUser $user,
138141
);
139142
}
140143

141-
/**
142-
* @param string $principalUri
143-
* @param string $calendarUri
144-
* @param string $calendarObjectUri
145-
* @return string
146-
*/
147-
protected function getDeepLinkToCalendarApp(string $principalUri,
148-
string $calendarUri,
149-
string $calendarObjectUri): string {
144+
protected function getDeepLinkToCalendarApp(
145+
string $principalUri,
146+
string $calendarUri,
147+
string $calendarObjectUri,
148+
): string {
150149
$davUrl = $this->getDavUrlForCalendarObject($principalUri, $calendarUri, $calendarObjectUri);
151150
// This route will automatically figure out what recurrence-id to open
152151
return $this->urlGenerator->getAbsoluteURL(
@@ -156,15 +155,9 @@ protected function getDeepLinkToCalendarApp(string $principalUri,
156155
);
157156
}
158157

159-
/**
160-
* @param string $principalUri
161-
* @param string $calendarUri
162-
* @param string $calendarObjectUri
163-
* @return string
164-
*/
165158
protected function getDavUrlForCalendarObject(string $principalUri,
166-
string $calendarUri,
167-
string $calendarObjectUri): string {
159+
string $calendarUri,
160+
string $calendarObjectUri): string {
168161
[,, $principalId] = explode('/', $principalUri, 3);
169162

170163
return $this->urlGenerator->linkTo('', 'remote.php') . '/dav/calendars/'
@@ -173,10 +166,6 @@ protected function getDavUrlForCalendarObject(string $principalUri,
173166
. $calendarObjectUri;
174167
}
175168

176-
/**
177-
* @param Component $eventComponent
178-
* @return string
179-
*/
180169
protected function generateSubline(Component $eventComponent): string {
181170
$dtStart = $eventComponent->DTSTART;
182171
$dtEnd = $this->getDTEndForEvent($eventComponent);
@@ -207,10 +196,6 @@ protected function generateSubline(Component $eventComponent): string {
207196
return "$formattedStartDate $formattedStartTime - $formattedEndDate $formattedEndTime";
208197
}
209198

210-
/**
211-
* @param Component $eventComponent
212-
* @return Property
213-
*/
214199
protected function getDTEndForEvent(Component $eventComponent):Property {
215200
if (isset($eventComponent->DTEND)) {
216201
$end = $eventComponent->DTEND;
@@ -233,13 +218,10 @@ protected function getDTEndForEvent(Component $eventComponent):Property {
233218
return $end;
234219
}
235220

236-
/**
237-
* @param \DateTime $dtStart
238-
* @param \DateTime $dtEnd
239-
* @return bool
240-
*/
241-
protected function isDayEqual(\DateTime $dtStart,
242-
\DateTime $dtEnd) {
221+
protected function isDayEqual(
222+
\DateTime $dtStart,
223+
\DateTime $dtEnd,
224+
): bool {
243225
return $dtStart->format('Y-m-d') === $dtEnd->format('Y-m-d');
244226
}
245227
}

apps/dav/lib/Search/TasksSearchProvider.php

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141
* @package OCA\DAV\Search
4242
*/
4343
class TasksSearchProvider extends ACalendarSearchProvider {
44-
4544
/**
4645
* @var string[]
4746
*/
@@ -88,8 +87,10 @@ public function getOrder(string $route, array $routeParameters): int {
8887
/**
8988
* @inheritDoc
9089
*/
91-
public function search(IUser $user,
92-
ISearchQuery $query): SearchResult {
90+
public function search(
91+
IUser $user,
92+
ISearchQuery $query,
93+
): SearchResult {
9394
if (!$this->appManager->isEnabledForUser('tasks', $user)) {
9495
return SearchResult::complete($this->getName(), []);
9596
}
@@ -100,13 +101,15 @@ public function search(IUser $user,
100101

101102
$searchResults = $this->backend->searchPrincipalUri(
102103
$principalUri,
103-
$query->getTerm(),
104+
$query->getFilter('term')?->get() ?? '',
104105
[self::$componentType],
105106
self::$searchProperties,
106107
self::$searchParameters,
107108
[
108109
'limit' => $query->getLimit(),
109110
'offset' => $query->getCursor(),
111+
'since' => $query->getFilter('since'),
112+
'until' => $query->getFilter('until'),
110113
]
111114
);
112115
$formattedResults = \array_map(function (array $taskRow) use ($calendarsById, $subscriptionsById):SearchResultEntry {
@@ -131,13 +134,10 @@ public function search(IUser $user,
131134
);
132135
}
133136

134-
/**
135-
* @param string $calendarUri
136-
* @param string $taskUri
137-
* @return string
138-
*/
139-
protected function getDeepLinkToTasksApp(string $calendarUri,
140-
string $taskUri): string {
137+
protected function getDeepLinkToTasksApp(
138+
string $calendarUri,
139+
string $taskUri,
140+
): string {
141141
return $this->urlGenerator->getAbsoluteURL(
142142
$this->urlGenerator->linkToRoute('tasks.page.index')
143143
. '#/calendars/'
@@ -147,10 +147,6 @@ protected function getDeepLinkToTasksApp(string $calendarUri,
147147
);
148148
}
149149

150-
/**
151-
* @param Component $taskComponent
152-
* @return string
153-
*/
154150
protected function generateSubline(Component $taskComponent): string {
155151
if ($taskComponent->COMPLETED) {
156152
$completedDateTime = new \DateTime($taskComponent->COMPLETED->getDateTime()->format(\DateTimeInterface::ATOM));

0 commit comments

Comments
 (0)