Skip to content

Commit

Permalink
feat(caldav): Allow advanced search for events/tasks
Browse files Browse the repository at this point in the history
Signed-off-by: Benjamin Gaussorgues <benjamin.gaussorgues@nextcloud.com>
  • Loading branch information
Altahrim authored and nfebe committed Oct 24, 2023
1 parent f5adf61 commit c8f8a0c
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 128 deletions.
55 changes: 24 additions & 31 deletions apps/dav/lib/CalDAV/CalDavBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -208,36 +208,23 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
*/
protected array $userDisplayNames;

private IDBConnection $db;
private Backend $calendarSharingBackend;
private Principal $principalBackend;
private IUserManager $userManager;
private ISecureRandom $random;
private LoggerInterface $logger;
private IEventDispatcher $dispatcher;
private IConfig $config;
private bool $legacyEndpoint;
private string $dbObjectPropertiesTable = 'calendarobjects_props';
private array $cachedObjects = [];

public function __construct(IDBConnection $db,
Principal $principalBackend,
IUserManager $userManager,
IGroupManager $groupManager,
ISecureRandom $random,
LoggerInterface $logger,
IEventDispatcher $dispatcher,
IConfig $config,
bool $legacyEndpoint = false) {
$this->db = $db;
$this->principalBackend = $principalBackend;
$this->userManager = $userManager;
public function __construct(
private IDBConnection $db,
private Principal $principalBackend,
private IUserManager $userManager,
IGroupManager $groupManager,
private ISecureRandom $random,
private LoggerInterface $logger,
private IEventDispatcher $dispatcher,
private IConfig $config,
bool $legacyEndpoint = false,
) {
$this->calendarSharingBackend = new Backend($this->db, $this->userManager, $groupManager, $principalBackend, 'calendar');
$this->random = $random;
$this->logger = $logger;
$this->dispatcher = $dispatcher;
$this->config = $config;
$this->legacyEndpoint = $legacyEndpoint;
}

/**
Expand Down Expand Up @@ -1855,7 +1842,7 @@ public function calendarSearch($principalUri, array $filters, $limit = null, $of
* @return array
*/
public function search(array $calendarInfo, $pattern, array $searchProperties,
array $options, $limit, $offset) {
array $options, $limit, $offset) {
$outerQuery = $this->db->getQueryBuilder();
$innerQuery = $this->db->getQueryBuilder();

Expand Down Expand Up @@ -2069,11 +2056,11 @@ private function transformSearchProperty(Property $prop) {
* @return array
*/
public function searchPrincipalUri(string $principalUri,
string $pattern,
array $componentTypes,
array $searchProperties,
array $searchParameters,
array $options = []): array {
string $pattern,
array $componentTypes,
array $searchProperties,
array $searchParameters,
array $options = []): array {
return $this->atomic(function () use ($principalUri, $pattern, $componentTypes, $searchProperties, $searchParameters, $options) {
$escapePattern = !\array_key_exists('escape_like_param', $options) || $options['escape_like_param'] !== false;

Expand Down Expand Up @@ -2155,6 +2142,12 @@ public function searchPrincipalUri(string $principalUri,
if (isset($options['offset'])) {
$calendarObjectIdQuery->setFirstResult($options['offset']);
}
if (isset($options['since'])) {
$calendarObjectIdQuery->andWhere($calendarObjectIdQuery->expr()->lte('co.lastoccurence', $calendarObjectIdQuery->createNamedParameter($options['since']->get()->getTimestamp())));
}
if (isset($options['until'])) {
$calendarObjectIdQuery->andWhere($calendarObjectIdQuery->expr()->gte('co.firstoccurence', $calendarObjectIdQuery->createNamedParameter($options['since']->get()->getTimestamp())));
}

$result = $calendarObjectIdQuery->executeQuery();
$matches = $result->fetchAll();
Expand Down Expand Up @@ -3183,7 +3176,7 @@ public function pruneOutdatedSyncTokens(int $keep = 10_000): int {
$maxId = (int) $result->fetchOne();
$result->closeCursor();
if (!$maxId || $maxId < $keep) {
return 0;
return 0;
}

$query = $this->db->getQueryBuilder();
Expand Down
64 changes: 18 additions & 46 deletions apps/dav/lib/Search/ContactsSearchProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,6 @@

class ContactsSearchProvider implements IProvider {

/** @var IAppManager */
private $appManager;

/** @var IL10N */
private $l10n;

/** @var IURLGenerator */
private $urlGenerator;

/** @var CardDavBackend */
private $backend;

/**
* @var string[]
*/
Expand All @@ -68,22 +56,12 @@ class ContactsSearchProvider implements IProvider {
'NOTE',
];

/**
* ContactsSearchProvider constructor.
*
* @param IAppManager $appManager
* @param IL10N $l10n
* @param IURLGenerator $urlGenerator
* @param CardDavBackend $backend
*/
public function __construct(IAppManager $appManager,
IL10N $l10n,
IURLGenerator $urlGenerator,
CardDavBackend $backend) {
$this->appManager = $appManager;
$this->l10n = $l10n;
$this->urlGenerator = $urlGenerator;
$this->backend = $backend;
public function __construct(
private IAppManager $appManager,
private IL10N $l10n,
private IURLGenerator $urlGenerator,
private CardDavBackend $backend,
) {
}

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

$searchResults = $this->backend->searchPrincipalUri(
$principalUri,
$query->getTerm(),
$query->getFilter('term')?->get() ?? '',
self::$searchProperties,
[
'limit' => $query->getLimit(),
'offset' => $query->getCursor(),
'since' => $query->getFilter('since'),
'until' => $query->getFilter('until'),
]
);
$formattedResults = \array_map(function (array $contactRow) use ($addressBooksById):SearchResultEntry {
Expand All @@ -158,15 +138,11 @@ public function search(IUser $user, ISearchQuery $query): SearchResult {
);
}

/**
* @param string $principalUri
* @param string $addressBookUri
* @param string $contactsUri
* @return string
*/
protected function getDavUrlForContact(string $principalUri,
string $addressBookUri,
string $contactsUri): string {
protected function getDavUrlForContact(
string $principalUri,
string $addressBookUri,
string $contactsUri,
): string {
[, $principalType, $principalId] = explode('/', $principalUri, 3);

return $this->urlGenerator->getAbsoluteURL(
Expand All @@ -178,13 +154,10 @@ protected function getDavUrlForContact(string $principalUri,
);
}

/**
* @param string $addressBookUri
* @param string $contactUid
* @return string
*/
protected function getDeepLinkToContactsApp(string $addressBookUri,
string $contactUid): string {
protected function getDeepLinkToContactsApp(
string $addressBookUri,
string $contactUid,
): string {
return $this->urlGenerator->getAbsoluteURL(
$this->urlGenerator->linkToRoute('contacts.contacts.direct', [
'contact' => $contactUid . '~' . $addressBookUri
Expand All @@ -194,7 +167,6 @@ protected function getDeepLinkToContactsApp(string $addressBookUri,

/**
* @param VCard $vCard
* @return string
*/
protected function generateSubline(VCard $vCard): string {
$emailAddresses = $vCard->select('EMAIL');
Expand Down
54 changes: 18 additions & 36 deletions apps/dav/lib/Search/EventsSearchProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
* @package OCA\DAV\Search
*/
class EventsSearchProvider extends ACalendarSearchProvider {

/**
* @var string[]
*/
Expand Down Expand Up @@ -95,8 +94,10 @@ public function getOrder(string $route, array $routeParameters): int {
/**
* @inheritDoc
*/
public function search(IUser $user,
ISearchQuery $query): SearchResult {
public function search(
IUser $user,
ISearchQuery $query,
): SearchResult {
if (!$this->appManager->isEnabledForUser('calendar', $user)) {
return SearchResult::complete($this->getName(), []);
}
Expand All @@ -107,13 +108,15 @@ public function search(IUser $user,

$searchResults = $this->backend->searchPrincipalUri(
$principalUri,
$query->getTerm(),
$query->getFilter('term')?->get() ?? '',
[self::$componentType],
self::$searchProperties,
self::$searchParameters,
[
'limit' => $query->getLimit(),
'offset' => $query->getCursor(),
'since' => $query->getFilter('since'),
'until' => $query->getFilter('until'),
]
);
$formattedResults = \array_map(function (array $eventRow) use ($calendarsById, $subscriptionsById):SearchResultEntry {
Expand All @@ -138,15 +141,11 @@ public function search(IUser $user,
);
}

/**
* @param string $principalUri
* @param string $calendarUri
* @param string $calendarObjectUri
* @return string
*/
protected function getDeepLinkToCalendarApp(string $principalUri,
string $calendarUri,
string $calendarObjectUri): string {
protected function getDeepLinkToCalendarApp(
string $principalUri,
string $calendarUri,
string $calendarObjectUri,
): string {
$davUrl = $this->getDavUrlForCalendarObject($principalUri, $calendarUri, $calendarObjectUri);
// This route will automatically figure out what recurrence-id to open
return $this->urlGenerator->getAbsoluteURL(
Expand All @@ -156,15 +155,9 @@ protected function getDeepLinkToCalendarApp(string $principalUri,
);
}

/**
* @param string $principalUri
* @param string $calendarUri
* @param string $calendarObjectUri
* @return string
*/
protected function getDavUrlForCalendarObject(string $principalUri,
string $calendarUri,
string $calendarObjectUri): string {
string $calendarUri,
string $calendarObjectUri): string {
[,, $principalId] = explode('/', $principalUri, 3);

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

/**
* @param Component $eventComponent
* @return string
*/
protected function generateSubline(Component $eventComponent): string {
$dtStart = $eventComponent->DTSTART;
$dtEnd = $this->getDTEndForEvent($eventComponent);
Expand Down Expand Up @@ -207,10 +196,6 @@ protected function generateSubline(Component $eventComponent): string {
return "$formattedStartDate $formattedStartTime - $formattedEndDate $formattedEndTime";
}

/**
* @param Component $eventComponent
* @return Property
*/
protected function getDTEndForEvent(Component $eventComponent):Property {
if (isset($eventComponent->DTEND)) {
$end = $eventComponent->DTEND;
Expand All @@ -233,13 +218,10 @@ protected function getDTEndForEvent(Component $eventComponent):Property {
return $end;
}

/**
* @param \DateTime $dtStart
* @param \DateTime $dtEnd
* @return bool
*/
protected function isDayEqual(\DateTime $dtStart,
\DateTime $dtEnd) {
protected function isDayEqual(
\DateTime $dtStart,
\DateTime $dtEnd,
): bool {
return $dtStart->format('Y-m-d') === $dtEnd->format('Y-m-d');
}
}
26 changes: 11 additions & 15 deletions apps/dav/lib/Search/TasksSearchProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
* @package OCA\DAV\Search
*/
class TasksSearchProvider extends ACalendarSearchProvider {

/**
* @var string[]
*/
Expand Down Expand Up @@ -88,8 +87,10 @@ public function getOrder(string $route, array $routeParameters): int {
/**
* @inheritDoc
*/
public function search(IUser $user,
ISearchQuery $query): SearchResult {
public function search(
IUser $user,
ISearchQuery $query,
): SearchResult {
if (!$this->appManager->isEnabledForUser('tasks', $user)) {
return SearchResult::complete($this->getName(), []);
}
Expand All @@ -100,13 +101,15 @@ public function search(IUser $user,

$searchResults = $this->backend->searchPrincipalUri(
$principalUri,
$query->getTerm(),
$query->getFilter('term')?->get() ?? '',
[self::$componentType],
self::$searchProperties,
self::$searchParameters,
[
'limit' => $query->getLimit(),
'offset' => $query->getCursor(),
'since' => $query->getFilter('since'),
'until' => $query->getFilter('until'),
]
);
$formattedResults = \array_map(function (array $taskRow) use ($calendarsById, $subscriptionsById):SearchResultEntry {
Expand All @@ -131,13 +134,10 @@ public function search(IUser $user,
);
}

/**
* @param string $calendarUri
* @param string $taskUri
* @return string
*/
protected function getDeepLinkToTasksApp(string $calendarUri,
string $taskUri): string {
protected function getDeepLinkToTasksApp(
string $calendarUri,
string $taskUri,
): string {
return $this->urlGenerator->getAbsoluteURL(
$this->urlGenerator->linkToRoute('tasks.page.index')
. '#/calendars/'
Expand All @@ -147,10 +147,6 @@ protected function getDeepLinkToTasksApp(string $calendarUri,
);
}

/**
* @param Component $taskComponent
* @return string
*/
protected function generateSubline(Component $taskComponent): string {
if ($taskComponent->COMPLETED) {
$completedDateTime = new \DateTime($taskComponent->COMPLETED->getDateTime()->format(\DateTimeInterface::ATOM));
Expand Down

0 comments on commit c8f8a0c

Please sign in to comment.