Skip to content

Commit

Permalink
feat(caldav): expose calendar subscriptions
Browse files Browse the repository at this point in the history
Signed-off-by: Daniel Kesselberg <[email protected]>
  • Loading branch information
kesselb committed May 24, 2024
1 parent 5481d81 commit 4e77538
Show file tree
Hide file tree
Showing 19 changed files with 591 additions and 31 deletions.
2 changes: 2 additions & 0 deletions apps/dav/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@
'OCA\\DAV\\CalDAV\\BirthdayCalendar\\EnablePlugin' => $baseDir . '/../lib/CalDAV/BirthdayCalendar/EnablePlugin.php',
'OCA\\DAV\\CalDAV\\BirthdayService' => $baseDir . '/../lib/CalDAV/BirthdayService.php',
'OCA\\DAV\\CalDAV\\CachedSubscription' => $baseDir . '/../lib/CalDAV/CachedSubscription.php',
'OCA\\DAV\\CalDAV\\CachedSubscriptionImpl' => $baseDir . '/../lib/CalDAV/CachedSubscriptionImpl.php',
'OCA\\DAV\\CalDAV\\CachedSubscriptionObject' => $baseDir . '/../lib/CalDAV/CachedSubscriptionObject.php',
'OCA\\DAV\\CalDAV\\CachedSubscriptionProvider' => $baseDir . '/../lib/CalDAV/CachedSubscriptionProvider.php',
'OCA\\DAV\\CalDAV\\CalDavBackend' => $baseDir . '/../lib/CalDAV/CalDavBackend.php',
'OCA\\DAV\\CalDAV\\Calendar' => $baseDir . '/../lib/CalDAV/Calendar.php',
'OCA\\DAV\\CalDAV\\CalendarHome' => $baseDir . '/../lib/CalDAV/CalendarHome.php',
Expand Down
2 changes: 2 additions & 0 deletions apps/dav/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ class ComposerStaticInitDAV
'OCA\\DAV\\CalDAV\\BirthdayCalendar\\EnablePlugin' => __DIR__ . '/..' . '/../lib/CalDAV/BirthdayCalendar/EnablePlugin.php',
'OCA\\DAV\\CalDAV\\BirthdayService' => __DIR__ . '/..' . '/../lib/CalDAV/BirthdayService.php',
'OCA\\DAV\\CalDAV\\CachedSubscription' => __DIR__ . '/..' . '/../lib/CalDAV/CachedSubscription.php',
'OCA\\DAV\\CalDAV\\CachedSubscriptionImpl' => __DIR__ . '/..' . '/../lib/CalDAV/CachedSubscriptionImpl.php',
'OCA\\DAV\\CalDAV\\CachedSubscriptionObject' => __DIR__ . '/..' . '/../lib/CalDAV/CachedSubscriptionObject.php',
'OCA\\DAV\\CalDAV\\CachedSubscriptionProvider' => __DIR__ . '/..' . '/../lib/CalDAV/CachedSubscriptionProvider.php',
'OCA\\DAV\\CalDAV\\CalDavBackend' => __DIR__ . '/..' . '/../lib/CalDAV/CalDavBackend.php',
'OCA\\DAV\\CalDAV\\Calendar' => __DIR__ . '/..' . '/../lib/CalDAV/Calendar.php',
'OCA\\DAV\\CalDAV\\CalendarHome' => __DIR__ . '/..' . '/../lib/CalDAV/CalendarHome.php',
Expand Down
2 changes: 2 additions & 0 deletions apps/dav/lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

use OCA\DAV\CalDAV\Activity\Backend;
use OCA\DAV\CalDAV\AppCalendar\AppCalendarPlugin;
use OCA\DAV\CalDAV\CachedSubscriptionProvider;
use OCA\DAV\CalDAV\CalendarManager;
use OCA\DAV\CalDAV\CalendarProvider;
use OCA\DAV\CalDAV\Reminder\NotificationProvider\AudioProvider;
Expand Down Expand Up @@ -206,6 +207,7 @@ public function register(IRegistrationContext $context): void {
$context->registerNotifierService(Notifier::class);

$context->registerCalendarProvider(CalendarProvider::class);
$context->registerCalendarProvider(CachedSubscriptionProvider::class);

$context->registerUserMigrator(CalendarMigrator::class);
$context->registerUserMigrator(ContactsMigrator::class);
Expand Down
2 changes: 1 addition & 1 deletion apps/dav/lib/CalDAV/AppCalendar/AppCalendarPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ protected function getWrappedCalendars(string $principalUri, array $calendarUris
return array_values(
array_filter($this->manager->getCalendarsForPrincipal($principalUri, $calendarUris), function ($c) {
// We must not provide a wrapper for DAV calendars
return ! ($c instanceof \OCA\DAV\CalDAV\CalendarImpl);
return ! (($c instanceof \OCA\DAV\CalDAV\CalendarImpl) || ($c instanceof \OCA\DAV\CalDAV\CachedSubscriptionImpl));
})
);
}
Expand Down
6 changes: 5 additions & 1 deletion apps/dav/lib/CalDAV/CachedSubscription.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ public function getACL() {
'principal' => '{DAV:}authenticated',
'protected' => true,
],
[
'privilege' => '{DAV:}write-properties',
'principal' => $this->getOwner(),
'protected' => true,
]
];
}

Expand All @@ -97,7 +102,6 @@ public function getChildACL() {
'principal' => $this->getOwner() . '/calendar-proxy-read',
'protected' => true,
],

];
}

Expand Down
117 changes: 117 additions & 0 deletions apps/dav/lib/CalDAV/CachedSubscriptionImpl.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<?php

declare(strict_types=1);

/**
* @copyright 2024 Daniel Kesselberg <[email protected]>
*
* @author Daniel Kesselberg <[email protected]>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\DAV\CalDAV;

use OCP\Calendar\ICalendar;
use OCP\Constants;

class CachedSubscriptionImpl implements ICalendar {
private CalDavBackend $backend;
private CachedSubscription $calendar;
/** @var array<string, mixed> */
private array $calendarInfo;

public function __construct(
CachedSubscription $calendar,
array $calendarInfo,
CalDavBackend $backend
) {
$this->calendar = $calendar;
$this->calendarInfo = $calendarInfo;
$this->backend = $backend;
}

/**
* @return string defining the technical unique key
* @since 13.0.0
*/
public function getKey(): string {
return (string) $this->calendarInfo['id'];
}

/**
* {@inheritDoc}
*/
public function getUri(): string {
return $this->calendarInfo['uri'];
}

/**
* In comparison to getKey() this function returns a human readable (maybe translated) name
* @since 13.0.0
*/
public function getDisplayName(): ?string {
return $this->calendarInfo['{DAV:}displayname'];
}

/**
* Calendar color
* @since 13.0.0
*/
public function getDisplayColor(): ?string {
return $this->calendarInfo['{http://apple.com/ns/ical/}calendar-color'];
}

/**
* @param string $pattern which should match within the $searchProperties
* @param array $searchProperties defines the properties within the query pattern should match
* @param array $options - optional parameters:
* ['timerange' => ['start' => new DateTime(...), 'end' => new DateTime(...)]]
* @param int|null $limit - limit number of search results
* @param int|null $offset - offset for paging of search results
* @return array an array of events/journals/todos which are arrays of key-value-pairs
* @since 13.0.0
*/
public function search(string $pattern, array $searchProperties = [], array $options = [], $limit = null, $offset = null): array {
return $this->backend->search($this->calendarInfo, $pattern, $searchProperties, $options, $limit, $offset);
}

/**
* @return int build up using \OCP\Constants
* @since 13.0.0
*/
public function getPermissions(): int {
$permissions = $this->calendar->getACL();
$result = 0;
foreach ($permissions as $permission) {
switch ($permission['privilege']) {
case '{DAV:}read':
$result |= Constants::PERMISSION_READ;
break;
}
}

return $result;
}

public function isDeleted(): bool {
return false;
}

public function getSource(): string {
return $this->calendarInfo['source'];
}
}
57 changes: 57 additions & 0 deletions apps/dav/lib/CalDAV/CachedSubscriptionProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

declare(strict_types=1);

/**
* @copyright 2024 Daniel Kesselberg <[email protected]>
*
* @author Daniel Kesselberg <[email protected]>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\DAV\CalDAV;

use OCP\Calendar\ICalendarProvider;

class CachedSubscriptionProvider implements ICalendarProvider {

public function __construct(
private CalDavBackend $calDavBackend
) {
}

public function getCalendars(string $principalUri, array $calendarUris = []): array {
$calendarInfos = $this->calDavBackend->getSubscriptionsForUser($principalUri);

if (count($calendarUris) > 0) {
$calendarInfos = array_filter($calendarInfos, fn (array $subscription) => in_array($subscription['uri'], $calendarUris));
}

$calendarInfos = array_values(array_filter($calendarInfos));

$iCalendars = [];
foreach ($calendarInfos as $calendarInfo) {
$calendar = new CachedSubscription($this->calDavBackend, $calendarInfo);
$iCalendars[] = new CachedSubscriptionImpl(
$calendar,
$calendarInfo,
$this->calDavBackend,
);
}
return $iCalendars;
}
}
8 changes: 7 additions & 1 deletion apps/dav/lib/CalDAV/CalDavBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -1873,12 +1873,18 @@ public function search(
$outerQuery = $this->db->getQueryBuilder();
$innerQuery = $this->db->getQueryBuilder();

if (isset($calendarInfo['source'])) {
$calendarType = self::CALENDAR_TYPE_SUBSCRIPTION;
} else {
$calendarType = self::CALENDAR_TYPE_CALENDAR;
}

$innerQuery->selectDistinct('op.objectid')
->from($this->dbObjectPropertiesTable, 'op')
->andWhere($innerQuery->expr()->eq('op.calendarid',
$outerQuery->createNamedParameter($calendarInfo['id'])))
->andWhere($innerQuery->expr()->eq('op.calendartype',
$outerQuery->createNamedParameter(self::CALENDAR_TYPE_CALENDAR)));
$outerQuery->createNamedParameter($calendarType)));

$outerQuery->select('c.id', 'c.calendardata', 'c.componenttype', 'c.uid', 'c.uri')
->from('calendarobjects', 'c')
Expand Down
14 changes: 6 additions & 8 deletions apps/dav/lib/CalDAV/CalendarHome.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,16 @@ class CalendarHome extends \Sabre\CalDAV\CalendarHome {
/** @var PluginManager */
private $pluginManager;

/** @var bool */
private $returnCachedSubscriptions = false;

/** @var LoggerInterface */
private $logger;
private ?array $cachedChildren = null;

public function __construct(BackendInterface $caldavBackend, $principalInfo, LoggerInterface $logger) {
public function __construct(
BackendInterface $caldavBackend,
array $principalInfo,
LoggerInterface $logger,
private bool $returnCachedSubscriptions
) {
parent::__construct($caldavBackend, $principalInfo);
$this->l10n = \OC::$server->getL10N('dav');
$this->config = \OC::$server->getConfig();
Expand Down Expand Up @@ -219,8 +221,4 @@ public function calendarSearch(array $filters, $limit = null, $offset = null) {
$principalUri = $this->principalInfo['uri'];
return $this->caldavBackend->calendarSearch($principalUri, $filters, $limit, $offset);
}

public function enableCachedSubscriptionsForThisRequest() {
$this->returnCachedSubscriptions = true;
}
}
13 changes: 12 additions & 1 deletion apps/dav/lib/CalDAV/CalendarRoot.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
class CalendarRoot extends \Sabre\CalDAV\CalendarRoot {
private LoggerInterface $logger;

private array $returnCachedSubscriptions = [];

public function __construct(
PrincipalBackend\BackendInterface $principalBackend,
Backend\BackendInterface $caldavBackend,
Expand All @@ -43,7 +45,12 @@ public function __construct(
}

public function getChildForPrincipal(array $principal) {
return new CalendarHome($this->caldavBackend, $principal, $this->logger);
return new CalendarHome(
$this->caldavBackend,
$principal,
$this->logger,
array_key_exists($principal['uri'], $this->returnCachedSubscriptions)
);
}

public function getName() {
Expand All @@ -56,4 +63,8 @@ public function getName() {

return parent::getName();
}

public function enableReturnCachedSubscriptions(string $principalUri): void {
$this->returnCachedSubscriptions['principals/users/' . $principalUri] = true;
}
}
20 changes: 10 additions & 10 deletions apps/dav/lib/CalDAV/WebcalCaching/Plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
*/
namespace OCA\DAV\CalDAV\WebcalCaching;

use OCA\DAV\CalDAV\CalendarHome;
use OCA\DAV\CalDAV\CalendarRoot;
use OCP\IRequest;
use Sabre\DAV\Exception\NotFound;
use Sabre\DAV\Server;
Expand Down Expand Up @@ -71,6 +71,11 @@ public function __construct(IRequest $request) {
if ($magicHeader === 'On') {
$this->enabled = true;
}

$isExportRequest = $request->getMethod() === 'GET' && array_key_exists('export', $request->getParams());
if ($isExportRequest) {
$this->enabled = true;
}
}

/**
Expand All @@ -85,7 +90,7 @@ public function __construct(IRequest $request) {
*/
public function initialize(Server $server) {
$this->server = $server;
$server->on('beforeMethod:*', [$this, 'beforeMethod']);
$server->on('beforeMethod:*', [$this, 'beforeMethod'], 15);
}

/**
Expand All @@ -103,16 +108,11 @@ public function beforeMethod(RequestInterface $request, ResponseInterface $respo
return;
}

// $calendarHomePath will look like: calendars/username
$calendarHomePath = $pathParts[0] . '/' . $pathParts[1];
try {
$calendarHome = $this->server->tree->getNodeForPath($calendarHomePath);
if (!($calendarHome instanceof CalendarHome)) {
//how did we end up here?
return;
$calendarRoot = $this->server->tree->getNodeForPath($pathParts[0]);
if ($calendarRoot instanceof CalendarRoot) {
$calendarRoot->enableReturnCachedSubscriptions($pathParts[1]);
}

$calendarHome->enableCachedSubscriptionsForThisRequest();
} catch (NotFound $ex) {
return;
}
Expand Down
6 changes: 6 additions & 0 deletions apps/dav/lib/Connector/Sabre/DavAclPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
*/
namespace OCA\DAV\Connector\Sabre;

use OCA\DAV\CalDAV\CachedSubscription;
use OCA\DAV\CalDAV\Calendar;
use OCA\DAV\CardDAV\AddressBook;
use Sabre\CalDAV\Principal\User;
use Sabre\DAV\Exception\NotFound;
Expand Down Expand Up @@ -59,6 +61,10 @@ public function checkPrivileges($uri, $privileges, $recursion = self::R_PARENT,
case AddressBook::class:
$type = 'Addressbook';
break;
case Calendar::class:
case CachedSubscription::class:
$type = 'Calendar';
break;
default:
$type = 'Node';
break;
Expand Down
Loading

0 comments on commit 4e77538

Please sign in to comment.