aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/dav/composer/composer/autoload_classmap.php2
-rw-r--r--apps/dav/composer/composer/autoload_static.php2
-rw-r--r--apps/dav/lib/AppInfo/Application.php8
-rw-r--r--apps/dav/lib/Db/Absence.php5
-rw-r--r--apps/dav/lib/Listener/OutOfOfficeListener.php210
-rw-r--r--apps/dav/lib/Server.php4
-rw-r--r--apps/dav/lib/ServerFactory.php35
-rw-r--r--apps/dav/lib/Service/AbsenceService.php12
-rw-r--r--apps/dav/tests/unit/Listener/OutOfOfficeListenerTest.php546
9 files changed, 819 insertions, 5 deletions
diff --git a/apps/dav/composer/composer/autoload_classmap.php b/apps/dav/composer/composer/autoload_classmap.php
index 0bec456d1f1..7891cad42eb 100644
--- a/apps/dav/composer/composer/autoload_classmap.php
+++ b/apps/dav/composer/composer/autoload_classmap.php
@@ -265,6 +265,7 @@ return array(
'OCA\\DAV\\Listener\\CalendarShareUpdateListener' => $baseDir . '/../lib/Listener/CalendarShareUpdateListener.php',
'OCA\\DAV\\Listener\\CardListener' => $baseDir . '/../lib/Listener/CardListener.php',
'OCA\\DAV\\Listener\\ClearPhotoCacheListener' => $baseDir . '/../lib/Listener/ClearPhotoCacheListener.php',
+ 'OCA\\DAV\\Listener\\OutOfOfficeListener' => $baseDir . '/../lib/Listener/OutOfOfficeListener.php',
'OCA\\DAV\\Listener\\SubscriptionListener' => $baseDir . '/../lib/Listener/SubscriptionListener.php',
'OCA\\DAV\\Listener\\TrustedServerRemovedListener' => $baseDir . '/../lib/Listener/TrustedServerRemovedListener.php',
'OCA\\DAV\\Listener\\UserPreferenceListener' => $baseDir . '/../lib/Listener/UserPreferenceListener.php',
@@ -316,6 +317,7 @@ return array(
'OCA\\DAV\\Search\\EventsSearchProvider' => $baseDir . '/../lib/Search/EventsSearchProvider.php',
'OCA\\DAV\\Search\\TasksSearchProvider' => $baseDir . '/../lib/Search/TasksSearchProvider.php',
'OCA\\DAV\\Server' => $baseDir . '/../lib/Server.php',
+ 'OCA\\DAV\\ServerFactory' => $baseDir . '/../lib/ServerFactory.php',
'OCA\\DAV\\Service\\AbsenceService' => $baseDir . '/../lib/Service/AbsenceService.php',
'OCA\\DAV\\Settings\\AvailabilitySettings' => $baseDir . '/../lib/Settings/AvailabilitySettings.php',
'OCA\\DAV\\Settings\\CalDAVSettings' => $baseDir . '/../lib/Settings/CalDAVSettings.php',
diff --git a/apps/dav/composer/composer/autoload_static.php b/apps/dav/composer/composer/autoload_static.php
index 3c1891cf192..fdfe7feb6d9 100644
--- a/apps/dav/composer/composer/autoload_static.php
+++ b/apps/dav/composer/composer/autoload_static.php
@@ -280,6 +280,7 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Listener\\CalendarShareUpdateListener' => __DIR__ . '/..' . '/../lib/Listener/CalendarShareUpdateListener.php',
'OCA\\DAV\\Listener\\CardListener' => __DIR__ . '/..' . '/../lib/Listener/CardListener.php',
'OCA\\DAV\\Listener\\ClearPhotoCacheListener' => __DIR__ . '/..' . '/../lib/Listener/ClearPhotoCacheListener.php',
+ 'OCA\\DAV\\Listener\\OutOfOfficeListener' => __DIR__ . '/..' . '/../lib/Listener/OutOfOfficeListener.php',
'OCA\\DAV\\Listener\\SubscriptionListener' => __DIR__ . '/..' . '/../lib/Listener/SubscriptionListener.php',
'OCA\\DAV\\Listener\\TrustedServerRemovedListener' => __DIR__ . '/..' . '/../lib/Listener/TrustedServerRemovedListener.php',
'OCA\\DAV\\Listener\\UserPreferenceListener' => __DIR__ . '/..' . '/../lib/Listener/UserPreferenceListener.php',
@@ -331,6 +332,7 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Search\\EventsSearchProvider' => __DIR__ . '/..' . '/../lib/Search/EventsSearchProvider.php',
'OCA\\DAV\\Search\\TasksSearchProvider' => __DIR__ . '/..' . '/../lib/Search/TasksSearchProvider.php',
'OCA\\DAV\\Server' => __DIR__ . '/..' . '/../lib/Server.php',
+ 'OCA\\DAV\\ServerFactory' => __DIR__ . '/..' . '/../lib/ServerFactory.php',
'OCA\\DAV\\Service\\AbsenceService' => __DIR__ . '/..' . '/../lib/Service/AbsenceService.php',
'OCA\\DAV\\Settings\\AvailabilitySettings' => __DIR__ . '/..' . '/../lib/Settings/AvailabilitySettings.php',
'OCA\\DAV\\Settings\\CalDAVSettings' => __DIR__ . '/..' . '/../lib/Settings/CalDAVSettings.php',
diff --git a/apps/dav/lib/AppInfo/Application.php b/apps/dav/lib/AppInfo/Application.php
index 08529435cae..52f44ee0235 100644
--- a/apps/dav/lib/AppInfo/Application.php
+++ b/apps/dav/lib/AppInfo/Application.php
@@ -69,6 +69,7 @@ use OCA\DAV\Events\CardDeletedEvent;
use OCA\DAV\Events\CardUpdatedEvent;
use OCA\DAV\Events\SubscriptionCreatedEvent;
use OCA\DAV\Events\SubscriptionDeletedEvent;
+use OCA\DAV\Listener\OutOfOfficeListener;
use OCP\Accounts\UserUpdatedEvent;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Federation\Events\TrustedServerRemovedEvent;
@@ -103,6 +104,9 @@ use OCP\Config\BeforePreferenceSetEvent;
use OCP\Contacts\IManager as IContactsManager;
use OCP\Files\AppData\IAppDataFactory;
use OCP\IUser;
+use OCP\User\Events\OutOfOfficeChangedEvent;
+use OCP\User\Events\OutOfOfficeClearedEvent;
+use OCP\User\Events\OutOfOfficeScheduledEvent;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\GenericEvent;
@@ -195,6 +199,10 @@ class Application extends App implements IBootstrap {
$context->registerEventListener(BeforePreferenceDeletedEvent::class, UserPreferenceListener::class);
$context->registerEventListener(BeforePreferenceSetEvent::class, UserPreferenceListener::class);
+ $context->registerEventListener(OutOfOfficeChangedEvent::class, OutOfOfficeListener::class);
+ $context->registerEventListener(OutOfOfficeClearedEvent::class, OutOfOfficeListener::class);
+ $context->registerEventListener(OutOfOfficeScheduledEvent::class, OutOfOfficeListener::class);
+
$context->registerNotifierService(Notifier::class);
$context->registerCalendarProvider(CalendarProvider::class);
diff --git a/apps/dav/lib/Db/Absence.php b/apps/dav/lib/Db/Absence.php
index e9ce1d2ea64..8de8ecc9aa0 100644
--- a/apps/dav/lib/Db/Absence.php
+++ b/apps/dav/lib/Db/Absence.php
@@ -27,6 +27,7 @@ declare(strict_types=1);
namespace OCA\DAV\Db;
use DateTimeImmutable;
+use Exception;
use InvalidArgumentException;
use JsonSerializable;
use OC\User\OutOfOfficeData;
@@ -70,8 +71,10 @@ class Absence extends Entity implements JsonSerializable {
if ($user->getUID() !== $this->getUserId()) {
throw new InvalidArgumentException("The user doesn't match the user id of this absence! Expected " . $this->getUserId() . ", got " . $user->getUID());
}
+ if ($this->getId() === null) {
+ throw new Exception('Creating out-of-office data without ID');
+ }
- //$user = $userManager->get($this->getUserId());
$startDate = new DateTimeImmutable($this->getFirstDay());
$endDate = new DateTimeImmutable($this->getLastDay());
return new OutOfOfficeData(
diff --git a/apps/dav/lib/Listener/OutOfOfficeListener.php b/apps/dav/lib/Listener/OutOfOfficeListener.php
new file mode 100644
index 00000000000..645a01a35cf
--- /dev/null
+++ b/apps/dav/lib/Listener/OutOfOfficeListener.php
@@ -0,0 +1,210 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright 2023 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2023 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ */
+
+namespace OCA\DAV\Listener;
+
+use DateTimeImmutable;
+use OCA\DAV\CalDAV\CalDavBackend;
+use OCA\DAV\CalDAV\Calendar;
+use OCA\DAV\CalDAV\CalendarHome;
+use OCA\DAV\ServerFactory;
+use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IEventListener;
+use OCP\IConfig;
+use OCP\User\Events\OutOfOfficeChangedEvent;
+use OCP\User\Events\OutOfOfficeClearedEvent;
+use OCP\User\Events\OutOfOfficeScheduledEvent;
+use OCP\User\IOutOfOfficeData;
+use Psr\Log\LoggerInterface;
+use Sabre\DAV\Exception\NotFound;
+use Sabre\VObject\Component\VCalendar;
+use Sabre\VObject\Component\VEvent;
+use Sabre\VObject\Component\VTimeZone;
+use Sabre\VObject\Reader;
+use function fclose;
+use function fopen;
+use function fwrite;
+use function rewind;
+
+/**
+ * @template-implements IEventListener<OutOfOfficeScheduledEvent|OutOfOfficeChangedEvent|OutOfOfficeClearedEvent>
+ */
+class OutOfOfficeListener implements IEventListener {
+ public function __construct(private ServerFactory $serverFactory,
+ private IConfig $appConfig,
+ private LoggerInterface $logger) {
+ }
+
+ public function handle(Event $event): void {
+ if ($event instanceof OutOfOfficeScheduledEvent) {
+ $userId = $event->getData()->getUser()->getUID();
+ $principal = "principals/users/$userId";
+
+ $calendarNode = $this->getCalendarNode($principal, $userId);
+ if ($calendarNode === null) {
+ return;
+ }
+
+ $tz = $calendarNode->getProperties([])['{urn:ietf:params:xml:ns:caldav}calendar-timezone'] ?? null;
+ $vCalendarEvent = $this->createVCalendarEvent($event->getData(), $tz);
+ $stream = fopen('php://memory', 'rb+');
+ try {
+ fwrite($stream, $vCalendarEvent->serialize());
+ rewind($stream);
+ $calendarNode->createFile(
+ $this->getEventFileName($event->getData()->getId()),
+ $stream,
+ );
+ } finally {
+ fclose($stream);
+ }
+ } else if ($event instanceof OutOfOfficeChangedEvent) {
+ $userId = $event->getData()->getUser()->getUID();
+ $principal = "principals/users/$userId";
+
+ $calendarNode = $this->getCalendarNode($principal, $userId);
+ if ($calendarNode === null) {
+ return;
+ }
+ $tz = $calendarNode->getProperties([])['{urn:ietf:params:xml:ns:caldav}calendar-timezone'] ?? null;
+ $vCalendarEvent = $this->createVCalendarEvent($event->getData(), $tz);
+ try {
+ $oldEvent = $calendarNode->getChild($this->getEventFileName($event->getData()->getId()));
+ $oldEvent->put($vCalendarEvent->serialize());
+ return;
+ } catch (NotFound) {
+ $stream = fopen('php://memory', 'rb+');
+ try {
+ fwrite($stream, $vCalendarEvent->serialize());
+ rewind($stream);
+ $calendarNode->createFile(
+ $this->getEventFileName($event->getData()->getId()),
+ $stream,
+ );
+ } finally {
+ fclose($stream);
+ }
+ }
+ } else if ($event instanceof OutOfOfficeClearedEvent) {
+ $userId = $event->getData()->getUser()->getUID();
+ $principal = "principals/users/$userId";
+
+ $calendarNode = $this->getCalendarNode($principal, $userId);
+ if ($calendarNode === null) {
+ return;
+ }
+
+ try {
+ $oldEvent = $calendarNode->getChild($this->getEventFileName($event->getData()->getId()));
+ $oldEvent->delete();
+ } catch (NotFound) {
+ // The user must have deleted it or the default calendar changed -> ignore
+ }
+ }
+ }
+
+ private function getCalendarNode(string $principal, string $userId): ?Calendar {
+ $invitationServer = $this->serverFactory->createInviationResponseServer(false);
+ $server = $invitationServer->getServer();
+
+ /** @var \OCA\DAV\CalDAV\Plugin $caldavPlugin */
+ $caldavPlugin = $server->getPlugin('caldav');
+ $calendarHomePath = $caldavPlugin->getCalendarHomeForPrincipal($principal);
+ if ($calendarHomePath === null) {
+ $this->logger->debug('Principal has no calendar home path');
+ return null;
+ }
+ try {
+ /** @var CalendarHome $calendarHome */
+ $calendarHome = $server->tree->getNodeForPath($calendarHomePath);
+ } catch (NotFound $e) {
+ $this->logger->debug('Calendar home not found', [
+ 'exception' => $e,
+ ]);
+ return null;
+ }
+ $uri = $this->appConfig->getUserValue($userId, 'dav', 'defaultCalendar', CalDavBackend::PERSONAL_CALENDAR_URI);
+ try {
+ $calendarNode = $calendarHome->getChild($uri);
+ } catch (NotFound $e) {
+ $this->logger->debug('Personal calendar does not exist', [
+ 'exception' => $e,
+ 'uri' => $uri,
+ ]);
+ return null;
+ }
+ if (!($calendarNode instanceof Calendar)) {
+ $this->logger->warning('Personal calendar node is not a calendar');
+ return null;
+ }
+ if ($calendarNode->isDeleted()) {
+ $this->logger->warning('Personal calendar has been deleted');
+ return null;
+ }
+
+ return $calendarNode;
+ }
+
+ private function getEventFileName(string $id): string {
+ return "out_of_office_$id.ics";
+ }
+
+ private function createVCalendarEvent(IOutOfOfficeData $data, ?string $timeZoneData): VCalendar {
+ $shortMessage = $data->getShortMessage();
+ $longMessage = $data->getMessage();
+ $start = (new DateTimeImmutable)
+ ->setTimestamp($data->getStartDate())
+ ->setTime(0, 0);
+ $end = (new DateTimeImmutable())
+ ->setTimestamp($data->getEndDate())
+ ->modify('+ 2 days')
+ ->setTime(0, 0);
+ $vCalendar = new VCalendar();
+ $vCalendar->add('VEVENT', [
+ 'SUMMARY' => $shortMessage,
+ 'DESCRIPTION' => $longMessage,
+ 'STATUS' => 'CONFIRMED',
+ 'DTSTART' => $start,
+ 'DTEND' => $end,
+ 'X-NEXTCLOUD-OUT-OF-OFFICE' => $data->getId(),
+ ]);
+ /** @var VEvent $vEvent */
+ $vEvent = $vCalendar->VEVENT;
+ if ($timeZoneData !== null) {
+ /** @var VCalendar $vtimezoneObj */
+ $vtimezoneObj = Reader::read($timeZoneData);
+ /** @var VTimeZone $vtimezone */
+ $vtimezone = $vtimezoneObj->VTIMEZONE;
+ $calendarTimeZone = $vtimezone->getTimeZone();
+ $vCalendar->add($vtimezone);
+
+ /** @psalm-suppress UndefinedMethod */
+ $vEvent->DTSTART->setDateTime($start->setTimezone($calendarTimeZone)->setTime(0, 0));
+ /** @psalm-suppress UndefinedMethod */
+ $vEvent->DTEND->setDateTime($end->setTimezone($calendarTimeZone)->setTime(0, 0));
+ }
+ return $vCalendar;
+ }
+}
diff --git a/apps/dav/lib/Server.php b/apps/dav/lib/Server.php
index 15b97d028cc..4f00004fc83 100644
--- a/apps/dav/lib/Server.php
+++ b/apps/dav/lib/Server.php
@@ -385,4 +385,8 @@ class Server {
}
return false;
}
+
+ public function getSabreServer(): Connector\Sabre\Server {
+ return $this->server;
+ }
}
diff --git a/apps/dav/lib/ServerFactory.php b/apps/dav/lib/ServerFactory.php
new file mode 100644
index 00000000000..7dc74f7d6ae
--- /dev/null
+++ b/apps/dav/lib/ServerFactory.php
@@ -0,0 +1,35 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright 2023 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2023 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ */
+
+namespace OCA\DAV;
+
+use OCA\DAV\CalDAV\InvitationResponse\InvitationResponseServer;
+
+class ServerFactory {
+
+ public function createInviationResponseServer(bool $public): InvitationResponseServer {
+ return new InvitationResponseServer(false);
+ }
+}
diff --git a/apps/dav/lib/Service/AbsenceService.php b/apps/dav/lib/Service/AbsenceService.php
index 69dee1bd8cc..b31a910c4d2 100644
--- a/apps/dav/lib/Service/AbsenceService.php
+++ b/apps/dav/lib/Service/AbsenceService.php
@@ -75,14 +75,18 @@ class AbsenceService {
if ($user === null) {
throw new InvalidArgumentException("User $userId does not exist");
}
- $eventData = $absence->toOutOufOfficeData($user);
if ($absence->getId() === null) {
- $this->eventDispatcher->dispatchTyped(new OutOfOfficeScheduledEvent($eventData));
- return $this->absenceMapper->insert($absence);
+ $persistedAbsence = $this->absenceMapper->insert($absence);
+ $this->eventDispatcher->dispatchTyped(new OutOfOfficeScheduledEvent(
+ $persistedAbsence->toOutOufOfficeData($user)
+ ));
+ return $persistedAbsence;
}
- $this->eventDispatcher->dispatchTyped(new OutOfOfficeChangedEvent($eventData));
+ $this->eventDispatcher->dispatchTyped(new OutOfOfficeChangedEvent(
+ $absence->toOutOufOfficeData($user)
+ ));
return $this->absenceMapper->update($absence);
}
diff --git a/apps/dav/tests/unit/Listener/OutOfOfficeListenerTest.php b/apps/dav/tests/unit/Listener/OutOfOfficeListenerTest.php
new file mode 100644
index 00000000000..02227adfbfc
--- /dev/null
+++ b/apps/dav/tests/unit/Listener/OutOfOfficeListenerTest.php
@@ -0,0 +1,546 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * @copyright 2023 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2023 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ */
+
+namespace OCA\DAV\Tests\Unit\Listener;
+
+use OCA\DAV\CalDAV\Calendar;
+use OCA\DAV\CalDAV\CalendarHome;
+use OCA\DAV\CalDAV\CalendarObject;
+use OCA\DAV\CalDAV\InvitationResponse\InvitationResponseServer;
+use OCA\DAV\CalDAV\Plugin;
+use OCA\DAV\Connector\Sabre\Server;
+use OCA\DAV\Listener\OutOfOfficeListener;
+use OCA\DAV\ServerFactory;
+use OCP\EventDispatcher\Event;
+use OCP\IConfig;
+use OCP\IUser;
+use OCP\User\Events\OutOfOfficeChangedEvent;
+use OCP\User\Events\OutOfOfficeClearedEvent;
+use OCP\User\Events\OutOfOfficeScheduledEvent;
+use OCP\User\IOutOfOfficeData;
+use PHPUnit\Framework\MockObject\MockObject;
+use Psr\Log\LoggerInterface;
+use Sabre\DAV\Exception\NotFound;
+use Sabre\DAV\Tree;
+use Test\TestCase;
+
+/**
+ * @covers \OCA\DAV\Listener\OutOfOfficeListener
+ */
+class OutOfOfficeListenerTest extends TestCase {
+
+ private ServerFactory|MockObject $serverFactory;
+ private IConfig|MockObject $appConfig;
+ private LoggerInterface|MockObject $loggerInterface;
+ private OutOfOfficeListener $listener;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->serverFactory = $this->createMock(ServerFactory::class);
+ $this->appConfig = $this->createMock(IConfig::class);
+ $this->loggerInterface = $this->createMock(LoggerInterface::class);
+
+ $this->listener = new OutOfOfficeListener(
+ $this->serverFactory,
+ $this->appConfig,
+ $this->loggerInterface,
+ );
+ }
+
+ public function testHandleUnrelated(): void {
+ $event = new Event();
+
+ $this->listener->handle($event);
+
+ $this->addToAssertionCount(1);
+ }
+
+ public function testHandleSchedulingNoCalendarHome(): void {
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn('user123');
+ $data = $this->createMock(IOutOfOfficeData::class);
+ $data->method('getUser')->willReturn($user);
+ $davServer = $this->createMock(Server::class);
+ $invitationServer = $this->createMock(InvitationResponseServer::class);
+ $invitationServer->method('getServer')->willReturn($davServer);
+ $this->serverFactory->method('createInviationResponseServer')->willReturn($invitationServer);
+ $caldavPlugin = $this->createMock(Plugin::class);
+ $davServer->expects(self::once())
+ ->method('getPlugin')
+ ->with('caldav')
+ ->willReturn($caldavPlugin);
+ $event = new OutOfOfficeScheduledEvent($data);
+
+ $this->listener->handle($event);
+ }
+
+ public function testHandleSchedulingNoCalendarHomeNode(): void {
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn('user123');
+ $data = $this->createMock(IOutOfOfficeData::class);
+ $data->method('getUser')->willReturn($user);
+ $davServer = $this->createMock(Server::class);
+ $invitationServer = $this->createMock(InvitationResponseServer::class);
+ $invitationServer->method('getServer')->willReturn($davServer);
+ $this->serverFactory->method('createInviationResponseServer')->willReturn($invitationServer);
+ $caldavPlugin = $this->createMock(Plugin::class);
+ $davServer->expects(self::once())
+ ->method('getPlugin')
+ ->with('caldav')
+ ->willReturn($caldavPlugin);
+ $caldavPlugin->expects(self::once())
+ ->method('getCalendarHomeForPrincipal')
+ ->willReturn('/home/calendar');
+ $tree = $this->createMock(Tree::class);
+ $davServer->tree = $tree;
+ $tree->expects(self::once())
+ ->method('getNodeForPath')
+ ->with('/home/calendar')
+ ->willThrowException(new NotFound('nope'));
+ $event = new OutOfOfficeScheduledEvent($data);
+
+ $this->listener->handle($event);
+ }
+
+ public function testHandleSchedulingPersonalCalendarNotFound(): void {
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn('user123');
+ $data = $this->createMock(IOutOfOfficeData::class);
+ $data->method('getUser')->willReturn($user);
+ $davServer = $this->createMock(Server::class);
+ $invitationServer = $this->createMock(InvitationResponseServer::class);
+ $invitationServer->method('getServer')->willReturn($davServer);
+ $this->serverFactory->method('createInviationResponseServer')->willReturn($invitationServer);
+ $caldavPlugin = $this->createMock(Plugin::class);
+ $davServer->expects(self::once())
+ ->method('getPlugin')
+ ->with('caldav')
+ ->willReturn($caldavPlugin);
+ $caldavPlugin->expects(self::once())
+ ->method('getCalendarHomeForPrincipal')
+ ->willReturn('/home/calendar');
+ $tree = $this->createMock(Tree::class);
+ $davServer->tree = $tree;
+ $calendarHome = $this->createMock(CalendarHome::class);
+ $tree->expects(self::once())
+ ->method('getNodeForPath')
+ ->with('/home/calendar')
+ ->willReturn($calendarHome);
+ $this->appConfig->expects(self::once())
+ ->method('getUserValue')
+ ->with('user123', 'dav', 'defaultCalendar', 'personal')
+ ->willReturn('personal-1');
+ $calendarHome->expects(self::once())
+ ->method('getChild')
+ ->with('personal-1')
+ ->willThrowException(new NotFound('nope'));
+ $event = new OutOfOfficeScheduledEvent($data);
+
+ $this->listener->handle($event);
+ }
+
+ public function testHandleScheduling(): void {
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn('user123');
+ $data = $this->createMock(IOutOfOfficeData::class);
+ $data->method('getUser')->willReturn($user);
+ $davServer = $this->createMock(Server::class);
+ $invitationServer = $this->createMock(InvitationResponseServer::class);
+ $invitationServer->method('getServer')->willReturn($davServer);
+ $this->serverFactory->method('createInviationResponseServer')->willReturn($invitationServer);
+ $caldavPlugin = $this->createMock(Plugin::class);
+ $davServer->expects(self::once())
+ ->method('getPlugin')
+ ->with('caldav')
+ ->willReturn($caldavPlugin);
+ $caldavPlugin->expects(self::once())
+ ->method('getCalendarHomeForPrincipal')
+ ->willReturn('/home/calendar');
+ $tree = $this->createMock(Tree::class);
+ $davServer->tree = $tree;
+ $calendarHome = $this->createMock(CalendarHome::class);
+ $tree->expects(self::once())
+ ->method('getNodeForPath')
+ ->with('/home/calendar')
+ ->willReturn($calendarHome);
+ $this->appConfig->expects(self::once())
+ ->method('getUserValue')
+ ->with('user123', 'dav', 'defaultCalendar', 'personal')
+ ->willReturn('personal-1');
+ $calendar = $this->createMock(Calendar::class);
+ $calendarHome->expects(self::once())
+ ->method('getChild')
+ ->with('personal-1')
+ ->willReturn($calendar);
+ $calendar->expects(self::once())
+ ->method('createFile');
+ $event = new OutOfOfficeScheduledEvent($data);
+
+ $this->listener->handle($event);
+ }
+
+ public function testHandleChangeNoCalendarHome(): void {
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn('user123');
+ $data = $this->createMock(IOutOfOfficeData::class);
+ $data->method('getUser')->willReturn($user);
+ $davServer = $this->createMock(Server::class);
+ $invitationServer = $this->createMock(InvitationResponseServer::class);
+ $invitationServer->method('getServer')->willReturn($davServer);
+ $this->serverFactory->method('createInviationResponseServer')->willReturn($invitationServer);
+ $caldavPlugin = $this->createMock(Plugin::class);
+ $davServer->expects(self::once())
+ ->method('getPlugin')
+ ->with('caldav')
+ ->willReturn($caldavPlugin);
+ $event = new OutOfOfficeChangedEvent($data);
+
+ $this->listener->handle($event);
+ }
+
+ public function testHandleChangeNoCalendarHomeNode(): void {
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn('user123');
+ $data = $this->createMock(IOutOfOfficeData::class);
+ $data->method('getUser')->willReturn($user);
+ $davServer = $this->createMock(Server::class);
+ $invitationServer = $this->createMock(InvitationResponseServer::class);
+ $invitationServer->method('getServer')->willReturn($davServer);
+ $this->serverFactory->method('createInviationResponseServer')->willReturn($invitationServer);
+ $caldavPlugin = $this->createMock(Plugin::class);
+ $davServer->expects(self::once())
+ ->method('getPlugin')
+ ->with('caldav')
+ ->willReturn($caldavPlugin);
+ $caldavPlugin->expects(self::once())
+ ->method('getCalendarHomeForPrincipal')
+ ->willReturn('/home/calendar');
+ $tree = $this->createMock(Tree::class);
+ $davServer->tree = $tree;
+ $tree->expects(self::once())
+ ->method('getNodeForPath')
+ ->with('/home/calendar')
+ ->willThrowException(new NotFound('nope'));
+ $event = new OutOfOfficeChangedEvent($data);
+
+ $this->listener->handle($event);
+ }
+
+ public function testHandleChangePersonalCalendarNotFound(): void {
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn('user123');
+ $data = $this->createMock(IOutOfOfficeData::class);
+ $data->method('getUser')->willReturn($user);
+ $davServer = $this->createMock(Server::class);
+ $invitationServer = $this->createMock(InvitationResponseServer::class);
+ $invitationServer->method('getServer')->willReturn($davServer);
+ $this->serverFactory->method('createInviationResponseServer')->willReturn($invitationServer);
+ $caldavPlugin = $this->createMock(Plugin::class);
+ $davServer->expects(self::once())
+ ->method('getPlugin')
+ ->with('caldav')
+ ->willReturn($caldavPlugin);
+ $caldavPlugin->expects(self::once())
+ ->method('getCalendarHomeForPrincipal')
+ ->willReturn('/home/calendar');
+ $tree = $this->createMock(Tree::class);
+ $davServer->tree = $tree;
+ $calendarHome = $this->createMock(CalendarHome::class);
+ $tree->expects(self::once())
+ ->method('getNodeForPath')
+ ->with('/home/calendar')
+ ->willReturn($calendarHome);
+ $this->appConfig->expects(self::once())
+ ->method('getUserValue')
+ ->with('user123', 'dav', 'defaultCalendar', 'personal')
+ ->willReturn('personal-1');
+ $calendarHome->expects(self::once())
+ ->method('getChild')
+ ->with('personal-1')
+ ->willThrowException(new NotFound('nope'));
+ $event = new OutOfOfficeChangedEvent($data);
+
+ $this->listener->handle($event);
+ }
+
+ public function testHandleChangeRecreate(): void {
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn('user123');
+ $data = $this->createMock(IOutOfOfficeData::class);
+ $data->method('getUser')->willReturn($user);
+ $davServer = $this->createMock(Server::class);
+ $invitationServer = $this->createMock(InvitationResponseServer::class);
+ $invitationServer->method('getServer')->willReturn($davServer);
+ $this->serverFactory->method('createInviationResponseServer')->willReturn($invitationServer);
+ $caldavPlugin = $this->createMock(Plugin::class);
+ $davServer->expects(self::once())
+ ->method('getPlugin')
+ ->with('caldav')
+ ->willReturn($caldavPlugin);
+ $caldavPlugin->expects(self::once())
+ ->method('getCalendarHomeForPrincipal')
+ ->willReturn('/home/calendar');
+ $tree = $this->createMock(Tree::class);
+ $davServer->tree = $tree;
+ $calendarHome = $this->createMock(CalendarHome::class);
+ $tree->expects(self::once())
+ ->method('getNodeForPath')
+ ->with('/home/calendar')
+ ->willReturn($calendarHome);
+ $this->appConfig->expects(self::once())
+ ->method('getUserValue')
+ ->with('user123', 'dav', 'defaultCalendar', 'personal')
+ ->willReturn('personal-1');
+ $calendar = $this->createMock(Calendar::class);
+ $calendarHome->expects(self::once())
+ ->method('getChild')
+ ->with('personal-1')
+ ->willReturn($calendar);
+ $calendar->expects(self::once())
+ ->method('getChild')
+ ->willThrowException(new NotFound());
+ $calendar->expects(self::once())
+ ->method('createFile');
+ $event = new OutOfOfficeChangedEvent($data);
+
+ $this->listener->handle($event);
+ }
+
+ public function testHandleChange(): void {
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn('user123');
+ $data = $this->createMock(IOutOfOfficeData::class);
+ $data->method('getUser')->willReturn($user);
+ $davServer = $this->createMock(Server::class);
+ $invitationServer = $this->createMock(InvitationResponseServer::class);
+ $invitationServer->method('getServer')->willReturn($davServer);
+ $this->serverFactory->method('createInviationResponseServer')->willReturn($invitationServer);
+ $caldavPlugin = $this->createMock(Plugin::class);
+ $davServer->expects(self::once())
+ ->method('getPlugin')
+ ->with('caldav')
+ ->willReturn($caldavPlugin);
+ $caldavPlugin->expects(self::once())
+ ->method('getCalendarHomeForPrincipal')
+ ->willReturn('/home/calendar');
+ $tree = $this->createMock(Tree::class);
+ $davServer->tree = $tree;
+ $calendarHome = $this->createMock(CalendarHome::class);
+ $tree->expects(self::once())
+ ->method('getNodeForPath')
+ ->with('/home/calendar')
+ ->willReturn($calendarHome);
+ $this->appConfig->expects(self::once())
+ ->method('getUserValue')
+ ->with('user123', 'dav', 'defaultCalendar', 'personal')
+ ->willReturn('personal-1');
+ $calendar = $this->createMock(Calendar::class);
+ $calendarHome->expects(self::once())
+ ->method('getChild')
+ ->with('personal-1')
+ ->willReturn($calendar);
+ $eventNode = $this->createMock(CalendarObject::class);
+ $calendar->expects(self::once())
+ ->method('getChild')
+ ->willReturn($eventNode);
+ $eventNode->expects(self::once())
+ ->method('put');
+ $calendar->expects(self::never())
+ ->method('createFile');
+ $event = new OutOfOfficeChangedEvent($data);
+
+ $this->listener->handle($event);
+ }
+
+ public function testHandleClearNoCalendarHome(): void {
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn('user123');
+ $data = $this->createMock(IOutOfOfficeData::class);
+ $data->method('getUser')->willReturn($user);
+ $davServer = $this->createMock(Server::class);
+ $invitationServer = $this->createMock(InvitationResponseServer::class);
+ $invitationServer->method('getServer')->willReturn($davServer);
+ $this->serverFactory->method('createInviationResponseServer')->willReturn($invitationServer);
+ $caldavPlugin = $this->createMock(Plugin::class);
+ $davServer->expects(self::once())
+ ->method('getPlugin')
+ ->with('caldav')
+ ->willReturn($caldavPlugin);
+ $event = new OutOfOfficeClearedEvent($data);
+
+ $this->listener->handle($event);
+ }
+
+ public function testHandleClearNoCalendarHomeNode(): void {
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn('user123');
+ $data = $this->createMock(IOutOfOfficeData::class);
+ $data->method('getUser')->willReturn($user);
+ $davServer = $this->createMock(Server::class);
+ $invitationServer = $this->createMock(InvitationResponseServer::class);
+ $invitationServer->method('getServer')->willReturn($davServer);
+ $this->serverFactory->method('createInviationResponseServer')->willReturn($invitationServer);
+ $caldavPlugin = $this->createMock(Plugin::class);
+ $davServer->expects(self::once())
+ ->method('getPlugin')
+ ->with('caldav')
+ ->willReturn($caldavPlugin);
+ $caldavPlugin->expects(self::once())
+ ->method('getCalendarHomeForPrincipal')
+ ->willReturn('/home/calendar');
+ $tree = $this->createMock(Tree::class);
+ $davServer->tree = $tree;
+ $tree->expects(self::once())
+ ->method('getNodeForPath')
+ ->with('/home/calendar')
+ ->willThrowException(new NotFound('nope'));
+ $event = new OutOfOfficeClearedEvent($data);
+
+ $this->listener->handle($event);
+ }
+
+ public function testHandleClearPersonalCalendarNotFound(): void {
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn('user123');
+ $data = $this->createMock(IOutOfOfficeData::class);
+ $data->method('getUser')->willReturn($user);
+ $davServer = $this->createMock(Server::class);
+ $invitationServer = $this->createMock(InvitationResponseServer::class);
+ $invitationServer->method('getServer')->willReturn($davServer);
+ $this->serverFactory->method('createInviationResponseServer')->willReturn($invitationServer);
+ $caldavPlugin = $this->createMock(Plugin::class);
+ $davServer->expects(self::once())
+ ->method('getPlugin')
+ ->with('caldav')
+ ->willReturn($caldavPlugin);
+ $caldavPlugin->expects(self::once())
+ ->method('getCalendarHomeForPrincipal')
+ ->willReturn('/home/calendar');
+ $tree = $this->createMock(Tree::class);
+ $davServer->tree = $tree;
+ $calendarHome = $this->createMock(CalendarHome::class);
+ $tree->expects(self::once())
+ ->method('getNodeForPath')
+ ->with('/home/calendar')
+ ->willReturn($calendarHome);
+ $this->appConfig->expects(self::once())
+ ->method('getUserValue')
+ ->with('user123', 'dav', 'defaultCalendar', 'personal')
+ ->willReturn('personal-1');
+ $calendarHome->expects(self::once())
+ ->method('getChild')
+ ->with('personal-1')
+ ->willThrowException(new NotFound('nope'));
+ $event = new OutOfOfficeClearedEvent($data);
+
+ $this->listener->handle($event);
+ }
+
+ public function testHandleClearRecreate(): void {
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn('user123');
+ $data = $this->createMock(IOutOfOfficeData::class);
+ $data->method('getUser')->willReturn($user);
+ $davServer = $this->createMock(Server::class);
+ $invitationServer = $this->createMock(InvitationResponseServer::class);
+ $invitationServer->method('getServer')->willReturn($davServer);
+ $this->serverFactory->method('createInviationResponseServer')->willReturn($invitationServer);
+ $caldavPlugin = $this->createMock(Plugin::class);
+ $davServer->expects(self::once())
+ ->method('getPlugin')
+ ->with('caldav')
+ ->willReturn($caldavPlugin);
+ $caldavPlugin->expects(self::once())
+ ->method('getCalendarHomeForPrincipal')
+ ->willReturn('/home/calendar');
+ $tree = $this->createMock(Tree::class);
+ $davServer->tree = $tree;
+ $calendarHome = $this->createMock(CalendarHome::class);
+ $tree->expects(self::once())
+ ->method('getNodeForPath')
+ ->with('/home/calendar')
+ ->willReturn($calendarHome);
+ $this->appConfig->expects(self::once())
+ ->method('getUserValue')
+ ->with('user123', 'dav', 'defaultCalendar', 'personal')
+ ->willReturn('personal-1');
+ $calendar = $this->createMock(Calendar::class);
+ $calendarHome->expects(self::once())
+ ->method('getChild')
+ ->with('personal-1')
+ ->willReturn($calendar);
+ $calendar->expects(self::once())
+ ->method('getChild')
+ ->willThrowException(new NotFound());
+ $event = new OutOfOfficeClearedEvent($data);
+
+ $this->listener->handle($event);
+ }
+
+ public function testHandleClear(): void {
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn('user123');
+ $data = $this->createMock(IOutOfOfficeData::class);
+ $data->method('getUser')->willReturn($user);
+ $davServer = $this->createMock(Server::class);
+ $invitationServer = $this->createMock(InvitationResponseServer::class);
+ $invitationServer->method('getServer')->willReturn($davServer);
+ $this->serverFactory->method('createInviationResponseServer')->willReturn($invitationServer);
+ $caldavPlugin = $this->createMock(Plugin::class);
+ $davServer->expects(self::once())
+ ->method('getPlugin')
+ ->with('caldav')
+ ->willReturn($caldavPlugin);
+ $caldavPlugin->expects(self::once())
+ ->method('getCalendarHomeForPrincipal')
+ ->willReturn('/home/calendar');
+ $tree = $this->createMock(Tree::class);
+ $davServer->tree = $tree;
+ $calendarHome = $this->createMock(CalendarHome::class);
+ $tree->expects(self::once())
+ ->method('getNodeForPath')
+ ->with('/home/calendar')
+ ->willReturn($calendarHome);
+ $this->appConfig->expects(self::once())
+ ->method('getUserValue')
+ ->with('user123', 'dav', 'defaultCalendar', 'personal')
+ ->willReturn('personal-1');
+ $calendar = $this->createMock(Calendar::class);
+ $calendarHome->expects(self::once())
+ ->method('getChild')
+ ->with('personal-1')
+ ->willReturn($calendar);
+ $eventNode = $this->createMock(CalendarObject::class);
+ $calendar->expects(self::once())
+ ->method('getChild')
+ ->willReturn($eventNode);
+ $eventNode->expects(self::once())
+ ->method('delete');
+ $event = new OutOfOfficeClearedEvent($data);
+
+ $this->listener->handle($event);
+ }
+}