Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(dav): Add an API for upcoming events #45435

Merged
merged 1 commit into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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{}>
Fixed Show fixed Hide fixed
*
* 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,
)),
Comment on lines +55 to +58
Copy link
Contributor

Choose a reason for hiding this comment

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

smoof 😍

]);
}

}
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
Loading