aboutsummaryrefslogtreecommitdiffstats
path: root/lib/public/Collaboration/Reference
diff options
context:
space:
mode:
Diffstat (limited to 'lib/public/Collaboration/Reference')
-rw-r--r--lib/public/Collaboration/Reference/ADiscoverableReferenceProvider.php20
-rw-r--r--lib/public/Collaboration/Reference/IDiscoverableReferenceProvider.php20
-rw-r--r--lib/public/Collaboration/Reference/IPublicReferenceProvider.php33
-rw-r--r--lib/public/Collaboration/Reference/IReference.php27
-rw-r--r--lib/public/Collaboration/Reference/IReferenceManager.php26
-rw-r--r--lib/public/Collaboration/Reference/IReferenceProvider.php20
-rw-r--r--lib/public/Collaboration/Reference/ISearchableReferenceProvider.php20
-rw-r--r--lib/public/Collaboration/Reference/LinkReferenceProvider.php227
-rw-r--r--lib/public/Collaboration/Reference/Reference.php26
-rw-r--r--lib/public/Collaboration/Reference/RenderReferenceEvent.php26
10 files changed, 296 insertions, 149 deletions
diff --git a/lib/public/Collaboration/Reference/ADiscoverableReferenceProvider.php b/lib/public/Collaboration/Reference/ADiscoverableReferenceProvider.php
index 9fa6fe691c9..582f51beea3 100644
--- a/lib/public/Collaboration/Reference/ADiscoverableReferenceProvider.php
+++ b/lib/public/Collaboration/Reference/ADiscoverableReferenceProvider.php
@@ -2,24 +2,8 @@
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/>.
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCP\Collaboration\Reference;
diff --git a/lib/public/Collaboration/Reference/IDiscoverableReferenceProvider.php b/lib/public/Collaboration/Reference/IDiscoverableReferenceProvider.php
index 847e03c602e..af4c6b3b1f6 100644
--- a/lib/public/Collaboration/Reference/IDiscoverableReferenceProvider.php
+++ b/lib/public/Collaboration/Reference/IDiscoverableReferenceProvider.php
@@ -2,24 +2,8 @@
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/>.
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCP\Collaboration\Reference;
diff --git a/lib/public/Collaboration/Reference/IPublicReferenceProvider.php b/lib/public/Collaboration/Reference/IPublicReferenceProvider.php
new file mode 100644
index 00000000000..db6c3d3828b
--- /dev/null
+++ b/lib/public/Collaboration/Reference/IPublicReferenceProvider.php
@@ -0,0 +1,33 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\Collaboration\Reference;
+
+/**
+ * @since 30.0.0
+ */
+interface IPublicReferenceProvider extends IReferenceProvider {
+ /**
+ * Return a reference with its metadata for a given reference identifier and sharingToken
+ *
+ * @since 30.0.0
+ */
+ public function resolveReferencePublic(string $referenceText, string $sharingToken): ?IReference;
+
+ /**
+ * Return a custom cache key to be used for caching the metadata
+ * This could be for example the current sharingToken if the reference
+ * access permissions are different for each share
+ *
+ * Should return null, if the cache is only related to the
+ * reference id and has no further dependency
+ *
+ * @since 30.0.0
+ */
+ public function getCacheKeyPublic(string $referenceId, string $sharingToken): ?string;
+}
diff --git a/lib/public/Collaboration/Reference/IReference.php b/lib/public/Collaboration/Reference/IReference.php
index 1b9157fd9b1..eaa82c323b5 100644
--- a/lib/public/Collaboration/Reference/IReference.php
+++ b/lib/public/Collaboration/Reference/IReference.php
@@ -2,24 +2,8 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2022 Julius Härtl <jus@bitgrid.net>
- *
- * @author Julius Härtl <jus@bitgrid.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/>.
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCP\Collaboration\Reference;
@@ -126,4 +110,11 @@ interface IReference extends JsonSerializable {
* @since 25.0.0
*/
public function getOpenGraphObject(): array;
+
+ /**
+ * @return array{richObjectType: string, richObject: array<string, mixed>, openGraphObject: array{id: string, name: string, description: ?string, thumb: ?string, link: string}, accessible: bool}
+ *
+ * @since 25.0.0
+ */
+ public function jsonSerialize(): array;
}
diff --git a/lib/public/Collaboration/Reference/IReferenceManager.php b/lib/public/Collaboration/Reference/IReferenceManager.php
index d757d35834a..c3cf7ca8e7b 100644
--- a/lib/public/Collaboration/Reference/IReferenceManager.php
+++ b/lib/public/Collaboration/Reference/IReferenceManager.php
@@ -2,24 +2,8 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2022 Julius Härtl <jus@bitgrid.net>
- *
- * @author Julius Härtl <jus@bitgrid.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/>.
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCP\Collaboration\Reference;
@@ -43,8 +27,9 @@ interface IReferenceManager {
* but may still return null in case this is disabled or the fetching fails
*
* @since 25.0.0
+ * @since 30.0.0 optional arguments `$public` and `$sharingToken`
*/
- public function resolveReference(string $referenceId): ?IReference;
+ public function resolveReference(string $referenceId, bool $public = false, string $sharingToken = ''): ?IReference;
/**
* Get a reference by its cache key
@@ -58,8 +43,9 @@ interface IReferenceManager {
* the cache can then be filled with a separate request from the frontend
*
* @since 25.0.0
+ * @since 30.0.0 optional arguments `$public` and `$sharingToken`
*/
- public function getReferenceFromCache(string $referenceId): ?IReference;
+ public function getReferenceFromCache(string $referenceId, bool $public = false, string $sharingToken = ''): ?IReference;
/**
* Invalidate all cache entries with a prefix or just one if the cache key is provided
diff --git a/lib/public/Collaboration/Reference/IReferenceProvider.php b/lib/public/Collaboration/Reference/IReferenceProvider.php
index 100374b78b3..9ff9a728063 100644
--- a/lib/public/Collaboration/Reference/IReferenceProvider.php
+++ b/lib/public/Collaboration/Reference/IReferenceProvider.php
@@ -2,24 +2,8 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2022 Julius Härtl <jus@bitgrid.net>
- *
- * @author Julius Härtl <jus@bitgrid.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/>.
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCP\Collaboration\Reference;
diff --git a/lib/public/Collaboration/Reference/ISearchableReferenceProvider.php b/lib/public/Collaboration/Reference/ISearchableReferenceProvider.php
index b863de468a8..d66a9de9453 100644
--- a/lib/public/Collaboration/Reference/ISearchableReferenceProvider.php
+++ b/lib/public/Collaboration/Reference/ISearchableReferenceProvider.php
@@ -2,24 +2,8 @@
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/>.
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCP\Collaboration\Reference;
diff --git a/lib/public/Collaboration/Reference/LinkReferenceProvider.php b/lib/public/Collaboration/Reference/LinkReferenceProvider.php
new file mode 100644
index 00000000000..65bdcecb577
--- /dev/null
+++ b/lib/public/Collaboration/Reference/LinkReferenceProvider.php
@@ -0,0 +1,227 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\Collaboration\Reference;
+
+use Fusonic\OpenGraph\Consumer;
+use GuzzleHttp\Psr7\LimitStream;
+use GuzzleHttp\Psr7\Utils;
+use OC\Security\RateLimiting\Exception\RateLimitExceededException;
+use OC\Security\RateLimiting\Limiter;
+use OC\SystemConfig;
+use OCP\Files\AppData\IAppDataFactory;
+use OCP\Files\NotFoundException;
+use OCP\Http\Client\IClientService;
+use OCP\IRequest;
+use OCP\IURLGenerator;
+use OCP\IUserSession;
+use Psr\Log\LoggerInterface;
+
+/**
+ * @since 29.0.0
+ */
+class LinkReferenceProvider implements IReferenceProvider, IPublicReferenceProvider {
+
+ /**
+ * for image size and webpage header
+ * @since 29.0.0
+ */
+ public const MAX_CONTENT_LENGTH = 5 * 1024 * 1024;
+
+ /**
+ * @since 29.0.0
+ */
+ public const ALLOWED_CONTENT_TYPES = [
+ 'image/png',
+ 'image/jpg',
+ 'image/jpeg',
+ 'image/gif',
+ 'image/svg+xml',
+ 'image/webp'
+ ];
+
+ /**
+ * @since 29.0.0
+ */
+ public function __construct(
+ private IClientService $clientService,
+ private LoggerInterface $logger,
+ private SystemConfig $systemConfig,
+ private IAppDataFactory $appDataFactory,
+ private IURLGenerator $urlGenerator,
+ private Limiter $limiter,
+ private IUserSession $userSession,
+ private IRequest $request,
+ ) {
+ }
+
+ /**
+ * @inheritDoc
+ * @since 29.0.0
+ */
+ public function matchReference(string $referenceText): bool {
+ if ($this->systemConfig->getValue('reference_opengraph', true) !== true) {
+ return false;
+ }
+
+ return (bool)preg_match(IURLGenerator::URL_REGEX, $referenceText);
+ }
+
+ /**
+ * @inheritDoc
+ * @since 29.0.0
+ */
+ public function resolveReference(string $referenceText): ?IReference {
+ if ($this->matchReference($referenceText)) {
+ $reference = new Reference($referenceText);
+ $this->fetchReference($reference);
+ return $reference;
+ }
+
+ return null;
+ }
+
+ /**
+ * @inheritDoc
+ * @since 30.0.0
+ */
+ public function resolveReferencePublic(string $referenceText, string $sharingToken): ?IReference {
+ return $this->resolveReference($referenceText);
+ }
+
+ /**
+ * Populates the reference with OpenGraph data
+ *
+ * @param Reference $reference
+ * @since 29.0.0
+ */
+ private function fetchReference(Reference $reference): void {
+ try {
+ $user = $this->userSession->getUser();
+ if ($user) {
+ $this->limiter->registerUserRequest('opengraph', 10, 120, $user);
+ } else {
+ $this->limiter->registerAnonRequest('opengraph', 10, 120, $this->request->getRemoteAddress());
+ }
+ } catch (RateLimitExceededException $e) {
+ return;
+ }
+
+ $client = $this->clientService->newClient();
+ try {
+ $headResponse = $client->head($reference->getId(), [ 'timeout' => 3 ]);
+ } catch (\Exception $e) {
+ $this->logger->debug('Failed to perform HEAD request to get target metadata', ['exception' => $e]);
+ return;
+ }
+
+ $linkContentLength = $headResponse->getHeader('Content-Length');
+ if (is_numeric($linkContentLength) && (int)$linkContentLength > self::MAX_CONTENT_LENGTH) {
+ $this->logger->debug('[Head] Skip resolving links pointing to content length > 5 MiB');
+ return;
+ }
+
+ $linkContentType = $headResponse->getHeader('Content-Type');
+ $expectedContentTypeRegex = '/^text\/html;?/i';
+
+ // check the header begins with the expected content type
+ if (!preg_match($expectedContentTypeRegex, $linkContentType)) {
+ $this->logger->debug('Skip resolving links pointing to content type that is not "text/html"');
+ return;
+ }
+
+ try {
+ $response = $client->get($reference->getId(), [ 'timeout' => 3, 'stream' => true ]);
+ } catch (\Exception $e) {
+ $this->logger->debug('Failed to fetch link for obtaining open graph data', ['exception' => $e]);
+ return;
+ }
+
+ $body = $response->getBody();
+ if (is_resource($body)) {
+ $responseContent = fread($body, self::MAX_CONTENT_LENGTH);
+ if (!feof($body)) {
+ $this->logger->debug('[Get] Skip resolving links pointing to content length > 5 MiB');
+ return;
+ }
+ } else {
+ $this->logger->error('[Get] Impossible to check content length');
+ return;
+ }
+
+ // OpenGraph handling
+ $consumer = new Consumer();
+ $consumer->useFallbackMode = true;
+ $object = $consumer->loadHtml($responseContent);
+
+ $reference->setUrl($reference->getId());
+
+ if ($object->title) {
+ $reference->setTitle($object->title);
+ }
+
+ if ($object->description) {
+ $reference->setDescription($object->description);
+ }
+
+ if ($object->images) {
+ try {
+ $host = parse_url($object->images[0]->url, PHP_URL_HOST);
+ if ($host === false || $host === null) {
+ $this->logger->warning('Could not detect host of open graph image URI for ' . $reference->getId());
+ return;
+ }
+
+ $appData = $this->appDataFactory->get('core');
+ try {
+ $folder = $appData->getFolder('opengraph');
+ } catch (NotFoundException $e) {
+ $folder = $appData->newFolder('opengraph');
+ }
+
+ $response = $client->get($object->images[0]->url, ['timeout' => 3]);
+ $contentType = $response->getHeader('Content-Type');
+ $contentLength = $response->getHeader('Content-Length');
+
+ if (in_array($contentType, self::ALLOWED_CONTENT_TYPES, true) && $contentLength < self::MAX_CONTENT_LENGTH) {
+ $stream = Utils::streamFor($response->getBody());
+ $bodyStream = new LimitStream($stream, self::MAX_CONTENT_LENGTH, 0);
+ $reference->setImageContentType($contentType);
+ $folder->newFile(md5($reference->getId()), $bodyStream->getContents());
+ $reference->setImageUrl($this->urlGenerator->linkToRouteAbsolute('core.Reference.preview', ['referenceId' => md5($reference->getId())]));
+ }
+ } catch (\Exception $e) {
+ $this->logger->debug('Failed to fetch and store the open graph image for ' . $reference->getId(), ['exception' => $e]);
+ }
+ }
+ }
+
+ /**
+ * @inheritDoc
+ * @since 29.0.0
+ */
+ public function getCachePrefix(string $referenceId): string {
+ return $referenceId;
+ }
+
+ /**
+ * @inheritDoc
+ * @since 29.0.0
+ */
+ public function getCacheKey(string $referenceId): ?string {
+ return null;
+ }
+
+ /**
+ * @inheritDoc
+ * @since 30.0.0
+ */
+ public function getCacheKeyPublic(string $referenceId, string $sharingToken): ?string {
+ return null;
+ }
+}
diff --git a/lib/public/Collaboration/Reference/Reference.php b/lib/public/Collaboration/Reference/Reference.php
index 6b92a0fae52..3698ae419b7 100644
--- a/lib/public/Collaboration/Reference/Reference.php
+++ b/lib/public/Collaboration/Reference/Reference.php
@@ -3,30 +3,15 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2022 Julius Härtl <jus@bitgrid.net>
- *
- * @author Julius Härtl <jus@bitgrid.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/>.
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCP\Collaboration\Reference;
/**
* @since 25.0.0
+ * @psalm-type OpenGraphObject = array{id: string, name: string, description: ?string, thumb: ?string, link: string}
*/
class Reference implements IReference {
protected string $reference;
@@ -176,6 +161,7 @@ class Reference implements IReference {
/**
* @inheritdoc
* @since 25.0.0
+ * @return array<string, mixed>
*/
public function getRichObject(): array {
if ($this->richObject === null) {
@@ -187,6 +173,7 @@ class Reference implements IReference {
/**
* @inheritdoc
* @since 25.0.0
+ * @return OpenGraphObject
*/
public function getOpenGraphObject(): array {
return [
@@ -237,8 +224,9 @@ class Reference implements IReference {
/**
* @inheritdoc
* @since 25.0.0
+ * @return array{richObjectType: string, richObject: array<string, mixed>, openGraphObject: OpenGraphObject, accessible: bool}
*/
- public function jsonSerialize() {
+ public function jsonSerialize(): array {
return [
'richObjectType' => $this->getRichObjectType(),
'richObject' => $this->getRichObject(),
diff --git a/lib/public/Collaboration/Reference/RenderReferenceEvent.php b/lib/public/Collaboration/Reference/RenderReferenceEvent.php
index b6d312636c4..692098dbf60 100644
--- a/lib/public/Collaboration/Reference/RenderReferenceEvent.php
+++ b/lib/public/Collaboration/Reference/RenderReferenceEvent.php
@@ -2,24 +2,8 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2022 Julius Härtl <jus@bitgrid.net>
- *
- * @author Julius Härtl <jus@bitgrid.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/>.
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCP\Collaboration\Reference;
@@ -27,8 +11,10 @@ namespace OCP\Collaboration\Reference;
use OCP\EventDispatcher\Event;
/**
- * Event that apps can emit on their page rendering to trigger loading of aditional
- * scripts for reference widget rendering
+ * Event emitted when apps might render references like link previews or smart picker widgets.
+ *
+ * This can be used to inject scripts for extending that.
+ * Further details can be found in the :ref:`Reference providers` deep dive.
*
* @since 25.0.0
*/