Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat(caldav): Allow advanced search for events/tasks
Signed-off-by: Benjamin Gaussorgues <benjamin.gaussorgues@nextcloud.com>
  • Loading branch information
Altahrim committed Nov 10, 2023
commit 3545a1c613b8310fd5956517c91bac48e73fbd20
73 changes: 40 additions & 33 deletions apps/dav/lib/CalDAV/CalDavBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -208,36 +208,22 @@ 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,
private 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,8 +1841,14 @@ public function calendarSearch($principalUri, array $filters, $limit = null, $of
*
* @return array
*/
public function search(array $calendarInfo, $pattern, array $searchProperties,
array $options, $limit, $offset) {
public function search(
array $calendarInfo,
$pattern,
array $searchProperties,
array $options,
$limit,
$offset
) {
$outerQuery = $this->db->getQueryBuilder();
$innerQuery = $this->db->getQueryBuilder();

Expand Down Expand Up @@ -2074,11 +2066,12 @@ 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 @@ -2160,6 +2153,20 @@ public function searchPrincipalUri(string $principalUri,
if (isset($options['offset'])) {
$calendarObjectIdQuery->setFirstResult($options['offset']);
}
if (isset($options['timerange'])) {
if (isset($options['timerange']['start']) && $options['timerange']['start'] instanceof DateTimeInterface) {
$calendarObjectIdQuery->andWhere($calendarObjectIdQuery->expr()->gt(
'lastoccurence',
$calendarObjectIdQuery->createNamedParameter($options['timerange']['start']->getTimeStamp()),
));
}
if (isset($options['timerange']['end']) && $options['timerange']['end'] instanceof DateTimeInterface) {
$calendarObjectIdQuery->andWhere($calendarObjectIdQuery->expr()->lt(
'firstoccurence',
$calendarObjectIdQuery->createNamedParameter($options['timerange']['end']->getTimeStamp()),
));
}
}

$result = $calendarObjectIdQuery->executeQuery();
$matches = [];
Expand Down Expand Up @@ -3187,7 +3194,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
60 changes: 23 additions & 37 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,17 @@ 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(),
'timerange' => [
'start' => $query->getFilter('since')?->get(),
'end' => $query->getFilter('until')?->get(),
],
]
);
$formattedResults = \array_map(function (array $eventRow) use ($calendarsById, $subscriptionsById):SearchResultEntry {
Expand All @@ -138,15 +143,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 +157,11 @@ 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 {
protected function getDavUrlForCalendarObject(
string $principalUri,
string $calendarUri,
string $calendarObjectUri
): string {
[,, $principalId] = explode('/', $principalUri, 3);

return $this->urlGenerator->linkTo('', 'remote.php') . '/dav/calendars/'
Expand All @@ -173,10 +170,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 +200,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 +222,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');
}
}
Loading