diff --git a/apps/cloud_federation_api/appinfo/info.xml b/apps/cloud_federation_api/appinfo/info.xml index f9ca8185dcf55..8de2444ff2aab 100644 --- a/apps/cloud_federation_api/appinfo/info.xml +++ b/apps/cloud_federation_api/appinfo/info.xml @@ -12,8 +12,8 @@ - files - https://github.com/nextcloud/cloud_federation/issues + integration + https://github.com/nextcloud/server/issues diff --git a/apps/cloud_federation_api/lib/Capabilities.php b/apps/cloud_federation_api/lib/Capabilities.php index 1682effb1457c..9b04145caa231 100644 --- a/apps/cloud_federation_api/lib/Capabilities.php +++ b/apps/cloud_federation_api/lib/Capabilities.php @@ -28,18 +28,17 @@ namespace OCA\CloudFederationAPI; -use OC\OCM\Model\OCMProvider; -use OC\OCM\Model\OCMResource; use OCP\Capabilities\ICapability; use OCP\IURLGenerator; use OCP\OCM\Exceptions\OCMArgumentException; +use OCP\OCM\IOCMProvider; class Capabilities implements ICapability { - public const API_VERSION = '1.0-proposal1'; public function __construct( private IURLGenerator $urlGenerator, + private IOCMProvider $provider, ) { } @@ -63,24 +62,23 @@ public function __construct( public function getCapabilities() { $url = $this->urlGenerator->linkToRouteAbsolute('cloud_federation_api.requesthandlercontroller.addShare'); - $provider = new OCMProvider(); - $provider->setEnabled(true); - $provider->setApiVersion(self::API_VERSION); + $this->provider->setEnabled(true); + $this->provider->setApiVersion(self::API_VERSION); $pos = strrpos($url, '/'); if (false === $pos) { throw new OCMArgumentException('generated route should contains a slash character'); } - $provider->setEndPoint(substr($url, 0, $pos)); + $this->provider->setEndPoint(substr($url, 0, $pos)); - $resource = new OCMResource(); + $resource = $this->provider->createNewResourceType(); $resource->setName('file') ->setShareTypes(['user', 'group']) ->setProtocols(['webdav' => '/public.php/webdav/']); - $provider->setResourceTypes([$resource]); + $this->provider->addResourceType($resource); - return ['ocm' => $provider->jsonSerialize()]; + return ['ocm' => $this->provider->jsonSerialize()]; } } diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 108a2f74b5031..4387852556d96 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -537,6 +537,7 @@ 'OCP\\Notification\\IManager' => $baseDir . '/lib/public/Notification/IManager.php', 'OCP\\Notification\\INotification' => $baseDir . '/lib/public/Notification/INotification.php', 'OCP\\Notification\\INotifier' => $baseDir . '/lib/public/Notification/INotifier.php', + 'OCP\\OCM\\Events\\ResourceTypeRegisterEvent' => $baseDir . '/lib/public/OCM/Events/ResourceTypeRegisterEvent.php', 'OCP\\OCM\\Exceptions\\OCMArgumentException' => $baseDir . '/lib/public/OCM/Exceptions/OCMArgumentException.php', 'OCP\\OCM\\Exceptions\\OCMProviderException' => $baseDir . '/lib/public/OCM/Exceptions/OCMProviderException.php', 'OCP\\OCM\\IOCMDiscoveryService' => $baseDir . '/lib/public/OCM/IOCMDiscoveryService.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index ae1f13fc89bde..9b33577d66a26 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -570,6 +570,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\Notification\\IManager' => __DIR__ . '/../../..' . '/lib/public/Notification/IManager.php', 'OCP\\Notification\\INotification' => __DIR__ . '/../../..' . '/lib/public/Notification/INotification.php', 'OCP\\Notification\\INotifier' => __DIR__ . '/../../..' . '/lib/public/Notification/INotifier.php', + 'OCP\\OCM\\Events\\ResourceTypeRegisterEvent' => __DIR__ . '/../../..' . '/lib/public/OCM/Events/ResourceTypeRegisterEvent.php', 'OCP\\OCM\\Exceptions\\OCMArgumentException' => __DIR__ . '/../../..' . '/lib/public/OCM/Exceptions/OCMArgumentException.php', 'OCP\\OCM\\Exceptions\\OCMProviderException' => __DIR__ . '/../../..' . '/lib/public/OCM/Exceptions/OCMProviderException.php', 'OCP\\OCM\\IOCMDiscoveryService' => __DIR__ . '/../../..' . '/lib/public/OCM/IOCMDiscoveryService.php', diff --git a/lib/private/OCM/Model/OCMProvider.php b/lib/private/OCM/Model/OCMProvider.php index 44f4fbebc107c..084d4f8479dc2 100644 --- a/lib/private/OCM/Model/OCMProvider.php +++ b/lib/private/OCM/Model/OCMProvider.php @@ -26,27 +26,36 @@ namespace OC\OCM\Model; -use JsonSerializable; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\OCM\Events\ResourceTypeRegisterEvent; use OCP\OCM\Exceptions\OCMArgumentException; use OCP\OCM\Exceptions\OCMProviderException; use OCP\OCM\IOCMProvider; +use OCP\OCM\IOCMResource; /** * @since 28.0.0 */ -class OCMProvider implements IOCMProvider, JsonSerializable { +class OCMProvider implements IOCMProvider { private bool $enabled = false; private string $apiVersion = ''; private string $endPoint = ''; - /** @var OCMResource[] */ + /** @var IOCMResource[] */ private array $resourceTypes = []; + private bool $emittedEvent = false; + + public function __construct( + protected IEventDispatcher $dispatcher, + ) { + } + /** * @param bool $enabled * - * @return OCMProvider + * @return $this */ - public function setEnabled(bool $enabled): self { + public function setEnabled(bool $enabled): static { $this->enabled = $enabled; return $this; @@ -62,9 +71,9 @@ public function isEnabled(): bool { /** * @param string $apiVersion * - * @return OCMProvider + * @return $this */ - public function setApiVersion(string $apiVersion): self { + public function setApiVersion(string $apiVersion): static { $this->apiVersion = $apiVersion; return $this; @@ -80,9 +89,9 @@ public function getApiVersion(): string { /** * @param string $endPoint * - * @return OCMProvider + * @return $this */ - public function setEndPoint(string $endPoint): self { + public function setEndPoint(string $endPoint): static { $this->endPoint = $endPoint; return $this; @@ -96,31 +105,45 @@ public function getEndPoint(): string { } /** - * @param OCMResource $resource + * create a new resource to later add it with {@see IOCMProvider::addResourceType()} + * @return IOCMResource + */ + public function createNewResourceType(): IOCMResource { + return new OCMResource(); + } + + /** + * @param IOCMResource $resource * * @return $this */ - public function addResourceType(OCMResource $resource): self { + public function addResourceType(IOCMResource $resource): static { $this->resourceTypes[] = $resource; return $this; } /** - * @param OCMResource[] $resourceTypes + * @param IOCMResource[] $resourceTypes * - * @return OCMProvider + * @return $this */ - public function setResourceTypes(array $resourceTypes): self { + public function setResourceTypes(array $resourceTypes): static { $this->resourceTypes = $resourceTypes; return $this; } /** - * @return OCMResource[] + * @return IOCMResource[] */ public function getResourceTypes(): array { + if (!$this->emittedEvent) { + $this->emittedEvent = true; + $event = new ResourceTypeRegisterEvent($this); + $this->dispatcher->dispatchTyped($event); + } + return $this->resourceTypes; } @@ -151,11 +174,11 @@ public function extractProtocolEntry(string $resourceName, string $protocol): st * * @param array $data * - * @return self + * @return $this * @throws OCMProviderException in case a descent provider cannot be generated from data * @see self::jsonSerialize() */ - public function import(array $data): self { + public function import(array $data): static { $this->setEnabled(is_bool($data['enabled'] ?? '') ? $data['enabled'] : false) ->setApiVersion((string)($data['apiVersion'] ?? '')) ->setEndPoint($data['endPoint'] ?? ''); diff --git a/lib/private/OCM/Model/OCMResource.php b/lib/private/OCM/Model/OCMResource.php index 12948aa8949ac..c4a91f2eabf63 100644 --- a/lib/private/OCM/Model/OCMResource.php +++ b/lib/private/OCM/Model/OCMResource.php @@ -26,13 +26,12 @@ namespace OC\OCM\Model; -use JsonSerializable; use OCP\OCM\IOCMResource; /** * @since 28.0.0 */ -class OCMResource implements IOCMResource, JsonSerializable { +class OCMResource implements IOCMResource { private string $name = ''; /** @var string[] */ private array $shareTypes = []; @@ -42,9 +41,9 @@ class OCMResource implements IOCMResource, JsonSerializable { /** * @param string $name * - * @return OCMResource + * @return $this */ - public function setName(string $name): self { + public function setName(string $name): static { $this->name = $name; return $this; @@ -60,9 +59,9 @@ public function getName(): string { /** * @param string[] $shareTypes * - * @return OCMResource + * @return $this */ - public function setShareTypes(array $shareTypes): self { + public function setShareTypes(array $shareTypes): static { $this->shareTypes = $shareTypes; return $this; @@ -80,7 +79,7 @@ public function getShareTypes(): array { * * @return $this */ - public function setProtocols(array $protocols): self { + public function setProtocols(array $protocols): static { $this->protocols = $protocols; return $this; @@ -98,17 +97,16 @@ public function getProtocols(): array { * * @param array $data * - * @return self + * @return $this * @see self::jsonSerialize() */ - public function import(array $data): self { + public function import(array $data): static { return $this->setName((string)($data['name'] ?? '')) ->setShareTypes($data['shareTypes'] ?? []) ->setProtocols($data['protocols'] ?? []); } /** - * * @return array{ * name: string, * shareTypes: string[], diff --git a/lib/private/OCM/OCMDiscoveryService.php b/lib/private/OCM/OCMDiscoveryService.php index e3b1d3508133d..ac9bf2a3965c6 100644 --- a/lib/private/OCM/OCMDiscoveryService.php +++ b/lib/private/OCM/OCMDiscoveryService.php @@ -27,7 +27,6 @@ namespace OC\OCM; use JsonException; -use OC\OCM\Model\OCMProvider; use OCP\AppFramework\Http; use OCP\Http\Client\IClientService; use OCP\ICache; @@ -54,7 +53,8 @@ public function __construct( ICacheFactory $cacheFactory, private IClientService $clientService, private IConfig $config, - private LoggerInterface $logger + private IOCMProvider $provider, + private LoggerInterface $logger, ) { $this->cache = $cacheFactory->createDistributed('ocm-discovery'); } @@ -69,13 +69,12 @@ public function __construct( */ public function discover(string $remote, bool $skipCache = false): IOCMProvider { $remote = rtrim($remote, '/'); - $provider = new OCMProvider(); if (!$skipCache) { try { - $provider->import(json_decode($this->cache->get($remote) ?? '', true, 8, JSON_THROW_ON_ERROR) ?? []); - if ($this->supportedAPIVersion($provider->getApiVersion())) { - return $provider; // if cache looks valid, we use it + $this->provider->import(json_decode($this->cache->get($remote) ?? '', true, 8, JSON_THROW_ON_ERROR) ?? []); + if ($this->supportedAPIVersion($this->provider->getApiVersion())) { + return $this->provider; // if cache looks valid, we use it } } catch (JsonException|OCMProviderException $e) { // we ignore cache on issues @@ -96,7 +95,7 @@ public function discover(string $remote, bool $skipCache = false): IOCMProvider if ($response->getStatusCode() === Http::STATUS_OK) { $body = $response->getBody(); // update provider with data returned by the request - $provider->import(json_decode($body, true, 8, JSON_THROW_ON_ERROR) ?? []); + $this->provider->import(json_decode($body, true, 8, JSON_THROW_ON_ERROR) ?? []); $this->cache->set($remote, $body, 60 * 60 * 24); } } catch (JsonException|OCMProviderException $e) { @@ -109,11 +108,11 @@ public function discover(string $remote, bool $skipCache = false): IOCMProvider throw new OCMProviderException('error while requesting remote ocm provider'); } - if (!$this->supportedAPIVersion($provider->getApiVersion())) { + if (!$this->supportedAPIVersion($this->provider->getApiVersion())) { throw new OCMProviderException('API version not supported'); } - return $provider; + return $this->provider; } /** diff --git a/lib/private/Server.php b/lib/private/Server.php index d2a1d890ccd25..949a7ccfd3f4d 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -124,6 +124,7 @@ use OC\Metadata\IMetadataManager; use OC\Metadata\MetadataManager; use OC\Notification\Manager; +use OC\OCM\Model\OCMProvider; use OC\OCM\OCMDiscoveryService; use OC\OCS\DiscoveryService; use OC\Preview\GeneratorHelper; @@ -232,6 +233,7 @@ use OCP\Log\ILogFactory; use OCP\Mail\IMailer; use OCP\OCM\IOCMDiscoveryService; +use OCP\OCM\IOCMProvider; use OCP\Remote\Api\IApiFactory; use OCP\Remote\IInstanceFactory; use OCP\RichObjectStrings\IValidator; @@ -1426,6 +1428,8 @@ public function __construct($webRoot, \OC\Config $config) { $this->registerAlias(IPhoneNumberUtil::class, PhoneNumberUtil::class); + $this->registerAlias(IOCMProvider::class, OCMProvider::class); + $this->connectDispatcher(); } diff --git a/lib/public/OCM/Events/ResourceTypeRegisterEvent.php b/lib/public/OCM/Events/ResourceTypeRegisterEvent.php new file mode 100644 index 0000000000000..1048d8d0d493b --- /dev/null +++ b/lib/public/OCM/Events/ResourceTypeRegisterEvent.php @@ -0,0 +1,62 @@ + + * + * @author Joas Schilling + * + * @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 . + * + */ + +namespace OCP\OCM\Events; + +use OCP\EventDispatcher\Event; +use OCP\OCM\IOCMProvider; + +/** + * Use this event to register additional OCM resources before the API returns + * them in the OCM provider list and capability + * + * @since 28.0.0 + */ +class ResourceTypeRegisterEvent extends Event { + /** + * @param IOCMProvider $provider + * @since 28.0.0 + */ + public function __construct( + protected IOCMProvider $provider, + ) { + parent::__construct(); + } + + /** + * @param string $name + * @param list $shareTypes List of supported share recipients, e.g. 'user', 'group', … + * @param array $protocols List of supported protocols and their location, + * e.g. ['webdav' => '/remote.php/webdav/'] + * @since 28.0.0 + */ + public function registerResourceType(string $name, array $shareTypes, array $protocols): void { + $resourceType = $this->provider->createNewResourceType(); + $resourceType->setName($name) + ->setShareTypes($shareTypes) + ->setProtocols($protocols); + $this->provider->addResourceType($resourceType); + } +} diff --git a/lib/public/OCM/IOCMProvider.php b/lib/public/OCM/IOCMProvider.php index f99ccf1cd238e..6df7eed370c50 100644 --- a/lib/public/OCM/IOCMProvider.php +++ b/lib/public/OCM/IOCMProvider.php @@ -26,7 +26,7 @@ namespace OCP\OCM; -use OC\OCM\Model\OCMResource; +use JsonSerializable; use OCP\OCM\Exceptions\OCMArgumentException; use OCP\OCM\Exceptions\OCMProviderException; @@ -35,16 +35,16 @@ * @link https://github.com/cs3org/OCM-API/ * @since 28.0.0 */ -interface IOCMProvider { +interface IOCMProvider extends JsonSerializable { /** * enable OCM * * @param bool $enabled * - * @return self + * @return $this * @since 28.0.0 */ - public function setEnabled(bool $enabled): self; + public function setEnabled(bool $enabled): static; /** * is set as enabled ? @@ -59,10 +59,10 @@ public function isEnabled(): bool; * * @param string $apiVersion * - * @return self + * @return $this * @since 28.0.0 */ - public function setApiVersion(string $apiVersion): self; + public function setApiVersion(string $apiVersion): static; /** * returns API version @@ -77,10 +77,10 @@ public function getApiVersion(): string; * * @param string $endPoint * - * @return self + * @return $this * @since 28.0.0 */ - public function setEndPoint(string $endPoint): self; + public function setEndPoint(string $endPoint): static; /** * get configured endpoint @@ -90,25 +90,32 @@ public function setEndPoint(string $endPoint): self; */ public function getEndPoint(): string; + /** + * create a new resource to later add it with {@see addResourceType()} + * @return IOCMResource + * @since 28.0.0 + */ + public function createNewResourceType(): IOCMResource; + /** * add a single resource to the object * - * @param OCMResource $resource + * @param IOCMResource $resource * - * @return self + * @return $this * @since 28.0.0 */ - public function addResourceType(OCMResource $resource): self; + public function addResourceType(IOCMResource $resource): static; /** * set resources * - * @param OCMResource[] $resourceTypes + * @param IOCMResource[] $resourceTypes * - * @return self + * @return $this * @since 28.0.0 */ - public function setResourceTypes(array $resourceTypes): self; + public function setResourceTypes(array $resourceTypes): static; /** * get all set resources @@ -135,9 +142,24 @@ public function extractProtocolEntry(string $resourceName, string $protocol): st * * @param array $data * - * @return self + * @return $this * @throws OCMProviderException in case a descent provider cannot be generated from data * @since 28.0.0 */ - public function import(array $data): self; + public function import(array $data): static; + + /** + * @return array{ + * enabled: bool, + * apiVersion: string, + * endPoint: string, + * resourceTypes: array{ + * name: string, + * shareTypes: string[], + * protocols: array + * }[] + * } + * @since 28.0.0 + */ + public function jsonSerialize(): array; } diff --git a/lib/public/OCM/IOCMResource.php b/lib/public/OCM/IOCMResource.php index 381af61cecc4c..242c77116f194 100644 --- a/lib/public/OCM/IOCMResource.php +++ b/lib/public/OCM/IOCMResource.php @@ -26,22 +26,24 @@ namespace OCP\OCM; +use JsonSerializable; + /** * Model based on the Open Cloud Mesh Discovery API * * @link https://github.com/cs3org/OCM-API/ * @since 28.0.0 */ -interface IOCMResource { +interface IOCMResource extends JsonSerializable { /** * set name of the resource * * @param string $name * - * @return self + * @return $this * @since 28.0.0 */ - public function setName(string $name): self; + public function setName(string $name): static; /** * get name of the resource @@ -56,10 +58,10 @@ public function getName(): string; * * @param string[] $shareTypes * - * @return self + * @return $this * @since 28.0.0 */ - public function setShareTypes(array $shareTypes): self; + public function setShareTypes(array $shareTypes): static; /** * get share types @@ -74,10 +76,10 @@ public function getShareTypes(): array; * * @param array $protocols * - * @return self + * @return $this * @since 28.0.0 */ - public function setProtocols(array $protocols): self; + public function setProtocols(array $protocols): static; /** * get configured protocols @@ -92,8 +94,18 @@ public function getProtocols(): array; * * @param array $data * - * @return self + * @return $this + * @since 28.0.0 + */ + public function import(array $data): static; + + /** + * @return array{ + * name: string, + * shareTypes: string[], + * protocols: array + * } * @since 28.0.0 */ - public function import(array $data): self; + public function jsonSerialize(): array; }