Skip to content

Commit 7641e76

Browse files
Merge pull request #45435 from nextcloud/feat/dav/upcoming-events-api
feat(dav): Add an API for upcoming events
2 parents 832a142 + 370a9d7 commit 7641e76

File tree

11 files changed

+525
-0
lines changed

11 files changed

+525
-0
lines changed

apps/dav/appinfo/routes.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
],
1515
'ocs' => [
1616
['name' => 'direct#getUrl', 'url' => '/api/v1/direct', 'verb' => 'POST'],
17+
['name' => 'upcoming_events#getEvents', 'url' => '/api/v1/events/upcoming', 'verb' => 'GET'],
1718
['name' => 'out_of_office#getCurrentOutOfOfficeData', 'url' => '/api/v1/outOfOffice/{userId}/now', 'verb' => 'GET'],
1819
['name' => 'out_of_office#getOutOfOffice', 'url' => '/api/v1/outOfOffice/{userId}', 'verb' => 'GET'],
1920
['name' => 'out_of_office#setOutOfOffice', 'url' => '/api/v1/outOfOffice/{userId}', 'verb' => 'POST'],

apps/dav/composer/composer/autoload_classmap.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@
116116
'OCA\\DAV\\CalDAV\\Trashbin\\Plugin' => $baseDir . '/../lib/CalDAV/Trashbin/Plugin.php',
117117
'OCA\\DAV\\CalDAV\\Trashbin\\RestoreTarget' => $baseDir . '/../lib/CalDAV/Trashbin/RestoreTarget.php',
118118
'OCA\\DAV\\CalDAV\\Trashbin\\TrashbinHome' => $baseDir . '/../lib/CalDAV/Trashbin/TrashbinHome.php',
119+
'OCA\\DAV\\CalDAV\\UpcomingEvent' => $baseDir . '/../lib/CalDAV/UpcomingEvent.php',
120+
'OCA\\DAV\\CalDAV\\UpcomingEventsService' => $baseDir . '/../lib/CalDAV/UpcomingEventsService.php',
119121
'OCA\\DAV\\CalDAV\\Validation\\CalDavValidatePlugin' => $baseDir . '/../lib/CalDAV/Validation/CalDavValidatePlugin.php',
120122
'OCA\\DAV\\CalDAV\\WebcalCaching\\Plugin' => $baseDir . '/../lib/CalDAV/WebcalCaching/Plugin.php',
121123
'OCA\\DAV\\CalDAV\\WebcalCaching\\RefreshWebcalService' => $baseDir . '/../lib/CalDAV/WebcalCaching/RefreshWebcalService.php',
@@ -213,6 +215,7 @@
213215
'OCA\\DAV\\Controller\\DirectController' => $baseDir . '/../lib/Controller/DirectController.php',
214216
'OCA\\DAV\\Controller\\InvitationResponseController' => $baseDir . '/../lib/Controller/InvitationResponseController.php',
215217
'OCA\\DAV\\Controller\\OutOfOfficeController' => $baseDir . '/../lib/Controller/OutOfOfficeController.php',
218+
'OCA\\DAV\\Controller\\UpcomingEventsController' => $baseDir . '/../lib/Controller/UpcomingEventsController.php',
216219
'OCA\\DAV\\DAV\\CustomPropertiesBackend' => $baseDir . '/../lib/DAV/CustomPropertiesBackend.php',
217220
'OCA\\DAV\\DAV\\GroupPrincipalBackend' => $baseDir . '/../lib/DAV/GroupPrincipalBackend.php',
218221
'OCA\\DAV\\DAV\\PublicAuth' => $baseDir . '/../lib/DAV/PublicAuth.php',

apps/dav/composer/composer/autoload_static.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ class ComposerStaticInitDAV
131131
'OCA\\DAV\\CalDAV\\Trashbin\\Plugin' => __DIR__ . '/..' . '/../lib/CalDAV/Trashbin/Plugin.php',
132132
'OCA\\DAV\\CalDAV\\Trashbin\\RestoreTarget' => __DIR__ . '/..' . '/../lib/CalDAV/Trashbin/RestoreTarget.php',
133133
'OCA\\DAV\\CalDAV\\Trashbin\\TrashbinHome' => __DIR__ . '/..' . '/../lib/CalDAV/Trashbin/TrashbinHome.php',
134+
'OCA\\DAV\\CalDAV\\UpcomingEvent' => __DIR__ . '/..' . '/../lib/CalDAV/UpcomingEvent.php',
135+
'OCA\\DAV\\CalDAV\\UpcomingEventsService' => __DIR__ . '/..' . '/../lib/CalDAV/UpcomingEventsService.php',
134136
'OCA\\DAV\\CalDAV\\Validation\\CalDavValidatePlugin' => __DIR__ . '/..' . '/../lib/CalDAV/Validation/CalDavValidatePlugin.php',
135137
'OCA\\DAV\\CalDAV\\WebcalCaching\\Plugin' => __DIR__ . '/..' . '/../lib/CalDAV/WebcalCaching/Plugin.php',
136138
'OCA\\DAV\\CalDAV\\WebcalCaching\\RefreshWebcalService' => __DIR__ . '/..' . '/../lib/CalDAV/WebcalCaching/RefreshWebcalService.php',
@@ -228,6 +230,7 @@ class ComposerStaticInitDAV
228230
'OCA\\DAV\\Controller\\DirectController' => __DIR__ . '/..' . '/../lib/Controller/DirectController.php',
229231
'OCA\\DAV\\Controller\\InvitationResponseController' => __DIR__ . '/..' . '/../lib/Controller/InvitationResponseController.php',
230232
'OCA\\DAV\\Controller\\OutOfOfficeController' => __DIR__ . '/..' . '/../lib/Controller/OutOfOfficeController.php',
233+
'OCA\\DAV\\Controller\\UpcomingEventsController' => __DIR__ . '/..' . '/../lib/Controller/UpcomingEventsController.php',
231234
'OCA\\DAV\\DAV\\CustomPropertiesBackend' => __DIR__ . '/..' . '/../lib/DAV/CustomPropertiesBackend.php',
232235
'OCA\\DAV\\DAV\\GroupPrincipalBackend' => __DIR__ . '/..' . '/../lib/DAV/GroupPrincipalBackend.php',
233236
'OCA\\DAV\\DAV\\PublicAuth' => __DIR__ . '/..' . '/../lib/DAV/PublicAuth.php',
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OCA\DAV\CalDAV;
11+
12+
use JsonSerializable;
13+
use OCA\DAV\ResponseDefinitions;
14+
15+
class UpcomingEvent implements JsonSerializable {
16+
public function __construct(private string $uri,
17+
private ?int $recurrenceId,
18+
private string $calendarUri,
19+
private ?int $start,
20+
private ?string $summary,
21+
private ?string $location,
22+
private ?string $calendarAppUrl) {
23+
}
24+
25+
public function getUri(): string {
26+
return $this->uri;
27+
}
28+
29+
public function getRecurrenceId(): ?int {
30+
return $this->recurrenceId;
31+
}
32+
33+
public function getCalendarUri(): string {
34+
return $this->calendarUri;
35+
}
36+
37+
public function getStart(): ?int {
38+
return $this->start;
39+
}
40+
41+
public function getSummary(): ?string {
42+
return $this->summary;
43+
}
44+
45+
public function getLocation(): ?string {
46+
return $this->location;
47+
}
48+
49+
public function getCalendarAppUrl(): ?string {
50+
return $this->calendarAppUrl;
51+
}
52+
53+
/**
54+
* @see ResponseDefinitions
55+
*/
56+
public function jsonSerialize(): array {
57+
return [
58+
'uri' => $this->uri,
59+
'recurrenceId' => $this->recurrenceId,
60+
'calendarUri' => $this->calendarUri,
61+
'start' => $this->start,
62+
'summary' => $this->summary,
63+
'location' => $this->location,
64+
'calendarAppUrl' => $this->calendarAppUrl,
65+
];
66+
}
67+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OCA\DAV\CalDAV;
11+
12+
use OCP\App\IAppManager;
13+
use OCP\AppFramework\Utility\ITimeFactory;
14+
use OCP\Calendar\IManager;
15+
use OCP\IURLGenerator;
16+
use OCP\IUserManager;
17+
use function array_map;
18+
19+
class UpcomingEventsService {
20+
public function __construct(private IManager $calendarManager,
21+
private ITimeFactory $timeFactory,
22+
private IUserManager $userManager,
23+
private IAppManager $appManager,
24+
private IURLGenerator $urlGenerator) {
25+
}
26+
27+
/**
28+
* @return UpcomingEvent[]
29+
*/
30+
public function getEvents(string $userId, ?string $location = null): array {
31+
$searchQuery = $this->calendarManager->newQuery('principals/users/' . $userId);
32+
if ($location !== null) {
33+
$searchQuery->addSearchProperty('LOCATION');
34+
$searchQuery->setSearchPattern($location);
35+
}
36+
$searchQuery->addType('VEVENT');
37+
$searchQuery->setLimit(3);
38+
$now = $this->timeFactory->now();
39+
$searchQuery->setTimerangeStart($now->modify('-1 minute'));
40+
$searchQuery->setTimerangeEnd($now->modify('+1 month'));
41+
42+
$events = $this->calendarManager->searchForPrincipal($searchQuery);
43+
$calendarAppEnabled = $this->appManager->isEnabledForUser(
44+
'calendar',
45+
$this->userManager->get($userId),
46+
);
47+
48+
return array_map(fn (array $event) => new UpcomingEvent(
49+
$event['uri'],
50+
($event['objects'][0]['RECURRENCE-ID'][0] ?? null)?->getTimeStamp(),
51+
$event['calendar-uri'],
52+
$event['objects'][0]['DTSTART'][0]?->getTimestamp(),
53+
$event['objects'][0]['SUMMARY'][0] ?? null,
54+
$event['objects'][0]['LOCATION'][0] ?? null,
55+
match ($calendarAppEnabled) {
56+
// TODO: create a named, deep route in calendar
57+
// TODO: it's a code smell to just assume this route exists, find an abstraction
58+
true => $this->urlGenerator->linkToRouteAbsolute('calendar.view.index'),
59+
false => null,
60+
},
61+
), $events);
62+
}
63+
64+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OCA\DAV\Controller;
11+
12+
use OCA\DAV\AppInfo\Application;
13+
use OCA\DAV\CalDAV\UpcomingEvent;
14+
use OCA\DAV\CalDAV\UpcomingEventsService;
15+
use OCA\DAV\ResponseDefinitions;
16+
use OCP\AppFramework\Http;
17+
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
18+
use OCP\AppFramework\Http\DataResponse;
19+
use OCP\AppFramework\OCSController;
20+
use OCP\IRequest;
21+
22+
/**
23+
* @psalm-import-type DAVUpcomingEvent from ResponseDefinitions
24+
*/
25+
class UpcomingEventsController extends OCSController {
26+
private ?string $userId;
27+
private UpcomingEventsService $service;
28+
29+
public function __construct(
30+
IRequest $request,
31+
?string $userId,
32+
UpcomingEventsService $service) {
33+
parent::__construct(Application::APP_ID, $request);
34+
35+
$this->userId = $userId;
36+
$this->service = $service;
37+
}
38+
39+
/**
40+
* Get information about upcoming events
41+
*
42+
* @param string|null $location location/URL to filter by
43+
* @return DataResponse<Http::STATUS_OK, array{events: DAVUpcomingEvent[]}, array{}>|DataResponse<Http::STATUS_UNAUTHORIZED, null, array{}>
44+
*
45+
* 200: Upcoming events
46+
* 401: When not authenticated
47+
*/
48+
#[NoAdminRequired]
49+
public function getEvents(?string $location = null): DataResponse {
50+
if ($this->userId === null) {
51+
return new DataResponse(null, Http::STATUS_UNAUTHORIZED);
52+
}
53+
54+
return new DataResponse([
55+
'events' => array_map(fn (UpcomingEvent $e) => $e->jsonSerialize(), $this->service->getEvents(
56+
$this->userId,
57+
$location,
58+
)),
59+
]);
60+
}
61+
62+
}

apps/dav/lib/ResponseDefinitions.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
namespace OCA\DAV;
1111

12+
use OCA\DAV\CalDAV\UpcomingEvent;
13+
1214
/**
1315
* @psalm-type DAVOutOfOfficeDataCommon = array{
1416
* userId: string,
@@ -31,6 +33,15 @@
3133
* endDate: int,
3234
* shortMessage: string,
3335
* }
36+
*
37+
* @see UpcomingEvent::jsonSerialize
38+
* @psalm-type DAVUpcomingEvent = array{
39+
* uri: string,
40+
* calendarUri: string,
41+
* start: ?int,
42+
* summary: ?string,
43+
* location: ?string,
44+
* }
3445
*/
3546
class ResponseDefinitions {
3647
}

0 commit comments

Comments
 (0)