diff --git a/apps/dav/lib/CalDAV/CalDavBackend.php b/apps/dav/lib/CalDAV/CalDavBackend.php index a55c8987be..20ae236700 100644 --- a/apps/dav/lib/CalDAV/CalDavBackend.php +++ b/apps/dav/lib/CalDAV/CalDavBackend.php @@ -2728,11 +2728,13 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription * @param string $uriName * @param string $uriOrigin * @param string $uriDestination + * @param string $newUriName (optional) the new uriName */ - public function moveCalendar($uriName, $uriOrigin, $uriDestination) { + public function moveCalendar($uriName, $uriOrigin, $uriDestination, $newUriName = null) { $query = $this->db->getQueryBuilder(); $query->update('calendars') ->set('principaluri', $query->createNamedParameter($uriDestination)) + ->set('uri', $query->createNamedParameter($newUriName ?: $uriName)) ->where($query->expr()->eq('principaluri', $query->createNamedParameter($uriOrigin))) ->andWhere($query->expr()->eq('uri', $query->createNamedParameter($uriName))) ->execute(); diff --git a/apps/dav/lib/Command/MoveCalendar.php b/apps/dav/lib/Command/MoveCalendar.php index 04786e6478..00bf596683 100644 --- a/apps/dav/lib/Command/MoveCalendar.php +++ b/apps/dav/lib/Command/MoveCalendar.php @@ -104,7 +104,7 @@ class MoveCalendar extends Command { ->addArgument('destinationuid', InputArgument::REQUIRED, 'User who will receive the calendar') - ->addOption('force', 'f', InputOption::VALUE_NONE, "Force the migration by removing existing shares"); + ->addOption('force', 'f', InputOption::VALUE_NONE, "Force the migration by removing existing shares and renaming calendars in case of conflicts"); } protected function execute(InputInterface $input, OutputInterface $output): int { @@ -122,6 +122,7 @@ class MoveCalendar extends Command { } $name = $input->getArgument('name'); + $newName = null; $calendar = $this->calDav->getCalendarByUri(self::URI_USERS . $userOrigin, $name); @@ -129,18 +130,74 @@ class MoveCalendar extends Command { throw new \InvalidArgumentException("User <$userOrigin> has no calendar named <$name>. You can run occ dav:list-calendars to list calendars URIs for this user."); } - if (null !== $this->calDav->getCalendarByUri(self::URI_USERS . $userDestination, $name)) { - throw new \InvalidArgumentException("User <$userDestination> already has a calendar named <$name>."); + // Calendar already exists + if ($this->calendarExists($userDestination, $name)) { + if ($input->getOption('force')) { + // Try to find a suitable name + $newName = $this->getNewCalendarName($userDestination, $name); + + // If we didn't find a suitable value after all the iterations, give up + if ($this->calendarExists($userDestination, $newName)) { + throw new \InvalidArgumentException("Unable to find a suitable calendar name for <$userDestination> with initial name <$name>."); + } + } else { + throw new \InvalidArgumentException("User <$userDestination> already has a calendar named <$name>."); + } } - $this->checkShares($calendar, $userOrigin, $userDestination, $input->getOption('force')); + $hadShares = $this->checkShares($calendar, $userOrigin, $userDestination, $input->getOption('force')); + if ($hadShares) { + /** + * Warn that share links have changed if there are shares + */ + $this->io->note([ + "Please note that moving calendar " . $calendar['uri'] . " from user <$userOrigin> to <$userDestination> has caused share links to change.", + "Sharees will need to change \"example.com/remote.php/dav/calendars/uid/" . $calendar['uri'] . "_shared_by_$userOrigin\" to \"example.com/remote.php/dav/calendars/uid/" . $newName ?: $calendar['uri'] . "_shared_by_$userDestination\"" + ]); + } - $this->calDav->moveCalendar($name, self::URI_USERS . $userOrigin, self::URI_USERS . $userDestination); + $this->calDav->moveCalendar($name, self::URI_USERS . $userOrigin, self::URI_USERS . $userDestination, $newName); - $this->io->success("Calendar <$name> was moved from user <$userOrigin> to <$userDestination>"); + $this->io->success("Calendar <$name> was moved from user <$userOrigin> to <$userDestination>" . ($newName ? " as <$newName>" : '')); return 0; } + /** + * Check if the calendar exists for user + * + * @param string $userDestination + * @param string $name + * @return bool + */ + protected function calendarExists(string $userDestination, string $name): bool { + return null !== $this->calDav->getCalendarByUri(self::URI_USERS . $userDestination, $name); + } + + /** + * Try to find a suitable new calendar name that + * doesn't exists for the provided user + * + * @param string $userDestination + * @param string $name + * @return string + */ + protected function getNewCalendarName(string $userDestination, string $name): string { + $increment = 1; + $newName = $name . '-' . $increment; + while ($increment <= 10) { + $this->io->writeln("Trying calendar name <$newName>", OutputInterface::VERBOSITY_VERBOSE); + if (!$this->calendarExists($userDestination, $newName)) { + // New name is good to go + $this->io->writeln("Found proper new calendar name <$newName>", OutputInterface::VERBOSITY_VERBOSE); + break; + } + $newName = $name . '-' . $increment; + $increment++; + } + + return $newName; + } + /** * Check that moving the calendar won't break shares * @@ -148,8 +205,10 @@ class MoveCalendar extends Command { * @param string $userOrigin * @param string $userDestination * @param bool $force + * @return bool had any shares or not + * @throws \InvalidArgumentException */ - private function checkShares(array $calendar, string $userOrigin, string $userDestination, bool $force = false) { + private function checkShares(array $calendar, string $userOrigin, string $userDestination, bool $force = false): bool { $shares = $this->calDav->getShares($calendar['id']); foreach ($shares as $share) { list(, $prefix, $userOrGroup) = explode('/', $share['href'], 3); @@ -177,14 +236,7 @@ class MoveCalendar extends Command { } } } - /** - * Warn that share links have changed if there are shares - */ - if (count($shares) > 0) { - $this->io->note([ - "Please note that moving calendar " . $calendar['uri'] . " from user <$userOrigin> to <$userDestination> has caused share links to change.", - "Sharees will need to change \"example.com/remote.php/dav/calendars/uid/" . $calendar['uri'] . "_shared_by_$userOrigin\" to \"example.com/remote.php/dav/calendars/uid/" . $calendar['uri'] . "_shared_by_$userDestination\"" - ]); - } + + return count($shares) > 0; } }