aboutsummaryrefslogtreecommitdiffstats
path: root/apps/dav/lib/Search/EventsSearchProvider.php
diff options
context:
space:
mode:
Diffstat (limited to 'apps/dav/lib/Search/EventsSearchProvider.php')
-rw-r--r--apps/dav/lib/Search/EventsSearchProvider.php231
1 files changed, 231 insertions, 0 deletions
diff --git a/apps/dav/lib/Search/EventsSearchProvider.php b/apps/dav/lib/Search/EventsSearchProvider.php
new file mode 100644
index 00000000000..43fc4f65dfc
--- /dev/null
+++ b/apps/dav/lib/Search/EventsSearchProvider.php
@@ -0,0 +1,231 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright Copyright (c) 2020, Georg Ehrke
+ *
+ * @author Georg Ehrke <oc.list@georgehrke.com>
+ *
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+namespace OCA\DAV\Search;
+
+use OCA\DAV\CalDAV\CalDavBackend;
+use OCP\IUser;
+use OCP\Search\ISearchQuery;
+use OCP\Search\SearchResult;
+use Sabre\VObject\Component;
+use Sabre\VObject\DateTimeParser;
+use Sabre\VObject\Property;
+
+/**
+ * Class EventsSearchProvider
+ *
+ * @package OCA\DAV\Search
+ */
+class EventsSearchProvider extends ACalendarSearchProvider {
+
+ /**
+ * @var string[]
+ */
+ private static $searchProperties = [
+ 'SUMMARY',
+ 'LOCATION',
+ 'DESCRIPTION',
+ 'ATTENDEE',
+ 'ORGANIZER',
+ 'CATEGORIES',
+ ];
+
+ /**
+ * @var string[]
+ */
+ private static $searchParameters = [
+ 'ATTENDEE' => ['CN'],
+ 'ORGANIZER' => ['CN'],
+ ];
+
+ /**
+ * @var string
+ */
+ private static $componentType = 'VEVENT';
+
+ /**
+ * @inheritDoc
+ */
+ public function getId(): string {
+ return 'calendar-dav';
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getName(): string {
+ return $this->l10n->t('Events');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function search(IUser $user,
+ ISearchQuery $query): SearchResult {
+ if (!$this->appManager->isEnabledForUser('calendar', $user)) {
+ return SearchResult::complete($this->getName(), []);
+ }
+
+ $principalUri = 'principals/users/' . $user->getUID();
+ $calendarsById = $this->getSortedCalendars($principalUri);
+ $subscriptionsById = $this->getSortedSubscriptions($principalUri);
+
+ $searchResults = $this->backend->searchPrincipalUri(
+ $principalUri,
+ $query->getTerm(),
+ [self::$componentType],
+ self::$searchProperties,
+ self::$searchParameters,
+ [
+ 'limit' => $query->getLimit(),
+ 'offset' => $query->getCursor(),
+ ]
+ );
+ $formattedResults = \array_map(function (array $eventRow) use ($calendarsById, $subscriptionsById):EventsSearchResultEntry {
+ $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']];
+ }
+ $resourceUrl = $this->getDeepLinkToCalendarApp($calendar['principaluri'], $calendar['uri'], $eventRow['uri']);
+
+ return new EventsSearchResultEntry('', $title, $subline, $resourceUrl, 'icon-calendar-dark', false);
+ }, $searchResults);
+
+ return SearchResult::paginated(
+ $this->getName(),
+ $formattedResults,
+ $query->getCursor() + count($formattedResults)
+ );
+ }
+
+ /**
+ * @param string $principalUri
+ * @param string $calendarUri
+ * @param string $calendarObjectUri
+ * @return 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(
+ $this->urlGenerator->linkToRoute('calendar.view.index')
+ . 'edit/'
+ . base64_encode($davUrl)
+ );
+ }
+
+ /**
+ * @param string $principalUri
+ * @param string $calendarUri
+ * @param string $calendarObjectUri
+ * @return string
+ */
+ protected function getDavUrlForCalendarObject(string $principalUri,
+ string $calendarUri,
+ string $calendarObjectUri): string {
+ [,, $principalId] = explode('/', $principalUri, 3);
+
+ return $this->urlGenerator->linkTo('', 'remote.php') . '/dav/calendars/'
+ . $principalId . '/'
+ . $calendarUri . '/'
+ . $calendarObjectUri;
+ }
+
+ /**
+ * @param Component $eventComponent
+ * @return string
+ */
+ protected function generateSubline(Component $eventComponent): string {
+ $dtStart = $eventComponent->DTSTART;
+ $dtEnd = $this->getDTEndForEvent($eventComponent);
+ $isAllDayEvent = $dtStart instanceof Property\ICalendar\Date;
+ $startDateTime = new \DateTime($dtStart->getDateTime()->format(\DateTime::ATOM));
+ $endDateTime = new \DateTime($dtEnd->getDateTime()->format(\DateTime::ATOM));
+
+ if ($isAllDayEvent) {
+ $endDateTime->modify('-1 day');
+ if ($this->isDayEqual($startDateTime, $endDateTime)) {
+ return $this->l10n->l('date', $startDateTime, ['width' => 'medium']);
+ }
+
+ $formattedStart = $this->l10n->l('date', $startDateTime, ['width' => 'medium']);
+ $formattedEnd = $this->l10n->l('date', $endDateTime, ['width' => 'medium']);
+ return "$formattedStart - $formattedEnd";
+ }
+
+ $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";
+ }
+
+ return "$formattedStartDate $formattedStartTime - $formattedEndDate $formattedEndTime";
+ }
+
+ /**
+ * @param Component $eventComponent
+ * @return Property
+ */
+ protected function getDTEndForEvent(Component $eventComponent):Property {
+ if (isset($eventComponent->DTEND)) {
+ $end = $eventComponent->DTEND;
+ } elseif (isset($eventComponent->DURATION)) {
+ $isFloating = $eventComponent->DTSTART->isFloating();
+ $end = clone $eventComponent->DTSTART;
+ $endDateTime = $end->getDateTime();
+ $endDateTime = $endDateTime->add(DateTimeParser::parse($eventComponent->DURATION->getValue()));
+ $end->setDateTime($endDateTime, $isFloating);
+ } elseif (!$eventComponent->DTSTART->hasTime()) {
+ $isFloating = $eventComponent->DTSTART->isFloating();
+ $end = clone $eventComponent->DTSTART;
+ $endDateTime = $end->getDateTime();
+ $endDateTime = $endDateTime->modify('+1 day');
+ $end->setDateTime($endDateTime, $isFloating);
+ } else {
+ $end = clone $eventComponent->DTSTART;
+ }
+
+ return $end;
+ }
+
+ /**
+ * @param \DateTime $dtStart
+ * @param \DateTime $dtEnd
+ * @return bool
+ */
+ protected function isDayEqual(\DateTime $dtStart,
+ \DateTime $dtEnd) {
+ return $dtStart->format('Y-m-d') === $dtEnd->format('Y-m-d');
+ }
+}