Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
Add address book plugins
Signed-off-by: Christoph Wurst <[email protected]>
  • Loading branch information
ChristophWurst committed Feb 27, 2020
commit fd489de57c074ad8d7649fe572067c3f631dacfb
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 @@ -91,6 +91,8 @@
'OCA\\DAV\\CardDAV\\Converter' => $baseDir . '/../lib/CardDAV/Converter.php',
'OCA\\DAV\\CardDAV\\HasPhotoPlugin' => $baseDir . '/../lib/CardDAV/HasPhotoPlugin.php',
'OCA\\DAV\\CardDAV\\ImageExportPlugin' => $baseDir . '/../lib/CardDAV/ImageExportPlugin.php',
'OCA\\DAV\\CardDAV\\Integration\\ExternalAddressBook' => $baseDir . '/../lib/CardDAV/Integration/ExternalAddressBook.php',
'OCA\\DAV\\CardDAV\\Integration\\IAddressBookProvider' => $baseDir . '/../lib/CardDAV/Integration/IAddressBookProvider.php',
'OCA\\DAV\\CardDAV\\MultiGetExportPlugin' => $baseDir . '/../lib/CardDAV/MultiGetExportPlugin.php',
'OCA\\DAV\\CardDAV\\PhotoCache' => $baseDir . '/../lib/CardDAV/PhotoCache.php',
'OCA\\DAV\\CardDAV\\Plugin' => $baseDir . '/../lib/CardDAV/Plugin.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 @@ -106,6 +106,8 @@ class ComposerStaticInitDAV
'OCA\\DAV\\CardDAV\\Converter' => __DIR__ . '/..' . '/../lib/CardDAV/Converter.php',
'OCA\\DAV\\CardDAV\\HasPhotoPlugin' => __DIR__ . '/..' . '/../lib/CardDAV/HasPhotoPlugin.php',
'OCA\\DAV\\CardDAV\\ImageExportPlugin' => __DIR__ . '/..' . '/../lib/CardDAV/ImageExportPlugin.php',
'OCA\\DAV\\CardDAV\\Integration\\ExternalAddressBook' => __DIR__ . '/..' . '/../lib/CardDAV/Integration/ExternalAddressBook.php',
'OCA\\DAV\\CardDAV\\Integration\\IAddressBookProvider' => __DIR__ . '/..' . '/../lib/CardDAV/Integration/IAddressBookProvider.php',
'OCA\\DAV\\CardDAV\\MultiGetExportPlugin' => __DIR__ . '/..' . '/../lib/CardDAV/MultiGetExportPlugin.php',
'OCA\\DAV\\CardDAV\\PhotoCache' => __DIR__ . '/..' . '/../lib/CardDAV/PhotoCache.php',
'OCA\\DAV\\CardDAV\\Plugin' => __DIR__ . '/..' . '/../lib/CardDAV/Plugin.php',
Expand Down
73 changes: 73 additions & 0 deletions apps/dav/lib/AppInfo/PluginManager.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
<?php

declare(strict_types=1);

/**
* @copyright Copyright (c) 2016, ownCloud GmbH.
*
Expand Down Expand Up @@ -26,8 +29,12 @@

use OC\ServerContainer;
use OCA\DAV\CalDAV\Integration\ICalendarProvider;
use OCA\DAV\CardDAV\Integration\IAddressBookProvider;
use OCP\App\IAppManager;
use OCP\AppFramework\QueryException;
use function array_map;
use function class_exists;
use function is_array;

/**
* Manager for DAV plugins from apps, used to register them
Expand Down Expand Up @@ -59,6 +66,13 @@ class PluginManager {
*/
private $collections = null;

/**
* Address book plugins
*
* @var IAddressBookProvider[]|null
*/
private $addressBookPlugins = null;

/**
* Calendar plugins
*
Expand Down Expand Up @@ -101,6 +115,16 @@ public function getAppCollections() {
return $this->collections;
}

/**
* @return IAddressBookProvider[]
*/
public function getAddressBookPlugins(): array {
if ($this->addressBookPlugins === null) {
$this->populate();
}
return $this->addressBookPlugins;
}

/**
* Returns an array of app-registered calendar plugins
*
Expand Down Expand Up @@ -128,6 +152,7 @@ private function populate() {
}
$this->loadSabrePluginsFromInfoXml($this->extractPluginList($info));
$this->loadSabreCollectionsFromInfoXml($this->extractCollectionList($info));
$this->loadSabreAddressBookPluginsFromInfoXml($this->extractAddressBookPluginList($info));
$this->loadSabreCalendarPluginsFromInfoXml($this->extractCalendarPluginList($info));
}
}
Expand Down Expand Up @@ -162,6 +187,29 @@ private function extractCollectionList(array $array) {
return [];
}

/**
* @param array $array
*
* @return string[]
*/
private function extractAddressBookPluginList(array $array):array {
if (!isset($array['sabre']) || !is_array($array['sabre'])) {
return [];
}
if (!isset($array['sabre']['address-book-plugins']) || !is_array($array['sabre']['address-book-plugins'])) {
return [];
}
if (!isset($array['sabre']['address-book-plugins']['plugin'])) {
return [];
}

$items = $array['sabre']['address-book-plugins']['plugin'];
if (!is_array($items)) {
$items = [$items];
}
return $items;
}

private function extractCalendarPluginList(array $array):array {
if (isset($array['sabre']) && is_array($array['sabre'])) {
if (isset($array['sabre']['calendar-plugins']) && is_array($array['sabre']['calendar-plugins'])) {
Expand Down Expand Up @@ -205,6 +253,31 @@ private function loadSabreCollectionsFromInfoXml(array $collections) {
}
}

private function createPluginInstance(string $className) {
try {
return $this->container->query($className);
} catch (QueryException $e) {
if (class_exists($className)) {
return new $className();
}
}

throw new \Exception("Sabre plugin class '$className' is unknown and could not be loaded");
}

/**
* @param string[] $plugin
*/
private function loadSabreAddressBookPluginsFromInfoXml(array $plugins): void {
$this->addressBookPlugins = array_map(function(string $className): IAddressBookProvider {
$instance = $this->createPluginInstance($className);
if (!($instance instanceof IAddressBookProvider)) {
throw new \Exception("Sabre address book plugin class '$className' does not implement the \OCA\DAV\CardDAV\Integration\IAddressBookProvider interface");
}
return $instance;
}, $plugins);
}

private function loadSabreCalendarPluginsFromInfoXml(array $calendarPlugins):void {
foreach ($calendarPlugins as $calendarPlugin) {
try {
Expand Down
113 changes: 113 additions & 0 deletions apps/dav/lib/CardDAV/Integration/ExternalAddressBook.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<?php

declare(strict_types=1);

namespace OCA\DAV\CardDAV\Integration;

use Sabre\CardDAV\IAddressBook;
use Sabre\DAV;

/**
* @since 19.0.0
*/
abstract class ExternalAddressBook implements IAddressBook, DAV\IProperties {

/** @var string */
private const PREFIX = 'app-generated';

/**
* @var string
*
* Double dash is a valid delimiter,
* because it will always split the URIs correctly:
* - our prefix contains only one dash and won't be split
* - appIds are not allowed to contain dashes as per spec:
* > must contain only lowercase ASCII characters and underscore
* - explode has a limit of three, so even if the app-generated
* URI has double dashes, it won't be split
*/
private const DELIMITER = '--';

/** @var string */
private $appId;

/** @var string */
private $uri;

/**
* @param string $appId
* @param string $uri
*/
public function __construct(string $appId, string $uri) {
$this->appId = $appId;
$this->uri = $uri;
}

/**
* @inheritDoc
*/
final public function getName() {
return implode(self::DELIMITER, [
self::PREFIX,
$this->appId,
$this->uri,
]);
}

/**
* @inheritDoc
*/
final public function setName($name) {
throw new DAV\Exception\MethodNotAllowed('Renaming address books is not yet supported');
}

/**
* @inheritDoc
*/
final public function createDirectory($name) {
throw new DAV\Exception\MethodNotAllowed('Creating collections in address book objects is not allowed');

}

/**
* Checks whether the address book uri is app-generated
*
* @param string $uri
*
* @return bool
*/
public static function isAppGeneratedAddressBook(string $uri): bool {
return strpos($uri, self::PREFIX) === 0 && substr_count($uri, self::DELIMITER) >= 2;
}

/**
* Splits an app-generated uri into appId and uri
*
* @param string $uri
*
* @return array
*/
public static function splitAppGeneratedAddressBookUri(string $uri): array {
$array = array_slice(explode(self::DELIMITER, $uri, 3), 1);
// Check the array has expected amount of elements
// and none of them is an empty string
if (\count($array) !== 2 || \in_array('', $array, true)) {
throw new \InvalidArgumentException('Provided address book uri was not app-generated');
}

return $array;
}

/**
* Checks whether a address book name the user wants to create violates
* the reserved name for URIs
*
* @param string $uri
*
* @return bool
*/
public static function doesViolateReservedName(string $uri): bool {
return strpos($uri, self::PREFIX) === 0;
}

}
53 changes: 53 additions & 0 deletions apps/dav/lib/CardDAV/Integration/IAddressBookProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

declare(strict_types=1);

namespace OCA\DAV\CardDAV\Integration;

use Sabre\CardDAV\IAddressBook;

/**
* @since 19.0.0
*/
interface IAddressBookProvider {

/**
* Provides the appId of the plugin
*
* @since 19.0.0
* @return string AppId
*/
public function getAppId(): string;

/**
* Fetches all calendars for a given principal uri
*
* @since 19.0.0
* @param string $principalUri E.g. principals/users/user1
* @return ExternalAddressBook[] Array of all calendars
*/
public function fetchAllForCalendarHome(string $principalUri): array;

/**
* Checks whether plugin has a calendar for a given principalUri and calendarUri
*
* @since 19.0.0
* @param string $principalUri E.g. principals/users/user1
* @param string $calendarUri E.g. personal
* @return bool True if calendar for principalUri and calendarUri exists, false otherwise
*/
public function hasCalendarInCalendarHome(string $principalUri, string $calendarUri): bool;

/**
* Fetches a calendar for a given principalUri and calendarUri
* Returns null if calendar does not exist
*
* @param string $principalUri E.g. principals/users/user1
* @param string $calendarUri E.g. personal
*
* @return ExternalAddressBook|null Calendar if it exists, null otherwise
*@since 19.0.0
*/
public function getCalendarInCalendarHome(string $principalUri, string $calendarUri): ?ExternalAddressBook;

}