Skip to content

Conversation

SebastianKrupinski
Copy link
Contributor

@SebastianKrupinski SebastianKrupinski commented Sep 18, 2025

Summary

Extracted from #49995

This adds the ability to export and import calendars via the OCS

OCS Export

Endpoint: /ocs/v2.php/calendar/export
Request: POST

{
    "id": "personal",
    "format": "xcal", (optional "ical, jcal, xcal", defaults to "ical")
	"options": {
		"rangeStart": '' or 'UID of previous last event',
		"rangeCount": 100
	},
	"user": "user1" (optional admin permissions required)
}

OCS Import

Endpoint: /ocs/v2.php/calendar/import
Request: POST

{
	"id": "personal",
	"options": {
		"format": "xcal", (optional "ical, jcal, xcal", defaults to "ical")
		"validation": 0, (0 - no validate, 1 - validate and skip, 2 - validate and error)
		"errors": 0, (0 - continue, 1 - fail)
		"supersede": true,
		"showCreated": true,
		"showUpdated": true,
		"showSkipped": true,
		"showErrors": true
	},
	"user": "user1", (optional admin permissions required)
	"data": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><calendar><event><title>Meeting</title><date>2025-02-18</date></event></calendar>"
}

Checklist

@SebastianKrupinski SebastianKrupinski requested a review from a team as a code owner September 18, 2025 19:28
@SebastianKrupinski SebastianKrupinski requested review from icewind1991, nfebe and CarlSchwan and removed request for a team September 18, 2025 19:28
@SebastianKrupinski SebastianKrupinski self-assigned this Sep 18, 2025
@SebastianKrupinski SebastianKrupinski added the 2. developing Work in progress label Sep 18, 2025
@SebastianKrupinski SebastianKrupinski force-pushed the feat/calendar-import-export-ocs branch from 12ad7af to 6838bbe Compare September 23, 2025 00:15
@SebastianKrupinski SebastianKrupinski force-pushed the feat/calendar-import-export-ocs branch 2 times, most recently from 95ef88c to e000a47 Compare September 23, 2025 00:22
Signed-off-by: SebastianKrupinski <[email protected]>
@SebastianKrupinski SebastianKrupinski force-pushed the feat/calendar-import-export-ocs branch from e000a47 to a3431ef Compare September 23, 2025 00:57
Copy link
Member

@provokateurin provokateurin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code comments in the controller are a bit useless, you might want to remove them to avoid confusion if the code is refactored later.

* @param array{rangeStart:string,rangeCount:int<1,max>} $options configuration options
* @param string|null $user system user id
*
* @return StreamGeneratorResponse<Http::STATUS_OK, array{Content-Type:string}> | DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_UNAUTHORIZED, array{error?: non-empty-string}, array{}>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you could limit the Content-Type type to the possible values, so it's clear in the OpenAPI spec what might be returned.

#[ApiRoute(verb: 'POST', url: '/export', root: '/calendar')]
#[UserRateLimit(limit: 60, period: 60)]
#[NoAdminRequired]
public function index(string $id, ?string $type = null, ?array $options = null, ?string $user = null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public function index(string $id, ?string $type = null, ?array $options = null, ?string $user = null) {
public function export(string $id, ?string $type = null, ?array $options = null, ?string $user = null) {

#[ApiRoute(verb: 'POST', url: '/import', root: '/calendar')]
#[UserRateLimit(limit: 1, period: 60)]
#[NoAdminRequired]
public function index(string $id, array $options, string $data, ?string $user = null): DataResponse {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public function index(string $id, array $options, string $data, ?string $user = null): DataResponse {
public function import(string $id, array $options, string $data, ?string $user = null): DataResponse {

*
* @param string $id calendar id
* @param string|null $type data format
* @param array{rangeStart:string,rangeCount:int<1,max>} $options configuration options
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @param array{rangeStart:string,rangeCount:int<1,max>} $options configuration options
* @param array{rangeStart:string,rangeCount:positive-int} $options configuration options

* Import calendar data
*
* @param string $id calendar id
* @param array{format?:string, validation?:int<0,2>, errors?:int<0,1>, supersede?:bool, showCreated?:bool, showUpdated?:bool, showSkipped?:bool, showErrors?:bool} $options configuration options
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @param array{format?:string, validation?:int<0,2>, errors?:int<0,1>, supersede?:bool, showCreated?:bool, showUpdated?:bool, showSkipped?:bool, showErrors?:bool} $options configuration options
* @param array{format?:string, validation?:0|1|2, errors?:0|1, supersede?:bool, showCreated?:bool, showUpdated?:bool, showSkipped?:bool, showErrors?:bool} $options configuration options

Comment on lines +64 to +71
$format = isset($options['format']) ? $options['format'] : null;
$validation = isset($options['validation']) ? (int)$options['validation'] : null;
$errors = isset($options['errors']) ? (int)$options['errors'] : null;
$supersede = $options['supersede'] ?? false;
$showCreated = $options['showCreated'] ?? false;
$showUpdated = $options['showUpdated'] ?? false;
$showSkipped = $options['showSkipped'] ?? false;
$showErrors = $options['showErrors'] ?? false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why have this options parameter instead of putting all of them into the parameters right away?

* 400: invalid parameters
* 401: user not authorized
*/
#[OpenAPI(scope: OpenAPI::SCOPE_DEFAULT)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#[OpenAPI(scope: OpenAPI::SCOPE_DEFAULT)]

Not necessary

* 400: invalid request
* 401: user not authorized
*/
#[OpenAPI(scope: OpenAPI::SCOPE_DEFAULT)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#[OpenAPI(scope: OpenAPI::SCOPE_DEFAULT)]

use OCP\AppFramework\Http;

/**
* @since 32.0.0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @since 32.0.0
* @since 33.0.0

All @SInCE are wrong here

Comment on lines +6 to +7
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-only
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-only
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2. developing Work in progress
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants