Sfoglia il codice sorgente

Calendar export and import

Signed-off-by: Christopher Ng <chrng8@gmail.com>
tags/v24.0.0beta1
Christopher Ng 2 anni fa
parent
commit
4eff359441

+ 2
- 0
apps/dav/appinfo/info.xml Vedi File

@@ -56,6 +56,8 @@
<command>OCA\DAV\Command\SyncBirthdayCalendar</command>
<command>OCA\DAV\Command\SyncSystemAddressBook</command>
<command>OCA\DAV\Command\RemoveInvalidShares</command>
<command>OCA\DAV\Command\ExportCalendars</command>
<command>OCA\DAV\Command\ImportCalendar</command>
</commands>

<settings>

+ 5
- 0
apps/dav/composer/composer/autoload_classmap.php Vedi File

@@ -124,6 +124,8 @@ return array(
'OCA\\DAV\\Command\\CreateAddressBook' => $baseDir . '/../lib/Command/CreateAddressBook.php',
'OCA\\DAV\\Command\\CreateCalendar' => $baseDir . '/../lib/Command/CreateCalendar.php',
'OCA\\DAV\\Command\\DeleteCalendar' => $baseDir . '/../lib/Command/DeleteCalendar.php',
'OCA\\DAV\\Command\\ExportCalendars' => $baseDir . '/../lib/Command/ExportCalendars.php',
'OCA\\DAV\\Command\\ImportCalendar' => $baseDir . '/../lib/Command/ImportCalendar.php',
'OCA\\DAV\\Command\\ListCalendars' => $baseDir . '/../lib/Command/ListCalendars.php',
'OCA\\DAV\\Command\\MoveCalendar' => $baseDir . '/../lib/Command/MoveCalendar.php',
'OCA\\DAV\\Command\\RemoveInvalidShares' => $baseDir . '/../lib/Command/RemoveInvalidShares.php',
@@ -299,4 +301,7 @@ return array(
'OCA\\DAV\\Upload\\UploadFile' => $baseDir . '/../lib/Upload/UploadFile.php',
'OCA\\DAV\\Upload\\UploadFolder' => $baseDir . '/../lib/Upload/UploadFolder.php',
'OCA\\DAV\\Upload\\UploadHome' => $baseDir . '/../lib/Upload/UploadHome.php',
'OCA\\DAV\\UserMigration\\CalendarMigrator' => $baseDir . '/../lib/UserMigration/CalendarMigrator.php',
'OCA\\DAV\\UserMigration\\CalendarMigratorException' => $baseDir . '/../lib/UserMigration/CalendarMigratorException.php',
'OCA\\DAV\\UserMigration\\InvalidCalendarException' => $baseDir . '/../lib/UserMigration/InvalidCalendarException.php',
);

+ 5
- 0
apps/dav/composer/composer/autoload_static.php Vedi File

@@ -139,6 +139,8 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Command\\CreateAddressBook' => __DIR__ . '/..' . '/../lib/Command/CreateAddressBook.php',
'OCA\\DAV\\Command\\CreateCalendar' => __DIR__ . '/..' . '/../lib/Command/CreateCalendar.php',
'OCA\\DAV\\Command\\DeleteCalendar' => __DIR__ . '/..' . '/../lib/Command/DeleteCalendar.php',
'OCA\\DAV\\Command\\ExportCalendars' => __DIR__ . '/..' . '/../lib/Command/ExportCalendars.php',
'OCA\\DAV\\Command\\ImportCalendar' => __DIR__ . '/..' . '/../lib/Command/ImportCalendar.php',
'OCA\\DAV\\Command\\ListCalendars' => __DIR__ . '/..' . '/../lib/Command/ListCalendars.php',
'OCA\\DAV\\Command\\MoveCalendar' => __DIR__ . '/..' . '/../lib/Command/MoveCalendar.php',
'OCA\\DAV\\Command\\RemoveInvalidShares' => __DIR__ . '/..' . '/../lib/Command/RemoveInvalidShares.php',
@@ -314,6 +316,9 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Upload\\UploadFile' => __DIR__ . '/..' . '/../lib/Upload/UploadFile.php',
'OCA\\DAV\\Upload\\UploadFolder' => __DIR__ . '/..' . '/../lib/Upload/UploadFolder.php',
'OCA\\DAV\\Upload\\UploadHome' => __DIR__ . '/..' . '/../lib/Upload/UploadHome.php',
'OCA\\DAV\\UserMigration\\CalendarMigrator' => __DIR__ . '/..' . '/../lib/UserMigration/CalendarMigrator.php',
'OCA\\DAV\\UserMigration\\CalendarMigratorException' => __DIR__ . '/..' . '/../lib/UserMigration/CalendarMigratorException.php',
'OCA\\DAV\\UserMigration\\InvalidCalendarException' => __DIR__ . '/..' . '/../lib/UserMigration/InvalidCalendarException.php',
);

public static function getInitializer(ClassLoader $loader)

+ 7
- 0
apps/dav/lib/CalDAV/CalendarImpl.php Vedi File

@@ -69,6 +69,13 @@ class CalendarImpl implements ICreateFromString {
return $this->calendarInfo['id'];
}

/**
* {@inheritDoc}
*/
public function getUri() {
return $this->calendarInfo['uri'];
}

/**
* In comparison to getKey() this function returns a human readable (maybe translated) name
* @return null|string

+ 83
- 0
apps/dav/lib/Command/ExportCalendars.php Vedi File

@@ -0,0 +1,83 @@
<?php

declare(strict_types=1);

/**
* @copyright 2022 Christopher Ng <chrng8@gmail.com>
*
* @author Christopher Ng <chrng8@gmail.com>
*
* @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\Command;

use OC\Core\Command\Base;
use OCA\DAV\UserMigration\CalendarMigrator;
use OCA\DAV\UserMigration\CalendarMigratorException;
use OCP\IUser;
use OCP\IUserManager;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class ExportCalendars extends Base {

/** @var IUserManager */
private $userManager;

/** @var CalendarMigrator */
private $calendarMigrator;

public function __construct(
IUserManager $userManager,
CalendarMigrator $calendarMigrator
) {
parent::__construct();
$this->userManager = $userManager;
$this->calendarMigrator = $calendarMigrator;
}

protected function configure() {
$this
->setName('dav:export-calendars')
->setDescription('Export the calendars of a user')
->addArgument(
'user',
InputArgument::REQUIRED,
'User to export',
);
}

protected function execute(InputInterface $input, OutputInterface $output): int {
$user = $this->userManager->get($input->getArgument('user'));

if (!$user instanceof IUser) {
$output->writeln('<error>User ' . $input->getArgument('user') . ' does not exist</error>');
return 1;
}

try {
$this->calendarMigrator->export($user, $output);
} catch (CalendarMigratorException $e) {
$output->writeln('<error>' . $e->getMessage() . '</error>');
return $e->getCode() !== 0 ? (int)$e->getCode() : 1;
}

return 0;
}
}

+ 94
- 0
apps/dav/lib/Command/ImportCalendar.php Vedi File

@@ -0,0 +1,94 @@
<?php

declare(strict_types=1);

/**
* @copyright 2022 Christopher Ng <chrng8@gmail.com>
*
* @author Christopher Ng <chrng8@gmail.com>
*
* @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\Command;

use OC\Core\Command\Base;
use OCA\DAV\UserMigration\CalendarMigrator;
use OCA\DAV\UserMigration\CalendarMigratorException;
use OCP\IUser;
use OCP\IUserManager;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class ImportCalendar extends Base {

/** @var IUserManager */
private $userManager;

/** @var CalendarMigrator */
private $calendarMigrator;

public function __construct(
IUserManager $userManager,
CalendarMigrator $calendarMigrator
) {
parent::__construct();
$this->userManager = $userManager;
$this->calendarMigrator = $calendarMigrator;
}

protected function configure() {
$this
->setName('dav:import-calendar')
->setDescription('Import a calendar to a user\'s account')
->addArgument(
'user',
InputArgument::REQUIRED,
'User to import the calendar for',
)
->addArgument(
'path',
InputArgument::REQUIRED,
'Path to the *.ics file',
);
}

protected function execute(InputInterface $input, OutputInterface $output): int {
$user = $this->userManager->get($input->getArgument('user'));

[
'basename' => $filename,
'dirname' => $srcDir,
] = pathinfo($input->getArgument('path'));


if (!$user instanceof IUser) {
$output->writeln('<error>User ' . $input->getArgument('user') . ' does not exist</error>');
return 1;
}

try {
$this->calendarMigrator->import($user, $srcDir, $filename, $output);
} catch (CalendarMigratorException $e) {
$output->writeln('<error>' . $e->getMessage() . '</error>');
return $e->getCode() !== 0 ? (int)$e->getCode() : 1;
}

return 0;
}
}

+ 457
- 0
apps/dav/lib/UserMigration/CalendarMigrator.php Vedi File

@@ -0,0 +1,457 @@
<?php

declare(strict_types=1);

/**
* @copyright 2022 Christopher Ng <chrng8@gmail.com>
*
* @author Christopher Ng <chrng8@gmail.com>
*
* @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\UserMigration;

use function Safe\fopen;
use function Safe\substr;
use OC\Files\Filesystem;
use OC\Files\View;
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CalDAV\ICSExportPlugin\ICSExportPlugin;
use OCA\DAV\CalDAV\Plugin as CalDAVPlugin;
use OCA\DAV\Connector\Sabre\CachingTree;
use OCA\DAV\Connector\Sabre\Server as SabreDavServer;
use OCA\DAV\RootCollection;
use OCP\Calendar\ICalendar;
use OCP\Calendar\IManager as ICalendarManager;
use OCP\Defaults;
use OCP\IL10N;
use OCP\IUser;
use Sabre\DAV\Exception\BadRequest;
use Sabre\DAV\Version as SabreDavVersion;
use Sabre\VObject\Component as VObjectComponent;
use Sabre\VObject\Component\VCalendar;
use Sabre\VObject\Component\VTimeZone;
use Sabre\VObject\Property\ICalendar\DateTime;
use Sabre\VObject\Reader as VObjectReader;
use Sabre\VObject\UUIDUtil;
use Safe\Exceptions\FilesystemException;
use Symfony\Component\Console\Output\OutputInterface;

class CalendarMigrator {

private CalDavBackend $calDavBackend;

private ICalendarManager $calendarManager;

// ICSExportPlugin is injected as the mergeObjects() method is required and is not to be used as a SabreDAV server plugin
private ICSExportPlugin $icsExportPlugin;

private Defaults $defaults;

private IL10N $l10n;

private SabreDavServer $sabreDavServer;

public const USERS_URI_ROOT = 'principals/users/';

public const FILENAME_EXT = '.ics';

public const MIGRATED_URI_PREFIX = 'migrated-';

public function __construct(
CalDavBackend $calDavBackend,
ICalendarManager $calendarManager,
ICSExportPlugin $icsExportPlugin,
Defaults $defaults,
IL10N $l10n
) {
$this->calDavBackend = $calDavBackend;
$this->calendarManager = $calendarManager;
$this->icsExportPlugin = $icsExportPlugin;
$this->defaults = $defaults;
$this->l10n = $l10n;

$root = new RootCollection();
$this->sabreDavServer = new SabreDavServer(new CachingTree($root));
$this->sabreDavServer->addPlugin(new CalDAVPlugin());
}

public function getPrincipalUri(IUser $user): string {
return CalendarMigrator::USERS_URI_ROOT . $user->getUID();
}

/**
* @return array{name: string, vCalendar: VCalendar}
*
* @throws CalendarMigratorException
* @throws InvalidCalendarException
*/
public function getCalendarExportData(IUser $user, ICalendar $calendar): array {
$userId = $user->getUID();
$calendarId = $calendar->getKey();
$calendarInfo = $this->calDavBackend->getCalendarById($calendarId);

if (!empty($calendarInfo)) {
$uri = $calendarInfo['uri'];
$path = CalDAVPlugin::CALENDAR_ROOT . "/$userId/$uri";

// NOTE implementation below based on \Sabre\CalDAV\ICSExportPlugin::httpGet()

$properties = $this->sabreDavServer->getProperties($path, [
'{DAV:}resourcetype',
'{DAV:}displayname',
'{http://sabredav.org/ns}sync-token',
'{DAV:}sync-token',
'{http://apple.com/ns/ical/}calendar-color',
]);

// Filter out invalid (e.g. deleted) calendars
if (!isset($properties['{DAV:}resourcetype']) || !$properties['{DAV:}resourcetype']->is('{' . CalDAVPlugin::NS_CALDAV . '}calendar')) {
throw new InvalidCalendarException();
}

// NOTE implementation below based on \Sabre\CalDAV\ICSExportPlugin::generateResponse()

$calDataProp = '{' . CalDAVPlugin::NS_CALDAV . '}calendar-data';
$calendarNode = $this->sabreDavServer->tree->getNodeForPath($path);
$nodes = $this->sabreDavServer->getPropertiesIteratorForPath($path, [$calDataProp], 1);

$blobs = [];
foreach ($nodes as $node) {
if (isset($node[200][$calDataProp])) {
$blobs[$node['href']] = $node[200][$calDataProp];
}
}

$mergedCalendar = $this->icsExportPlugin->mergeObjects(
$properties,
$blobs,
);

return [
'name' => $calendarNode->getName(),
'vCalendar' => $mergedCalendar,
];
}

throw new CalendarMigratorException();
}

/**
* @return array<int, array{name: string, vCalendar: VCalendar}>
*
* @throws CalendarMigratorException
*/
public function getCalendarExports(IUser $user): array {
$principalUri = $this->getPrincipalUri($user);

return array_values(array_filter(array_map(
function (ICalendar $calendar) use ($user) {
try {
return $this->getCalendarExportData($user, $calendar);
} catch (CalendarMigratorException $e) {
throw new CalendarMigratorException();
} catch (InvalidCalendarException $e) {
// Allow this exception as invalid (e.g. deleted) calendars are not to be exported
}
},
$this->calendarManager->getCalendarsForPrincipal($principalUri),
)));
}

public function getUniqueCalendarUri(IUser $user, string $initialCalendarUri): string {
$principalUri = $this->getPrincipalUri($user);
$initialCalendarUri = substr($initialCalendarUri, 0, strlen(CalendarMigrator::MIGRATED_URI_PREFIX)) === CalendarMigrator::MIGRATED_URI_PREFIX
? $initialCalendarUri
: CalendarMigrator::MIGRATED_URI_PREFIX . $initialCalendarUri;

$existingCalendarUris = array_map(
fn (ICalendar $calendar) => $calendar->getUri(),
$this->calendarManager->getCalendarsForPrincipal($principalUri),
);

$calendarUri = $initialCalendarUri;
$acc = 1;
while (in_array($calendarUri, $existingCalendarUris, true)) {
$calendarUri = $initialCalendarUri . "-$acc";
++$acc;
}

return $calendarUri;
}

/**
* @throws CalendarMigratorException
*/
protected function writeExport(IUser $user, string $data, string $destDir, string $filename, OutputInterface $output): void {
$userId = $user->getUID();

\OC::$server->getUserFolder($userId);
Filesystem::initMountPoints($userId);

$view = new View();

if ($view->file_put_contents("$destDir/$filename", $data) === false) {
throw new CalendarMigratorException('Could not export calendar');
}

$output->writeln("<info>✅ Exported calendar of <$userId> into $destDir/$filename</info>");
}

public function export(IUser $user, OutputInterface $output): void {
$userId = $user->getUID();

try {
$calendarExports = $this->getCalendarExports($user);
} catch (CalendarMigratorException $e) {
$output->writeln("<error>Error exporting <$userId> calendars</error>");
}

if (empty($calendarExports)) {
$output->writeln("<info>User <$userId> has no calendars to export</info>");
}

/**
* @var string $name
* @var VCalendar $vCalendar
*/
foreach ($calendarExports as ['name' => $name, 'vCalendar' => $vCalendar]) {
// Set filename to sanitized calendar name appended with the date
$filename = preg_replace('/[^a-zA-Z0-9-_ ]/um', '', $name) . '_' . date('Y-m-d') . CalendarMigrator::FILENAME_EXT;

$this->writeExport(
$user,
$vCalendar->serialize(),
// TESTING directory does not automatically get created so just write to user directory, this will be put in a zip with all other user_migration data
// "/$userId/export/$appId",
"/$userId",
$filename,
$output,
);
}
}

/**
* Return an associative array mapping Time Zone ID to VTimeZone component
*
* @return array<string, VTimeZone>
*/
public function getCalendarTimezones(VCalendar $vCalendar): array {
/** @var VTimeZone[] $calendarTimezones */
$calendarTimezones = array_values(array_filter(
$vCalendar->getComponents(),
fn ($component) => $component->name === 'VTIMEZONE',
));

/** @var array<string, VTimeZone> $calendarTimezoneMap */
$calendarTimezoneMap = [];
foreach ($calendarTimezones as $vTimeZone) {
$calendarTimezoneMap[$vTimeZone->getTimeZone()->getName()] = $vTimeZone;
}

return $calendarTimezoneMap;
}

/**
* @return VTimeZone[]
*/
public function getTimezonesForComponent(VCalendar $vCalendar, VObjectComponent $component): array {
$componentTimezoneIds = [];

foreach ($component->children() as $child) {
if ($child instanceof DateTime && isset($child->parameters['TZID'])) {
$timezoneId = $child->parameters['TZID']->getValue();
if (!in_array($timezoneId, $componentTimezoneIds, true)) {
$componentTimezoneIds[] = $timezoneId;
}
}
}

$calendarTimezoneMap = $this->getCalendarTimezones($vCalendar);

return array_values(array_filter(array_map(
fn (string $timezoneId) => $calendarTimezoneMap[$timezoneId],
$componentTimezoneIds,
)));
}

public function sanitizeComponent(VObjectComponent $component): VObjectComponent {
// Operate on the component clone to prevent mutation of the original
$componentClone = clone $component;

// Remove RSVP parameters to prevent automatically sending invitation emails to attendees on import
foreach ($componentClone->children() as $child) {
if (
$child->name === 'ATTENDEE'
&& isset($child->parameters['RSVP'])
) {
unset($child->parameters['RSVP']);
}
}

return $componentClone;
}

/**
* @return VObjectComponent[]
*/
public function getRequiredImportComponents(VCalendar $vCalendar, VObjectComponent $component): array {
$component = $this->sanitizeComponent($component);
/** @var array<int, VTimeZone> $timezoneComponents */
$timezoneComponents = $this->getTimezonesForComponent($vCalendar, $component);
return [
...$timezoneComponents,
$component,
];
}

public function initCalendarObject(): VCalendar {
$vCalendarObject = new VCalendar();
$vCalendarObject->PRODID = $this->sabreDavServer::$exposeVersion
? '-//SabreDAV//SabreDAV ' . SabreDavVersion::VERSION . '//EN'
: '-//SabreDAV//SabreDAV//EN';
return $vCalendarObject;
}

public function importCalendarObject(int $calendarId, VCalendar $vCalendarObject): void {
try {
$this->calDavBackend->createCalendarObject(
$calendarId,
UUIDUtil::getUUID() . CalendarMigrator::FILENAME_EXT,
$vCalendarObject->serialize(),
CalDavBackend::CALENDAR_TYPE_CALENDAR,
);
} catch (BadRequest $e) {
// Rollback creation of calendar on error
$this->calDavBackend->deleteCalendar($calendarId, true);
}
}

/**
* @throws CalendarMigratorException
*/
public function importCalendar(IUser $user, string $filename, string $initialCalendarUri, VCalendar $vCalendar): void {
$principalUri = $this->getPrincipalUri($user);
$calendarUri = $this->getUniqueCalendarUri($user, $initialCalendarUri);

$calendarId = $this->calDavBackend->createCalendar($principalUri, $calendarUri, [
'{DAV:}displayname' => isset($vCalendar->{'X-WR-CALNAME'}) ? $vCalendar->{'X-WR-CALNAME'}->getValue() : $this->l10n->t('Migrated calendar (%1$s)', [$filename]),
'{http://apple.com/ns/ical/}calendar-color' => isset($vCalendar->{'X-APPLE-CALENDAR-COLOR'}) ? $vCalendar->{'X-APPLE-CALENDAR-COLOR'}->getValue() : $this->defaults->getColorPrimary(),
'components' => implode(
',',
array_reduce(
$vCalendar->getComponents(),
function (array $componentNames, VObjectComponent $component) {
/** @var array<int, string> $componentNames */
return !in_array($component->name, $componentNames, true)
? [...$componentNames, $component->name]
: $componentNames;
},
[],
)
),
]);

/** @var VObjectComponent[] $calendarComponents */
$calendarComponents = array_values(array_filter(
$vCalendar->getComponents(),
// VTIMEZONE components are handled separately and added to the calendar object only if depended on by the component
fn (VObjectComponent $component) => $component->name !== 'VTIMEZONE',
));

/** @var array<string, VObjectComponent[]> $groupedCalendarComponents */
$groupedCalendarComponents = [];
/** @var VObjectComponent[] $ungroupedCalendarComponents */
$ungroupedCalendarComponents = [];

foreach ($calendarComponents as $component) {
if (isset($component->UID)) {
$uid = $component->UID->getValue();
// Components with the same UID (e.g. recurring events) are grouped together into a single calendar object
if (isset($groupedCalendarComponents[$uid])) {
$groupedCalendarComponents[$uid][] = $component;
} else {
$groupedCalendarComponents[$uid] = [$component];
}
} else {
$ungroupedCalendarComponents[] = $component;
}
}

foreach ($groupedCalendarComponents as $uid => $components) {
// Construct and import a calendar object containing all components of a group
$vCalendarObject = $this->initCalendarObject();
foreach ($components as $component) {
foreach ($this->getRequiredImportComponents($vCalendar, $component) as $component) {
$vCalendarObject->add($component);
}
}
$this->importCalendarObject($calendarId, $vCalendarObject);
}

foreach ($ungroupedCalendarComponents as $component) {
// Construct and import a calendar object for a single component
$vCalendarObject = $this->initCalendarObject();
foreach ($this->getRequiredImportComponents($vCalendar, $component) as $component) {
$vCalendarObject->add($component);
}
$this->importCalendarObject($calendarId, $vCalendarObject);
}
}

/**
* @throws FilesystemException
* @throws CalendarMigratorException
*/
public function import(IUser $user, string $srcDir, string $filename, OutputInterface $output): void {
$userId = $user->getUID();

try {
/** @var VCalendar $vCalendar */
$vCalendar = VObjectReader::read(
fopen("$srcDir/$filename", 'r'),
VObjectReader::OPTION_FORGIVING,
);
} catch (FilesystemException $e) {
throw new FilesystemException("Failed to read file: \"$srcDir/$filename\"");
}

$problems = $vCalendar->validate();
if (empty($problems)) {
$splitFilename = explode('_', $filename, 2);
if (count($splitFilename) !== 2) {
$output->writeln("<error>Invalid filename, filename must be of the format: \"<calendar_name>_YYYY-MM-DD" . CalendarMigrator::FILENAME_EXT . "\"</error>");
throw new CalendarMigratorException();
}
[$initialCalendarUri, $suffix] = $splitFilename;

$this->importCalendar(
$user,
$filename,
$initialCalendarUri,
$vCalendar,
);

$vCalendar->destroy();

$output->writeln("<info>✅ Imported calendar \"$filename\" into account of <$userId></info>");
} else {
throw new CalendarMigratorException("Invalid data contained in \"$srcDir/$filename\"");
}
}
}

+ 32
- 0
apps/dav/lib/UserMigration/CalendarMigratorException.php Vedi File

@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

/**
* @copyright 2022 Christopher Ng <chrng8@gmail.com>
*
* @author Christopher Ng <chrng8@gmail.com>
*
* @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\UserMigration;

use Exception;

class CalendarMigratorException extends Exception {
}

+ 32
- 0
apps/dav/lib/UserMigration/InvalidCalendarException.php Vedi File

@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

/**
* @copyright 2022 Christopher Ng <chrng8@gmail.com>
*
* @author Christopher Ng <chrng8@gmail.com>
*
* @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\UserMigration;

use Exception;

class InvalidCalendarException extends Exception {
}

+ 5
- 0
lib/public/Calendar/ICalendar.php Vedi File

@@ -39,6 +39,11 @@ interface ICalendar {
*/
public function getKey();

/**
* @since 24.0.0
*/
public function getUri();

/**
* In comparison to getKey() this function returns a human readable (maybe translated) name
* @return null|string

Loading…
Annulla
Salva