Skip to content

Commit

Permalink
feat(dav): Add an API for upcoming events
Browse files Browse the repository at this point in the history
Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
  • Loading branch information
ChristophWurst committed Aug 13, 2024
1 parent cee227a commit b7d848c
Show file tree
Hide file tree
Showing 11 changed files with 527 additions and 0 deletions.
1 change: 1 addition & 0 deletions apps/dav/appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
],
'ocs' => [
['name' => 'direct#getUrl', 'url' => '/api/v1/direct', 'verb' => 'POST'],
['name' => 'upcoming_events#getEvents', 'url' => '/api/v1/events/upcoming', 'verb' => 'GET'],
['name' => 'out_of_office#getCurrentOutOfOfficeData', 'url' => '/api/v1/outOfOffice/{userId}/now', 'verb' => 'GET'],
['name' => 'out_of_office#getOutOfOffice', 'url' => '/api/v1/outOfOffice/{userId}', 'verb' => 'GET'],
['name' => 'out_of_office#setOutOfOffice', 'url' => '/api/v1/outOfOffice/{userId}', 'verb' => 'POST'],
Expand Down
3 changes: 3 additions & 0 deletions apps/dav/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@
'OCA\\DAV\\CalDAV\\Trashbin\\Plugin' => $baseDir . '/../lib/CalDAV/Trashbin/Plugin.php',
'OCA\\DAV\\CalDAV\\Trashbin\\RestoreTarget' => $baseDir . '/../lib/CalDAV/Trashbin/RestoreTarget.php',
'OCA\\DAV\\CalDAV\\Trashbin\\TrashbinHome' => $baseDir . '/../lib/CalDAV/Trashbin/TrashbinHome.php',
'OCA\\DAV\\CalDAV\\UpcomingEvent' => $baseDir . '/../lib/CalDAV/UpcomingEvent.php',
'OCA\\DAV\\CalDAV\\UpcomingEventsService' => $baseDir . '/../lib/CalDAV/UpcomingEventsService.php',
'OCA\\DAV\\CalDAV\\Validation\\CalDavValidatePlugin' => $baseDir . '/../lib/CalDAV/Validation/CalDavValidatePlugin.php',
'OCA\\DAV\\CalDAV\\WebcalCaching\\Plugin' => $baseDir . '/../lib/CalDAV/WebcalCaching/Plugin.php',
'OCA\\DAV\\CalDAV\\WebcalCaching\\RefreshWebcalService' => $baseDir . '/../lib/CalDAV/WebcalCaching/RefreshWebcalService.php',
Expand Down Expand Up @@ -213,6 +215,7 @@
'OCA\\DAV\\Controller\\DirectController' => $baseDir . '/../lib/Controller/DirectController.php',
'OCA\\DAV\\Controller\\InvitationResponseController' => $baseDir . '/../lib/Controller/InvitationResponseController.php',
'OCA\\DAV\\Controller\\OutOfOfficeController' => $baseDir . '/../lib/Controller/OutOfOfficeController.php',
'OCA\\DAV\\Controller\\UpcomingEventsController' => $baseDir . '/../lib/Controller/UpcomingEventsController.php',
'OCA\\DAV\\DAV\\CustomPropertiesBackend' => $baseDir . '/../lib/DAV/CustomPropertiesBackend.php',
'OCA\\DAV\\DAV\\GroupPrincipalBackend' => $baseDir . '/../lib/DAV/GroupPrincipalBackend.php',
'OCA\\DAV\\DAV\\PublicAuth' => $baseDir . '/../lib/DAV/PublicAuth.php',
Expand Down
3 changes: 3 additions & 0 deletions apps/dav/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ class ComposerStaticInitDAV
'OCA\\DAV\\CalDAV\\Trashbin\\Plugin' => __DIR__ . '/..' . '/../lib/CalDAV/Trashbin/Plugin.php',
'OCA\\DAV\\CalDAV\\Trashbin\\RestoreTarget' => __DIR__ . '/..' . '/../lib/CalDAV/Trashbin/RestoreTarget.php',
'OCA\\DAV\\CalDAV\\Trashbin\\TrashbinHome' => __DIR__ . '/..' . '/../lib/CalDAV/Trashbin/TrashbinHome.php',
'OCA\\DAV\\CalDAV\\UpcomingEvent' => __DIR__ . '/..' . '/../lib/CalDAV/UpcomingEvent.php',
'OCA\\DAV\\CalDAV\\UpcomingEventsService' => __DIR__ . '/..' . '/../lib/CalDAV/UpcomingEventsService.php',
'OCA\\DAV\\CalDAV\\Validation\\CalDavValidatePlugin' => __DIR__ . '/..' . '/../lib/CalDAV/Validation/CalDavValidatePlugin.php',
'OCA\\DAV\\CalDAV\\WebcalCaching\\Plugin' => __DIR__ . '/..' . '/../lib/CalDAV/WebcalCaching/Plugin.php',
'OCA\\DAV\\CalDAV\\WebcalCaching\\RefreshWebcalService' => __DIR__ . '/..' . '/../lib/CalDAV/WebcalCaching/RefreshWebcalService.php',
Expand Down Expand Up @@ -228,6 +230,7 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Controller\\DirectController' => __DIR__ . '/..' . '/../lib/Controller/DirectController.php',
'OCA\\DAV\\Controller\\InvitationResponseController' => __DIR__ . '/..' . '/../lib/Controller/InvitationResponseController.php',
'OCA\\DAV\\Controller\\OutOfOfficeController' => __DIR__ . '/..' . '/../lib/Controller/OutOfOfficeController.php',
'OCA\\DAV\\Controller\\UpcomingEventsController' => __DIR__ . '/..' . '/../lib/Controller/UpcomingEventsController.php',
'OCA\\DAV\\DAV\\CustomPropertiesBackend' => __DIR__ . '/..' . '/../lib/DAV/CustomPropertiesBackend.php',
'OCA\\DAV\\DAV\\GroupPrincipalBackend' => __DIR__ . '/..' . '/../lib/DAV/GroupPrincipalBackend.php',
'OCA\\DAV\\DAV\\PublicAuth' => __DIR__ . '/..' . '/../lib/DAV/PublicAuth.php',
Expand Down
67 changes: 67 additions & 0 deletions apps/dav/lib/CalDAV/UpcomingEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\DAV\CalDAV;

use JsonSerializable;
use OCA\DAV\ResponseDefinitions;

class UpcomingEvent implements JsonSerializable {
public function __construct(private string $uri,
private ?int $recurrenceId,
private string $calendarUri,
private ?int $start,
private ?string $summary,
private ?string $location,
private ?string $calendarAppUrl) {
}

public function getUri(): string {
return $this->uri;
}

public function getRecurrenceId(): ?int {
return $this->recurrenceId;
}

public function getCalendarUri(): string {
return $this->calendarUri;
}

public function getStart(): ?int {
return $this->start;
}

public function getSummary(): ?string {
return $this->summary;
}

public function getLocation(): ?string {
return $this->location;
}

public function getCalendarAppUrl(): ?string {
return $this->calendarAppUrl;
}

/**
* @see ResponseDefinitions
*/
public function jsonSerialize(): array {
return [
'uri' => $this->uri,
'recurrenceId' => $this->recurrenceId,
'calendarUri' => $this->calendarUri,
'start' => $this->start,
'summary' => $this->summary,
'location' => $this->location,
'calendarAppUrl' => $this->calendarAppUrl,
];
}
}
64 changes: 64 additions & 0 deletions apps/dav/lib/CalDAV/UpcomingEventsService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\DAV\CalDAV;

use OCP\App\IAppManager;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Calendar\IManager;
use OCP\IURLGenerator;
use OCP\IUserManager;
use function array_map;

class UpcomingEventsService {
public function __construct(private IManager $calendarManager,
private ITimeFactory $timeFactory,
private IUserManager $userManager,
private IAppManager $appManager,
private IURLGenerator $urlGenerator) {
}

/**
* @return UpcomingEvent[]
*/
public function getEvents(string $userId, ?string $location = null): array {
$searchQuery = $this->calendarManager->newQuery('principals/users/' . $userId);
if ($location !== null) {
$searchQuery->addSearchProperty('LOCATION');
$searchQuery->setSearchPattern($location);
}
$searchQuery->addType('VEVENT');
$searchQuery->setLimit(3);
$now = $this->timeFactory->now();
$searchQuery->setTimerangeStart($now->modify('-1 minute'));
$searchQuery->setTimerangeEnd($now->modify('+1 month'));

$events = $this->calendarManager->searchForPrincipal($searchQuery);
$calendarAppEnabled = $this->appManager->isEnabledForUser(
'calendar',
$this->userManager->get($userId),
);

return array_map(fn (array $event) => new UpcomingEvent(
$event['uri'],
($event['objects'][0]['RECURRENCE-ID'][0] ?? null)?->getTimeStamp(),
$event['calendar-uri'],
$event['objects'][0]['DTSTART'][0]?->getTimestamp(),
$event['objects'][0]['SUMMARY'][0] ?? null,
$event['objects'][0]['LOCATION'][0] ?? null,
match ($calendarAppEnabled) {
// TODO: create a named, deep route in calendar
// TODO: it's a code smell to just assume this route exists, find an abstraction
true => $this->urlGenerator->linkToRouteAbsolute('calendar.view.index'),
false => null,
},
), $events);
}

}
62 changes: 62 additions & 0 deletions apps/dav/lib/Controller/UpcomingEventsController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\DAV\Controller;

use OCA\DAV\AppInfo\Application;
use OCA\DAV\CalDAV\UpcomingEvent;
use OCA\DAV\CalDAV\UpcomingEventsService;
use OCA\DAV\ResponseDefinitions;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\IRequest;

/**
* @psalm-import-type DAVUpcomingEvent from ResponseDefinitions
*/
class UpcomingEventsController extends OCSController {
private ?string $userId;
private UpcomingEventsService $service;

public function __construct(
IRequest $request,
?string $userId,
UpcomingEventsService $service) {
parent::__construct(Application::APP_ID, $request);

$this->userId = $userId;
$this->service = $service;
}

/**
* Get information about upcoming events
*
* @param string|null $location location/URL to filter by
* @return DataResponse<Http::STATUS_OK, array{events: DAVUpcomingEvent[]}, array{}>|DataResponse<Http::STATUS_UNAUTHORIZED, null, array{}>
*
* 200: Upcoming events
* 401: When not authenticated
*/
#[NoAdminRequired]
public function getEvents(?string $location = null): DataResponse {
if ($this->userId === null) {
return new DataResponse(null, Http::STATUS_UNAUTHORIZED);
}

return new DataResponse([
'events' => array_map(fn (UpcomingEvent $e) => $e->jsonSerialize(), $this->service->getEvents(
$this->userId,
$location,
)),
]);
}

}
11 changes: 11 additions & 0 deletions apps/dav/lib/ResponseDefinitions.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

namespace OCA\DAV;

use OCA\DAV\CalDAV\UpcomingEvent;

/**
* @psalm-type DAVOutOfOfficeDataCommon = array{
* userId: string,
Expand All @@ -31,6 +33,15 @@
* endDate: int,
* shortMessage: string,
* }
*
* @see UpcomingEvent::jsonSerialize
* @psalm-type DAVUpcomingEvent = array{
* uri: string,
* calendarUri: string,
* start: ?int,
* summary: ?string,
* location: ?string,
* }
*/
class ResponseDefinitions {
}
Loading

0 comments on commit b7d848c

Please sign in to comment.