diff --git a/apps/dav/composer/composer/autoload_classmap.php b/apps/dav/composer/composer/autoload_classmap.php index 0608472303..fe6c6ab325 100644 --- a/apps/dav/composer/composer/autoload_classmap.php +++ b/apps/dav/composer/composer/autoload_classmap.php @@ -206,6 +206,9 @@ return array( 'OCA\\DAV\\Files\\Sharing\\PublicLinkCheckPlugin' => $baseDir . '/../lib/Files/Sharing/PublicLinkCheckPlugin.php', 'OCA\\DAV\\HookManager' => $baseDir . '/../lib/HookManager.php', 'OCA\\DAV\\Listener\\CalendarContactInteractionListener' => $baseDir . '/../lib/Listener/CalendarContactInteractionListener.php', + 'OCA\\DAV\\Listener\\CalendarDeletionActivityUpdaterListener' => $baseDir . '/../lib/Listener/CalendarDeletionActivityUpdaterListener.php', + 'OCA\\DAV\\Listener\\CalendarDeletionDefaultUpdaterListener' => $baseDir . '/../lib/Listener/CalendarDeletionDefaultUpdaterListener.php', + 'OCA\\DAV\\Listener\\CalendarDeletionReminderUpdaterListener' => $baseDir . '/../lib/Listener/CalendarDeletionReminderUpdaterListener.php', 'OCA\\DAV\\Migration\\BuildCalendarSearchIndex' => $baseDir . '/../lib/Migration/BuildCalendarSearchIndex.php', 'OCA\\DAV\\Migration\\BuildCalendarSearchIndexBackgroundJob' => $baseDir . '/../lib/Migration/BuildCalendarSearchIndexBackgroundJob.php', 'OCA\\DAV\\Migration\\BuildSocialSearchIndex' => $baseDir . '/../lib/Migration/BuildSocialSearchIndex.php', diff --git a/apps/dav/composer/composer/autoload_static.php b/apps/dav/composer/composer/autoload_static.php index e0a779c1e3..48e453b72f 100644 --- a/apps/dav/composer/composer/autoload_static.php +++ b/apps/dav/composer/composer/autoload_static.php @@ -221,6 +221,9 @@ class ComposerStaticInitDAV 'OCA\\DAV\\Files\\Sharing\\PublicLinkCheckPlugin' => __DIR__ . '/..' . '/../lib/Files/Sharing/PublicLinkCheckPlugin.php', 'OCA\\DAV\\HookManager' => __DIR__ . '/..' . '/../lib/HookManager.php', 'OCA\\DAV\\Listener\\CalendarContactInteractionListener' => __DIR__ . '/..' . '/../lib/Listener/CalendarContactInteractionListener.php', + 'OCA\\DAV\\Listener\\CalendarDeletionActivityUpdaterListener' => __DIR__ . '/..' . '/../lib/Listener/CalendarDeletionActivityUpdaterListener.php', + 'OCA\\DAV\\Listener\\CalendarDeletionDefaultUpdaterListener' => __DIR__ . '/..' . '/../lib/Listener/CalendarDeletionDefaultUpdaterListener.php', + 'OCA\\DAV\\Listener\\CalendarDeletionReminderUpdaterListener' => __DIR__ . '/..' . '/../lib/Listener/CalendarDeletionReminderUpdaterListener.php', 'OCA\\DAV\\Migration\\BuildCalendarSearchIndex' => __DIR__ . '/..' . '/../lib/Migration/BuildCalendarSearchIndex.php', 'OCA\\DAV\\Migration\\BuildCalendarSearchIndexBackgroundJob' => __DIR__ . '/..' . '/../lib/Migration/BuildCalendarSearchIndexBackgroundJob.php', 'OCA\\DAV\\Migration\\BuildSocialSearchIndex' => __DIR__ . '/..' . '/../lib/Migration/BuildSocialSearchIndex.php', diff --git a/apps/dav/lib/AppInfo/Application.php b/apps/dav/lib/AppInfo/Application.php index 7d5b98199d..8298a9dd6f 100644 --- a/apps/dav/lib/AppInfo/Application.php +++ b/apps/dav/lib/AppInfo/Application.php @@ -40,7 +40,6 @@ use OCA\DAV\CalDAV\Activity\Provider\Event; use OCA\DAV\CalDAV\BirthdayService; use OCA\DAV\CalDAV\CalDavBackend; use OCA\DAV\CalDAV\CalendarManager; -use OCA\DAV\CalDAV\Reminder\Backend as ReminderBackend; use OCA\DAV\CalDAV\Reminder\NotificationProvider\AudioProvider; use OCA\DAV\CalDAV\Reminder\NotificationProvider\EmailProvider; use OCA\DAV\CalDAV\Reminder\NotificationProvider\PushProvider; @@ -53,11 +52,15 @@ use OCA\DAV\CardDAV\CardDavBackend; use OCA\DAV\CardDAV\ContactsManager; use OCA\DAV\CardDAV\PhotoCache; use OCA\DAV\CardDAV\SyncService; +use OCA\DAV\Events\CalendarDeletedEvent; use OCA\DAV\Events\CalendarObjectCreatedEvent; use OCA\DAV\Events\CalendarObjectUpdatedEvent; use OCA\DAV\Events\CalendarShareUpdatedEvent; use OCA\DAV\HookManager; use OCA\DAV\Listener\CalendarContactInteractionListener; +use OCA\DAV\Listener\CalendarDeletionActivityUpdaterListener; +use OCA\DAV\Listener\CalendarDeletionDefaultUpdaterListener; +use OCA\DAV\Listener\CalendarDeletionReminderUpdaterListener; use OCA\DAV\Search\ContactsSearchProvider; use OCA\DAV\Search\EventsSearchProvider; use OCA\DAV\Search\TasksSearchProvider; @@ -68,7 +71,6 @@ use OCP\AppFramework\Bootstrap\IRegistrationContext; use OCP\AppFramework\IAppContainer; use OCP\Calendar\IManager as ICalendarManager; use OCP\Contacts\IManager as IContactsManager; -use OCP\IConfig; use OCP\ILogger; use OCP\IServerContainer; use OCP\IUser; @@ -77,7 +79,6 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\GenericEvent; use Throwable; use function is_null; -use function strpos; class Application extends App implements IBootstrap { public const APP_ID = 'dav'; @@ -113,6 +114,9 @@ class Application extends App implements IBootstrap { /** * Register event listeners */ + $context->registerEventListener(CalendarDeletedEvent::class, CalendarDeletionActivityUpdaterListener::class); + $context->registerEventListener(CalendarDeletedEvent::class, CalendarDeletionReminderUpdaterListener::class); + $context->registerEventListener(CalendarDeletedEvent::class, CalendarDeletionDefaultUpdaterListener::class); $context->registerEventListener(CalendarObjectCreatedEvent::class, CalendarContactInteractionListener::class); $context->registerEventListener(CalendarObjectUpdatedEvent::class, CalendarContactInteractionListener::class); $context->registerEventListener(CalendarShareUpdatedEvent::class, CalendarContactInteractionListener::class); @@ -204,20 +208,6 @@ class Application extends App implements IBootstrap { $event->getArgument('propertyMutations') ); }); - $dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::deleteCalendar', function (GenericEvent $event) use ($container) { - /** @var Backend $backend */ - $backend = $container->query(Backend::class); - $backend->onCalendarDelete( - $event->getArgument('calendarData'), - $event->getArgument('shares') - ); - - /** @var ReminderBackend $reminderBackend */ - $reminderBackend = $container->query(ReminderBackend::class); - $reminderBackend->cleanRemindersForCalendar( - (int) $event->getArgument('calendarId') - ); - }); $dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::updateShares', function (GenericEvent $event) use ($container) { /** @var Backend $backend */ $backend = $container->query(Backend::class); @@ -269,22 +259,6 @@ class Application extends App implements IBootstrap { $dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::updateCalendarObject', $listener); $dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject', $listener); - /** - * In case the user has set their default calendar to this one - */ - $dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::deleteCalendar', function (GenericEvent $event) use ($serverContainer) { - /** @var IConfig $config */ - $config = $serverContainer->getConfig(); - $principalUri = $event->getArgument('calendarData')['principaluri']; - if (strpos($principalUri, 'principals/users') === 0) { - [, $UID] = \Sabre\Uri\split($principalUri); - $uri = $event->getArgument('calendarData')['uri']; - if ($config->getUserValue($UID, 'dav', 'defaultCalendar') === $uri) { - $config->deleteUserValue($UID, 'dav', 'defaultCalendar'); - } - } - }); - $dispatcher->addListener('OCP\Federation\TrustedServerEvent::remove', function (GenericEvent $event) { /** @var CardDavBackend $cardDavBackend */ diff --git a/apps/dav/lib/CalDAV/CalDavBackend.php b/apps/dav/lib/CalDAV/CalDavBackend.php index 0a57ce1745..2196505b70 100644 --- a/apps/dav/lib/CalDAV/CalDavBackend.php +++ b/apps/dav/lib/CalDAV/CalDavBackend.php @@ -890,14 +890,6 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription $calendarData = $this->getCalendarById($calendarId); $shares = $this->getShares($calendarId); - $this->legacyDispatcher->dispatch('\OCA\DAV\CalDAV\CalDavBackend::deleteCalendar', new GenericEvent( - '\OCA\DAV\CalDAV\CalDavBackend::deleteCalendar', - [ - 'calendarId' => $calendarId, - 'calendarData' => $calendarData, - 'shares' => $shares, - ])); - $stmt = $this->db->prepare('DELETE FROM `*PREFIX*calendarobjects` WHERE `calendarid` = ? AND `calendartype` = ?'); $stmt->execute([$calendarId, self::CALENDAR_TYPE_CALENDAR]); @@ -915,6 +907,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription ->andWhere($query->expr()->eq('calendartype', $query->createNamedParameter(self::CALENDAR_TYPE_CALENDAR))) ->executeUpdate(); + // Only dispatch if we actually deleted anything if ($calendarData) { $this->dispatcher->dispatchTyped(new CalendarDeletedEvent((int)$calendarId, $calendarData, $shares)); } diff --git a/apps/dav/lib/Listener/CalendarDeletionActivityUpdaterListener.php b/apps/dav/lib/Listener/CalendarDeletionActivityUpdaterListener.php new file mode 100644 index 0000000000..4139aba226 --- /dev/null +++ b/apps/dav/lib/Listener/CalendarDeletionActivityUpdaterListener.php @@ -0,0 +1,75 @@ + + * + * @author 2021 Christoph Wurst + * + * @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\Listener; + +use OCA\DAV\CalDAV\Activity\Backend as ActivityBackend; +use OCA\DAV\Events\CalendarDeletedEvent; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use Psr\Log\LoggerInterface; +use Throwable; +use function sprintf; + +/** + * @template-implements IEventListener<\OCA\DAV\Events\CalendarDeletedEvent> + */ +class CalendarDeletionActivityUpdaterListener implements IEventListener { + + /** @var ActivityBackend */ + private $activityBackend; + + /** @var LoggerInterface */ + private $logger; + + public function __construct(ActivityBackend $activityBackend, + LoggerInterface $logger) { + $this->activityBackend = $activityBackend; + $this->logger = $logger; + } + + public function handle(Event $event): void { + if (!($event instanceof CalendarDeletedEvent)) { + // Not what we subscribed to + return; + } + + try { + $this->activityBackend->onCalendarDelete( + $event->getCalendarData(), + $event->getShares() + ); + + $this->logger->debug( + sprintf('Activity generated for deleted calendar %d', $event->getCalendarId()) + ); + } catch (Throwable $e) { + // Any error with activities shouldn't abort the calendar deletion, so we just log it + $this->logger->error('Error generating activities for a deleted calendar: ' . $e->getMessage(), [ + 'exception' => $e, + ]); + } + } +} diff --git a/apps/dav/lib/Listener/CalendarDeletionDefaultUpdaterListener.php b/apps/dav/lib/Listener/CalendarDeletionDefaultUpdaterListener.php new file mode 100644 index 0000000000..8ffe112628 --- /dev/null +++ b/apps/dav/lib/Listener/CalendarDeletionDefaultUpdaterListener.php @@ -0,0 +1,86 @@ + + * + * @author 2021 Christoph Wurst + * + * @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\Listener; + +use OCA\DAV\Events\CalendarDeletedEvent; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use OCP\IConfig; +use Psr\Log\LoggerInterface; +use Throwable; +use function strpos; + +/** + * @template-implements IEventListener<\OCA\DAV\Events\CalendarDeletedEvent> + */ +class CalendarDeletionDefaultUpdaterListener implements IEventListener { + + /** @var IConfig */ + private $config; + + /** @var LoggerInterface */ + private $logger; + + public function __construct(IConfig $config, + LoggerInterface $logger) { + $this->config = $config; + $this->logger = $logger; + } + + /** + * In case the user has set their default calendar to the deleted one + */ + public function handle(Event $event): void { + if (!($event instanceof CalendarDeletedEvent)) { + // Not what we subscribed to + return; + } + + try { + $principalUri = $event->getCalendarData()['principaluri']; + if (strpos($principalUri, 'principals/users') !== 0) { + $this->logger->debug('Default calendar needs no update because the deleted calendar does not belong to a user principal'); + return; + } + + [, $uid] = \Sabre\Uri\split($principalUri); + $uri = $event->getCalendarData()['uri']; + if ($this->config->getUserValue($uid, 'dav', 'defaultCalendar') !== $uri) { + $this->logger->debug('Default calendar needs no update because the deleted calendar is no the user\'s default one'); + return; + } + + $this->config->deleteUserValue($uid, 'dav', 'defaultCalendar'); + + $this->logger->debug('Default user calendar reset'); + } catch (Throwable $e) { + // Any error with activities shouldn't abort the calendar deletion, so we just log it + $this->logger->error('Error generating activities for a deleted calendar: ' . $e->getMessage(), [ + 'exception' => $e, + ]); + } + } +} diff --git a/apps/dav/lib/Listener/CalendarDeletionReminderUpdaterListener.php b/apps/dav/lib/Listener/CalendarDeletionReminderUpdaterListener.php new file mode 100644 index 0000000000..3beecc8ff0 --- /dev/null +++ b/apps/dav/lib/Listener/CalendarDeletionReminderUpdaterListener.php @@ -0,0 +1,74 @@ + + * + * @author 2021 Christoph Wurst + * + * @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\Listener; + +use OCA\DAV\CalDAV\Reminder\Backend as ReminderBackend; +use OCA\DAV\Events\CalendarDeletedEvent; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use Psr\Log\LoggerInterface; +use Throwable; +use function sprintf; + +/** + * @template-implements IEventListener<\OCA\DAV\Events\CalendarDeletedEvent> + */ +class CalendarDeletionReminderUpdaterListener implements IEventListener { + + /** @var ReminderBackend */ + private $reminderBackend; + + /** @var LoggerInterface */ + private $logger; + + public function __construct(ReminderBackend $reminderBackend, + LoggerInterface $logger) { + $this->reminderBackend = $reminderBackend; + $this->logger = $logger; + } + + public function handle(Event $event): void { + if (!($event instanceof CalendarDeletedEvent)) { + // Not what we subscribed to + return; + } + + try { + $this->reminderBackend->cleanRemindersForCalendar( + $event->getCalendarId() + ); + + $this->logger->debug( + sprintf('Reminders of calendar %d cleaned up', $event->getCalendarId()) + ); + } catch (Throwable $e) { + // Any error with activities shouldn't abort the calendar deletion, so we just log it + $this->logger->error('Error cleaning up reminders of a deleted calendar: ' . $e->getMessage(), [ + 'exception' => $e, + ]); + } + } +} diff --git a/apps/dav/tests/unit/CalDAV/AbstractCalDavBackend.php b/apps/dav/tests/unit/CalDAV/AbstractCalDavBackend.php index 51ba8c1867..b512847a7e 100644 --- a/apps/dav/tests/unit/CalDAV/AbstractCalDavBackend.php +++ b/apps/dav/tests/unit/CalDAV/AbstractCalDavBackend.php @@ -31,6 +31,7 @@ use OC\KnownUser\KnownUserService; use OCA\DAV\CalDAV\CalDavBackend; use OCA\DAV\CalDAV\Proxy\ProxyMapper; use OCA\DAV\Connector\Sabre\Principal; +use OCA\DAV\Events\CalendarDeletedEvent; use OCP\App\IAppManager; use OCP\EventDispatcher\IEventDispatcher; use OCP\IConfig; @@ -133,11 +134,12 @@ abstract class AbstractCalDavBackend extends TestCase { private function cleanupForPrincipal($principal): void { $calendars = $this->backend->getCalendarsForUser($principal); + $this->legacyDispatcher->expects(self::exactly(count($calendars))) + ->method('dispatchTyped') + ->with(self::callback(function ($event) { + return $event instanceof CalendarDeletedEvent; + })); foreach ($calendars as $calendar) { - $this->legacyDispatcher->expects($this->at(0)) - ->method('dispatch') - ->with('\OCA\DAV\CalDAV\CalDavBackend::deleteCalendar'); - $this->backend->deleteCalendar($calendar['id']); } $subscriptions = $this->backend->getSubscriptionsForUser($principal); diff --git a/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php b/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php index 5016b70871..25966aa5c9 100644 --- a/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php +++ b/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php @@ -36,6 +36,7 @@ use DateTime; use DateTimeZone; use OCA\DAV\CalDAV\CalDavBackend; use OCA\DAV\CalDAV\Calendar; +use OCA\DAV\Events\CalendarDeletedEvent; use OCP\IConfig; use OCP\IL10N; use Sabre\DAV\Exception\NotFound; @@ -72,12 +73,14 @@ class CalDavBackendTest extends AbstractCalDavBackend { $this->assertEquals('User\'s displayname', $calendars[0]['{http://nextcloud.com/ns}owner-displayname']); // delete the address book - $this->legacyDispatcher->expects($this->at(0)) - ->method('dispatch') - ->with('\OCA\DAV\CalDAV\CalDavBackend::deleteCalendar'); + $this->dispatcher->expects(self::once()) + ->method('dispatchTyped') + ->with(self::callback(function ($event) { + return $event instanceof CalendarDeletedEvent; + })); $this->backend->deleteCalendar($calendars[0]['id']); $calendars = $this->backend->getCalendarsForUser(self::UNIT_TEST_USER); - $this->assertCount(0, $calendars); + self::assertEmpty($calendars); } public function providesSharingData() { @@ -196,13 +199,15 @@ EOD; $this->assertAccess($userCanRead, self::UNIT_TEST_USER1, '{DAV:}read', $acl); $this->assertAccess($userCanWrite, self::UNIT_TEST_USER1, '{DAV:}write', $acl); - // delete the address book - $this->legacyDispatcher->expects($this->at(0)) - ->method('dispatch') - ->with('\OCA\DAV\CalDAV\CalDavBackend::deleteCalendar'); + // delete the calendar + $this->dispatcher->expects(self::once()) + ->method('dispatchTyped') + ->with(self::callback(function ($event) { + return $event instanceof CalendarDeletedEvent; + })); $this->backend->deleteCalendar($calendars[0]['id']); $calendars = $this->backend->getCalendarsForUser(self::UNIT_TEST_USER); - $this->assertCount(0, $calendars); + self::assertEmpty($calendars); } public function testCalendarObjectsOperations() { diff --git a/build/psalm-baseline.xml b/build/psalm-baseline.xml index d8d558822d..d39a742959 100644 --- a/build/psalm-baseline.xml +++ b/build/psalm-baseline.xml @@ -112,9 +112,6 @@ - - \Sabre\Uri\split($principalUri) - getAppDataDir @@ -164,13 +161,12 @@ - + '\OCA\DAV\CalDAV\CalDavBackend::createCachedCalendarObject' '\OCA\DAV\CalDAV\CalDavBackend::createCalendar' '\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject' '\OCA\DAV\CalDAV\CalDavBackend::createSubscription' '\OCA\DAV\CalDAV\CalDavBackend::deleteCachedCalendarObject' - '\OCA\DAV\CalDAV\CalDavBackend::deleteCalendar' '\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject' '\OCA\DAV\CalDAV\CalDavBackend::deleteSubscription' '\OCA\DAV\CalDAV\CalDavBackend::publishCalendar' @@ -195,8 +191,7 @@ (int)$calendarId - - dispatch + dispatch dispatch dispatch @@ -860,6 +855,11 @@ $this->usersToDelete + + + \Sabre\Uri\split($principalUri) + + $publicCalendarRoot->disableListing @@ -1087,7 +1087,6 @@ $id $id $id - $id (int)$share['id'] @@ -3164,7 +3163,6 @@ dispatch - dispatch