aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJulius Härtl <jus@bitgrid.net>2023-01-27 12:35:44 +0100
committerGitHub <noreply@github.com>2023-01-27 12:35:44 +0100
commita63b5575687bb838c82f86a11a1545668157dc71 (patch)
treeaa0768aeee92df212b2af031a2032c1e906e42bd
parent8744029e14f0003f0878a953478c24394687c01a (diff)
parent81c2122ff8214332e2aeca5724b79c565b0979b3 (diff)
downloadnextcloud-server-a63b5575687bb838c82f86a11a1545668157dc71.tar.gz
nextcloud-server-a63b5575687bb838c82f86a11a1545668157dc71.zip
Merge pull request #35557 from nextcloud/enh/31667/extend-reference-api-for-frontend-picker
-rw-r--r--build/psalm-baseline.xml5
-rw-r--r--core/Controller/ReferenceApiController.php30
-rw-r--r--core/routes.php2
-rw-r--r--lib/base.php5
-rw-r--r--lib/composer/composer/autoload_classmap.php4
-rw-r--r--lib/composer/composer/autoload_static.php4
-rw-r--r--lib/private/Collaboration/Reference/File/FileReferenceEventListener.php4
-rw-r--r--lib/private/Collaboration/Reference/ReferenceManager.php116
-rw-r--r--lib/private/Collaboration/Reference/RenderReferenceEventListener.php66
-rw-r--r--lib/public/Collaboration/Reference/ADiscoverableReferenceProvider.php48
-rw-r--r--lib/public/Collaboration/Reference/IDiscoverableReferenceProvider.php60
-rw-r--r--lib/public/Collaboration/Reference/IReferenceManager.php28
-rw-r--r--lib/public/Collaboration/Reference/ISearchableReferenceProvider.php36
13 files changed, 405 insertions, 3 deletions
diff --git a/build/psalm-baseline.xml b/build/psalm-baseline.xml
index ed7b19eaa7f..c1c39c135f8 100644
--- a/build/psalm-baseline.xml
+++ b/build/psalm-baseline.xml
@@ -2015,6 +2015,11 @@
<code>addServiceListener</code>
</InvalidArgument>
</file>
+ <file src="lib/private/Collaboration/Reference/RenderReferenceEventListener.php">
+ <InvalidArgument occurrences="1">
+ <code>addServiceListener</code>
+ </InvalidArgument>
+ </file>
<file src="lib/private/Command/CallableJob.php">
<ParamNameMismatch occurrences="1">
<code>$serializedCallable</code>
diff --git a/core/Controller/ReferenceApiController.php b/core/Controller/ReferenceApiController.php
index 266532113d8..6aba56d7e77 100644
--- a/core/Controller/ReferenceApiController.php
+++ b/core/Controller/ReferenceApiController.php
@@ -25,15 +25,21 @@ declare(strict_types=1);
namespace OC\Core\Controller;
use OCP\AppFramework\Http\DataResponse;
+use OCP\Collaboration\Reference\IDiscoverableReferenceProvider;
use OCP\Collaboration\Reference\IReferenceManager;
use OCP\IRequest;
class ReferenceApiController extends \OCP\AppFramework\OCSController {
private IReferenceManager $referenceManager;
+ private ?string $userId;
- public function __construct(string $appName, IRequest $request, IReferenceManager $referenceManager) {
+ public function __construct(string $appName,
+ IRequest $request,
+ IReferenceManager $referenceManager,
+ ?string $userId) {
parent::__construct($appName, $request);
$this->referenceManager = $referenceManager;
+ $this->userId = $userId;
}
/**
@@ -88,4 +94,26 @@ class ReferenceApiController extends \OCP\AppFramework\OCSController {
'references' => array_filter($result)
]);
}
+
+ /**
+ * @NoAdminRequired
+ */
+ public function getProvidersInfo(): DataResponse {
+ $providers = $this->referenceManager->getDiscoverableProviders();
+ $jsonProviders = array_map(static function (IDiscoverableReferenceProvider $provider) {
+ return $provider->jsonSerialize();
+ }, $providers);
+ return new DataResponse($jsonProviders);
+ }
+
+ /**
+ * @NoAdminRequired
+ */
+ public function touchProvider(string $providerId, ?int $timestamp = null): DataResponse {
+ if ($this->userId !== null) {
+ $success = $this->referenceManager->touchProvider($this->userId, $providerId, $timestamp);
+ return new DataResponse(['success' => $success]);
+ }
+ return new DataResponse(['success' => false]);
+ }
}
diff --git a/core/routes.php b/core/routes.php
index a3fdfafd7bf..dcf8e4024af 100644
--- a/core/routes.php
+++ b/core/routes.php
@@ -134,6 +134,8 @@ $application->registerRoutes($this, [
['root' => '/references', 'name' => 'ReferenceApi#resolveOne', 'url' => '/resolve', 'verb' => 'GET'],
['root' => '/references', 'name' => 'ReferenceApi#extract', 'url' => '/extract', 'verb' => 'POST'],
['root' => '/references', 'name' => 'ReferenceApi#resolve', 'url' => '/resolve', 'verb' => 'POST'],
+ ['root' => '/references', 'name' => 'ReferenceApi#getProvidersInfo', 'url' => '/providers', 'verb' => 'GET'],
+ ['root' => '/references', 'name' => 'ReferenceApi#touchProvider', 'url' => '/provider/{providerId}', 'verb' => 'PUT'],
['root' => '/profile', 'name' => 'ProfileApi#setVisibility', 'url' => '/{targetUserId}', 'verb' => 'PUT'],
diff --git a/lib/base.php b/lib/base.php
index 6186e29bd6d..b5c5845b5a0 100644
--- a/lib/base.php
+++ b/lib/base.php
@@ -762,6 +762,7 @@ class OC {
self::registerAccountHooks();
self::registerResourceCollectionHooks();
self::registerFileReferenceEventListener();
+ self::registerRenderReferenceEventListener();
self::registerAppRestrictionsHooks();
// Make sure that the application class is not loaded before the database is setup
@@ -925,6 +926,10 @@ class OC {
\OC\Collaboration\Reference\File\FileReferenceEventListener::register(Server::get(IEventDispatcher::class));
}
+ private static function registerRenderReferenceEventListener() {
+ \OC\Collaboration\Reference\RenderReferenceEventListener::register(Server::get(IEventDispatcher::class));
+ }
+
/**
* register hooks for sharing
*/
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php
index 853d97f4a2e..465ea6b20e3 100644
--- a/lib/composer/composer/autoload_classmap.php
+++ b/lib/composer/composer/autoload_classmap.php
@@ -144,9 +144,12 @@ return array(
'OCP\\Collaboration\\Collaborators\\ISearchPlugin' => $baseDir . '/lib/public/Collaboration/Collaborators/ISearchPlugin.php',
'OCP\\Collaboration\\Collaborators\\ISearchResult' => $baseDir . '/lib/public/Collaboration/Collaborators/ISearchResult.php',
'OCP\\Collaboration\\Collaborators\\SearchResultType' => $baseDir . '/lib/public/Collaboration/Collaborators/SearchResultType.php',
+ 'OCP\\Collaboration\\Reference\\ADiscoverableReferenceProvider' => $baseDir . '/lib/public/Collaboration/Reference/ADiscoverableReferenceProvider.php',
+ 'OCP\\Collaboration\\Reference\\IDiscoverableReferenceProvider' => $baseDir . '/lib/public/Collaboration/Reference/IDiscoverableReferenceProvider.php',
'OCP\\Collaboration\\Reference\\IReference' => $baseDir . '/lib/public/Collaboration/Reference/IReference.php',
'OCP\\Collaboration\\Reference\\IReferenceManager' => $baseDir . '/lib/public/Collaboration/Reference/IReferenceManager.php',
'OCP\\Collaboration\\Reference\\IReferenceProvider' => $baseDir . '/lib/public/Collaboration/Reference/IReferenceProvider.php',
+ 'OCP\\Collaboration\\Reference\\ISearchableReferenceProvider' => $baseDir . '/lib/public/Collaboration/Reference/ISearchableReferenceProvider.php',
'OCP\\Collaboration\\Reference\\Reference' => $baseDir . '/lib/public/Collaboration/Reference/Reference.php',
'OCP\\Collaboration\\Reference\\RenderReferenceEvent' => $baseDir . '/lib/public/Collaboration/Reference/RenderReferenceEvent.php',
'OCP\\Collaboration\\Resources\\CollectionException' => $baseDir . '/lib/public/Collaboration/Resources/CollectionException.php',
@@ -837,6 +840,7 @@ return array(
'OC\\Collaboration\\Reference\\File\\FileReferenceProvider' => $baseDir . '/lib/private/Collaboration/Reference/File/FileReferenceProvider.php',
'OC\\Collaboration\\Reference\\LinkReferenceProvider' => $baseDir . '/lib/private/Collaboration/Reference/LinkReferenceProvider.php',
'OC\\Collaboration\\Reference\\ReferenceManager' => $baseDir . '/lib/private/Collaboration/Reference/ReferenceManager.php',
+ 'OC\\Collaboration\\Reference\\RenderReferenceEventListener' => $baseDir . '/lib/private/Collaboration/Reference/RenderReferenceEventListener.php',
'OC\\Collaboration\\Resources\\Collection' => $baseDir . '/lib/private/Collaboration/Resources/Collection.php',
'OC\\Collaboration\\Resources\\Listener' => $baseDir . '/lib/private/Collaboration/Resources/Listener.php',
'OC\\Collaboration\\Resources\\Manager' => $baseDir . '/lib/private/Collaboration/Resources/Manager.php',
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index 455eccc45fd..bf19606cb4a 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.php
@@ -177,9 +177,12 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\Collaboration\\Collaborators\\ISearchPlugin' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Collaborators/ISearchPlugin.php',
'OCP\\Collaboration\\Collaborators\\ISearchResult' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Collaborators/ISearchResult.php',
'OCP\\Collaboration\\Collaborators\\SearchResultType' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Collaborators/SearchResultType.php',
+ 'OCP\\Collaboration\\Reference\\ADiscoverableReferenceProvider' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Reference/ADiscoverableReferenceProvider.php',
+ 'OCP\\Collaboration\\Reference\\IDiscoverableReferenceProvider' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Reference/IDiscoverableReferenceProvider.php',
'OCP\\Collaboration\\Reference\\IReference' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Reference/IReference.php',
'OCP\\Collaboration\\Reference\\IReferenceManager' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Reference/IReferenceManager.php',
'OCP\\Collaboration\\Reference\\IReferenceProvider' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Reference/IReferenceProvider.php',
+ 'OCP\\Collaboration\\Reference\\ISearchableReferenceProvider' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Reference/ISearchableReferenceProvider.php',
'OCP\\Collaboration\\Reference\\Reference' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Reference/Reference.php',
'OCP\\Collaboration\\Reference\\RenderReferenceEvent' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Reference/RenderReferenceEvent.php',
'OCP\\Collaboration\\Resources\\CollectionException' => __DIR__ . '/../../..' . '/lib/public/Collaboration/Resources/CollectionException.php',
@@ -870,6 +873,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Collaboration\\Reference\\File\\FileReferenceProvider' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Reference/File/FileReferenceProvider.php',
'OC\\Collaboration\\Reference\\LinkReferenceProvider' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Reference/LinkReferenceProvider.php',
'OC\\Collaboration\\Reference\\ReferenceManager' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Reference/ReferenceManager.php',
+ 'OC\\Collaboration\\Reference\\RenderReferenceEventListener' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Reference/RenderReferenceEventListener.php',
'OC\\Collaboration\\Resources\\Collection' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Resources/Collection.php',
'OC\\Collaboration\\Resources\\Listener' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Resources/Listener.php',
'OC\\Collaboration\\Resources\\Manager' => __DIR__ . '/../../..' . '/lib/private/Collaboration/Resources/Manager.php',
diff --git a/lib/private/Collaboration/Reference/File/FileReferenceEventListener.php b/lib/private/Collaboration/Reference/File/FileReferenceEventListener.php
index 6ccae9903dc..cb53b496406 100644
--- a/lib/private/Collaboration/Reference/File/FileReferenceEventListener.php
+++ b/lib/private/Collaboration/Reference/File/FileReferenceEventListener.php
@@ -27,11 +27,13 @@ namespace OC\Collaboration\Reference\File;
use OCP\Collaboration\Reference\IReferenceManager;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventDispatcher;
+use OCP\EventDispatcher\IEventListener;
use OCP\Files\Events\Node\NodeDeletedEvent;
use OCP\Share\Events\ShareCreatedEvent;
use OCP\Share\Events\ShareDeletedEvent;
-class FileReferenceEventListener implements \OCP\EventDispatcher\IEventListener {
+/** @template-implements IEventListener<Event|NodeDeletedEvent|ShareDeletedEvent|ShareCreatedEvent> */
+class FileReferenceEventListener implements IEventListener {
private IReferenceManager $manager;
public function __construct(IReferenceManager $manager) {
diff --git a/lib/private/Collaboration/Reference/ReferenceManager.php b/lib/private/Collaboration/Reference/ReferenceManager.php
index a11d92fe2a9..2897410f5d6 100644
--- a/lib/private/Collaboration/Reference/ReferenceManager.php
+++ b/lib/private/Collaboration/Reference/ReferenceManager.php
@@ -26,13 +26,16 @@ namespace OC\Collaboration\Reference;
use OC\AppFramework\Bootstrap\Coordinator;
use OC\Collaboration\Reference\File\FileReferenceProvider;
+use OCP\Collaboration\Reference\IDiscoverableReferenceProvider;
use OCP\Collaboration\Reference\IReference;
use OCP\Collaboration\Reference\IReferenceManager;
use OCP\Collaboration\Reference\IReferenceProvider;
use OCP\Collaboration\Reference\Reference;
use OCP\ICache;
use OCP\ICacheFactory;
+use OCP\IConfig;
use OCP\IURLGenerator;
+use OCP\IUserSession;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
use Throwable;
@@ -47,15 +50,31 @@ class ReferenceManager implements IReferenceManager {
private ContainerInterface $container;
private LinkReferenceProvider $linkReferenceProvider;
private LoggerInterface $logger;
+ private IConfig $config;
+ private IUserSession $userSession;
- public function __construct(LinkReferenceProvider $linkReferenceProvider, ICacheFactory $cacheFactory, Coordinator $coordinator, ContainerInterface $container, LoggerInterface $logger) {
+ public function __construct(LinkReferenceProvider $linkReferenceProvider,
+ ICacheFactory $cacheFactory,
+ Coordinator $coordinator,
+ ContainerInterface $container,
+ LoggerInterface $logger,
+ IConfig $config,
+ IUserSession $userSession) {
$this->linkReferenceProvider = $linkReferenceProvider;
$this->cache = $cacheFactory->createDistributed('reference');
$this->coordinator = $coordinator;
$this->container = $container;
$this->logger = $logger;
+ $this->config = $config;
+ $this->userSession = $userSession;
}
+ /**
+ * Extract a list of URLs from a text
+ *
+ * @param string $text
+ * @return string[]
+ */
public function extractReferences(string $text): array {
preg_match_all(IURLGenerator::URL_REGEX, $text, $matches);
$references = $matches[0] ?? [];
@@ -64,6 +83,12 @@ class ReferenceManager implements IReferenceManager {
}, $references);
}
+ /**
+ * Try to get a cached reference object from a reference string
+ *
+ * @param string $referenceId
+ * @return IReference|null
+ */
public function getReferenceFromCache(string $referenceId): ?IReference {
$matchedProvider = $this->getMatchedProvider($referenceId);
@@ -75,6 +100,12 @@ class ReferenceManager implements IReferenceManager {
return $this->getReferenceByCacheKey($cacheKey);
}
+ /**
+ * Try to get a cached reference object from a full cache key
+ *
+ * @param string $cacheKey
+ * @return IReference|null
+ */
public function getReferenceByCacheKey(string $cacheKey): ?IReference {
$cached = $this->cache->get($cacheKey);
if ($cached) {
@@ -84,6 +115,13 @@ class ReferenceManager implements IReferenceManager {
return null;
}
+ /**
+ * Get a reference object from a reference string with a matching provider
+ * Use a cached reference if possible
+ *
+ * @param string $referenceId
+ * @return IReference|null
+ */
public function resolveReference(string $referenceId): ?IReference {
$matchedProvider = $this->getMatchedProvider($referenceId);
@@ -106,6 +144,13 @@ class ReferenceManager implements IReferenceManager {
return null;
}
+ /**
+ * Try to match a reference string with all the registered providers
+ * Fallback to the link reference provider (using OpenGraph)
+ *
+ * @param string $referenceId
+ * @return IReferenceProvider|null the first matching provider
+ */
private function getMatchedProvider(string $referenceId): ?IReferenceProvider {
$matchedProvider = null;
foreach ($this->getProviders() as $provider) {
@@ -122,6 +167,13 @@ class ReferenceManager implements IReferenceManager {
return $matchedProvider;
}
+ /**
+ * Get a hashed full cache key from a key and prefix given by a provider
+ *
+ * @param IReferenceProvider $provider
+ * @param string $referenceId
+ * @return string
+ */
private function getFullCacheKey(IReferenceProvider $provider, string $referenceId): string {
$cacheKey = $provider->getCacheKey($referenceId);
return md5($provider->getCachePrefix($referenceId)) . (
@@ -129,6 +181,13 @@ class ReferenceManager implements IReferenceManager {
);
}
+ /**
+ * Remove a specific cache entry from its key+prefix
+ *
+ * @param string $cachePrefix
+ * @param string|null $cacheKey
+ * @return void
+ */
public function invalidateCache(string $cachePrefix, ?string $cacheKey = null): void {
if ($cacheKey === null) {
$this->cache->clear(md5($cachePrefix));
@@ -167,4 +226,59 @@ class ReferenceManager implements IReferenceManager {
return $this->providers;
}
+
+ /**
+ * @inheritDoc
+ */
+ public function getDiscoverableProviders(): array {
+ // preserve 0 based index to avoid returning an object in data responses
+ return array_values(
+ array_filter($this->getProviders(), static function (IReferenceProvider $provider) {
+ return $provider instanceof IDiscoverableReferenceProvider;
+ })
+ );
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function touchProvider(string $userId, string $providerId, ?int $timestamp = null): bool {
+ $providers = $this->getDiscoverableProviders();
+ $matchingProviders = array_filter($providers, static function (IDiscoverableReferenceProvider $provider) use ($providerId) {
+ return $provider->getId() === $providerId;
+ });
+ if (!empty($matchingProviders)) {
+ if ($timestamp === null) {
+ $timestamp = time();
+ }
+
+ $configKey = 'provider-last-use_' . $providerId;
+ $this->config->setUserValue($userId, 'references', $configKey, (string) $timestamp);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getUserProviderTimestamps(): array {
+ $user = $this->userSession->getUser();
+ if ($user === null) {
+ return [];
+ }
+ $userId = $user->getUID();
+ $keys = $this->config->getUserKeys($userId, 'references');
+ $prefix = 'provider-last-use_';
+ $keys = array_filter($keys, static function (string $key) use ($prefix) {
+ return str_starts_with($key, $prefix);
+ });
+ $timestamps = [];
+ foreach ($keys as $key) {
+ $providerId = substr($key, strlen($prefix));
+ $timestamp = (int) $this->config->getUserValue($userId, 'references', $key);
+ $timestamps[$providerId] = $timestamp;
+ }
+ return $timestamps;
+ }
}
diff --git a/lib/private/Collaboration/Reference/RenderReferenceEventListener.php b/lib/private/Collaboration/Reference/RenderReferenceEventListener.php
new file mode 100644
index 00000000000..dc2c5612666
--- /dev/null
+++ b/lib/private/Collaboration/Reference/RenderReferenceEventListener.php
@@ -0,0 +1,66 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2022 Julien Veyssier <eneiluj@posteo.net>
+ *
+ * @author Julien Veyssier <eneiluj@posteo.net>
+ *
+ * @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 OC\Collaboration\Reference;
+
+use OCP\Collaboration\Reference\IDiscoverableReferenceProvider;
+use OCP\Collaboration\Reference\IReferenceManager;
+use OCP\Collaboration\Reference\RenderReferenceEvent;
+use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IEventDispatcher;
+use OCP\EventDispatcher\IEventListener;
+use OCP\IInitialStateService;
+
+/** @template-implements IEventListener<Event|RenderReferenceEvent> */
+class RenderReferenceEventListener implements IEventListener {
+ private IReferenceManager $manager;
+ private IInitialStateService $initialStateService;
+
+ public function __construct(IReferenceManager $manager, IInitialStateService $initialStateService) {
+ $this->manager = $manager;
+ $this->initialStateService = $initialStateService;
+ }
+
+ public static function register(IEventDispatcher $eventDispatcher): void {
+ $eventDispatcher->addServiceListener(RenderReferenceEvent::class, RenderReferenceEventListener::class);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function handle(Event $event): void {
+ if (!($event instanceof RenderReferenceEvent)) {
+ return;
+ }
+
+ $providers = $this->manager->getDiscoverableProviders();
+ $jsonProviders = array_map(static function (IDiscoverableReferenceProvider $provider) {
+ return $provider->jsonSerialize();
+ }, $providers);
+ $this->initialStateService->provideInitialState('core', 'reference-provider-list', $jsonProviders);
+
+ $timestamps = $this->manager->getUserProviderTimestamps();
+ $this->initialStateService->provideInitialState('core', 'reference-provider-timestamps', $timestamps);
+ }
+}
diff --git a/lib/public/Collaboration/Reference/ADiscoverableReferenceProvider.php b/lib/public/Collaboration/Reference/ADiscoverableReferenceProvider.php
new file mode 100644
index 00000000000..9fa6fe691c9
--- /dev/null
+++ b/lib/public/Collaboration/Reference/ADiscoverableReferenceProvider.php
@@ -0,0 +1,48 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2022 Julien Veyssier <eneiluj@posteo.net>
+ *
+ * @author Julien Veyssier <eneiluj@posteo.net>
+ *
+ * @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 OCP\Collaboration\Reference;
+
+use JsonSerializable;
+
+/**
+ * @since 26.0.0
+ */
+abstract class ADiscoverableReferenceProvider implements IDiscoverableReferenceProvider, JsonSerializable {
+ /**
+ * @since 26.0.0
+ */
+ public function jsonSerialize(): array {
+ $json = [
+ 'id' => $this->getId(),
+ 'title' => $this->getTitle(),
+ 'icon_url' => $this->getIconUrl(),
+ 'order' => $this->getOrder(),
+ ];
+ if ($this instanceof ISearchableReferenceProvider) {
+ $json['search_providers_ids'] = $this->getSupportedSearchProviderIds();
+ }
+ return $json;
+ }
+}
diff --git a/lib/public/Collaboration/Reference/IDiscoverableReferenceProvider.php b/lib/public/Collaboration/Reference/IDiscoverableReferenceProvider.php
new file mode 100644
index 00000000000..847e03c602e
--- /dev/null
+++ b/lib/public/Collaboration/Reference/IDiscoverableReferenceProvider.php
@@ -0,0 +1,60 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2022 Julien Veyssier <eneiluj@posteo.net>
+ *
+ * @author Julien Veyssier <eneiluj@posteo.net>
+ *
+ * @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 OCP\Collaboration\Reference;
+
+/**
+ * @since 26.0.0
+ */
+interface IDiscoverableReferenceProvider extends IReferenceProvider {
+ /**
+ * @return string Unique id that identifies the reference provider
+ * @since 26.0.0
+ */
+ public function getId(): string;
+
+ /**
+ * @return string User facing title of the widget
+ * @since 26.0.0
+ */
+ public function getTitle(): string;
+
+ /**
+ * @return int Initial order for reference provider sorting
+ * @since 26.0.0
+ */
+ public function getOrder(): int;
+
+ /**
+ * @return string url to an icon that can be displayed next to the reference provider title
+ * @since 26.0.0
+ */
+ public function getIconUrl(): string;
+
+ /**
+ * @return array representation of the provider
+ * @since 26.0.0
+ */
+ public function jsonSerialize(): array;
+}
diff --git a/lib/public/Collaboration/Reference/IReferenceManager.php b/lib/public/Collaboration/Reference/IReferenceManager.php
index 487e243c7ed..d757d35834a 100644
--- a/lib/public/Collaboration/Reference/IReferenceManager.php
+++ b/lib/public/Collaboration/Reference/IReferenceManager.php
@@ -67,4 +67,32 @@ interface IReferenceManager {
* @since 25.0.0
*/
public function invalidateCache(string $cachePrefix, ?string $cacheKey = null): void;
+
+ /**
+ * Get information on discoverable reference providers (id, title, icon and order)
+ * If the provider is searchable, also get the list of supported unified search providers
+ *
+ * @return IDiscoverableReferenceProvider[]
+ * @since 26.0.0
+ */
+ public function getDiscoverableProviders(): array;
+
+ /**
+ * Update or set the last used timestamp for a provider
+ *
+ * @param string $userId
+ * @param string $providerId
+ * @param int|null $timestamp use current timestamp if null
+ * @return bool
+ * @since 26.0.0
+ */
+ public function touchProvider(string $userId, string $providerId, ?int $timestamp = null): bool;
+
+ /**
+ * Get all known last used timestamps for reference providers
+ *
+ * @return int[]
+ * @since 26.0.0
+ */
+ public function getUserProviderTimestamps(): array;
}
diff --git a/lib/public/Collaboration/Reference/ISearchableReferenceProvider.php b/lib/public/Collaboration/Reference/ISearchableReferenceProvider.php
new file mode 100644
index 00000000000..b863de468a8
--- /dev/null
+++ b/lib/public/Collaboration/Reference/ISearchableReferenceProvider.php
@@ -0,0 +1,36 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2022 Julien Veyssier <eneiluj@posteo.net>
+ *
+ * @author Julien Veyssier <eneiluj@posteo.net>
+ *
+ * @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 OCP\Collaboration\Reference;
+
+/**
+ * @since 26.0.0
+ */
+interface ISearchableReferenceProvider extends IDiscoverableReferenceProvider {
+ /**
+ * @return string[] list of search provider IDs that can be used by the vue-richtext picker
+ * @since 26.0.0
+ */
+ public function getSupportedSearchProviderIds(): array;
+}