diff --git a/apps/dav/composer/composer/autoload_classmap.php b/apps/dav/composer/composer/autoload_classmap.php index ce98cece3a192..9caec5722198c 100644 --- a/apps/dav/composer/composer/autoload_classmap.php +++ b/apps/dav/composer/composer/autoload_classmap.php @@ -47,9 +47,11 @@ 'OCA\\DAV\\CalDAV\\Calendar' => $baseDir . '/../lib/CalDAV/Calendar.php', 'OCA\\DAV\\CalDAV\\CalendarHome' => $baseDir . '/../lib/CalDAV/CalendarHome.php', 'OCA\\DAV\\CalDAV\\CalendarImpl' => $baseDir . '/../lib/CalDAV/CalendarImpl.php', + 'OCA\\DAV\\CalDAV\\CalendarImplV2' => $baseDir . '/../lib/CalDAV/CalendarImplV2.php', 'OCA\\DAV\\CalDAV\\CalendarManager' => $baseDir . '/../lib/CalDAV/CalendarManager.php', 'OCA\\DAV\\CalDAV\\CalendarObject' => $baseDir . '/../lib/CalDAV/CalendarObject.php', 'OCA\\DAV\\CalDAV\\CalendarProvider' => $baseDir . '/../lib/CalDAV/CalendarProvider.php', + 'OCA\\DAV\\CalDAV\\CalendarObjectImplV2' => $baseDir . '/../lib/CalDAV/CalendarObjectImplV2.php', 'OCA\\DAV\\CalDAV\\CalendarRoot' => $baseDir . '/../lib/CalDAV/CalendarRoot.php', 'OCA\\DAV\\CalDAV\\ICSExportPlugin\\ICSExportPlugin' => $baseDir . '/../lib/CalDAV/ICSExportPlugin/ICSExportPlugin.php', 'OCA\\DAV\\CalDAV\\IRestorable' => $baseDir . '/../lib/CalDAV/IRestorable.php', diff --git a/apps/dav/composer/composer/autoload_static.php b/apps/dav/composer/composer/autoload_static.php index a5a7d34d128d4..c07b28f4cbcfa 100644 --- a/apps/dav/composer/composer/autoload_static.php +++ b/apps/dav/composer/composer/autoload_static.php @@ -7,14 +7,14 @@ class ComposerStaticInitDAV { public static $prefixLengthsPsr4 = array ( - 'O' => + 'O' => array ( 'OCA\\DAV\\' => 8, ), ); public static $prefixDirsPsr4 = array ( - 'OCA\\DAV\\' => + 'OCA\\DAV\\' => array ( 0 => __DIR__ . '/..' . '/../lib', ), @@ -62,9 +62,11 @@ class ComposerStaticInitDAV 'OCA\\DAV\\CalDAV\\Calendar' => __DIR__ . '/..' . '/../lib/CalDAV/Calendar.php', 'OCA\\DAV\\CalDAV\\CalendarHome' => __DIR__ . '/..' . '/../lib/CalDAV/CalendarHome.php', 'OCA\\DAV\\CalDAV\\CalendarImpl' => __DIR__ . '/..' . '/../lib/CalDAV/CalendarImpl.php', + 'OCA\\DAV\\CalDAV\\CalendarImplV2' => __DIR__ . '/..' . '/../lib/CalDAV/CalendarImplV2.php', 'OCA\\DAV\\CalDAV\\CalendarManager' => __DIR__ . '/..' . '/../lib/CalDAV/CalendarManager.php', 'OCA\\DAV\\CalDAV\\CalendarObject' => __DIR__ . '/..' . '/../lib/CalDAV/CalendarObject.php', 'OCA\\DAV\\CalDAV\\CalendarProvider' => __DIR__ . '/..' . '/../lib/CalDAV/CalendarProvider.php', + 'OCA\\DAV\\CalDAV\\CalendarObjectImplV2' => __DIR__ . '/..' . '/../lib/CalDAV/CalendarObjectImplV2.php', 'OCA\\DAV\\CalDAV\\CalendarRoot' => __DIR__ . '/..' . '/../lib/CalDAV/CalendarRoot.php', 'OCA\\DAV\\CalDAV\\ICSExportPlugin\\ICSExportPlugin' => __DIR__ . '/..' . '/../lib/CalDAV/ICSExportPlugin/ICSExportPlugin.php', 'OCA\\DAV\\CalDAV\\IRestorable' => __DIR__ . '/..' . '/../lib/CalDAV/IRestorable.php', diff --git a/apps/dav/lib/AppInfo/Application.php b/apps/dav/lib/AppInfo/Application.php index 8674986262623..3b9e5d96de992 100644 --- a/apps/dav/lib/AppInfo/Application.php +++ b/apps/dav/lib/AppInfo/Application.php @@ -42,7 +42,6 @@ use OCA\DAV\CalDAV\Reminder\NotificationProvider\PushProvider; use OCA\DAV\CalDAV\Reminder\NotificationProviderManager; use OCA\DAV\CalDAV\Reminder\Notifier; - use OCA\DAV\Capabilities; use OCA\DAV\CardDAV\CardDavBackend; use OCA\DAV\CardDAV\ContactsManager; @@ -99,6 +98,7 @@ use OCP\Calendar\IManager as ICalendarManager; use OCP\Config\BeforePreferenceDeletedEvent; use OCP\Config\BeforePreferenceSetEvent; +use OCP\Calendar\IManagerV2 as ICalendarManagerV2; use OCP\Contacts\IManager as IContactsManager; use OCP\IServerContainer; use OCP\IUser; @@ -207,6 +207,8 @@ public function boot(IBootContext $context): void { $context->injectFn([$this, 'registerHooks']); $context->injectFn([$this, 'registerContactsManager']); $context->injectFn([$this, 'registerCalendarManager']); + $context->injectFn([$this, 'registerCalendarManagerV2']); + $context->injectFn([$this, 'registerNotifier']); $context->injectFn([$this, 'registerCalendarReminders']); } @@ -214,8 +216,6 @@ public function registerHooks(HookManager $hm, EventDispatcherInterface $dispatcher, IAppContainer $container, IServerContainer $serverContainer) { - $hm->setup(); - // first time login event setup $dispatcher->addListener(IUser::class . '::firstLogin', function ($event) use ($hm) { if ($event instanceof GenericEvent) { @@ -304,6 +304,32 @@ private function setupCalendarProvider(ICalendarManager $calendarManager, $cm->setupCalendarProvider($calendarManager, $userId); } + public function registerCalendarManagerV2(ICalendarManagerV2 $calendarManager, + IAppContainer $container): void { + $calendarManager->register(function () use ($container, $calendarManager) { + $user = \OC::$server->getUserSession()->getUser(); + if ($user !== null) { + $this->setupCalendarProviderV2($calendarManager, $container, $user->getUID()); + } + }); + } + + /** + * @param ICalendarManagerV2 $calendarManager + * @param string $userId + */ + public function setupCalendarProviderV2(ICalendarManagerV2 $calendarManager, + IAppContainer $container, + $userId) { + /** @var CalendarManager $cm */ + $cm = $container->query(CalendarManager::class); + $cm->setupCalendarProviderV2($calendarManager, $userId); + } + + public function registerNotifier(INotificationManager $manager): void { + $manager->registerNotifierService(Notifier::class); + } + public function registerCalendarReminders(NotificationProviderManager $manager, LoggerInterface $logger): void { try { diff --git a/apps/dav/lib/CalDAV/CalendarImplV2.php b/apps/dav/lib/CalDAV/CalendarImplV2.php new file mode 100644 index 0000000000000..afa72db88e33e --- /dev/null +++ b/apps/dav/lib/CalDAV/CalendarImplV2.php @@ -0,0 +1,154 @@ + + * + * @author Thomas Citharel + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + +namespace OCA\DAV\CalDAV; + +use OCP\Calendar\ICalendarObjectV2; +use OCP\Calendar\ICalendarV2; +use OCP\Constants; +use Sabre\DAV\Exception\BadRequest; +use Sabre\VObject\Component\VCalendar; +use Sabre\VObject\Reader; +use Sabre\VObject\UUIDUtil; + +class CalendarImplV2 implements ICalendarV2 { + + /** @var CalDavBackend */ + private $backend; + + /** @var Calendar */ + private $calendar; + + /** @var array */ + private $calendarInfo; + + /** + * CalendarImpl constructor. + * + * @param Calendar $calendar + * @param array $calendarInfo + * @param CalDavBackend $backend + */ + public function __construct(Calendar $calendar, array $calendarInfo, + CalDavBackend $backend) { + $this->calendar = $calendar; + $this->calendarInfo = $calendarInfo; + $this->backend = $backend; + } + + /** + * @return string defining the technical unique key + * @since 21.0.0 + */ + public function getKey(): string { + return $this->calendarInfo['id']; + } + + /** + * In comparison to getKey() this function returns a human readable (maybe translated) name + * @return null|string + * @since 21.0.0 + */ + public function getDisplayName(): ?string { + return $this->calendarInfo['{DAV:}displayname']; + } + + /** + * Calendar color + * @return null|string + * @since 21.0.0 + */ + public function getDisplayColor(): ?string { + return $this->calendarInfo['{http://apple.com/ns/ical/}calendar-color']; + } + + /** + * @param string $pattern which should match within the $searchProperties + * @param array $searchProperties defines the properties within the query pattern should match + * @param array $options - optional parameters: + * ['timerange' => ['start' => new DateTime(...), 'end' => new DateTime(...)]] + * @param integer|null $limit - limit number of search results + * @param integer|null $offset - offset for paging of search results + * @return array an array of events/journals/todos which are arrays of key-value-pairs + * @since 21.0.0 + */ + public function search($pattern, array $searchProperties=[], array $options=[], int $limit = null, int $offset = null): array { + return $this->backend->search($this->calendarInfo, $pattern, + $searchProperties, $options, $limit, $offset); + } + + /** + * @return bool + * @since 21.0.0 + */ + public function isWriteable(): bool { + $permissions = $this->calendar->getACL(); + $result = 0; + foreach ($permissions as $permission) { + switch ($permission['privilege']) { + case '{DAV:}read': + $result |= Constants::PERMISSION_READ; + break; + case '{DAV:}write': + $result |= Constants::PERMISSION_CREATE; + $result |= Constants::PERMISSION_UPDATE; + break; + case '{DAV:}all': + $result |= Constants::PERMISSION_ALL; + break; + } + } + + return $result > Constants::PERMISSION_READ; + } + + /** + * @param string $uri + * @return ICalendarObjectV2|null + * @since 21.0.0 + */ + public function getByUri(string $uri): ?ICalendarObjectV2 { + if ($calendarObjectData = $this->backend->getCalendarObject($this->getKey(), $uri)) { + if (isset($calendarObjectData['calendardata'])) { + $calendarData = Reader::read($calendarObjectData['calendardata']); + if ($calendarData instanceof VCalendar) { + return new CalendarObjectImplV2($calendarObjectData['calendarid'], $calendarObjectData['uri'], $calendarData, $this->backend); + } + } + } + return null; + } + + /** + * @param VCalendar $vObject + * @return ICalendarObjectV2|null + * @throws BadRequest + * @since 21.0.0 + */ + public function create(VCalendar $vObject): ?ICalendarObjectV2 { + CalendarObjectImplV2::validateCalendarData($vObject); + $uuid = UUIDUtil::getUUID() . '.ics'; + $this->backend->createCalendarObject($this->getKey(), $uuid, $vObject->serialize()); + return $this->getByUri($uuid); + } +} diff --git a/apps/dav/lib/CalDAV/CalendarManager.php b/apps/dav/lib/CalDAV/CalendarManager.php index daa96a5139202..62e426058d65c 100644 --- a/apps/dav/lib/CalDAV/CalendarManager.php +++ b/apps/dav/lib/CalDAV/CalendarManager.php @@ -25,6 +25,7 @@ namespace OCA\DAV\CalDAV; use OCP\Calendar\IManager; +use OCP\Calendar\IManagerV2; use OCP\IConfig; use OCP\IL10N; use Psr\Log\LoggerInterface; @@ -66,6 +67,15 @@ public function setupCalendarProvider(IManager $cm, $userId) { $this->register($cm, $calendars); } + /** + * @param IManagerV2 $cm + * @param string $userId + */ + public function setupCalendarProviderV2(IManagerV2 $cm, $userId) { + $calendars = $this->backend->getCalendarsForUser("principals/users/$userId"); + $this->registerV2($cm, $calendars); + } + /** * @param IManager $cm * @param array $calendars @@ -80,4 +90,19 @@ private function register(IManager $cm, array $calendars) { )); } } + + /** + * @param IManagerV2 $cm + * @param array $calendars + */ + private function registerV2(IManagerV2 $cm, array $calendars) { + foreach ($calendars as $calendarInfo) { + $calendar = new Calendar($this->backend, $calendarInfo, $this->l10n, $this->config); + $cm->registerCalendar(new CalendarImplV2( + $calendar, + $calendarInfo, + $this->backend + )); + } + } } diff --git a/apps/dav/lib/CalDAV/CalendarObjectImplV2.php b/apps/dav/lib/CalDAV/CalendarObjectImplV2.php new file mode 100644 index 0000000000000..6a39a0b8e4c80 --- /dev/null +++ b/apps/dav/lib/CalDAV/CalendarObjectImplV2.php @@ -0,0 +1,94 @@ + + * + * @author Thomas Citharel + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + +namespace OCA\DAV\CalDAV; + +use InvalidArgumentException; +use OCP\Calendar\ICalendarObjectV2; +use Sabre\VObject\Component\VCalendar; + +class CalendarObjectImplV2 implements ICalendarObjectV2 { + + /** @var int */ + private $calendarId; + + /** @var string */ + private $uri; + + /** @var VCalendar */ + private $data; + + /** @var CalDavBackend */ + private $backend; + + /** + * CalendarImpl constructor. + * + * @param int $calendarId + * @param string $uri + * @param VCalendar $data + * @param CalDavBackend $backend + */ + public function __construct(int $calendarId, string $uri, VCalendar $data, CalDavBackend $backend) { + $this->calendarId = $calendarId; + $this->uri = $uri; + $this->data = $data; + $this->backend = $backend; + } + + public function getCalendarKey(): string { + return (string) $this->calendarId; + } + + public function getUri(): string { + return $this->uri; + } + + public function getVObject(): VCalendar { + return $this->data; + } + + public function update(VCalendar $data): void { + self::validateCalendarData($data); + $serializedData = $data->serialize(); + $this->backend->updateCalendarObject($this->getCalendarKey(), $this->getUri(), $serializedData); + $this->data = $data; + } + + public function delete(): void { + $this->backend->deleteCalendarObject($this->getCalendarKey(), $this->getUri()); + } + + /** + * @param VCalendar $data + * @throws InvalidArgumentException + */ + public static function validateCalendarData(VCalendar $data): void { + $result = $data->validate(VCalendar::PROFILE_CALDAV); + foreach ($result as $warning) { + if ($warning['level'] === 3) { + throw new InvalidArgumentException($warning['message']); + } + } + } +} diff --git a/apps/dav/tests/unit/CalDAV/CalendarImplTest.php b/apps/dav/tests/unit/CalDAV/CalendarImplTest.php index 5adb7041e8b5f..a423673a0504a 100644 --- a/apps/dav/tests/unit/CalDAV/CalendarImplTest.php +++ b/apps/dav/tests/unit/CalDAV/CalendarImplTest.php @@ -38,11 +38,12 @@ use Sabre\VObject\Component\VEvent; use Sabre\VObject\ITip\Message; use Sabre\VObject\Reader; +use Test\TestCase; /** * @group DB */ -class CalendarImplTest extends \Test\TestCase { +class CalendarImplTest extends TestCase { /** @var CalendarImpl */ private $calendarImpl; diff --git a/apps/dav/tests/unit/CalDAV/CalendarImplV2Test.php b/apps/dav/tests/unit/CalDAV/CalendarImplV2Test.php new file mode 100644 index 0000000000000..d6bf181a7f9d8 --- /dev/null +++ b/apps/dav/tests/unit/CalDAV/CalendarImplV2Test.php @@ -0,0 +1,189 @@ + + * + * @author Georg Ehrke + * @author Roeland Jago Douma + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + +namespace OCA\DAV\Tests\unit\CalDAV; + +use InvalidArgumentException; +use OCA\DAV\CalDAV\CalDavBackend; +use OCA\DAV\CalDAV\Calendar; +use OCA\DAV\CalDAV\CalendarImplV2; +use OCA\DAV\CalDAV\CalendarObjectImplV2; +use PHPUnit\Framework\MockObject\MockObject; +use Sabre\VObject\Component\VCalendar; +use Test\TestCase; + +class CalendarImplV2Test extends TestCase { + + /** @var CalendarImplV2 */ + private $calendarImpl; + + /** @var Calendar | MockObject */ + private $calendar; + + /** @var array */ + private $calendarInfo; + + /** @var CalDavBackend | MockObject */ + private $backend; + + protected function setUp(): void { + parent::setUp(); + + $this->calendar = $this->createMock(Calendar::class); + $this->calendarInfo = [ + 'id' => '4', + '{DAV:}displayname' => 'user readable name 123', + '{http://apple.com/ns/ical/}calendar-color' => '#AABBCC', + ]; + $this->backend = $this->createMock(CalDavBackend::class); + + $this->calendarImpl = new CalendarImplV2($this->calendar, + $this->calendarInfo, $this->backend); + } + + + public function testGetKey() { + $this->assertEquals($this->calendarImpl->getKey(), '4'); + } + + public function testGetDisplayname() { + $this->assertEquals($this->calendarImpl->getDisplayName(),'user readable name 123'); + } + + public function testGetDisplayColor() { + $this->assertEquals($this->calendarImpl->getDisplayColor(), '#AABBCC'); + } + + public function testSearch() { + $this->backend->expects($this->once()) + ->method('search') + ->with($this->calendarInfo, 'abc', ['def'], ['ghi'], 42, 1337) + ->willReturn(['SEARCHRESULTS']); + + $result = $this->calendarImpl->search('abc', ['def'], ['ghi'], 42, 1337); + $this->assertEquals($result, ['SEARCHRESULTS']); + } + + /** + * @dataProvider dataTestIsWritable + */ + public function testGetIsWritable(array $privileges, bool $result) { + $this->calendar->expects($this->once()) + ->method('getACL') + ->with() + ->willReturn($privileges); + + $this->assertEquals($result, $this->calendarImpl->isWriteable()); + } + + public function dataTestIsWritable(): array { + return [ + [ + [ + ['privilege' => '{DAV:}read'] + ], + false + ], + [ + [ + ['privilege' => '{DAV:}write'] + ], + true + ], + [ + [ + ['privilege' => '{DAV:}read'], + ['privilege' => '{DAV:}write'] + ], + true + ], + [ + [ + ['privilege' => '{DAV:}all'] + ], + true + ] + ]; + } + + public function testGetByUri() { + $vcalendar = new VCalendar(); + $calendarObjectData = ['id' => 'someid', 'calendarid' => '4', 'uri' => 'someuri.ics', 'calendardata' => $vcalendar->serialize()]; + $calendarObject = new CalendarObjectImplV2( + $calendarObjectData['calendarid'], + $calendarObjectData['uri'], + $vcalendar, + $this->backend); + + $this->backend->expects($this->once()) + ->method('getCalendarObject') + ->with($calendarObjectData['calendarid'], $calendarObjectData['uri']) + ->willReturn($calendarObjectData); + $foundObject = $this->calendarImpl->getByUri($calendarObjectData['uri']); + $this->assertEquals($calendarObject->getCalendarKey(), $foundObject->getCalendarKey()); + $this->assertEquals($calendarObject->getUri(), $foundObject->getUri()); + // Asserting equality for VObject PHP objects fails on parent references, so we serialize them + $this->assertEquals($calendarObject->getVObject()->serialize(), $foundObject->getVObject()->serialize()); + } + + public function testGetByUriNotFound() { + $this->backend->expects($this->once()) + ->method('getCalendarObject') + ->willReturn(null); + $this->assertEquals(null, $this->calendarImpl->getByUri('uri')); + } + + public function testCreateCalendarObject() { + $calendarObjectDataObject = new VCalendar([ + 'VEVENT' => [ + 'SUMMARY' => 'Birthday party!', + 'DTSTART' => new \DateTime('2016-07-04 21:00:00'), + 'DTEND' => new \DateTime('2016-07-05 03:00:00') + ] + ]); + $calendarObjectData = ['id' => 'someid', 'calendarid' => '4', 'uri' => 'someuri.ics', 'calendardata' => $calendarObjectDataObject->serialize()]; + $calendarObject = new CalendarObjectImplV2($calendarObjectData['calendarid'], $calendarObjectData['uri'], $calendarObjectDataObject, $this->backend); + $this->backend->expects($this->once()) + ->method('createCalendarObject') + ->with( + $calendarObjectData['calendarid'], + $this->anything(), + $calendarObjectDataObject->serialize() + ); + $this->backend->expects($this->once()) + ->method('getCalendarObject') + ->willReturn($calendarObjectData); + $createdCalendarObject = $this->calendarImpl->create($calendarObjectDataObject); + $this->assertEquals($calendarObject->getCalendarKey(), $createdCalendarObject->getCalendarKey()); + $this->assertEquals($calendarObject->getUri(), $createdCalendarObject->getUri()); + // Asserting equality for VObject PHP objects fails on parent references (and therefore on the whole CalendarImplV2 object), so we serialize them + $this->assertEquals($calendarObject->getVObject()->serialize(), $createdCalendarObject->getVObject()->serialize()); + } + + public function testCreateCalendarObjectWithBadCalendarData() { + $vobject = new VCalendar([]); + $this->expectException(InvalidArgumentException::class); + $this->calendarImpl->create($vobject); + } +} diff --git a/apps/dav/tests/unit/CalDAV/CalendarManagerTest.php b/apps/dav/tests/unit/CalDAV/CalendarManagerTest.php index f16a06f953dbb..168708f471028 100644 --- a/apps/dav/tests/unit/CalDAV/CalendarManagerTest.php +++ b/apps/dav/tests/unit/CalDAV/CalendarManagerTest.php @@ -6,6 +6,7 @@ * @author Georg Ehrke * @author Morris Jobke * @author Roeland Jago Douma + * @author Thomas Citharel * * @license GNU AGPL version 3 or any later version * @@ -26,16 +27,20 @@ namespace OCA\DAV\Tests\unit\CalDAV; use OC\Calendar\Manager; +use OC\Calendar\ManagerV2; use OCA\DAV\CalDAV\CalDavBackend; use OCA\DAV\CalDAV\CalendarImpl; +use OCA\DAV\CalDAV\CalendarImplV2; use OCA\DAV\CalDAV\CalendarManager; use OCP\Calendar\IManager; +use OCP\Calendar\IManagerV2; use OCP\IConfig; use OCP\IL10N; use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; +use Test\TestCase; -class CalendarManagerTest extends \Test\TestCase { +class CalendarManagerTest extends TestCase { /** @var CalDavBackend | MockObject */ private $backend; @@ -95,4 +100,34 @@ public function testSetupCalendarProvider() { $this->manager->setupCalendarProvider($calendarManager, 'user123'); } + + public function testSetupCalendarProviderV2() { + $this->backend->expects($this->once()) + ->method('getCalendarsForUser') + ->with('principals/users/user123') + ->willReturn([ + ['id' => 123, 'uri' => 'blablub1'], + ['id' => 456, 'uri' => 'blablub2'], + ]); + + /** @var IManagerV2 | MockObject $calendarManager */ + $calendarManager = $this->createMock(ManagerV2::class); + $calendarManager->expects($this->at(0)) + ->method('registerCalendar') + ->willReturnCallback(function () { + $parameter = func_get_arg(0); + $this->assertInstanceOf(CalendarImplV2::class, $parameter); + $this->assertEquals(123, $parameter->getKey()); + }); + + $calendarManager->expects($this->at(1)) + ->method('registerCalendar') + ->willReturnCallback(function () { + $parameter = func_get_arg(0); + $this->assertInstanceOf(CalendarImplV2::class, $parameter); + $this->assertEquals(456, $parameter->getKey()); + }); + + $this->manager->setupCalendarProviderV2($calendarManager, 'user123'); + } } diff --git a/apps/dav/tests/unit/CalDAV/CalendarObjectImplV2Test.php b/apps/dav/tests/unit/CalDAV/CalendarObjectImplV2Test.php new file mode 100644 index 0000000000000..b39ba37ea0e65 --- /dev/null +++ b/apps/dav/tests/unit/CalDAV/CalendarObjectImplV2Test.php @@ -0,0 +1,110 @@ + + * + * @author Georg Ehrke + * @author Roeland Jago Douma + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + +namespace OCA\DAV\Tests\unit\CalDAV; + +use InvalidArgumentException; +use OCA\DAV\CalDAV\CalDavBackend; +use OCA\DAV\CalDAV\CalendarObjectImplV2; +use PHPUnit\Framework\MockObject\MockObject; +use Sabre\VObject\Component\VCalendar; +use Test\TestCase; + +class CalendarObjectImplV2Test extends TestCase { + + /** @var array */ + private $calendarObject; + + /** @var CalDavBackend | MockObject */ + private $backend; + /** + * @var CalendarObjectImplV2 + */ + private $calendarObjectImpl; + + protected function setUp(): void { + parent::setUp(); + + $calendarObjectData = new VCalendar([ + 'VEVENT' => [ + 'SUMMARY' => 'Birthday party!', + 'DTSTART' => new \DateTime('2016-07-04 21:00:00'), + 'DTEND' => new \DateTime('2016-07-05 03:00:00') + ] + ]); + + $this->calendarObject = [ + 'calendarid' => '4', + 'uri' => 'something.ics', + 'calendardata' => $calendarObjectData->serialize() + ]; + $this->backend = $this->createMock(CalDavBackend::class); + + $this->calendarObjectImpl = new CalendarObjectImplV2($this->calendarObject['calendarid'], $this->calendarObject['uri'], $calendarObjectData, $this->backend); + } + + + public function testGetCalendarKey() { + $this->assertEquals($this->calendarObjectImpl->getCalendarKey(), $this->calendarObject['calendarid']); + } + + public function testGetUri() { + $this->assertEquals($this->calendarObjectImpl->getUri(),$this->calendarObject['uri']); + } + + public function testGetVObject() { + $this->assertEquals($this->calendarObjectImpl->getVObject()->serialize(), $this->calendarObject['calendardata']); + } + + public function testUpdate() { + $vobject = new VCalendar([ + 'VEVENT' => [ + 'SUMMARY' => 'Updated birthday party!', + 'DTSTART' => new \DateTime('2020-07-04 21:00:00'), + 'DTEND' => new \DateTime('2020-07-05 03:00:00') + ] + ]); + + $this->backend->expects($this->once()) + ->method('updateCalendarObject') + ->with($this->calendarObject['calendarid'], $this->calendarObject['uri'], $vobject->serialize()); + + $this->calendarObjectImpl->update($vobject); + $this->assertEquals($this->calendarObjectImpl->getVObject()->serialize(), $vobject->serialize()); + } + + public function testUpdateWithBadCalendarData() { + $vobject = new VCalendar([]); + $this->expectException(InvalidArgumentException::class); + $this->calendarObjectImpl->update($vobject); + } + + public function testDelete() { + $this->backend->expects($this->once()) + ->method('deleteCalendarObject') + ->with($this->calendarObject['calendarid'], $this->calendarObject['uri']); + + $this->calendarObjectImpl->delete(); + } +} diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index a92b208ba7a5f..548860d94add7 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -124,7 +124,10 @@ 'OCP\\Calendar\\ICalendarQuery' => $baseDir . '/lib/public/Calendar/ICalendarQuery.php', 'OCP\\Calendar\\ICreateFromString' => $baseDir . '/lib/public/Calendar/ICreateFromString.php', 'OCP\\Calendar\\IHandleImipMessage' => $baseDir . '/lib/public/Calendar/IHandleImipMessage.php', + 'OCP\\Calendar\\ICalendarObjectV2' => $baseDir . '/lib/public/Calendar/ICalendarObjectV2.php', + 'OCP\\Calendar\\ICalendarV2' => $baseDir . '/lib/public/Calendar/ICalendarV2.php', 'OCP\\Calendar\\IManager' => $baseDir . '/lib/public/Calendar/IManager.php', + 'OCP\\Calendar\\IManagerV2' => $baseDir . '/lib/public/Calendar/IManagerV2.php', 'OCP\\Calendar\\IMetadataProvider' => $baseDir . '/lib/public/Calendar/IMetadataProvider.php', 'OCP\\Calendar\\Resource\\IBackend' => $baseDir . '/lib/public/Calendar/Resource/IBackend.php', 'OCP\\Calendar\\Resource\\IManager' => $baseDir . '/lib/public/Calendar/Resource/IManager.php', @@ -820,6 +823,7 @@ 'OC\\Cache\\File' => $baseDir . '/lib/private/Cache/File.php', 'OC\\Calendar\\CalendarQuery' => $baseDir . '/lib/private/Calendar/CalendarQuery.php', 'OC\\Calendar\\Manager' => $baseDir . '/lib/private/Calendar/Manager.php', + 'OC\\Calendar\\ManagerV2' => $baseDir . '/lib/private/Calendar/ManagerV2.php', 'OC\\Calendar\\Resource\\Manager' => $baseDir . '/lib/private/Calendar/Resource/Manager.php', 'OC\\Calendar\\Room\\Manager' => $baseDir . '/lib/private/Calendar/Room/Manager.php', 'OC\\CapabilitiesManager' => $baseDir . '/lib/private/CapabilitiesManager.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index c0ff8e6d9a4b3..1efe80572c07c 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -11,7 +11,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 ); public static $prefixLengthsPsr4 = array ( - 'O' => + 'O' => array ( 'OC\\Core\\' => 8, 'OC\\' => 3, @@ -20,15 +20,15 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 ); public static $prefixDirsPsr4 = array ( - 'OC\\Core\\' => + 'OC\\Core\\' => array ( 0 => __DIR__ . '/../../..' . '/core', ), - 'OC\\' => + 'OC\\' => array ( 0 => __DIR__ . '/../../..' . '/lib/private', ), - 'OCP\\' => + 'OCP\\' => array ( 0 => __DIR__ . '/../../..' . '/lib/public', ), @@ -157,7 +157,10 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\Calendar\\ICalendarQuery' => __DIR__ . '/../../..' . '/lib/public/Calendar/ICalendarQuery.php', 'OCP\\Calendar\\ICreateFromString' => __DIR__ . '/../../..' . '/lib/public/Calendar/ICreateFromString.php', 'OCP\\Calendar\\IHandleImipMessage' => __DIR__ . '/../../..' . '/lib/public/Calendar/IHandleImipMessage.php', + 'OCP\\Calendar\\ICalendarObjectV2' => __DIR__ . '/../../..' . '/lib/public/Calendar/ICalendarObjectV2.php', + 'OCP\\Calendar\\ICalendarV2' => __DIR__ . '/../../..' . '/lib/public/Calendar/ICalendarV2.php', 'OCP\\Calendar\\IManager' => __DIR__ . '/../../..' . '/lib/public/Calendar/IManager.php', + 'OCP\\Calendar\\IManagerV2' => __DIR__ . '/../../..' . '/lib/public/Calendar/IManagerV2.php', 'OCP\\Calendar\\IMetadataProvider' => __DIR__ . '/../../..' . '/lib/public/Calendar/IMetadataProvider.php', 'OCP\\Calendar\\Resource\\IBackend' => __DIR__ . '/../../..' . '/lib/public/Calendar/Resource/IBackend.php', 'OCP\\Calendar\\Resource\\IManager' => __DIR__ . '/../../..' . '/lib/public/Calendar/Resource/IManager.php', @@ -853,6 +856,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Cache\\File' => __DIR__ . '/../../..' . '/lib/private/Cache/File.php', 'OC\\Calendar\\CalendarQuery' => __DIR__ . '/../../..' . '/lib/private/Calendar/CalendarQuery.php', 'OC\\Calendar\\Manager' => __DIR__ . '/../../..' . '/lib/private/Calendar/Manager.php', + 'OC\\Calendar\\ManagerV2' => __DIR__ . '/../../..' . '/lib/private/Calendar/ManagerV2.php', 'OC\\Calendar\\Resource\\Manager' => __DIR__ . '/../../..' . '/lib/private/Calendar/Resource/Manager.php', 'OC\\Calendar\\Room\\Manager' => __DIR__ . '/../../..' . '/lib/private/Calendar/Room/Manager.php', 'OC\\CapabilitiesManager' => __DIR__ . '/../../..' . '/lib/private/CapabilitiesManager.php', diff --git a/lib/private/Calendar/ManagerV2.php b/lib/private/Calendar/ManagerV2.php new file mode 100644 index 0000000000000..cd6eb2d3b67f6 --- /dev/null +++ b/lib/private/Calendar/ManagerV2.php @@ -0,0 +1,153 @@ + + * + * @author Thomas Citharel + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + +namespace OC\Calendar; + +use OCP\Calendar\ICalendarV2; +use OCP\Calendar\IManagerV2; +use OCP\Calendar\ICalendarObjectV2; + +class ManagerV2 implements IManagerV2 { + + /** + * @var ICalendarV2[] holds all registered calendars + */ + private $calendars=[]; + + /** + * @var \Closure[] to call to load/register calendar providers + */ + private $calendarLoaders=[]; + + /** + * This function is used to search and find objects within the user's calendars. + * In case $pattern is empty all events/journals/todos will be returned. + * + * @param string $pattern which should match within the $searchProperties + * @param array $searchProperties defines the properties within the query pattern should match + * @param array $options - optional parameters: + * ['timerange' => ['start' => new DateTime(...), 'end' => new DateTime(...)]] + * @param integer|null $limit - limit number of search results + * @param integer|null $offset - offset for paging of search results + * @return ICalendarObjectV2[] an array of events/journals/todos which are arrays of arrays of key-value-pairs + * @since 13.0.0 + */ + public function search($pattern, array $searchProperties=[], array $options=[], int $limit = null, int $offset=null): array { + $this->loadCalendars(); + $result = []; + foreach ($this->calendars as $calendar) { + $r = $calendar->search($pattern, $searchProperties, $options, $limit, $offset); + foreach ($r as $o) { + $o['calendar-key'] = $calendar->getKey(); + $result[] = $o; + } + } + + return $result; + } + + /** + * Check if calendars are available + * + * @return bool true if enabled, false if not + * @since 13.0.0 + */ + public function isEnabled(): bool { + return !empty($this->calendars) || !empty($this->calendarLoaders); + } + + /** + * Registers a calendar + * + * @param ICalendarV2 $calendar + * @return void + * @since 13.0.0 + */ + public function registerCalendar(ICalendarV2 $calendar): void { + $this->calendars[$calendar->getKey()] = $calendar; + } + + /** + * Unregisters a calendar + * + * @param ICalendarV2 $calendar + * @return void + * @since 13.0.0 + */ + public function unregisterCalendar(ICalendarV2 $calendar): void { + unset($this->calendars[$calendar->getKey()]); + } + + /** + * In order to improve lazy loading a closure can be registered which will be called in case + * calendars are actually requested + * + * @param \Closure $callable + * @return void + * @since 13.0.0 + */ + public function register(\Closure $callable): void { + $this->calendarLoaders[] = $callable; + } + + /** + * @return ICalendarV2[] + * @since 13.0.0 + */ + public function getCalendars(): array { + $this->loadCalendars(); + + return array_values($this->calendars); + } + + /** + * removes all registered calendar instances + * @return void + * @since 13.0.0 + */ + public function clear(): void { + $this->calendars = []; + $this->calendarLoaders = []; + } + + /** + * loads all calendars + */ + private function loadCalendars() { + foreach ($this->calendarLoaders as $callable) { + $callable($this); + } + $this->calendarLoaders = []; + } + + /** + * Get a calendar by it's key + * + * @param string $key + * @return ICalendarV2|null + * @since 21.0.0 + */ + public function getCalendar(string $key): ?ICalendarV2 { + return isset($this->calendars[$key]) ? $this->calendars[$key] : null; + } +} diff --git a/lib/private/Server.php b/lib/private/Server.php index d8857b4efc6c1..2307c44657927 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -1468,6 +1468,13 @@ public function getCalendarManager() { return $this->get(\OC\Calendar\Manager::class); } + /** + * @return \OCP\Calendar\IManagerV2 + */ + public function getCalendarManagerV2() { + return $this->query(\OC\Calendar\ManagerV2::class); + } + /** * @return \OCP\Calendar\Resource\IManager * @deprecated 20.0.0 diff --git a/lib/public/Calendar/ICalendar.php b/lib/public/Calendar/ICalendar.php index c6037690f6572..1461027b7367b 100644 --- a/lib/public/Calendar/ICalendar.php +++ b/lib/public/Calendar/ICalendar.php @@ -30,6 +30,7 @@ * Interface ICalendar * * @since 13.0.0 + * @deprecated 20.0.0 */ interface ICalendar { /** diff --git a/lib/public/Calendar/ICalendarObjectV2.php b/lib/public/Calendar/ICalendarObjectV2.php new file mode 100644 index 0000000000000..2a3c8b7ab450b --- /dev/null +++ b/lib/public/Calendar/ICalendarObjectV2.php @@ -0,0 +1,67 @@ + + * + * @author Thomas Citharel + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + +namespace OCP\Calendar; + +use Sabre\VObject\Component\VCalendar; + +/** + * Interface ICalendarObjectV2 + * + * @package OCP + * @since 21.0.0 + */ +interface ICalendarObjectV2 { + + /** + * @return string defining the technical unique key + * @since 21.0.0 + */ + public function getCalendarKey(): string; + + /** + * @return string calendar object unique URI + * @since 21.0.0 + */ + public function getUri(): string; + + /** + * @return VCalendar the calendar object data + * @since 21.0.0 + */ + public function getVObject(): VCalendar; + + /** + * Update calendar object data + * + * @param VCalendar $data + * @since 21.0.0 + */ + public function update(VCalendar $data): void; + + /** + * Delete calendar object + * @since 21.0.0 + */ + public function delete(): void; +} diff --git a/lib/public/Calendar/ICalendarV2.php b/lib/public/Calendar/ICalendarV2.php new file mode 100644 index 0000000000000..5f2425c6794ed --- /dev/null +++ b/lib/public/Calendar/ICalendarV2.php @@ -0,0 +1,93 @@ + + * + * @author Thomas Citharel + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + +namespace OCP\Calendar; + +use Sabre\VObject\Component\VCalendar; + +/** + * Interface ICalendarV2 + * + * @package OCP + * @since 20.0.0 + */ +interface ICalendarV2 { + + /** + * @return string defining the technical unique key + * @since 20.0.0 + */ + public function getKey(): string; + + /** + * In comparison to getKey() this function returns a human readable (maybe translated) name + * @return null|string + * @since 20.0.0 + */ + public function getDisplayName(): ?string; + + /** + * Calendar color + * @return null|string + * @since 20.0.0 + */ + public function getDisplayColor(): ?string; + + /** + * Whether the calendar is writeable + * + * @return bool + * @since 20.0.0 + */ + public function isWriteable(): bool; + + /** + * Get a calendar object by it's URI + * + * @param string $uri + * @return ICalendarObjectV2|null + * @since 20.0.0 + */ + public function getByUri(string $uri): ?ICalendarObjectV2; + + /** + * @param string $pattern which should match within the $searchProperties + * @param array $searchProperties defines the properties within the query pattern should match + * @param array $options - optional parameters: + * ['timerange' => ['start' => new DateTime(...), 'end' => new DateTime(...)]] + * @param integer|null $limit - limit number of search results + * @param integer|null $offset - offset for paging of search results + * @return array an array of events/journals/todos which are arrays of key-value-pairs + * @since 19.0.0 + */ + public function search(string $pattern, array $searchProperties=[], array $options=[], int $limit=null, int $offset=null): array; + + /** + * Create a new calendar object into a calendar. Accepts a VCalendar object for calendar data. + * + * @param VCalendar $vObject + * @return ICalendarObjectV2|null + * @since 20.0.0 + */ + public function create(VCalendar $vObject): ?ICalendarObjectV2; +} diff --git a/lib/public/Calendar/IManager.php b/lib/public/Calendar/IManager.php index bd23e67bebc93..f0f76179099bb 100644 --- a/lib/public/Calendar/IManager.php +++ b/lib/public/Calendar/IManager.php @@ -56,6 +56,7 @@ * ] * * @since 13.0.0 + * @deprecated 20.0.0 */ interface IManager { /** diff --git a/lib/public/Calendar/IManagerV2.php b/lib/public/Calendar/IManagerV2.php new file mode 100644 index 0000000000000..6fc5d8589a034 --- /dev/null +++ b/lib/public/Calendar/IManagerV2.php @@ -0,0 +1,112 @@ + + * + * @author Thomas Citharel + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + +namespace OCP\Calendar; + +use Closure; + +/** + * This class provides access to the Nextcloud CalDAV backend. + * Use this class exclusively if you want to access and manage calendars. + * + * Events/Journals/Todos are expressed as instances of ICalendarObjectV2 + * + * @since 21.0.0 + */ +interface IManagerV2 { + + /** + * Check if calendars are available + * + * @return bool true if enabled, false if not + * @since 21.0.0 + */ + public function isEnabled(): bool; + + /** + * Get all registered calendars + * + * @return ICalendarV2[] + * @since 21.0.0 + */ + public function getCalendars(): array; + + /** + * Get a calendar by it's key + * + * @param string $key + * @return ICalendarV2|null + * @since 21.0.0 + */ + public function getCalendar(string $key): ?ICalendarV2; + + /** + * This function is used to search and find objects within the user's calendars. + * In case $pattern is empty all events/journals/todos will be returned. + * + * @param string $pattern which should match within the $searchProperties + * @param array $searchProperties defines the properties within the query pattern should match + * @param array $options - optional parameters: + * ['timerange' => ['start' => new DateTime(...), 'end' => new DateTime(...)]] + * @param integer|null $limit - limit number of search results + * @param integer|null $offset - offset for paging of search results + * @return ICalendarObjectV2[] + * @since 21.0.0 + */ + public function search(string $pattern, array $searchProperties=[], array $options=[], int $limit=null, int $offset=null): array; + + /** + * Registers a calendar + * + * @param ICalendarV2 $calendar + * @return void + * @since 21.0.0 + */ + public function registerCalendar(ICalendarV2 $calendar): void; + + /** + * Unregisters a calendar + * + * @param ICalendarV2 $calendar + * @return void + * @since 21.0.0 + */ + public function unregisterCalendar(ICalendarV2 $calendar): void; + + /** + * In order to improve lazy loading a closure can be registered which will be called in case + * calendars are actually requested + * + * @param Closure $callable + * @return void + * @since 21.0.0 + */ + public function register(Closure $callable): void; + + /** + * removes all registered calendar instances + * @return void + * @since 21.0.0 + */ + public function clear(): void; +} diff --git a/tests/lib/Calendar/ManagerTest.php b/tests/lib/Calendar/ManagerTest.php index fcf6ab3a58894..bdaa5c9326091 100644 --- a/tests/lib/Calendar/ManagerTest.php +++ b/tests/lib/Calendar/ManagerTest.php @@ -3,6 +3,7 @@ * @copyright 2017, Georg Ehrke * * @author Georg Ehrke + * @author Thomas Citharel * * @license GNU AGPL version 3 or any later version * diff --git a/tests/lib/Calendar/ManagerV2Test.php b/tests/lib/Calendar/ManagerV2Test.php new file mode 100644 index 0000000000000..f313265a1b85b --- /dev/null +++ b/tests/lib/Calendar/ManagerV2Test.php @@ -0,0 +1,246 @@ + + * + * @author Georg Ehrke + * @author Thomas Citharel + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ + +namespace Test\Calendar; + +use OC\Calendar\ManagerV2; +use OCP\Calendar\ICalendarV2; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class ManagerV2Test extends TestCase { + + /** @var ManagerV2 */ + private $manager; + + protected function setUp(): void { + parent::setUp(); + + $this->manager = new ManagerV2(); + } + + /** + * @dataProvider searchProvider + */ + public function testSearch($search1, $search2, $expected) { + /** @var ICalendarV2 | MockObject $calendar1 */ + $calendar1 = $this->createMock(ICalendarV2::class); + $calendar1->method('getKey')->willReturn('simple:1'); + $calendar1->expects($this->once()) + ->method('search') + ->with('', [], [], null, null) + ->willReturn($search1); + + /** @var ICalendarV2 | MockObject $calendar2 */ + $calendar2 = $this->createMock(ICalendarV2::class); + $calendar2->method('getKey')->willReturn('simple:2'); + $calendar2->expects($this->once()) + ->method('search') + ->with('', [], [], null, null) + ->willReturn($search2); + + $this->manager->registerCalendar($calendar1); + $this->manager->registerCalendar($calendar2); + + $result = $this->manager->search(''); + $this->assertEquals($expected, $result); + } + + /** + * @dataProvider searchProvider + */ + public function testSearchOptions($search1, $search2, $expected) { + /** @var ICalendarV2 | MockObject $calendar1 */ + $calendar1 = $this->createMock(ICalendarV2::class); + $calendar1->method('getKey')->willReturn('simple:1'); + $calendar1->expects($this->once()) + ->method('search') + ->with('searchTerm', ['SUMMARY', 'DESCRIPTION'], + ['timerange' => ['start' => null, 'end' => null]], 5, 20) + ->willReturn($search1); + + /** @var ICalendarV2 | MockObject $calendar2 */ + $calendar2 = $this->createMock(ICalendarV2::class); + $calendar2->method('getKey')->willReturn('simple:2'); + $calendar2->expects($this->once()) + ->method('search') + ->with('searchTerm', ['SUMMARY', 'DESCRIPTION'], + ['timerange' => ['start' => null, 'end' => null]], 5, 20) + ->willReturn($search2); + + $this->manager->registerCalendar($calendar1); + $this->manager->registerCalendar($calendar2); + + $result = $this->manager->search('searchTerm', ['SUMMARY', 'DESCRIPTION'], + ['timerange' => ['start' => null, 'end' => null]], 5, 20); + $this->assertEquals($expected, $result); + } + + public function searchProvider() { + $search1 = [ + [ + 'id' => 1, + 'data' => 'foobar', + ], + [ + 'id' => 2, + 'data' => 'barfoo', + ] + ]; + $search2 = [ + [ + 'id' => 3, + 'data' => 'blablub', + ], + [ + 'id' => 4, + 'data' => 'blubbla', + ] + ]; + + $expected = [ + [ + 'id' => 1, + 'data' => 'foobar', + 'calendar-key' => 'simple:1', + ], + [ + 'id' => 2, + 'data' => 'barfoo', + 'calendar-key' => 'simple:1', + ], + [ + 'id' => 3, + 'data' => 'blablub', + 'calendar-key' => 'simple:2', + ], + [ + 'id' => 4, + 'data' => 'blubbla', + 'calendar-key' => 'simple:2', + ] + ]; + + return [ + [ + $search1, + $search2, + $expected + ] + ]; + } + + public function testRegisterUnregister() { + /** @var ICalendarV2 | MockObject $calendar1 */ + $calendar1 = $this->createMock(ICalendarV2::class); + $calendar1->method('getKey')->willReturn('key1'); + + /** @var ICalendarV2 | MockObject $calendar2 */ + $calendar2 = $this->createMock(ICalendarV2::class); + $calendar2->method('getKey')->willReturn('key2'); + + $this->manager->registerCalendar($calendar1); + $this->manager->registerCalendar($calendar2); + + $result = $this->manager->getCalendars(); + $this->assertCount(2, $result); + $this->assertContains($calendar1, $result); + $this->assertContains($calendar2, $result); + + $this->manager->unregisterCalendar($calendar1); + + $result = $this->manager->getCalendars(); + $this->assertCount(1, $result); + $this->assertContains($calendar2, $result); + } + + public function testGetCalendars() { + /** @var ICalendarV2 | MockObject $calendar1 */ + $calendar1 = $this->createMock(ICalendarV2::class); + $calendar1->method('getKey')->willReturn('key1'); + + /** @var ICalendarV2 | MockObject $calendar2 */ + $calendar2 = $this->createMock(ICalendarV2::class); + $calendar2->method('getKey')->willReturn('key2'); + + $this->manager->registerCalendar($calendar1); + $this->manager->registerCalendar($calendar2); + + $result = $this->manager->getCalendars(); + $this->assertCount(2, $result); + $this->assertContainsOnlyInstancesOf(ICalendarV2::class, $result); + $this->assertContains($calendar1, $result); + $this->assertContains($calendar2, $result); + + $this->manager->clear(); + + $result = $this->manager->getCalendars(); + + $this->assertCount(0, $result); + } + + public function testGetCalendar() { + /** @var ICalendarV2 | MockObject $calendar1 */ + $calendar1 = $this->createMock(ICalendarV2::class); + $calendar1->method('getKey')->willReturn('key1'); + + /** @var ICalendarV2 | MockObject $calendar2 */ + $calendar2 = $this->createMock(ICalendarV2::class); + $calendar2->method('getKey')->willReturn('key2'); + + $this->manager->registerCalendar($calendar1); + $this->manager->registerCalendar($calendar2); + + $result = $this->manager->getCalendar('key1'); + $this->assertEquals($calendar1, $result); + $this->assertInstanceOf(ICalendarV2::class, $result); + + $result = $this->manager->getCalendar('key2'); + $this->assertEquals($calendar1, $result); + $this->assertInstanceOf(ICalendarV2::class, $result); + + $result = $this->manager->getCalendar('key3'); + $this->assertNull($result); + + $this->manager->clear(); + + $result = $this->manager->getCalendar('key1'); + + $this->assertNull($result); + } + + public function testEnabledIfNot() { + $isEnabled = $this->manager->isEnabled(); + $this->assertFalse($isEnabled); + } + + public function testIfEnabledIfSo() { + /** @var ICalendarV2 | MockObject $calendar */ + $calendar = $this->createMock(ICalendarV2::class); + $this->manager->registerCalendar($calendar); + + $isEnabled = $this->manager->isEnabled(); + $this->assertTrue($isEnabled); + } +}