mirror of
https://github.com/nextcloud/server.git
synced 2026-06-12 10:10:49 -04:00
Merge pull request #22020 from nextcloud/feature/20918/calendar_search
Add Event and Task Backends for Unified Search
This commit is contained in:
commit
72b45f9546
14 changed files with 1834 additions and 13 deletions
|
|
@ -210,8 +210,13 @@ return array(
|
|||
'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningNode' => $baseDir . '/../lib/Provisioning/Apple/AppleProvisioningNode.php',
|
||||
'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningPlugin' => $baseDir . '/../lib/Provisioning/Apple/AppleProvisioningPlugin.php',
|
||||
'OCA\\DAV\\RootCollection' => $baseDir . '/../lib/RootCollection.php',
|
||||
'OCA\\DAV\\Search\\ACalendarSearchProvider' => $baseDir . '/../lib/Search/ACalendarSearchProvider.php',
|
||||
'OCA\\DAV\\Search\\ContactsSearchProvider' => $baseDir . '/../lib/Search/ContactsSearchProvider.php',
|
||||
'OCA\\DAV\\Search\\ContactsSearchResultEntry' => $baseDir . '/../lib/Search/ContactsSearchResultEntry.php',
|
||||
'OCA\\DAV\\Search\\EventsSearchProvider' => $baseDir . '/../lib/Search/EventsSearchProvider.php',
|
||||
'OCA\\DAV\\Search\\EventsSearchResultEntry' => $baseDir . '/../lib/Search/EventsSearchResultEntry.php',
|
||||
'OCA\\DAV\\Search\\TasksSearchProvider' => $baseDir . '/../lib/Search/TasksSearchProvider.php',
|
||||
'OCA\\DAV\\Search\\TasksSearchResultEntry' => $baseDir . '/../lib/Search/TasksSearchResultEntry.php',
|
||||
'OCA\\DAV\\Server' => $baseDir . '/../lib/Server.php',
|
||||
'OCA\\DAV\\Settings\\CalDAVSettings' => $baseDir . '/../lib/Settings/CalDAVSettings.php',
|
||||
'OCA\\DAV\\Storage\\PublicOwnerWrapper' => $baseDir . '/../lib/Storage/PublicOwnerWrapper.php',
|
||||
|
|
|
|||
|
|
@ -225,8 +225,13 @@ class ComposerStaticInitDAV
|
|||
'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningNode' => __DIR__ . '/..' . '/../lib/Provisioning/Apple/AppleProvisioningNode.php',
|
||||
'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningPlugin' => __DIR__ . '/..' . '/../lib/Provisioning/Apple/AppleProvisioningPlugin.php',
|
||||
'OCA\\DAV\\RootCollection' => __DIR__ . '/..' . '/../lib/RootCollection.php',
|
||||
'OCA\\DAV\\Search\\ACalendarSearchProvider' => __DIR__ . '/..' . '/../lib/Search/ACalendarSearchProvider.php',
|
||||
'OCA\\DAV\\Search\\ContactsSearchProvider' => __DIR__ . '/..' . '/../lib/Search/ContactsSearchProvider.php',
|
||||
'OCA\\DAV\\Search\\ContactsSearchResultEntry' => __DIR__ . '/..' . '/../lib/Search/ContactsSearchResultEntry.php',
|
||||
'OCA\\DAV\\Search\\EventsSearchProvider' => __DIR__ . '/..' . '/../lib/Search/EventsSearchProvider.php',
|
||||
'OCA\\DAV\\Search\\EventsSearchResultEntry' => __DIR__ . '/..' . '/../lib/Search/EventsSearchResultEntry.php',
|
||||
'OCA\\DAV\\Search\\TasksSearchProvider' => __DIR__ . '/..' . '/../lib/Search/TasksSearchProvider.php',
|
||||
'OCA\\DAV\\Search\\TasksSearchResultEntry' => __DIR__ . '/..' . '/../lib/Search/TasksSearchResultEntry.php',
|
||||
'OCA\\DAV\\Server' => __DIR__ . '/..' . '/../lib/Server.php',
|
||||
'OCA\\DAV\\Settings\\CalDAVSettings' => __DIR__ . '/..' . '/../lib/Settings/CalDAVSettings.php',
|
||||
'OCA\\DAV\\Storage\\PublicOwnerWrapper' => __DIR__ . '/..' . '/../lib/Storage/PublicOwnerWrapper.php',
|
||||
|
|
|
|||
|
|
@ -55,6 +55,8 @@ use OCA\DAV\CardDAV\PhotoCache;
|
|||
use OCA\DAV\CardDAV\SyncService;
|
||||
use OCA\DAV\HookManager;
|
||||
use OCA\DAV\Search\ContactsSearchProvider;
|
||||
use OCA\DAV\Search\EventsSearchProvider;
|
||||
use OCA\DAV\Search\TasksSearchProvider;
|
||||
use OCP\AppFramework\App;
|
||||
use OCP\AppFramework\Bootstrap\IBootContext;
|
||||
use OCP\AppFramework\Bootstrap\IBootstrap;
|
||||
|
|
@ -102,6 +104,8 @@ class Application extends App implements IBootstrap {
|
|||
* Register Search Providers
|
||||
*/
|
||||
$context->registerSearchProvider(ContactsSearchProvider::class);
|
||||
$context->registerSearchProvider(EventsSearchProvider::class);
|
||||
$context->registerSearchProvider(TasksSearchProvider::class);
|
||||
}
|
||||
|
||||
public function boot(IBootContext $context): void {
|
||||
|
|
|
|||
|
|
@ -1669,6 +1669,124 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $principalUri
|
||||
* @param string $pattern
|
||||
* @param array $componentTypes
|
||||
* @param array $searchProperties
|
||||
* @param array $searchParameters
|
||||
* @param array $options
|
||||
* @return array
|
||||
*/
|
||||
public function searchPrincipalUri(string $principalUri,
|
||||
string $pattern,
|
||||
array $componentTypes,
|
||||
array $searchProperties,
|
||||
array $searchParameters,
|
||||
array $options = []): array {
|
||||
$escapePattern = !\array_key_exists('escape_like_param', $options) || $options['escape_like_param'] !== false;
|
||||
|
||||
$calendarObjectIdQuery = $this->db->getQueryBuilder();
|
||||
$calendarOr = $calendarObjectIdQuery->expr()->orX();
|
||||
$searchOr = $calendarObjectIdQuery->expr()->orX();
|
||||
|
||||
// Fetch calendars and subscription
|
||||
$calendars = $this->getCalendarsForUser($principalUri);
|
||||
$subscriptions = $this->getSubscriptionsForUser($principalUri);
|
||||
foreach ($calendars as $calendar) {
|
||||
$calendarAnd = $calendarObjectIdQuery->expr()->andX();
|
||||
$calendarAnd->add($calendarObjectIdQuery->expr()->eq('cob.calendarid', $calendarObjectIdQuery->createNamedParameter((int)$calendar['id'])));
|
||||
$calendarAnd->add($calendarObjectIdQuery->expr()->eq('cob.calendartype', $calendarObjectIdQuery->createNamedParameter(self::CALENDAR_TYPE_CALENDAR)));
|
||||
|
||||
// If it's shared, limit search to public events
|
||||
if ($calendar['principaluri'] !== $calendar['{http://owncloud.org/ns}owner-principal']) {
|
||||
$calendarAnd->add($calendarObjectIdQuery->expr()->eq('co.classification', $calendarObjectIdQuery->createNamedParameter(self::CLASSIFICATION_PUBLIC)));
|
||||
}
|
||||
|
||||
$calendarOr->add($calendarAnd);
|
||||
}
|
||||
foreach ($subscriptions as $subscription) {
|
||||
$subscriptionAnd = $calendarObjectIdQuery->expr()->andX();
|
||||
$subscriptionAnd->add($calendarObjectIdQuery->expr()->eq('cob.calendarid', $calendarObjectIdQuery->createNamedParameter((int)$subscription['id'])));
|
||||
$subscriptionAnd->add($calendarObjectIdQuery->expr()->eq('cob.calendartype', $calendarObjectIdQuery->createNamedParameter(self::CALENDAR_TYPE_SUBSCRIPTION)));
|
||||
|
||||
// If it's shared, limit search to public events
|
||||
if ($subscription['principaluri'] !== $subscription['{http://owncloud.org/ns}owner-principal']) {
|
||||
$subscriptionAnd->add($calendarObjectIdQuery->expr()->eq('co.classification', $calendarObjectIdQuery->createNamedParameter(self::CLASSIFICATION_PUBLIC)));
|
||||
}
|
||||
|
||||
$calendarOr->add($subscriptionAnd);
|
||||
}
|
||||
|
||||
foreach ($searchProperties as $property) {
|
||||
$propertyAnd = $calendarObjectIdQuery->expr()->andX();
|
||||
$propertyAnd->add($calendarObjectIdQuery->expr()->eq('cob.name', $calendarObjectIdQuery->createNamedParameter($property, IQueryBuilder::PARAM_STR)));
|
||||
$propertyAnd->add($calendarObjectIdQuery->expr()->isNull('cob.parameter'));
|
||||
|
||||
$searchOr->add($propertyAnd);
|
||||
}
|
||||
foreach ($searchParameters as $property => $parameter) {
|
||||
$parameterAnd = $calendarObjectIdQuery->expr()->andX();
|
||||
$parameterAnd->add($calendarObjectIdQuery->expr()->eq('cob.name', $calendarObjectIdQuery->createNamedParameter($property, IQueryBuilder::PARAM_STR)));
|
||||
$parameterAnd->add($calendarObjectIdQuery->expr()->eq('cob.parameter', $calendarObjectIdQuery->createNamedParameter($parameter, IQueryBuilder::PARAM_STR)));
|
||||
|
||||
$searchOr->add($parameterAnd);
|
||||
}
|
||||
|
||||
if ($calendarOr->count() === 0) {
|
||||
return [];
|
||||
}
|
||||
if ($searchOr->count() === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$calendarObjectIdQuery->selectDistinct('cob.objectid')
|
||||
->from($this->dbObjectPropertiesTable, 'cob')
|
||||
->leftJoin('cob', 'calendarobjects', 'co', $calendarObjectIdQuery->expr()->eq('co.id', 'cob.objectid'))
|
||||
->andWhere($calendarObjectIdQuery->expr()->in('co.componenttype', $calendarObjectIdQuery->createNamedParameter($componentTypes, IQueryBuilder::PARAM_STR_ARRAY)))
|
||||
->andWhere($calendarOr)
|
||||
->andWhere($searchOr);
|
||||
|
||||
if ('' !== $pattern) {
|
||||
if (!$escapePattern) {
|
||||
$calendarObjectIdQuery->andWhere($calendarObjectIdQuery->expr()->ilike('cob.value', $calendarObjectIdQuery->createNamedParameter($pattern)));
|
||||
} else {
|
||||
$calendarObjectIdQuery->andWhere($calendarObjectIdQuery->expr()->ilike('cob.value', $calendarObjectIdQuery->createNamedParameter('%' . $this->db->escapeLikeParameter($pattern) . '%')));
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($options['limit'])) {
|
||||
$calendarObjectIdQuery->setMaxResults($options['limit']);
|
||||
}
|
||||
if (isset($options['offset'])) {
|
||||
$calendarObjectIdQuery->setFirstResult($options['offset']);
|
||||
}
|
||||
|
||||
$result = $calendarObjectIdQuery->execute();
|
||||
$matches = $result->fetchAll();
|
||||
$result->closeCursor();
|
||||
$matches = array_map(static function (array $match):int {
|
||||
return (int) $match['objectid'];
|
||||
}, $matches);
|
||||
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('calendardata', 'uri', 'calendarid', 'calendartype')
|
||||
->from('calendarobjects')
|
||||
->where($query->expr()->in('id', $query->createNamedParameter($matches, IQueryBuilder::PARAM_INT_ARRAY)));
|
||||
|
||||
$result = $query->execute();
|
||||
$calendarObjects = $result->fetchAll();
|
||||
$result->closeCursor();
|
||||
|
||||
return array_map(function (array $array): array {
|
||||
$array['calendarid'] = (int)$array['calendarid'];
|
||||
$array['calendartype'] = (int)$array['calendartype'];
|
||||
$array['calendardata'] = $this->readBlob($array['calendardata']);
|
||||
|
||||
return $array;
|
||||
}, $calendarObjects);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches through all of a users calendars and calendar objects to find
|
||||
* an object with a specific UID.
|
||||
|
|
|
|||
138
apps/dav/lib/Search/ACalendarSearchProvider.php
Normal file
138
apps/dav/lib/Search/ACalendarSearchProvider.php
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
<?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\App\IAppManager;
|
||||
use OCP\IL10N;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\Search\IProvider;
|
||||
use Sabre\VObject\Component;
|
||||
use Sabre\VObject\Reader;
|
||||
|
||||
/**
|
||||
* Class ACalendarSearchProvider
|
||||
*
|
||||
* @package OCA\DAV\Search
|
||||
*/
|
||||
abstract class ACalendarSearchProvider implements IProvider {
|
||||
|
||||
/** @var IAppManager */
|
||||
protected $appManager;
|
||||
|
||||
/** @var IL10N */
|
||||
protected $l10n;
|
||||
|
||||
/** @var IURLGenerator */
|
||||
protected $urlGenerator;
|
||||
|
||||
/** @var CalDavBackend */
|
||||
protected $backend;
|
||||
|
||||
/**
|
||||
* ACalendarSearchProvider constructor.
|
||||
*
|
||||
* @param IAppManager $appManager
|
||||
* @param IL10N $l10n
|
||||
* @param IURLGenerator $urlGenerator
|
||||
* @param CalDavBackend $backend
|
||||
*/
|
||||
public function __construct(IAppManager $appManager,
|
||||
IL10N $l10n,
|
||||
IURLGenerator $urlGenerator,
|
||||
CalDavBackend $backend) {
|
||||
$this->appManager = $appManager;
|
||||
$this->l10n = $l10n;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->backend = $backend;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an associative array of calendars
|
||||
* calendarId => calendar
|
||||
*
|
||||
* @param string $principalUri
|
||||
* @return array
|
||||
*/
|
||||
protected function getSortedCalendars(string $principalUri): array {
|
||||
$calendars = $this->backend->getCalendarsForUser($principalUri);
|
||||
$calendarsById = [];
|
||||
foreach ($calendars as $calendar) {
|
||||
$calendarsById[(int) $calendar['id']] = $calendar;
|
||||
}
|
||||
|
||||
return $calendarsById;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an associative array of subscriptions
|
||||
* subscriptionId => subscription
|
||||
*
|
||||
* @param string $principalUri
|
||||
* @return array
|
||||
*/
|
||||
protected function getSortedSubscriptions(string $principalUri): array {
|
||||
$subscriptions = $this->backend->getSubscriptionsForUser($principalUri);
|
||||
$subscriptionsById = [];
|
||||
foreach ($subscriptions as $subscription) {
|
||||
$subscriptionsById[(int) $subscription['id']] = $subscription;
|
||||
}
|
||||
|
||||
return $subscriptionsById;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the primary VEvent / VJournal / VTodo component
|
||||
* If it's a component with recurrence-ids, it will return
|
||||
* the primary component
|
||||
*
|
||||
* TODO: It would be a nice enhancement to show recurrence-exceptions
|
||||
* as individual search-results.
|
||||
* For now we will just display the primary element of a recurrence-set.
|
||||
*
|
||||
* @param string $calendarData
|
||||
* @param string $componentName
|
||||
* @return Component
|
||||
*/
|
||||
protected function getPrimaryComponent(string $calendarData, string $componentName): Component {
|
||||
$vCalendar = Reader::read($calendarData, Reader::OPTION_FORGIVING);
|
||||
|
||||
$components = $vCalendar->select($componentName);
|
||||
if (count($components) === 1) {
|
||||
return $components[0];
|
||||
}
|
||||
|
||||
// If it's a recurrence-set, take the primary element
|
||||
foreach ($components as $component) {
|
||||
/** @var Component $component */
|
||||
if (!$component->{'RECURRENCE-ID'}) {
|
||||
return $component;
|
||||
}
|
||||
}
|
||||
|
||||
// In case of error, just fallback to the first element in the set
|
||||
return $components[0];
|
||||
}
|
||||
}
|
||||
231
apps/dav/lib/Search/EventsSearchProvider.php
Normal file
231
apps/dav/lib/Search/EventsSearchProvider.php
Normal file
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
30
apps/dav/lib/Search/EventsSearchResultEntry.php
Normal file
30
apps/dav/lib/Search/EventsSearchResultEntry.php
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<?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 OCP\Search\ASearchResultEntry;
|
||||
|
||||
class EventsSearchResultEntry extends ASearchResultEntry {
|
||||
}
|
||||
160
apps/dav/lib/Search/TasksSearchProvider.php
Normal file
160
apps/dav/lib/Search/TasksSearchProvider.php
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
<?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;
|
||||
|
||||
/**
|
||||
* Class TasksSearchProvider
|
||||
*
|
||||
* @package OCA\DAV\Search
|
||||
*/
|
||||
class TasksSearchProvider extends ACalendarSearchProvider {
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private static $searchProperties = [
|
||||
'SUMMARY',
|
||||
'DESCRIPTION',
|
||||
'CATEGORIES',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private static $searchParameters = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private static $componentType = 'VTODO';
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getId(): string {
|
||||
return 'tasks-dav';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getName(): string {
|
||||
return $this->l10n->t('Tasks');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function search(IUser $user,
|
||||
ISearchQuery $query): SearchResult {
|
||||
if (!$this->appManager->isEnabledForUser('tasks', $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 $taskRow) use ($calendarsById, $subscriptionsById):TasksSearchResultEntry {
|
||||
$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']];
|
||||
}
|
||||
$resourceUrl = $this->getDeepLinkToTasksApp($calendar['uri'], $taskRow['uri']);
|
||||
|
||||
return new TasksSearchResultEntry('', $title, $subline, $resourceUrl, 'icon-checkmark', false);
|
||||
}, $searchResults);
|
||||
|
||||
return SearchResult::paginated(
|
||||
$this->getName(),
|
||||
$formattedResults,
|
||||
$query->getCursor() + count($formattedResults)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $calendarUri
|
||||
* @param string $taskUri
|
||||
* @return string
|
||||
*/
|
||||
protected function getDeepLinkToTasksApp(string $calendarUri,
|
||||
string $taskUri): string {
|
||||
return $this->urlGenerator->getAbsoluteURL(
|
||||
$this->urlGenerator->linkToRoute('tasks.page.index')
|
||||
. '#/calendars/'
|
||||
. $calendarUri
|
||||
. '/tasks/'
|
||||
. $taskUri
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Component $taskComponent
|
||||
* @return string
|
||||
*/
|
||||
protected function generateSubline(Component $taskComponent): string {
|
||||
if ($taskComponent->COMPLETED) {
|
||||
$completedDateTime = new \DateTime($taskComponent->COMPLETED->getDateTime()->format(\DateTime::ATOM));
|
||||
$formattedDate = $this->l10n->l('date', $completedDateTime, ['width' => 'medium']);
|
||||
return $this->l10n->t('Completed on %s', [$formattedDate]);
|
||||
}
|
||||
|
||||
if ($taskComponent->DUE) {
|
||||
$dueDateTime = new \DateTime($taskComponent->DUE->getDateTime()->format(\DateTime::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]);
|
||||
}
|
||||
|
||||
return $this->l10n->t('Due on %s', [$formattedDate]);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
30
apps/dav/lib/Search/TasksSearchResultEntry.php
Normal file
30
apps/dav/lib/Search/TasksSearchResultEntry.php
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<?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 OCP\Search\ASearchResultEntry;
|
||||
|
||||
class TasksSearchResultEntry extends ASearchResultEntry {
|
||||
}
|
||||
|
|
@ -121,7 +121,12 @@ abstract class AbstractCalDavBackend extends TestCase {
|
|||
$this->principal->expects($this->any())->method('getGroupMembership')
|
||||
->withAnyParameters()
|
||||
->willReturn([self::UNIT_TEST_GROUP, self::UNIT_TEST_GROUP2]);
|
||||
$calendars = $this->backend->getCalendarsForUser(self::UNIT_TEST_USER);
|
||||
$this->cleanupForPrincipal(self::UNIT_TEST_USER);
|
||||
$this->cleanupForPrincipal(self::UNIT_TEST_USER1);
|
||||
}
|
||||
|
||||
private function cleanupForPrincipal($principal): void {
|
||||
$calendars = $this->backend->getCalendarsForUser($principal);
|
||||
foreach ($calendars as $calendar) {
|
||||
$this->dispatcher->expects($this->at(0))
|
||||
->method('dispatch')
|
||||
|
|
@ -129,7 +134,7 @@ abstract class AbstractCalDavBackend extends TestCase {
|
|||
|
||||
$this->backend->deleteCalendar($calendar['id']);
|
||||
}
|
||||
$subscriptions = $this->backend->getSubscriptionsForUser(self::UNIT_TEST_USER);
|
||||
$subscriptions = $this->backend->getSubscriptionsForUser($principal);
|
||||
foreach ($subscriptions as $subscription) {
|
||||
$this->backend->deleteSubscription($subscription['id']);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -605,21 +605,21 @@ DTSTART;TZID=Europe/Warsaw:20170325T150000
|
|||
DTEND;TZID=Europe/Warsaw:20170325T160000
|
||||
TRANSP:OPAQUE
|
||||
DESCRIPTION:Magiczna treść uzyskana za pomocą magicznego proszku.\n\nę
|
||||
żźćńłóÓŻŹĆŁĘ€śśśŚŚ\n \,\,))))))))\;\,\n
|
||||
żźćńłóÓŻŹĆŁĘ€śśśŚŚ\n \,\,))))))))\;\,\n
|
||||
__))))))))))))))\,\n \\|/ -\\(((((''''((((((((.\n -*-==///
|
||||
///(('' . `))))))\,\n /|\\ ))| o \;-. '(((((
|
||||
\,(\,\n ( `| / ) \;))))'
|
||||
///(('' . `))))))\,\n /|\\ ))| o \;-. '(((((
|
||||
\,(\,\n ( `| / ) \;))))'
|
||||
\,_))^\;(~\n | | | \,))((((_ _____-
|
||||
-----~~~-. %\,\;(\;(>'\;'~\n o_)\; \; )))(((` ~---
|
||||
~ `:: \\ %%~~)(v\;(`('~\n \; ''''````
|
||||
`: `:::|\\\,__\,%% )\;`'\; ~\n | _
|
||||
) / `:|`----' `-'\n ______/\\/~ |
|
||||
`: `:::|\\\,__\,%% )\;`'\; ~\n | _
|
||||
) / `:|`----' `-'\n ______/\\/~ |
|
||||
/ /\n /~\;\;.____/\;\;' / ___--\
|
||||
,-( `\;\;\;/\n / // _\;______\;'------~~~~~ /\;\;/\\ /\n
|
||||
// | | / \; \\\;\;\,\\\n (<_ | \;
|
||||
/'\,/-----' _>\n \\_| ||_
|
||||
//~\;~~~~~~~~~\n `\\_| (\,~~ -Tua Xiong\n
|
||||
\\~\\\n
|
||||
,-( `\;\;\;/\n / // _\;______\;'------~~~~~ /\;\;/\\ /\n
|
||||
// | | / \; \\\;\;\,\\\n (<_ | \;
|
||||
/'\,/-----' _>\n \\_| ||_
|
||||
//~\;~~~~~~~~~\n `\\_| (\,~~ -Tua Xiong\n
|
||||
\\~\\\n
|
||||
~~\n\n
|
||||
SEQUENCE:1
|
||||
X-MOZ-GENERATION:1
|
||||
|
|
@ -999,4 +999,282 @@ EOD;
|
|||
$this->assertEquals($calendarInfoUser['id'], $calendarInfoUser1['id']);
|
||||
$this->assertEquals($calendarInfoUser['uri'], $calendarInfoUser1['uri']);
|
||||
}
|
||||
|
||||
public function testSearchPrincipal(): void {
|
||||
$myPublic = <<<EOD
|
||||
BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
PRODID:-//dmfs.org//mimedir.icalendar//EN
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:Europe/Berlin
|
||||
X-LIC-LOCATION:Europe/Berlin
|
||||
BEGIN:DAYLIGHT
|
||||
TZOFFSETFROM:+0100
|
||||
TZOFFSETTO:+0200
|
||||
TZNAME:CEST
|
||||
DTSTART:19700329T020000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
|
||||
END:DAYLIGHT
|
||||
BEGIN:STANDARD
|
||||
TZOFFSETFROM:+0200
|
||||
TZOFFSETTO:+0100
|
||||
TZNAME:CET
|
||||
DTSTART:19701025T030000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
|
||||
END:STANDARD
|
||||
END:VTIMEZONE
|
||||
BEGIN:VEVENT
|
||||
DTSTART;TZID=Europe/Berlin:20160419T130000
|
||||
SUMMARY:My Test (public)
|
||||
CLASS:PUBLIC
|
||||
TRANSP:OPAQUE
|
||||
STATUS:CONFIRMED
|
||||
DTEND;TZID=Europe/Berlin:20160419T140000
|
||||
LAST-MODIFIED:20160419T074202Z
|
||||
DTSTAMP:20160419T074202Z
|
||||
CREATED:20160419T074202Z
|
||||
UID:2e468c48-7860-492e-bc52-92fa0daeeccf.1461051722310-1
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
EOD;
|
||||
$myPrivate = <<<EOD
|
||||
BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
PRODID:-//dmfs.org//mimedir.icalendar//EN
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:Europe/Berlin
|
||||
X-LIC-LOCATION:Europe/Berlin
|
||||
BEGIN:DAYLIGHT
|
||||
TZOFFSETFROM:+0100
|
||||
TZOFFSETTO:+0200
|
||||
TZNAME:CEST
|
||||
DTSTART:19700329T020000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
|
||||
END:DAYLIGHT
|
||||
BEGIN:STANDARD
|
||||
TZOFFSETFROM:+0200
|
||||
TZOFFSETTO:+0100
|
||||
TZNAME:CET
|
||||
DTSTART:19701025T030000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
|
||||
END:STANDARD
|
||||
END:VTIMEZONE
|
||||
BEGIN:VEVENT
|
||||
DTSTART;TZID=Europe/Berlin:20160419T130000
|
||||
SUMMARY:My Test (private)
|
||||
CLASS:PRIVATE
|
||||
TRANSP:OPAQUE
|
||||
STATUS:CONFIRMED
|
||||
DTEND;TZID=Europe/Berlin:20160419T140000
|
||||
LAST-MODIFIED:20160419T074202Z
|
||||
DTSTAMP:20160419T074202Z
|
||||
CREATED:20160419T074202Z
|
||||
UID:2e468c48-7860-492e-bc52-92fa0daeeccf.1461051722310-2
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
EOD;
|
||||
$myConfidential = <<<EOD
|
||||
BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
PRODID:-//dmfs.org//mimedir.icalendar//EN
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:Europe/Berlin
|
||||
X-LIC-LOCATION:Europe/Berlin
|
||||
BEGIN:DAYLIGHT
|
||||
TZOFFSETFROM:+0100
|
||||
TZOFFSETTO:+0200
|
||||
TZNAME:CEST
|
||||
DTSTART:19700329T020000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
|
||||
END:DAYLIGHT
|
||||
BEGIN:STANDARD
|
||||
TZOFFSETFROM:+0200
|
||||
TZOFFSETTO:+0100
|
||||
TZNAME:CET
|
||||
DTSTART:19701025T030000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
|
||||
END:STANDARD
|
||||
END:VTIMEZONE
|
||||
BEGIN:VEVENT
|
||||
DTSTART;TZID=Europe/Berlin:20160419T130000
|
||||
SUMMARY:My Test (confidential)
|
||||
CLASS:CONFIDENTIAL
|
||||
TRANSP:OPAQUE
|
||||
STATUS:CONFIRMED
|
||||
DTEND;TZID=Europe/Berlin:20160419T140000
|
||||
LAST-MODIFIED:20160419T074202Z
|
||||
DTSTAMP:20160419T074202Z
|
||||
CREATED:20160419T074202Z
|
||||
UID:2e468c48-7860-492e-bc52-92fa0daeeccf.1461051722310-3
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
EOD;
|
||||
|
||||
$sharerPublic = <<<EOD
|
||||
BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
PRODID:-//dmfs.org//mimedir.icalendar//EN
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:Europe/Berlin
|
||||
X-LIC-LOCATION:Europe/Berlin
|
||||
BEGIN:DAYLIGHT
|
||||
TZOFFSETFROM:+0100
|
||||
TZOFFSETTO:+0200
|
||||
TZNAME:CEST
|
||||
DTSTART:19700329T020000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
|
||||
END:DAYLIGHT
|
||||
BEGIN:STANDARD
|
||||
TZOFFSETFROM:+0200
|
||||
TZOFFSETTO:+0100
|
||||
TZNAME:CET
|
||||
DTSTART:19701025T030000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
|
||||
END:STANDARD
|
||||
END:VTIMEZONE
|
||||
BEGIN:VEVENT
|
||||
DTSTART;TZID=Europe/Berlin:20160419T130000
|
||||
SUMMARY:Sharer Test (public)
|
||||
CLASS:PUBLIC
|
||||
TRANSP:OPAQUE
|
||||
STATUS:CONFIRMED
|
||||
DTEND;TZID=Europe/Berlin:20160419T140000
|
||||
LAST-MODIFIED:20160419T074202Z
|
||||
DTSTAMP:20160419T074202Z
|
||||
CREATED:20160419T074202Z
|
||||
UID:2e468c48-7860-492e-bc52-92fa0daeeccf.1461051722310-4
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
EOD;
|
||||
$sharerPrivate = <<<EOD
|
||||
BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
PRODID:-//dmfs.org//mimedir.icalendar//EN
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:Europe/Berlin
|
||||
X-LIC-LOCATION:Europe/Berlin
|
||||
BEGIN:DAYLIGHT
|
||||
TZOFFSETFROM:+0100
|
||||
TZOFFSETTO:+0200
|
||||
TZNAME:CEST
|
||||
DTSTART:19700329T020000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
|
||||
END:DAYLIGHT
|
||||
BEGIN:STANDARD
|
||||
TZOFFSETFROM:+0200
|
||||
TZOFFSETTO:+0100
|
||||
TZNAME:CET
|
||||
DTSTART:19701025T030000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
|
||||
END:STANDARD
|
||||
END:VTIMEZONE
|
||||
BEGIN:VEVENT
|
||||
DTSTART;TZID=Europe/Berlin:20160419T130000
|
||||
SUMMARY:Sharer Test (private)
|
||||
CLASS:PRIVATE
|
||||
TRANSP:OPAQUE
|
||||
STATUS:CONFIRMED
|
||||
DTEND;TZID=Europe/Berlin:20160419T140000
|
||||
LAST-MODIFIED:20160419T074202Z
|
||||
DTSTAMP:20160419T074202Z
|
||||
CREATED:20160419T074202Z
|
||||
UID:2e468c48-7860-492e-bc52-92fa0daeeccf.1461051722310-5
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
EOD;
|
||||
$sharerConfidential = <<<EOD
|
||||
BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
PRODID:-//dmfs.org//mimedir.icalendar//EN
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:Europe/Berlin
|
||||
X-LIC-LOCATION:Europe/Berlin
|
||||
BEGIN:DAYLIGHT
|
||||
TZOFFSETFROM:+0100
|
||||
TZOFFSETTO:+0200
|
||||
TZNAME:CEST
|
||||
DTSTART:19700329T020000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
|
||||
END:DAYLIGHT
|
||||
BEGIN:STANDARD
|
||||
TZOFFSETFROM:+0200
|
||||
TZOFFSETTO:+0100
|
||||
TZNAME:CET
|
||||
DTSTART:19701025T030000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
|
||||
END:STANDARD
|
||||
END:VTIMEZONE
|
||||
BEGIN:VEVENT
|
||||
DTSTART;TZID=Europe/Berlin:20160419T130000
|
||||
SUMMARY:Sharer Test (confidential)
|
||||
CLASS:CONFIDENTIAL
|
||||
TRANSP:OPAQUE
|
||||
STATUS:CONFIRMED
|
||||
DTEND;TZID=Europe/Berlin:20160419T140000
|
||||
LAST-MODIFIED:20160419T074202Z
|
||||
DTSTAMP:20160419T074202Z
|
||||
CREATED:20160419T074202Z
|
||||
UID:2e468c48-7860-492e-bc52-92fa0daeeccf.1461051722310-6
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
EOD;
|
||||
|
||||
$l10n = $this->createMock(IL10N::class);
|
||||
$l10n
|
||||
->expects($this->any())
|
||||
->method('t')
|
||||
->willReturnCallback(function ($text, $parameters = []) {
|
||||
return vsprintf($text, $parameters);
|
||||
});
|
||||
$config = $this->createMock(IConfig::class);
|
||||
$this->userManager->expects($this->any())
|
||||
->method('userExists')
|
||||
->willReturn(true);
|
||||
$this->groupManager->expects($this->any())
|
||||
->method('groupExists')
|
||||
->willReturn(true);
|
||||
|
||||
$me = self::UNIT_TEST_USER;
|
||||
$sharer = self::UNIT_TEST_USER1;
|
||||
$this->backend->createCalendar($me, 'calendar-uri-me', []);
|
||||
$this->backend->createCalendar($sharer, 'calendar-uri-sharer', []);
|
||||
|
||||
$myCalendars = $this->backend->getCalendarsForUser($me);
|
||||
$this->assertCount(1, $myCalendars);
|
||||
|
||||
$sharerCalendars = $this->backend->getCalendarsForUser($sharer);
|
||||
$this->assertCount(1, $sharerCalendars);
|
||||
$sharerCalendar = new Calendar($this->backend, $sharerCalendars[0], $l10n, $config);
|
||||
$this->backend->updateShares($sharerCalendar, [
|
||||
[
|
||||
'href' => 'principal:' . $me,
|
||||
'readOnly' => false,
|
||||
],
|
||||
], []);
|
||||
|
||||
$this->assertCount(2, $this->backend->getCalendarsForUser($me));
|
||||
|
||||
$this->backend->createCalendarObject($myCalendars[0]['id'], 'event0.ics', $myPublic);
|
||||
$this->backend->createCalendarObject($myCalendars[0]['id'], 'event1.ics', $myPrivate);
|
||||
$this->backend->createCalendarObject($myCalendars[0]['id'], 'event2.ics', $myConfidential);
|
||||
|
||||
$this->backend->createCalendarObject($sharerCalendars[0]['id'], 'event3.ics', $sharerPublic);
|
||||
$this->backend->createCalendarObject($sharerCalendars[0]['id'], 'event4.ics', $sharerPrivate);
|
||||
$this->backend->createCalendarObject($sharerCalendars[0]['id'], 'event5.ics', $sharerConfidential);
|
||||
|
||||
$mySearchResults = $this->backend->searchPrincipalUri($me, 'Test', ['VEVENT'], ['SUMMARY'], []);
|
||||
$sharerSearchResults = $this->backend->searchPrincipalUri($sharer, 'Test', ['VEVENT'], ['SUMMARY'], []);
|
||||
|
||||
$this->assertCount(4, $mySearchResults);
|
||||
$this->assertCount(3, $sharerSearchResults);
|
||||
|
||||
$this->assertEquals($myPublic, $mySearchResults[0]['calendardata']);
|
||||
$this->assertEquals($myPrivate, $mySearchResults[1]['calendardata']);
|
||||
$this->assertEquals($myConfidential, $mySearchResults[2]['calendardata']);
|
||||
$this->assertEquals($sharerPublic, $mySearchResults[3]['calendardata']);
|
||||
|
||||
$this->assertEquals($sharerPublic, $sharerSearchResults[0]['calendardata']);
|
||||
$this->assertEquals($sharerPrivate, $sharerSearchResults[1]['calendardata']);
|
||||
$this->assertEquals($sharerConfidential, $sharerSearchResults[2]['calendardata']);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -224,7 +224,7 @@ class ContactsSearchProviderTest extends TestCase {
|
|||
$this->assertEquals('icon-contacts-dark', $result0Data['iconClass']);
|
||||
$this->assertTrue($result0Data['rounded']);
|
||||
|
||||
$this->assertInstanceOf(ContactsSearchResultEntry::class, $result0);
|
||||
$this->assertInstanceOf(ContactsSearchResultEntry::class, $result1);
|
||||
$this->assertEquals('absolute-thumbnail-url?photo', $result1Data['thumbnailUrl']);
|
||||
$this->assertEquals('FN of Test2', $result1Data['title']);
|
||||
$this->assertEquals('subline', $result1Data['subline']);
|
||||
|
|
|
|||
473
apps/dav/tests/unit/Search/EventsSearchProviderTest.php
Normal file
473
apps/dav/tests/unit/Search/EventsSearchProviderTest.php
Normal file
|
|
@ -0,0 +1,473 @@
|
|||
<?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\Tests\unit\Search;
|
||||
|
||||
use OCA\DAV\CalDAV\CalDavBackend;
|
||||
use OCA\DAV\Search\EventsSearchProvider;
|
||||
use OCA\DAV\Search\EventsSearchResultEntry;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\IL10N;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\IUser;
|
||||
use OCP\Search\ISearchQuery;
|
||||
use OCP\Search\SearchResult;
|
||||
use Sabre\VObject\Reader;
|
||||
use Test\TestCase;
|
||||
|
||||
class EventsSearchProviderTest extends TestCase {
|
||||
|
||||
/** @var IAppManager|\PHPUnit\Framework\MockObject\MockObject */
|
||||
private $appManager;
|
||||
|
||||
/** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */
|
||||
private $l10n;
|
||||
|
||||
/** @var IURLGenerator|\PHPUnit\Framework\MockObject\MockObject */
|
||||
private $urlGenerator;
|
||||
|
||||
/** @var CalDavBackend|\PHPUnit\Framework\MockObject\MockObject */
|
||||
private $backend;
|
||||
|
||||
/** @var EventsSearchProvider */
|
||||
private $provider;
|
||||
|
||||
// NO SUMMARY
|
||||
private $vEvent0 = 'BEGIN:VCALENDAR'.PHP_EOL.
|
||||
'VERSION:2.0'.PHP_EOL.
|
||||
'PRODID:-//Apple Inc.//Mac OS X 10.11.6//EN'.PHP_EOL.
|
||||
'CALSCALE:GREGORIAN'.PHP_EOL.
|
||||
'BEGIN:VEVENT'.PHP_EOL.
|
||||
'CREATED:20161004T144433Z'.PHP_EOL.
|
||||
'UID:85560E76-1B0D-47E1-A735-21625767FCA4'.PHP_EOL.
|
||||
'DTEND;VALUE=DATE:20161008'.PHP_EOL.
|
||||
'TRANSP:TRANSPARENT'.PHP_EOL.
|
||||
'DTSTART;VALUE=DATE:20161005'.PHP_EOL.
|
||||
'DTSTAMP:20161004T144437Z'.PHP_EOL.
|
||||
'SEQUENCE:0'.PHP_EOL.
|
||||
'END:VEVENT'.PHP_EOL.
|
||||
'END:VCALENDAR';
|
||||
|
||||
// TIMED SAME DAY
|
||||
private $vEvent1 = 'BEGIN:VCALENDAR'.PHP_EOL.
|
||||
'VERSION:2.0'.PHP_EOL.
|
||||
'PRODID:-//Tests//'.PHP_EOL.
|
||||
'CALSCALE:GREGORIAN'.PHP_EOL.
|
||||
'BEGIN:VTIMEZONE'.PHP_EOL.
|
||||
'TZID:Europe/Berlin'.PHP_EOL.
|
||||
'BEGIN:DAYLIGHT'.PHP_EOL.
|
||||
'TZOFFSETFROM:+0100'.PHP_EOL.
|
||||
'RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU'.PHP_EOL.
|
||||
'DTSTART:19810329T020000'.PHP_EOL.
|
||||
'TZNAME:GMT+2'.PHP_EOL.
|
||||
'TZOFFSETTO:+0200'.PHP_EOL.
|
||||
'END:DAYLIGHT'.PHP_EOL.
|
||||
'BEGIN:STANDARD'.PHP_EOL.
|
||||
'TZOFFSETFROM:+0200'.PHP_EOL.
|
||||
'RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU'.PHP_EOL.
|
||||
'DTSTART:19961027T030000'.PHP_EOL.
|
||||
'TZNAME:GMT+1'.PHP_EOL.
|
||||
'TZOFFSETTO:+0100'.PHP_EOL.
|
||||
'END:STANDARD'.PHP_EOL.
|
||||
'END:VTIMEZONE'.PHP_EOL.
|
||||
'BEGIN:VEVENT'.PHP_EOL.
|
||||
'CREATED:20160809T163629Z'.PHP_EOL.
|
||||
'UID:0AD16F58-01B3-463B-A215-FD09FC729A02'.PHP_EOL.
|
||||
'DTEND;TZID=Europe/Berlin:20160816T100000'.PHP_EOL.
|
||||
'TRANSP:OPAQUE'.PHP_EOL.
|
||||
'SUMMARY:Test Europe Berlin'.PHP_EOL.
|
||||
'DTSTART;TZID=Europe/Berlin:20160816T090000'.PHP_EOL.
|
||||
'DTSTAMP:20160809T163632Z'.PHP_EOL.
|
||||
'SEQUENCE:0'.PHP_EOL.
|
||||
'END:VEVENT'.PHP_EOL.
|
||||
'END:VCALENDAR';
|
||||
|
||||
// TIMED DIFFERENT DAY
|
||||
private $vEvent2 = 'BEGIN:VCALENDAR'.PHP_EOL.
|
||||
'VERSION:2.0'.PHP_EOL.
|
||||
'PRODID:-//Tests//'.PHP_EOL.
|
||||
'CALSCALE:GREGORIAN'.PHP_EOL.
|
||||
'BEGIN:VTIMEZONE'.PHP_EOL.
|
||||
'TZID:Europe/Berlin'.PHP_EOL.
|
||||
'BEGIN:DAYLIGHT'.PHP_EOL.
|
||||
'TZOFFSETFROM:+0100'.PHP_EOL.
|
||||
'RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU'.PHP_EOL.
|
||||
'DTSTART:19810329T020000'.PHP_EOL.
|
||||
'TZNAME:GMT+2'.PHP_EOL.
|
||||
'TZOFFSETTO:+0200'.PHP_EOL.
|
||||
'END:DAYLIGHT'.PHP_EOL.
|
||||
'BEGIN:STANDARD'.PHP_EOL.
|
||||
'TZOFFSETFROM:+0200'.PHP_EOL.
|
||||
'RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU'.PHP_EOL.
|
||||
'DTSTART:19961027T030000'.PHP_EOL.
|
||||
'TZNAME:GMT+1'.PHP_EOL.
|
||||
'TZOFFSETTO:+0100'.PHP_EOL.
|
||||
'END:STANDARD'.PHP_EOL.
|
||||
'END:VTIMEZONE'.PHP_EOL.
|
||||
'BEGIN:VEVENT'.PHP_EOL.
|
||||
'CREATED:20160809T163629Z'.PHP_EOL.
|
||||
'UID:0AD16F58-01B3-463B-A215-FD09FC729A02'.PHP_EOL.
|
||||
'DTEND;TZID=Europe/Berlin:20160817T100000'.PHP_EOL.
|
||||
'TRANSP:OPAQUE'.PHP_EOL.
|
||||
'SUMMARY:Test Europe Berlin'.PHP_EOL.
|
||||
'DTSTART;TZID=Europe/Berlin:20160816T090000'.PHP_EOL.
|
||||
'DTSTAMP:20160809T163632Z'.PHP_EOL.
|
||||
'SEQUENCE:0'.PHP_EOL.
|
||||
'END:VEVENT'.PHP_EOL.
|
||||
'END:VCALENDAR';
|
||||
|
||||
// ALL-DAY ONE-DAY
|
||||
private $vEvent3 = 'BEGIN:VCALENDAR'.PHP_EOL.
|
||||
'VERSION:2.0'.PHP_EOL.
|
||||
'PRODID:-//Apple Inc.//Mac OS X 10.11.6//EN'.PHP_EOL.
|
||||
'CALSCALE:GREGORIAN'.PHP_EOL.
|
||||
'BEGIN:VEVENT'.PHP_EOL.
|
||||
'CREATED:20161004T144433Z'.PHP_EOL.
|
||||
'UID:85560E76-1B0D-47E1-A735-21625767FCA4'.PHP_EOL.
|
||||
'DTEND;VALUE=DATE:20161006'.PHP_EOL.
|
||||
'TRANSP:TRANSPARENT'.PHP_EOL.
|
||||
'DTSTART;VALUE=DATE:20161005'.PHP_EOL.
|
||||
'DTSTAMP:20161004T144437Z'.PHP_EOL.
|
||||
'SEQUENCE:0'.PHP_EOL.
|
||||
'END:VEVENT'.PHP_EOL.
|
||||
'END:VCALENDAR';
|
||||
|
||||
// ALL-DAY MULTIPLE DAYS
|
||||
private $vEvent4 = 'BEGIN:VCALENDAR'.PHP_EOL.
|
||||
'VERSION:2.0'.PHP_EOL.
|
||||
'PRODID:-//Apple Inc.//Mac OS X 10.11.6//EN'.PHP_EOL.
|
||||
'CALSCALE:GREGORIAN'.PHP_EOL.
|
||||
'BEGIN:VEVENT'.PHP_EOL.
|
||||
'CREATED:20161004T144433Z'.PHP_EOL.
|
||||
'UID:85560E76-1B0D-47E1-A735-21625767FCA4'.PHP_EOL.
|
||||
'DTEND;VALUE=DATE:20161008'.PHP_EOL.
|
||||
'TRANSP:TRANSPARENT'.PHP_EOL.
|
||||
'DTSTART;VALUE=DATE:20161005'.PHP_EOL.
|
||||
'DTSTAMP:20161004T144437Z'.PHP_EOL.
|
||||
'SEQUENCE:0'.PHP_EOL.
|
||||
'END:VEVENT'.PHP_EOL.
|
||||
'END:VCALENDAR';
|
||||
|
||||
// DURATION
|
||||
private $vEvent5 = 'BEGIN:VCALENDAR'.PHP_EOL.
|
||||
'VERSION:2.0'.PHP_EOL.
|
||||
'PRODID:-//Apple Inc.//Mac OS X 10.11.6//EN'.PHP_EOL.
|
||||
'CALSCALE:GREGORIAN'.PHP_EOL.
|
||||
'BEGIN:VEVENT'.PHP_EOL.
|
||||
'CREATED:20161004T144433Z'.PHP_EOL.
|
||||
'UID:85560E76-1B0D-47E1-A735-21625767FCA4'.PHP_EOL.
|
||||
'DURATION:P5D'.PHP_EOL.
|
||||
'TRANSP:TRANSPARENT'.PHP_EOL.
|
||||
'DTSTART;VALUE=DATE:20161005'.PHP_EOL.
|
||||
'DTSTAMP:20161004T144437Z'.PHP_EOL.
|
||||
'SEQUENCE:0'.PHP_EOL.
|
||||
'END:VEVENT'.PHP_EOL.
|
||||
'END:VCALENDAR';
|
||||
|
||||
// NO DTEND - DATE
|
||||
private $vEvent6 = 'BEGIN:VCALENDAR'.PHP_EOL.
|
||||
'VERSION:2.0'.PHP_EOL.
|
||||
'PRODID:-//Apple Inc.//Mac OS X 10.11.6//EN'.PHP_EOL.
|
||||
'CALSCALE:GREGORIAN'.PHP_EOL.
|
||||
'BEGIN:VEVENT'.PHP_EOL.
|
||||
'CREATED:20161004T144433Z'.PHP_EOL.
|
||||
'UID:85560E76-1B0D-47E1-A735-21625767FCA4'.PHP_EOL.
|
||||
'TRANSP:TRANSPARENT'.PHP_EOL.
|
||||
'DTSTART;VALUE=DATE:20161005'.PHP_EOL.
|
||||
'DTSTAMP:20161004T144437Z'.PHP_EOL.
|
||||
'SEQUENCE:0'.PHP_EOL.
|
||||
'END:VEVENT'.PHP_EOL.
|
||||
'END:VCALENDAR';
|
||||
|
||||
// NO DTEND - DATE-TIME
|
||||
private $vEvent7 = 'BEGIN:VCALENDAR'.PHP_EOL.
|
||||
'VERSION:2.0'.PHP_EOL.
|
||||
'PRODID:-//Tests//'.PHP_EOL.
|
||||
'CALSCALE:GREGORIAN'.PHP_EOL.
|
||||
'BEGIN:VTIMEZONE'.PHP_EOL.
|
||||
'TZID:Europe/Berlin'.PHP_EOL.
|
||||
'BEGIN:DAYLIGHT'.PHP_EOL.
|
||||
'TZOFFSETFROM:+0100'.PHP_EOL.
|
||||
'RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU'.PHP_EOL.
|
||||
'DTSTART:19810329T020000'.PHP_EOL.
|
||||
'TZNAME:GMT+2'.PHP_EOL.
|
||||
'TZOFFSETTO:+0200'.PHP_EOL.
|
||||
'END:DAYLIGHT'.PHP_EOL.
|
||||
'BEGIN:STANDARD'.PHP_EOL.
|
||||
'TZOFFSETFROM:+0200'.PHP_EOL.
|
||||
'RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU'.PHP_EOL.
|
||||
'DTSTART:19961027T030000'.PHP_EOL.
|
||||
'TZNAME:GMT+1'.PHP_EOL.
|
||||
'TZOFFSETTO:+0100'.PHP_EOL.
|
||||
'END:STANDARD'.PHP_EOL.
|
||||
'END:VTIMEZONE'.PHP_EOL.
|
||||
'BEGIN:VEVENT'.PHP_EOL.
|
||||
'CREATED:20160809T163629Z'.PHP_EOL.
|
||||
'UID:0AD16F58-01B3-463B-A215-FD09FC729A02'.PHP_EOL.
|
||||
'TRANSP:OPAQUE'.PHP_EOL.
|
||||
'SUMMARY:Test Europe Berlin'.PHP_EOL.
|
||||
'DTSTART;TZID=Europe/Berlin:20160816T090000'.PHP_EOL.
|
||||
'DTSTAMP:20160809T163632Z'.PHP_EOL.
|
||||
'SEQUENCE:0'.PHP_EOL.
|
||||
'END:VEVENT'.PHP_EOL.
|
||||
'END:VCALENDAR';
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->appManager = $this->createMock(IAppManager::class);
|
||||
$this->l10n = $this->createMock(IL10N::class);
|
||||
$this->urlGenerator = $this->createMock(IURLGenerator::class);
|
||||
$this->backend = $this->createMock(CalDavBackend::class);
|
||||
|
||||
$this->provider = new EventsSearchProvider(
|
||||
$this->appManager,
|
||||
$this->l10n,
|
||||
$this->urlGenerator,
|
||||
$this->backend
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetId(): void {
|
||||
$this->assertEquals('calendar-dav', $this->provider->getId());
|
||||
}
|
||||
|
||||
public function testGetName(): void {
|
||||
$this->l10n->expects($this->exactly(1))
|
||||
->method('t')
|
||||
->with('Events')
|
||||
->willReturnArgument(0);
|
||||
|
||||
$this->assertEquals('Events', $this->provider->getName());
|
||||
}
|
||||
|
||||
public function testSearchAppDisabled(): void {
|
||||
$user = $this->createMock(IUser::class);
|
||||
$query = $this->createMock(ISearchQuery::class);
|
||||
$this->appManager->expects($this->once())
|
||||
->method('isEnabledForUser')
|
||||
->with('calendar', $user)
|
||||
->willReturn(false);
|
||||
$this->l10n->expects($this->exactly(1))
|
||||
->method('t')
|
||||
->willReturnArgument(0);
|
||||
$this->backend->expects($this->never())
|
||||
->method('getCalendarsForUser');
|
||||
$this->backend->expects($this->never())
|
||||
->method('getSubscriptionsForUser');
|
||||
$this->backend->expects($this->never())
|
||||
->method('searchPrincipalUri');
|
||||
|
||||
$actual = $this->provider->search($user, $query);
|
||||
$data = $actual->jsonSerialize();
|
||||
$this->assertInstanceOf(SearchResult::class, $actual);
|
||||
$this->assertEquals('Events', $data['name']);
|
||||
$this->assertEmpty($data['entries']);
|
||||
$this->assertFalse($data['isPaginated']);
|
||||
$this->assertNull($data['cursor']);
|
||||
}
|
||||
|
||||
public function testSearch(): void {
|
||||
$user = $this->createMock(IUser::class);
|
||||
$user->method('getUID')->willReturn('john.doe');
|
||||
$query = $this->createMock(ISearchQuery::class);
|
||||
$query->method('getTerm')->willReturn('search term');
|
||||
$query->method('getLimit')->willReturn(5);
|
||||
$query->method('getCursor')->willReturn(20);
|
||||
$this->appManager->expects($this->once())
|
||||
->method('isEnabledForUser')
|
||||
->with('calendar', $user)
|
||||
->willReturn(true);
|
||||
$this->l10n->method('t')->willReturnArgument(0);
|
||||
|
||||
$this->backend->expects($this->once())
|
||||
->method('getCalendarsForUser')
|
||||
->with('principals/users/john.doe')
|
||||
->willReturn([
|
||||
[
|
||||
'id' => 99,
|
||||
'principaluri' => 'principals/users/john.doe',
|
||||
'uri' => 'calendar-uri-99',
|
||||
], [
|
||||
'id' => 123,
|
||||
'principaluri' => 'principals/users/john.doe',
|
||||
'uri' => 'calendar-uri-123',
|
||||
]
|
||||
]);
|
||||
$this->backend->expects($this->once())
|
||||
->method('getSubscriptionsForUser')
|
||||
->with('principals/users/john.doe')
|
||||
->willReturn([
|
||||
[
|
||||
'id' => 1337,
|
||||
'principaluri' => 'principals/users/john.doe',
|
||||
'uri' => 'subscription-uri-1337',
|
||||
]
|
||||
]);
|
||||
$this->backend->expects($this->once())
|
||||
->method('searchPrincipalUri')
|
||||
->with('principals/users/john.doe', 'search term', ['VEVENT'],
|
||||
['SUMMARY', 'LOCATION', 'DESCRIPTION', 'ATTENDEE', 'ORGANIZER', 'CATEGORIES'],
|
||||
['ATTENDEE' => ['CN'], 'ORGANIZER' => ['CN']],
|
||||
['limit' => 5, 'offset' => 20])
|
||||
->willReturn([
|
||||
[
|
||||
'calendarid' => 99,
|
||||
'calendartype' => CalDavBackend::CALENDAR_TYPE_CALENDAR,
|
||||
'uri' => 'event0.ics',
|
||||
'calendardata' => $this->vEvent0,
|
||||
],
|
||||
[
|
||||
'calendarid' => 123,
|
||||
'calendartype' => CalDavBackend::CALENDAR_TYPE_CALENDAR,
|
||||
'uri' => 'event1.ics',
|
||||
'calendardata' => $this->vEvent1,
|
||||
],
|
||||
[
|
||||
'calendarid' => 1337,
|
||||
'calendartype' => CalDavBackend::CALENDAR_TYPE_SUBSCRIPTION,
|
||||
'uri' => 'event2.ics',
|
||||
'calendardata' => $this->vEvent2,
|
||||
]
|
||||
]);
|
||||
|
||||
$provider = $this->getMockBuilder(EventsSearchProvider::class)
|
||||
->setConstructorArgs([
|
||||
$this->appManager,
|
||||
$this->l10n,
|
||||
$this->urlGenerator,
|
||||
$this->backend,
|
||||
])
|
||||
->setMethods([
|
||||
'getDeepLinkToCalendarApp',
|
||||
'generateSubline',
|
||||
])
|
||||
->getMock();
|
||||
|
||||
$provider->expects($this->exactly(3))
|
||||
->method('generateSubline')
|
||||
->willReturn('subline');
|
||||
$provider->expects($this->exactly(3))
|
||||
->method('getDeepLinkToCalendarApp')
|
||||
->withConsecutive(
|
||||
['principals/users/john.doe', 'calendar-uri-99', 'event0.ics'],
|
||||
['principals/users/john.doe', 'calendar-uri-123', 'event1.ics'],
|
||||
['principals/users/john.doe', 'subscription-uri-1337', 'event2.ics']
|
||||
)
|
||||
->willReturn('deep-link-to-calendar');
|
||||
|
||||
$actual = $provider->search($user, $query);
|
||||
$data = $actual->jsonSerialize();
|
||||
$this->assertInstanceOf(SearchResult::class, $actual);
|
||||
$this->assertEquals('Events', $data['name']);
|
||||
$this->assertCount(3, $data['entries']);
|
||||
$this->assertTrue($data['isPaginated']);
|
||||
$this->assertEquals(23, $data['cursor']);
|
||||
|
||||
$result0 = $data['entries'][0];
|
||||
$result0Data = $result0->jsonSerialize();
|
||||
$result1 = $data['entries'][1];
|
||||
$result1Data = $result1->jsonSerialize();
|
||||
$result2 = $data['entries'][2];
|
||||
$result2Data = $result2->jsonSerialize();
|
||||
|
||||
$this->assertInstanceOf(EventsSearchResultEntry::class, $result0);
|
||||
$this->assertEmpty($result0Data['thumbnailUrl']);
|
||||
$this->assertEquals('Untitled event', $result0Data['title']);
|
||||
$this->assertEquals('subline', $result0Data['subline']);
|
||||
$this->assertEquals('deep-link-to-calendar', $result0Data['resourceUrl']);
|
||||
$this->assertEquals('icon-calendar-dark', $result0Data['iconClass']);
|
||||
$this->assertFalse($result0Data['rounded']);
|
||||
|
||||
$this->assertInstanceOf(EventsSearchResultEntry::class, $result1);
|
||||
$this->assertEmpty($result1Data['thumbnailUrl']);
|
||||
$this->assertEquals('Test Europe Berlin', $result1Data['title']);
|
||||
$this->assertEquals('subline', $result1Data['subline']);
|
||||
$this->assertEquals('deep-link-to-calendar', $result1Data['resourceUrl']);
|
||||
$this->assertEquals('icon-calendar-dark', $result1Data['iconClass']);
|
||||
$this->assertFalse($result1Data['rounded']);
|
||||
|
||||
$this->assertInstanceOf(EventsSearchResultEntry::class, $result2);
|
||||
$this->assertEmpty($result2Data['thumbnailUrl']);
|
||||
$this->assertEquals('Test Europe Berlin', $result2Data['title']);
|
||||
$this->assertEquals('subline', $result2Data['subline']);
|
||||
$this->assertEquals('deep-link-to-calendar', $result2Data['resourceUrl']);
|
||||
$this->assertEquals('icon-calendar-dark', $result2Data['iconClass']);
|
||||
$this->assertFalse($result2Data['rounded']);
|
||||
}
|
||||
|
||||
public function testGetDeepLinkToCalendarApp(): void {
|
||||
$this->urlGenerator->expects($this->at(0))
|
||||
->method('linkTo')
|
||||
->with('', 'remote.php')
|
||||
->willReturn('link-to-remote.php');
|
||||
$this->urlGenerator->expects($this->at(1))
|
||||
->method('linkToRoute')
|
||||
->with('calendar.view.index')
|
||||
->willReturn('link-to-route-calendar/');
|
||||
$this->urlGenerator->expects($this->at(2))
|
||||
->method('getAbsoluteURL')
|
||||
->with('link-to-route-calendar/edit/bGluay10by1yZW1vdGUucGhwL2Rhdi9jYWxlbmRhcnMvam9obi5kb2UvZm9vL2Jhci5pY3M=')
|
||||
->willReturn('absolute-url-to-route');
|
||||
|
||||
$actual = self::invokePrivate($this->provider, 'getDeepLinkToCalendarApp', ['principals/users/john.doe', 'foo', 'bar.ics']);
|
||||
|
||||
$this->assertEquals('absolute-url-to-route', $actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $ics
|
||||
* @param string $expectedSubline
|
||||
*
|
||||
* @dataProvider generateSublineDataProvider
|
||||
*/
|
||||
public function testGenerateSubline(string $ics, string $expectedSubline): void {
|
||||
$vCalendar = Reader::read($ics, Reader::OPTION_FORGIVING);
|
||||
$eventComponent = $vCalendar->VEVENT;
|
||||
|
||||
$this->l10n->method('l')
|
||||
->willReturnCallback(static function (string $type, \DateTime $date, $_):string {
|
||||
if ($type === 'time') {
|
||||
return $date->format('H:i');
|
||||
}
|
||||
|
||||
return $date->format('m-d');
|
||||
});
|
||||
|
||||
$actual = self::invokePrivate($this->provider, 'generateSubline', [$eventComponent]);
|
||||
$this->assertEquals($expectedSubline, $actual);
|
||||
}
|
||||
|
||||
public function generateSublineDataProvider(): array {
|
||||
return [
|
||||
[$this->vEvent1, '08-16 09:00 - 10:00'],
|
||||
[$this->vEvent2, '08-16 09:00 - 08-17 10:00'],
|
||||
[$this->vEvent3, '10-05'],
|
||||
[$this->vEvent4, '10-05 - 10-07'],
|
||||
[$this->vEvent5, '10-05 - 10-09'],
|
||||
[$this->vEvent6, '10-05'],
|
||||
[$this->vEvent7, '08-16 09:00 - 09:00'],
|
||||
];
|
||||
}
|
||||
}
|
||||
344
apps/dav/tests/unit/Search/TasksSearchProviderTest.php
Normal file
344
apps/dav/tests/unit/Search/TasksSearchProviderTest.php
Normal file
|
|
@ -0,0 +1,344 @@
|
|||
<?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\Tests\unit\Search;
|
||||
|
||||
use OCA\DAV\CalDAV\CalDavBackend;
|
||||
use OCA\DAV\Search\TasksSearchProvider;
|
||||
use OCA\DAV\Search\TasksSearchResultEntry;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\IL10N;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\IUser;
|
||||
use OCP\Search\ISearchQuery;
|
||||
use OCP\Search\SearchResult;
|
||||
use Sabre\VObject\Reader;
|
||||
use Test\TestCase;
|
||||
|
||||
class TasksSearchProviderTest extends TestCase {
|
||||
|
||||
/** @var IAppManager|\PHPUnit\Framework\MockObject\MockObject */
|
||||
private $appManager;
|
||||
|
||||
/** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */
|
||||
private $l10n;
|
||||
|
||||
/** @var IURLGenerator|\PHPUnit\Framework\MockObject\MockObject */
|
||||
private $urlGenerator;
|
||||
|
||||
/** @var CalDavBackend|\PHPUnit\Framework\MockObject\MockObject */
|
||||
private $backend;
|
||||
|
||||
/** @var TasksSearchProvider */
|
||||
private $provider;
|
||||
|
||||
// NO DUE NOR COMPLETED NOR SUMMARY
|
||||
private $vTodo0 = 'BEGIN:VCALENDAR'.PHP_EOL.
|
||||
'PRODID:TEST'.PHP_EOL.
|
||||
'VERSION:2.0'.PHP_EOL.
|
||||
'BEGIN:VTODO'.PHP_EOL.
|
||||
'UID:20070313T123432Z-456553@example.com'.PHP_EOL.
|
||||
'DTSTAMP:20070313T123432Z'.PHP_EOL.
|
||||
'STATUS:NEEDS-ACTION'.PHP_EOL.
|
||||
'END:VTODO'.PHP_EOL.
|
||||
'END:VCALENDAR';
|
||||
|
||||
// DUE AND COMPLETED
|
||||
private $vTodo1 = 'BEGIN:VCALENDAR'.PHP_EOL.
|
||||
'PRODID:TEST'.PHP_EOL.
|
||||
'VERSION:2.0'.PHP_EOL.
|
||||
'BEGIN:VTODO'.PHP_EOL.
|
||||
'UID:20070313T123432Z-456553@example.com'.PHP_EOL.
|
||||
'DTSTAMP:20070313T123432Z'.PHP_EOL.
|
||||
'COMPLETED:20070707T100000Z'.PHP_EOL.
|
||||
'DUE;VALUE=DATE:20070501'.PHP_EOL.
|
||||
'SUMMARY:Task title'.PHP_EOL.
|
||||
'STATUS:NEEDS-ACTION'.PHP_EOL.
|
||||
'END:VTODO'.PHP_EOL.
|
||||
'END:VCALENDAR';
|
||||
|
||||
// COMPLETED ONLY
|
||||
private $vTodo2 = 'BEGIN:VCALENDAR'.PHP_EOL.
|
||||
'PRODID:TEST'.PHP_EOL.
|
||||
'VERSION:2.0'.PHP_EOL.
|
||||
'BEGIN:VTODO'.PHP_EOL.
|
||||
'UID:20070313T123432Z-456553@example.com'.PHP_EOL.
|
||||
'DTSTAMP:20070313T123432Z'.PHP_EOL.
|
||||
'COMPLETED:20070707T100000Z'.PHP_EOL.
|
||||
'SUMMARY:Task title'.PHP_EOL.
|
||||
'STATUS:NEEDS-ACTION'.PHP_EOL.
|
||||
'END:VTODO'.PHP_EOL.
|
||||
'END:VCALENDAR';
|
||||
|
||||
// DUE DATE
|
||||
private $vTodo3 = 'BEGIN:VCALENDAR'.PHP_EOL.
|
||||
'PRODID:TEST'.PHP_EOL.
|
||||
'VERSION:2.0'.PHP_EOL.
|
||||
'BEGIN:VTODO'.PHP_EOL.
|
||||
'UID:20070313T123432Z-456553@example.com'.PHP_EOL.
|
||||
'DTSTAMP:20070313T123432Z'.PHP_EOL.
|
||||
'DUE;VALUE=DATE:20070501'.PHP_EOL.
|
||||
'SUMMARY:Task title'.PHP_EOL.
|
||||
'STATUS:NEEDS-ACTION'.PHP_EOL.
|
||||
'END:VTODO'.PHP_EOL.
|
||||
'END:VCALENDAR';
|
||||
|
||||
// DUE DATETIME
|
||||
private $vTodo4 = 'BEGIN:VCALENDAR'.PHP_EOL.
|
||||
'PRODID:TEST'.PHP_EOL.
|
||||
'VERSION:2.0'.PHP_EOL.
|
||||
'BEGIN:VTODO'.PHP_EOL.
|
||||
'UID:20070313T123432Z-456553@example.com'.PHP_EOL.
|
||||
'DTSTAMP:20070313T123432Z'.PHP_EOL.
|
||||
'DUE:20070709T130000Z'.PHP_EOL.
|
||||
'SUMMARY:Task title'.PHP_EOL.
|
||||
'STATUS:NEEDS-ACTION'.PHP_EOL.
|
||||
'END:VTODO'.PHP_EOL.
|
||||
'END:VCALENDAR';
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->appManager = $this->createMock(IAppManager::class);
|
||||
$this->l10n = $this->createMock(IL10N::class);
|
||||
$this->urlGenerator = $this->createMock(IURLGenerator::class);
|
||||
$this->backend = $this->createMock(CalDavBackend::class);
|
||||
|
||||
$this->provider = new TasksSearchProvider(
|
||||
$this->appManager,
|
||||
$this->l10n,
|
||||
$this->urlGenerator,
|
||||
$this->backend
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetId(): void {
|
||||
$this->assertEquals('tasks-dav', $this->provider->getId());
|
||||
}
|
||||
|
||||
public function testGetName(): void {
|
||||
$this->l10n->expects($this->exactly(1))
|
||||
->method('t')
|
||||
->with('Tasks')
|
||||
->willReturnArgument(0);
|
||||
|
||||
$this->assertEquals('Tasks', $this->provider->getName());
|
||||
}
|
||||
|
||||
public function testSearchAppDisabled(): void {
|
||||
$user = $this->createMock(IUser::class);
|
||||
$query = $this->createMock(ISearchQuery::class);
|
||||
$this->appManager->expects($this->once())
|
||||
->method('isEnabledForUser')
|
||||
->with('tasks', $user)
|
||||
->willReturn(false);
|
||||
$this->l10n->expects($this->exactly(1))
|
||||
->method('t')
|
||||
->willReturnArgument(0);
|
||||
$this->backend->expects($this->never())
|
||||
->method('getCalendarsForUser');
|
||||
$this->backend->expects($this->never())
|
||||
->method('getSubscriptionsForUser');
|
||||
$this->backend->expects($this->never())
|
||||
->method('searchPrincipalUri');
|
||||
|
||||
$actual = $this->provider->search($user, $query);
|
||||
$data = $actual->jsonSerialize();
|
||||
$this->assertInstanceOf(SearchResult::class, $actual);
|
||||
$this->assertEquals('Tasks', $data['name']);
|
||||
$this->assertEmpty($data['entries']);
|
||||
$this->assertFalse($data['isPaginated']);
|
||||
$this->assertNull($data['cursor']);
|
||||
}
|
||||
|
||||
public function testSearch(): void {
|
||||
$user = $this->createMock(IUser::class);
|
||||
$user->method('getUID')->willReturn('john.doe');
|
||||
$query = $this->createMock(ISearchQuery::class);
|
||||
$query->method('getTerm')->willReturn('search term');
|
||||
$query->method('getLimit')->willReturn(5);
|
||||
$query->method('getCursor')->willReturn(20);
|
||||
$this->appManager->expects($this->once())
|
||||
->method('isEnabledForUser')
|
||||
->with('tasks', $user)
|
||||
->willReturn(true);
|
||||
$this->l10n->method('t')->willReturnArgument(0);
|
||||
|
||||
$this->backend->expects($this->once())
|
||||
->method('getCalendarsForUser')
|
||||
->with('principals/users/john.doe')
|
||||
->willReturn([
|
||||
[
|
||||
'id' => 99,
|
||||
'principaluri' => 'principals/users/john.doe',
|
||||
'uri' => 'calendar-uri-99',
|
||||
], [
|
||||
'id' => 123,
|
||||
'principaluri' => 'principals/users/john.doe',
|
||||
'uri' => 'calendar-uri-123',
|
||||
]
|
||||
]);
|
||||
$this->backend->expects($this->once())
|
||||
->method('getSubscriptionsForUser')
|
||||
->with('principals/users/john.doe')
|
||||
->willReturn([
|
||||
[
|
||||
'id' => 1337,
|
||||
'principaluri' => 'principals/users/john.doe',
|
||||
'uri' => 'subscription-uri-1337',
|
||||
]
|
||||
]);
|
||||
$this->backend->expects($this->once())
|
||||
->method('searchPrincipalUri')
|
||||
->with('principals/users/john.doe', 'search term', ['VTODO'],
|
||||
['SUMMARY', 'DESCRIPTION', 'CATEGORIES'],
|
||||
[],
|
||||
['limit' => 5, 'offset' => 20])
|
||||
->willReturn([
|
||||
[
|
||||
'calendarid' => 99,
|
||||
'calendartype' => CalDavBackend::CALENDAR_TYPE_CALENDAR,
|
||||
'uri' => 'todo0.ics',
|
||||
'calendardata' => $this->vTodo0,
|
||||
],
|
||||
[
|
||||
'calendarid' => 123,
|
||||
'calendartype' => CalDavBackend::CALENDAR_TYPE_CALENDAR,
|
||||
'uri' => 'todo1.ics',
|
||||
'calendardata' => $this->vTodo1,
|
||||
],
|
||||
[
|
||||
'calendarid' => 1337,
|
||||
'calendartype' => CalDavBackend::CALENDAR_TYPE_SUBSCRIPTION,
|
||||
'uri' => 'todo2.ics',
|
||||
'calendardata' => $this->vTodo2,
|
||||
]
|
||||
]);
|
||||
|
||||
$provider = $this->getMockBuilder(TasksSearchProvider::class)
|
||||
->setConstructorArgs([
|
||||
$this->appManager,
|
||||
$this->l10n,
|
||||
$this->urlGenerator,
|
||||
$this->backend,
|
||||
])
|
||||
->setMethods([
|
||||
'getDeepLinkToTasksApp',
|
||||
'generateSubline',
|
||||
])
|
||||
->getMock();
|
||||
|
||||
$provider->expects($this->exactly(3))
|
||||
->method('generateSubline')
|
||||
->willReturn('subline');
|
||||
$provider->expects($this->exactly(3))
|
||||
->method('getDeepLinkToTasksApp')
|
||||
->withConsecutive(
|
||||
['calendar-uri-99', 'todo0.ics'],
|
||||
['calendar-uri-123', 'todo1.ics'],
|
||||
['subscription-uri-1337', 'todo2.ics']
|
||||
)
|
||||
->willReturn('deep-link-to-tasks');
|
||||
|
||||
$actual = $provider->search($user, $query);
|
||||
$data = $actual->jsonSerialize();
|
||||
$this->assertInstanceOf(SearchResult::class, $actual);
|
||||
$this->assertEquals('Tasks', $data['name']);
|
||||
$this->assertCount(3, $data['entries']);
|
||||
$this->assertTrue($data['isPaginated']);
|
||||
$this->assertEquals(23, $data['cursor']);
|
||||
|
||||
$result0 = $data['entries'][0];
|
||||
$result0Data = $result0->jsonSerialize();
|
||||
$result1 = $data['entries'][1];
|
||||
$result1Data = $result1->jsonSerialize();
|
||||
$result2 = $data['entries'][2];
|
||||
$result2Data = $result2->jsonSerialize();
|
||||
|
||||
$this->assertInstanceOf(TasksSearchResultEntry::class, $result0);
|
||||
$this->assertEmpty($result0Data['thumbnailUrl']);
|
||||
$this->assertEquals('Untitled task', $result0Data['title']);
|
||||
$this->assertEquals('subline', $result0Data['subline']);
|
||||
$this->assertEquals('deep-link-to-tasks', $result0Data['resourceUrl']);
|
||||
$this->assertEquals('icon-checkmark', $result0Data['iconClass']);
|
||||
$this->assertFalse($result0Data['rounded']);
|
||||
|
||||
$this->assertInstanceOf(TasksSearchResultEntry::class, $result1);
|
||||
$this->assertEmpty($result1Data['thumbnailUrl']);
|
||||
$this->assertEquals('Task title', $result1Data['title']);
|
||||
$this->assertEquals('subline', $result1Data['subline']);
|
||||
$this->assertEquals('deep-link-to-tasks', $result1Data['resourceUrl']);
|
||||
$this->assertEquals('icon-checkmark', $result1Data['iconClass']);
|
||||
$this->assertFalse($result1Data['rounded']);
|
||||
|
||||
$this->assertInstanceOf(TasksSearchResultEntry::class, $result2);
|
||||
$this->assertEmpty($result2Data['thumbnailUrl']);
|
||||
$this->assertEquals('Task title', $result2Data['title']);
|
||||
$this->assertEquals('subline', $result2Data['subline']);
|
||||
$this->assertEquals('deep-link-to-tasks', $result2Data['resourceUrl']);
|
||||
$this->assertEquals('icon-checkmark', $result2Data['iconClass']);
|
||||
$this->assertFalse($result2Data['rounded']);
|
||||
}
|
||||
|
||||
public function testGetDeepLinkToTasksApp(): void {
|
||||
$this->urlGenerator->expects($this->once())
|
||||
->method('linkToRoute')
|
||||
->with('tasks.page.index')
|
||||
->willReturn('link-to-route-tasks.index');
|
||||
$this->urlGenerator->expects($this->once())
|
||||
->method('getAbsoluteURL')
|
||||
->with('link-to-route-tasks.index#/calendars/uri-john.doe/tasks/task-uri.ics')
|
||||
->willReturn('absolute-url-link-to-route-tasks.index#/calendars/uri-john.doe/tasks/task-uri.ics');
|
||||
|
||||
$actual = self::invokePrivate($this->provider, 'getDeepLinkToTasksApp', ['uri-john.doe', 'task-uri.ics']);
|
||||
$this->assertEquals('absolute-url-link-to-route-tasks.index#/calendars/uri-john.doe/tasks/task-uri.ics', $actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $ics
|
||||
* @param string $expectedSubline
|
||||
*
|
||||
* @dataProvider generateSublineDataProvider
|
||||
*/
|
||||
public function testGenerateSubline(string $ics, string $expectedSubline): void {
|
||||
$vCalendar = Reader::read($ics, Reader::OPTION_FORGIVING);
|
||||
$taskComponent = $vCalendar->VTODO;
|
||||
|
||||
$this->l10n->method('t')->willReturnArgument(0);
|
||||
$this->l10n->method('l')->willReturnArgument('');
|
||||
|
||||
$actual = self::invokePrivate($this->provider, 'generateSubline', [$taskComponent]);
|
||||
$this->assertEquals($expectedSubline, $actual);
|
||||
}
|
||||
|
||||
public function generateSublineDataProvider(): array {
|
||||
return [
|
||||
[$this->vTodo0, ''],
|
||||
[$this->vTodo1, 'Completed on %s'],
|
||||
[$this->vTodo2, 'Completed on %s'],
|
||||
[$this->vTodo3, 'Due on %s'],
|
||||
[$this->vTodo4, 'Due on %s by %s'],
|
||||
];
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue