From c410e08fec9cf771c08c63d0e8e93ab825f40841 Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Fri, 28 Feb 2025 13:28:07 +0100 Subject: [PATCH] feat: command to list and delete calendar subscriptions Signed-off-by: Daniel Kesselberg --- apps/dav/appinfo/info.xml | 8 +- .../composer/composer/autoload_classmap.php | 2 + .../dav/composer/composer/autoload_static.php | 2 + apps/dav/lib/CalDAV/CalDavBackend.php | 37 +++++++++ apps/dav/lib/Command/DeleteSubscription.php | 79 +++++++++++++++++++ apps/dav/lib/Command/ListSubscriptions.php | 77 ++++++++++++++++++ 6 files changed, 202 insertions(+), 3 deletions(-) create mode 100644 apps/dav/lib/Command/DeleteSubscription.php create mode 100644 apps/dav/lib/Command/ListSubscriptions.php diff --git a/apps/dav/appinfo/info.xml b/apps/dav/appinfo/info.xml index 6abc8ac9d80..222b5e14b8c 100644 --- a/apps/dav/appinfo/info.xml +++ b/apps/dav/appinfo/info.xml @@ -56,18 +56,20 @@ OCA\DAV\Command\CreateAddressBook - OCA\DAV\Command\ListAddressbooks OCA\DAV\Command\CreateCalendar OCA\DAV\Command\CreateSubscription OCA\DAV\Command\DeleteCalendar + OCA\DAV\Command\DeleteSubscription OCA\DAV\Command\FixCalendarSyncCommand - OCA\DAV\Command\MoveCalendar + OCA\DAV\Command\ListAddressbooks OCA\DAV\Command\ListCalendars + OCA\DAV\Command\ListSubscriptions + OCA\DAV\Command\MoveCalendar + OCA\DAV\Command\RemoveInvalidShares OCA\DAV\Command\RetentionCleanupCommand OCA\DAV\Command\SendEventReminders OCA\DAV\Command\SyncBirthdayCalendar OCA\DAV\Command\SyncSystemAddressBook - OCA\DAV\Command\RemoveInvalidShares diff --git a/apps/dav/composer/composer/autoload_classmap.php b/apps/dav/composer/composer/autoload_classmap.php index 17533c9cf74..13d7c05379b 100644 --- a/apps/dav/composer/composer/autoload_classmap.php +++ b/apps/dav/composer/composer/autoload_classmap.php @@ -157,9 +157,11 @@ return array( 'OCA\\DAV\\Command\\CreateCalendar' => $baseDir . '/../lib/Command/CreateCalendar.php', 'OCA\\DAV\\Command\\CreateSubscription' => $baseDir . '/../lib/Command/CreateSubscription.php', 'OCA\\DAV\\Command\\DeleteCalendar' => $baseDir . '/../lib/Command/DeleteCalendar.php', + 'OCA\\DAV\\Command\\DeleteSubscription' => $baseDir . '/../lib/Command/DeleteSubscription.php', 'OCA\\DAV\\Command\\FixCalendarSyncCommand' => $baseDir . '/../lib/Command/FixCalendarSyncCommand.php', 'OCA\\DAV\\Command\\ListAddressbooks' => $baseDir . '/../lib/Command/ListAddressbooks.php', 'OCA\\DAV\\Command\\ListCalendars' => $baseDir . '/../lib/Command/ListCalendars.php', + 'OCA\\DAV\\Command\\ListSubscriptions' => $baseDir . '/../lib/Command/ListSubscriptions.php', 'OCA\\DAV\\Command\\MoveCalendar' => $baseDir . '/../lib/Command/MoveCalendar.php', 'OCA\\DAV\\Command\\RemoveInvalidShares' => $baseDir . '/../lib/Command/RemoveInvalidShares.php', 'OCA\\DAV\\Command\\RetentionCleanupCommand' => $baseDir . '/../lib/Command/RetentionCleanupCommand.php', diff --git a/apps/dav/composer/composer/autoload_static.php b/apps/dav/composer/composer/autoload_static.php index 768fda98443..237ab53558c 100644 --- a/apps/dav/composer/composer/autoload_static.php +++ b/apps/dav/composer/composer/autoload_static.php @@ -172,9 +172,11 @@ class ComposerStaticInitDAV 'OCA\\DAV\\Command\\CreateCalendar' => __DIR__ . '/..' . '/../lib/Command/CreateCalendar.php', 'OCA\\DAV\\Command\\CreateSubscription' => __DIR__ . '/..' . '/../lib/Command/CreateSubscription.php', 'OCA\\DAV\\Command\\DeleteCalendar' => __DIR__ . '/..' . '/../lib/Command/DeleteCalendar.php', + 'OCA\\DAV\\Command\\DeleteSubscription' => __DIR__ . '/..' . '/../lib/Command/DeleteSubscription.php', 'OCA\\DAV\\Command\\FixCalendarSyncCommand' => __DIR__ . '/..' . '/../lib/Command/FixCalendarSyncCommand.php', 'OCA\\DAV\\Command\\ListAddressbooks' => __DIR__ . '/..' . '/../lib/Command/ListAddressbooks.php', 'OCA\\DAV\\Command\\ListCalendars' => __DIR__ . '/..' . '/../lib/Command/ListCalendars.php', + 'OCA\\DAV\\Command\\ListSubscriptions' => __DIR__ . '/..' . '/../lib/Command/ListSubscriptions.php', 'OCA\\DAV\\Command\\MoveCalendar' => __DIR__ . '/..' . '/../lib/Command/MoveCalendar.php', 'OCA\\DAV\\Command\\RemoveInvalidShares' => __DIR__ . '/..' . '/../lib/Command/RemoveInvalidShares.php', 'OCA\\DAV\\Command\\RetentionCleanupCommand' => __DIR__ . '/..' . '/../lib/Command/RetentionCleanupCommand.php', diff --git a/apps/dav/lib/CalDAV/CalDavBackend.php b/apps/dav/lib/CalDAV/CalDavBackend.php index 349a4ec3630..81e999cc356 100644 --- a/apps/dav/lib/CalDAV/CalDavBackend.php +++ b/apps/dav/lib/CalDAV/CalDavBackend.php @@ -735,6 +735,43 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription return $this->rowToSubscription($row, $subscription); } + public function getSubscriptionByUri(string $principal, string $uri): ?array { + $fields = array_column($this->subscriptionPropertyMap, 0); + $fields[] = 'id'; + $fields[] = 'uri'; + $fields[] = 'source'; + $fields[] = 'synctoken'; + $fields[] = 'principaluri'; + $fields[] = 'lastmodified'; + + $query = $this->db->getQueryBuilder(); + $query->select($fields) + ->from('calendarsubscriptions') + ->where($query->expr()->eq('uri', $query->createNamedParameter($uri))) + ->andWhere($query->expr()->eq('principaluri', $query->createNamedParameter($principal))) + ->setMaxResults(1); + $stmt = $query->executeQuery(); + + $row = $stmt->fetch(); + $stmt->closeCursor(); + if ($row === false) { + return null; + } + + $row['principaluri'] = (string)$row['principaluri']; + $subscription = [ + 'id' => $row['id'], + 'uri' => $row['uri'], + 'principaluri' => $row['principaluri'], + 'source' => $row['source'], + 'lastmodified' => $row['lastmodified'], + '{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet(['VTODO', 'VEVENT']), + '{http://sabredav.org/ns}sync-token' => $row['synctoken'] ?: '0', + ]; + + return $this->rowToSubscription($row, $subscription); + } + /** * Creates a new calendar for a principal. * diff --git a/apps/dav/lib/Command/DeleteSubscription.php b/apps/dav/lib/Command/DeleteSubscription.php new file mode 100644 index 00000000000..db0cb6295c9 --- /dev/null +++ b/apps/dav/lib/Command/DeleteSubscription.php @@ -0,0 +1,79 @@ +addArgument( + 'uid', + InputArgument::REQUIRED, + 'User who owns the calendar subscription' + ) + ->addArgument( + 'uri', + InputArgument::REQUIRED, + 'URI of the calendar to be deleted' + ); + } + + protected function execute(InputInterface $input, OutputInterface $output): int { + $user = (string)$input->getArgument('uid'); + if (!$this->userManager->userExists($user)) { + throw new \InvalidArgumentException("User $user is unknown"); + } + + $uri = (string)$input->getArgument('uri'); + if ($uri === '') { + throw new \InvalidArgumentException('Specify the URI of the calendar to be deleted'); + } + + $subscriptionInfo = $this->calDavBackend->getSubscriptionByUri( + 'principals/users/' . $user, + $uri + ); + + if ($subscriptionInfo === null) { + throw new \InvalidArgumentException("User $user has no calendar subscription with the URI $uri"); + } + + $subscription = new CachedSubscription( + $this->calDavBackend, + $subscriptionInfo, + ); + + $subscription->delete(); + + $output->writeln("Calendar subscription with the URI $uri for user $user deleted"); + + return self::SUCCESS; + } +} diff --git a/apps/dav/lib/Command/ListSubscriptions.php b/apps/dav/lib/Command/ListSubscriptions.php new file mode 100644 index 00000000000..67753f25973 --- /dev/null +++ b/apps/dav/lib/Command/ListSubscriptions.php @@ -0,0 +1,77 @@ +addArgument( + 'uid', + InputArgument::REQUIRED, + 'User whose calendar subscriptions will be listed' + ); + } + + protected function execute(InputInterface $input, OutputInterface $output): int { + $user = (string)$input->getArgument('uid'); + if (!$this->userManager->userExists($user)) { + throw new \InvalidArgumentException("User $user is unknown"); + } + + $defaultRefreshRate = $this->appConfig->getValueString('dav', 'calendarSubscriptionRefreshRate', 'P1D'); + $subscriptions = $this->caldav->getSubscriptionsForUser("principals/users/$user"); + $rows = []; + + foreach ($subscriptions as $subscription) { + $rows[] = [ + $subscription['uri'], + $subscription['{DAV:}displayname'], + $subscription['{http://apple.com/ns/ical/}refreshrate'] ?? ($defaultRefreshRate . ' (default)'), + $subscription['source'], + ]; + } + + usort($rows, static fn (array $a, array $b) => $a[0] <=> $b[0]); + + if (count($rows) > 0) { + $table = new Table($output); + $table + ->setHeaders(['URI', 'Displayname', 'Refresh rate', 'Source']) + ->setRows($rows) + ->render(); + } else { + $output->writeln("User $user has no subscriptions"); + } + + return self::SUCCESS; + } +}