From b0ee6146a1c560a9513146dcd4835810153f5417 Mon Sep 17 00:00:00 2001 From: SebastianKrupinski Date: Mon, 30 Dec 2024 15:37:47 -0500 Subject: [PATCH] feat: OCC and OCS Calendar Import/Export Signed-off-by: SebastianKrupinski --- apps/dav/lib/CalDAV/CalDavBackend.php | 25 ++++++++ apps/dav/lib/CalDAV/CalendarImpl.php | 27 ++++++++- lib/composer/composer/autoload_classmap.php | 3 + lib/composer/composer/autoload_static.php | 3 + .../Http/StreamGeneratorResponse.php | 57 +++++++++++++++++++ lib/public/Calendar/CalendarExportRange.php | 21 +++++++ lib/public/Calendar/ICalendarExport.php | 29 ++++++++++ 7 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 lib/public/AppFramework/Http/StreamGeneratorResponse.php create mode 100644 lib/public/Calendar/CalendarExportRange.php create mode 100644 lib/public/Calendar/ICalendarExport.php diff --git a/apps/dav/lib/CalDAV/CalDavBackend.php b/apps/dav/lib/CalDAV/CalDavBackend.php index 0c8b52a7491c7..3fead4bbd507b 100644 --- a/apps/dav/lib/CalDAV/CalDavBackend.php +++ b/apps/dav/lib/CalDAV/CalDavBackend.php @@ -9,6 +9,7 @@ use DateTime; use DateTimeImmutable; use DateTimeInterface; +use Generator; use OCA\DAV\AppInfo\Application; use OCA\DAV\CalDAV\Sharing\Backend; use OCA\DAV\Connector\Sabre\Principal; @@ -34,6 +35,7 @@ use OCA\DAV\Events\SubscriptionDeletedEvent; use OCA\DAV\Events\SubscriptionUpdatedEvent; use OCP\AppFramework\Db\TTransactional; +use OCP\Calendar\CalendarExportRange; use OCP\Calendar\Exceptions\CalendarException; use OCP\DB\Exception; use OCP\DB\QueryBuilder\IQueryBuilder; @@ -950,6 +952,29 @@ public function restoreCalendar(int $id): void { }, $this->db); } + + public function exportCalendar($calendarId, $calendarType = self::CALENDAR_TYPE_CALENDAR, ?CalendarExportRange $range = null): Generator { + $qb = $this->db->getQueryBuilder(); + $qb->select('*') + ->from('calendarobjects') + ->where($qb->expr()->eq('calendarid', $qb->createNamedParameter($calendarId))) + ->andWhere($qb->expr()->eq('calendartype', $qb->createNamedParameter($calendarType))) + ->andWhere($qb->expr()->isNull('deleted_at')); + if ($range?->start !== null) { + $qb->setFirstResult($range->start); + } + if ($range?->count !== null) { + $qb->setMaxResults($range->count); + } + $rs = $qb->executeQuery(); + + while (($row = $rs->fetch()) !== false) { + yield $row; + } + + $rs->closeCursor(); + } + /** * Returns all calendar objects with limited metadata for a calendar * diff --git a/apps/dav/lib/CalDAV/CalendarImpl.php b/apps/dav/lib/CalDAV/CalendarImpl.php index b3062f005ee27..76ddd92eb82c5 100644 --- a/apps/dav/lib/CalDAV/CalendarImpl.php +++ b/apps/dav/lib/CalDAV/CalendarImpl.php @@ -8,9 +8,14 @@ */ namespace OCA\DAV\CalDAV; +use Generator; use OCA\DAV\CalDAV\Auth\CustomPrincipalPlugin; use OCA\DAV\CalDAV\InvitationResponse\InvitationResponseServer; +use OCP\Calendar\CalendarExportRange; use OCP\Calendar\Exceptions\CalendarException; +use OCP\Calendar\ICalendarExport; +use OCP\Calendar\ICalendarIsShared; +use OCP\Calendar\ICalendarIsWritable; use OCP\Calendar\ICreateFromString; use OCP\Calendar\IHandleImipMessage; use OCP\Constants; @@ -24,7 +29,7 @@ use Sabre\VObject\Reader; use function Sabre\Uri\split as uriSplit; -class CalendarImpl implements ICreateFromString, IHandleImipMessage { +class CalendarImpl implements ICreateFromString, IHandleImipMessage, ICalendarIsWritable, ICalendarIsShared, ICalendarExport { public function __construct( private Calendar $calendar, /** @var array */ @@ -257,4 +262,24 @@ public function handleIMipMessage(string $name, string $calendarData): void { public function getInvitationResponseServer(): InvitationResponseServer { return new InvitationResponseServer(false); } + + /** + * Export objects + * + * @since 31.0.0 + * + * @return Generator<\Sabre\VObject\Component\VCalendar> + */ + public function export(?CalendarExportRange $range = null): Generator { + foreach ( + $this->backend->exportCalendar( + $this->calendarInfo['id'], + $this->backend::CALENDAR_TYPE_CALENDAR, + $range + ) as $event + ) { + yield Reader::read($event['calendardata']); + } + } + } diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index fd5d9e62ba63e..3192286c75163 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -110,6 +110,7 @@ 'OCP\\AppFramework\\Http\\RedirectToDefaultAppResponse' => $baseDir . '/lib/public/AppFramework/Http/RedirectToDefaultAppResponse.php', 'OCP\\AppFramework\\Http\\Response' => $baseDir . '/lib/public/AppFramework/Http/Response.php', 'OCP\\AppFramework\\Http\\StandaloneTemplateResponse' => $baseDir . '/lib/public/AppFramework/Http/StandaloneTemplateResponse.php', + 'OCP\\AppFramework\\Http\\StreamGeneratorResponse' => $baseDir . '/lib/public/AppFramework/Http/StreamGeneratorResponse.php', 'OCP\\AppFramework\\Http\\StreamResponse' => $baseDir . '/lib/public/AppFramework/Http/StreamResponse.php', 'OCP\\AppFramework\\Http\\StrictContentSecurityPolicy' => $baseDir . '/lib/public/AppFramework/Http/StrictContentSecurityPolicy.php', 'OCP\\AppFramework\\Http\\StrictEvalContentSecurityPolicy' => $baseDir . '/lib/public/AppFramework/Http/StrictEvalContentSecurityPolicy.php', @@ -190,8 +191,10 @@ 'OCP\\Broadcast\\Events\\IBroadcastEvent' => $baseDir . '/lib/public/Broadcast/Events/IBroadcastEvent.php', 'OCP\\Cache\\CappedMemoryCache' => $baseDir . '/lib/public/Cache/CappedMemoryCache.php', 'OCP\\Calendar\\BackendTemporarilyUnavailableException' => $baseDir . '/lib/public/Calendar/BackendTemporarilyUnavailableException.php', + 'OCP\\Calendar\\CalendarExportRange' => $baseDir . '/lib/public/Calendar/CalendarExportRange.php', 'OCP\\Calendar\\Exceptions\\CalendarException' => $baseDir . '/lib/public/Calendar/Exceptions/CalendarException.php', 'OCP\\Calendar\\ICalendar' => $baseDir . '/lib/public/Calendar/ICalendar.php', + 'OCP\\Calendar\\ICalendarExport' => $baseDir . '/lib/public/Calendar/ICalendarExport.php', 'OCP\\Calendar\\ICalendarIsShared' => $baseDir . '/lib/public/Calendar/ICalendarIsShared.php', 'OCP\\Calendar\\ICalendarIsWritable' => $baseDir . '/lib/public/Calendar/ICalendarIsWritable.php', 'OCP\\Calendar\\ICalendarProvider' => $baseDir . '/lib/public/Calendar/ICalendarProvider.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index d9af7846bd882..fd31a1586bad2 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -159,6 +159,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\AppFramework\\Http\\RedirectToDefaultAppResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/RedirectToDefaultAppResponse.php', 'OCP\\AppFramework\\Http\\Response' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Response.php', 'OCP\\AppFramework\\Http\\StandaloneTemplateResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/StandaloneTemplateResponse.php', + 'OCP\\AppFramework\\Http\\StreamGeneratorResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/StreamGeneratorResponse.php', 'OCP\\AppFramework\\Http\\StreamResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/StreamResponse.php', 'OCP\\AppFramework\\Http\\StrictContentSecurityPolicy' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/StrictContentSecurityPolicy.php', 'OCP\\AppFramework\\Http\\StrictEvalContentSecurityPolicy' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/StrictEvalContentSecurityPolicy.php', @@ -239,8 +240,10 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\Broadcast\\Events\\IBroadcastEvent' => __DIR__ . '/../../..' . '/lib/public/Broadcast/Events/IBroadcastEvent.php', 'OCP\\Cache\\CappedMemoryCache' => __DIR__ . '/../../..' . '/lib/public/Cache/CappedMemoryCache.php', 'OCP\\Calendar\\BackendTemporarilyUnavailableException' => __DIR__ . '/../../..' . '/lib/public/Calendar/BackendTemporarilyUnavailableException.php', + 'OCP\\Calendar\\CalendarExportRange' => __DIR__ . '/../../..' . '/lib/public/Calendar/CalendarExportRange.php', 'OCP\\Calendar\\Exceptions\\CalendarException' => __DIR__ . '/../../..' . '/lib/public/Calendar/Exceptions/CalendarException.php', 'OCP\\Calendar\\ICalendar' => __DIR__ . '/../../..' . '/lib/public/Calendar/ICalendar.php', + 'OCP\\Calendar\\ICalendarExport' => __DIR__ . '/../../..' . '/lib/public/Calendar/ICalendarExport.php', 'OCP\\Calendar\\ICalendarIsShared' => __DIR__ . '/../../..' . '/lib/public/Calendar/ICalendarIsShared.php', 'OCP\\Calendar\\ICalendarIsWritable' => __DIR__ . '/../../..' . '/lib/public/Calendar/ICalendarIsWritable.php', 'OCP\\Calendar\\ICalendarProvider' => __DIR__ . '/../../..' . '/lib/public/Calendar/ICalendarProvider.php', diff --git a/lib/public/AppFramework/Http/StreamGeneratorResponse.php b/lib/public/AppFramework/Http/StreamGeneratorResponse.php new file mode 100644 index 0000000000000..d231529aec360 --- /dev/null +++ b/lib/public/AppFramework/Http/StreamGeneratorResponse.php @@ -0,0 +1,57 @@ +> + */ +class StreamGeneratorResponse extends Response implements ICallbackResponse { + protected $generator; + + /** + * @since 31.0.0 + * + * @param \Generator $generator the function to call to generate the response + * @param String $contentType http response content type e.g. 'application/json; charset=UTF-8' + * @param int $status http response status + */ + public function __construct($generator, $contentType, $status = Http::STATUS_OK) { + parent::__construct(); + + $this->generator = $generator; + + $this->setStatus($status); + $this->cacheFor(0); + $this->addHeader('Content-Type', $contentType); + + } + + /** + * Streams content directly to client + * + * @since 31.0.0 + * + * @param IOutput $output a small wrapper that handles output + */ + public function callback(IOutput $output) { + + foreach ($this->generator as $chunk) { + print($chunk); + flush(); + } + + } + +} diff --git a/lib/public/Calendar/CalendarExportRange.php b/lib/public/Calendar/CalendarExportRange.php new file mode 100644 index 0000000000000..e81f27dfd65c6 --- /dev/null +++ b/lib/public/Calendar/CalendarExportRange.php @@ -0,0 +1,21 @@ + + */ + public function export(?CalendarExportRange $range = null): Generator; + +}