diff options
Diffstat (limited to 'apps/dav/lib/CalDAV/Export/ExportService.php')
-rw-r--r-- | apps/dav/lib/CalDAV/Export/ExportService.php | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/apps/dav/lib/CalDAV/Export/ExportService.php b/apps/dav/lib/CalDAV/Export/ExportService.php new file mode 100644 index 00000000000..552b9e2b675 --- /dev/null +++ b/apps/dav/lib/CalDAV/Export/ExportService.php @@ -0,0 +1,107 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OCA\DAV\CalDAV\Export; + +use Generator; +use OCP\Calendar\CalendarExportOptions; +use OCP\Calendar\ICalendarExport; +use OCP\ServerVersion; +use Sabre\VObject\Component; +use Sabre\VObject\Writer; + +/** + * Calendar Export Service + */ +class ExportService { + + public const FORMATS = ['ical', 'jcal', 'xcal']; + private string $systemVersion; + + public function __construct(ServerVersion $serverVersion) { + $this->systemVersion = $serverVersion->getVersionString(); + } + + /** + * Generates serialized content stream for a calendar and objects based in selected format + * + * @return Generator<string> + */ + public function export(ICalendarExport $calendar, CalendarExportOptions $options): Generator { + // output start of serialized content based on selected format + yield $this->exportStart($options->getFormat()); + // iterate through each returned vCalendar entry + // extract each component except timezones, convert to appropriate format and output + // extract any timezones and save them but do not output + $timezones = []; + foreach ($calendar->export($options) as $entry) { + $consecutive = false; + foreach ($entry->getComponents() as $vComponent) { + if ($vComponent->name === 'VTIMEZONE') { + if (isset($vComponent->TZID) && !isset($timezones[$vComponent->TZID->getValue()])) { + $timezones[$vComponent->TZID->getValue()] = clone $vComponent; + } + } else { + yield $this->exportObject($vComponent, $options->getFormat(), $consecutive); + $consecutive = true; + } + } + } + // iterate through each saved vTimezone entry, convert to appropriate format and output + foreach ($timezones as $vComponent) { + yield $this->exportObject($vComponent, $options->getFormat(), $consecutive); + $consecutive = true; + } + // output end of serialized content based on selected format + yield $this->exportFinish($options->getFormat()); + } + + /** + * Generates serialized content start based on selected format + */ + private function exportStart(string $format): string { + return match ($format) { + 'jcal' => '["vcalendar",[["version",{},"text","2.0"],["prodid",{},"text","-\/\/IDN nextcloud.com\/\/Calendar Export v' . $this->systemVersion . '\/\/EN"]],[', + 'xcal' => '<?xml version="1.0" encoding="UTF-8"?><icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"><vcalendar><properties><version><text>2.0</text></version><prodid><text>-//IDN nextcloud.com//Calendar Export v' . $this->systemVersion . '//EN</text></prodid></properties><components>', + default => "BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:-//IDN nextcloud.com//Calendar Export v" . $this->systemVersion . "//EN\n" + }; + } + + /** + * Generates serialized content end based on selected format + */ + private function exportFinish(string $format): string { + return match ($format) { + 'jcal' => ']]', + 'xcal' => '</components></vcalendar></icalendar>', + default => "END:VCALENDAR\n" + }; + } + + /** + * Generates serialized content for a component based on selected format + */ + private function exportObject(Component $vobject, string $format, bool $consecutive): string { + return match ($format) { + 'jcal' => $consecutive ? ',' . Writer::writeJson($vobject) : Writer::writeJson($vobject), + 'xcal' => $this->exportObjectXml($vobject), + default => Writer::write($vobject) + }; + } + + /** + * Generates serialized content for a component in xml format + */ + private function exportObjectXml(Component $vobject): string { + $writer = new \Sabre\Xml\Writer(); + $writer->openMemory(); + $writer->setIndent(false); + $vobject->xmlSerialize($writer); + return $writer->outputMemory(); + } + +} |