summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoas Schilling <213943+nickvergessen@users.noreply.github.com>2023-12-07 10:34:12 +0100
committerGitHub <noreply@github.com>2023-12-07 10:34:12 +0100
commit3f9fadc993eed2d06b4d6cf71773cc063cb82493 (patch)
treea7541fd0464019fd1a066efb0a354a9d9c487101
parent96d7813f5cb414ea6a6efd118f5a10ae420877c8 (diff)
parentefdebddb5c2564d7592bdcbc1e7ffc8705d886e8 (diff)
Merge pull request #11131 from nextcloud/backport/11093/stable28
[stable28] Add metadata to file parameters
-rw-r--r--lib/Chat/Parser/SystemMessage.php22
-rw-r--r--lib/Controller/ChatController.php14
-rw-r--r--lib/Share/Helper/FilesMetadataCache.php103
-rw-r--r--tests/php/Chat/Parser/SystemMessageTest.php31
-rw-r--r--tests/php/Controller/ChatControllerTest.php5
5 files changed, 164 insertions, 11 deletions
diff --git a/lib/Chat/Parser/SystemMessage.php b/lib/Chat/Parser/SystemMessage.php
index be9e06ec6..7c3c607dc 100644
--- a/lib/Chat/Parser/SystemMessage.php
+++ b/lib/Chat/Parser/SystemMessage.php
@@ -34,6 +34,7 @@ use OCA\Talk\Model\Message;
use OCA\Talk\Participant;
use OCA\Talk\Room;
use OCA\Talk\Service\ParticipantService;
+use OCA\Talk\Share\Helper\FilesMetadataCache;
use OCA\Talk\Share\RoomShareProvider;
use OCP\Comments\IComment;
use OCP\EventDispatcher\Event;
@@ -43,6 +44,7 @@ use OCP\Files\InvalidPathException;
use OCP\Files\IRootFolder;
use OCP\Files\Node;
use OCP\Files\NotFoundException;
+use OCP\FilesMetadata\Exceptions\FilesMetadataNotFoundException;
use OCP\IGroup;
use OCP\IGroupManager;
use OCP\IL10N;
@@ -85,6 +87,7 @@ class SystemMessage implements IEventListener {
protected IRootFolder $rootFolder,
protected ICloudIdManager $cloudIdManager,
protected IURLGenerator $url,
+ protected FilesMetadataCache $metadataCache,
) {
}
@@ -719,9 +722,12 @@ class SystemMessage implements IEventListener {
]);
}
+ $fileId = $node->getId();
+ $isPreviewAvailable = $this->previewManager->isAvailable($node);
+
$data = [
'type' => 'file',
- 'id' => (string) $node->getId(),
+ 'id' => (string) $fileId,
'name' => $name,
'size' => $size,
'path' => $path,
@@ -729,9 +735,21 @@ class SystemMessage implements IEventListener {
'etag' => $node->getEtag(),
'permissions' => $node->getPermissions(),
'mimetype' => $node->getMimeType(),
- 'preview-available' => $this->previewManager->isAvailable($node) ? 'yes' : 'no',
+ 'preview-available' => $isPreviewAvailable ? 'yes' : 'no',
];
+ // If a preview is available, check if we can get the dimensions of the file from the metadata API
+ if ($isPreviewAvailable && str_starts_with($node->getMimeType(), 'image/')) {
+ try {
+ $sizeMetadata = $this->metadataCache->getMetadataPhotosSizeForFileId($fileId);
+ if (isset($sizeMetadata['width'], $sizeMetadata['height'])) {
+ $data['width'] = $sizeMetadata['width'];
+ $data['height'] = $sizeMetadata['height'];
+ }
+ } catch (FilesMetadataNotFoundException) {
+ }
+ }
+
if ($node->getMimeType() === 'text/vcard') {
$vCard = $node->getContent();
diff --git a/lib/Controller/ChatController.php b/lib/Controller/ChatController.php
index 18b21c97c..6c4570667 100644
--- a/lib/Controller/ChatController.php
+++ b/lib/Controller/ChatController.php
@@ -51,6 +51,7 @@ use OCA\Talk\Service\AvatarService;
use OCA\Talk\Service\ParticipantService;
use OCA\Talk\Service\ReminderService;
use OCA\Talk\Service\SessionService;
+use OCA\Talk\Share\Helper\FilesMetadataCache;
use OCA\Talk\Share\RoomShareProvider;
use OCP\App\IAppManager;
use OCP\AppFramework\Db\DoesNotExistException;
@@ -73,6 +74,7 @@ use OCP\RichObjectStrings\InvalidObjectExeption;
use OCP\RichObjectStrings\IValidator;
use OCP\Security\ITrustedDomainHelper;
use OCP\Share\Exceptions\ShareNotFound;
+use OCP\Share\IShare;
use OCP\User\Events\UserLiveStatusEvent;
use OCP\UserStatus\IManager as IUserStatusManager;
use OCP\UserStatus\IUserStatus;
@@ -103,6 +105,7 @@ class ChatController extends AEnvironmentAwareController {
private GuestManager $guestManager,
private MessageParser $messageParser,
protected RoomShareProvider $shareProvider,
+ protected FilesMetadataCache $filesMetadataCache,
private IManager $autoCompleteManager,
private IUserStatusManager $statusManager,
protected MatterbridgeManager $matterbridgeManager,
@@ -308,7 +311,8 @@ class ChatController extends AEnvironmentAwareController {
/*
* Gather share IDs from the comments and preload share definitions
- * to avoid separate database query for each individual share.
+ * and files metadata to avoid separate database query for each
+ * individual share/node later on.
*
* @param IComment[] $comments
*/
@@ -326,10 +330,14 @@ class ChatController extends AEnvironmentAwareController {
}
}
if (!empty($shareIds)) {
- // Ignore the result for now. Retrieved Share objects will be cached by
+ // Retrieved Share objects will be cached by
// the RoomShareProvider and returned from the cache to
// the Parser\SystemMessage without additional database queries.
- $this->shareProvider->getSharesByIds($shareIds);
+ $shares = $this->shareProvider->getSharesByIds($shareIds);
+
+ // Preload files metadata as well
+ $fileIds = array_filter(array_map(static fn (IShare $share) => $share->getNodeId(), $shares));
+ $this->filesMetadataCache->preloadMetadata($fileIds);
}
}
diff --git a/lib/Share/Helper/FilesMetadataCache.php b/lib/Share/Helper/FilesMetadataCache.php
new file mode 100644
index 000000000..587d2a706
--- /dev/null
+++ b/lib/Share/Helper/FilesMetadataCache.php
@@ -0,0 +1,103 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com>
+ *
+ * @author Joas Schilling <coding@schilljs.com>
+ *
+ * @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\Talk\Share\Helper;
+
+use OCP\FilesMetadata\Exceptions\FilesMetadataNotFoundException;
+use OCP\FilesMetadata\Exceptions\FilesMetadataTypeException;
+use OCP\FilesMetadata\IFilesMetadataManager;
+use OCP\FilesMetadata\Model\IFilesMetadata;
+
+class FilesMetadataCache {
+ /** @var array<int, ?array{width: int, height: int}> */
+ protected array $filesSizeData = [];
+
+ public function __construct(
+ protected IFilesMetadataManager $filesMetadataManager,
+ ) {
+ }
+
+ /**
+ * @param list<int> $fileIds
+ */
+ public function preloadMetadata(array $fileIds): void {
+ $missingFileIds = array_diff($fileIds, array_keys($this->filesSizeData));
+ if (empty($missingFileIds)) {
+ return;
+ }
+
+ $data = $this->filesMetadataManager->getMetadataForFiles($missingFileIds);
+ foreach ($data as $fileId => $metadata) {
+ $this->cachePhotosSize($fileId, $metadata);
+ }
+ }
+
+ /**
+ * @param int $fileId
+ * @return array
+ * @psalm-return array{width: int, height: int}
+ * @throws FilesMetadataNotFoundException
+ */
+ public function getMetadataPhotosSizeForFileId(int $fileId): array {
+ if (!array_key_exists($fileId, $this->filesSizeData)) {
+ try {
+ $this->cachePhotosSize($fileId, $this->filesMetadataManager->getMetadata($fileId, true));
+ } catch (FilesMetadataNotFoundException) {
+ $this->filesSizeData[$fileId] = null;
+ }
+ }
+
+ if ($this->filesSizeData[$fileId] === null) {
+ throw new FilesMetadataNotFoundException();
+ }
+
+ return $this->filesSizeData[$fileId];
+ }
+
+ protected function cachePhotosSize(int $fileId, IFilesMetadata $metadata): void {
+ if ($metadata->hasKey('photos-size')) {
+ try {
+ $sizeMetadata = $metadata->getArray('photos-size');
+ } catch (FilesMetadataNotFoundException|FilesMetadataTypeException) {
+ $this->filesSizeData[$fileId] = null;
+ return;
+ }
+
+ if (isset($sizeMetadata['width'], $sizeMetadata['height'])) {
+ $dimensions = [
+ 'width' => $sizeMetadata['width'],
+ 'height' => $sizeMetadata['height'],
+ ];
+ $this->filesSizeData[$fileId] = $dimensions;
+ } else {
+ $this->filesSizeData[$fileId] = null;
+ }
+ } else {
+ $this->filesSizeData[$fileId] = null;
+ }
+
+ }
+}
diff --git a/tests/php/Chat/Parser/SystemMessageTest.php b/tests/php/Chat/Parser/SystemMessageTest.php
index 0df7a9594..1db0029a3 100644
--- a/tests/php/Chat/Parser/SystemMessageTest.php
+++ b/tests/php/Chat/Parser/SystemMessageTest.php
@@ -32,6 +32,7 @@ use OCA\Talk\Model\Session;
use OCA\Talk\Participant;
use OCA\Talk\Room;
use OCA\Talk\Service\ParticipantService;
+use OCA\Talk\Share\Helper\FilesMetadataCache;
use OCA\Talk\Share\RoomShareProvider;
use OCP\Comments\IComment;
use OCP\Federation\ICloudIdManager;
@@ -75,6 +76,8 @@ class SystemMessageTest extends TestCase {
protected $url;
/** @var ICloudIdManager|MockObject */
protected $cloudIdManager;
+ /** @var FilesMetadataCache|MockObject */
+ protected $filesMetadataCache;
/** @var IL10N|MockObject */
protected $l;
@@ -91,6 +94,7 @@ class SystemMessageTest extends TestCase {
$this->rootFolder = $this->createMock(IRootFolder::class);
$this->url = $this->createMock(IURLGenerator::class);
$this->cloudIdManager = $this->createMock(ICloudIdManager::class);
+ $this->filesMetadataCache = $this->createMock(FilesMetadataCache::class);
$this->l = $this->createMock(IL10N::class);
$this->l->method('t')
->willReturnCallback(function ($text, $parameters = []) {
@@ -121,6 +125,7 @@ class SystemMessageTest extends TestCase {
$this->rootFolder,
$this->cloudIdManager,
$this->url,
+ $this->filesMetadataCache,
])
->onlyMethods($methods)
->getMock();
@@ -137,7 +142,8 @@ class SystemMessageTest extends TestCase {
$this->photoCache,
$this->rootFolder,
$this->cloudIdManager,
- $this->url
+ $this->url,
+ $this->filesMetadataCache,
);
}
@@ -611,13 +617,13 @@ class SystemMessageTest extends TestCase {
$node = $this->createMock(Node::class);
$node->expects($this->once())
->method('getId')
- ->willReturn('54');
+ ->willReturn(54);
$node->expects($this->once())
->method('getName')
->willReturn('name');
$node->expects($this->atLeastOnce())
->method('getMimeType')
- ->willReturn('text/plain');
+ ->willReturn('image/png');
$node->expects($this->once())
->method('getSize')
->willReturn(65530);
@@ -653,6 +659,11 @@ class SystemMessageTest extends TestCase {
->with($node)
->willReturn(true);
+ $this->filesMetadataCache->expects($this->once())
+ ->method('getMetadataPhotosSizeForFileId')
+ ->with(54)
+ ->willReturn(['width' => 1234, 'height' => 4567]);
+
$participant = $this->createMock(Participant::class);
$participant->expects($this->once())
->method('isGuest')
@@ -668,8 +679,10 @@ class SystemMessageTest extends TestCase {
'link' => 'absolute-link',
'etag' => '1872ade88f3013edeb33decd74a4f947',
'permissions' => 27,
- 'mimetype' => 'text/plain',
+ 'mimetype' => 'image/png',
'preview-available' => 'yes',
+ 'width' => 1234,
+ 'height' => 4567,
], self::invokePrivate($parser, 'getFileFromShare', [$participant, '23']));
}
@@ -677,7 +690,7 @@ class SystemMessageTest extends TestCase {
$node = $this->createMock(Node::class);
$node->expects($this->exactly(2))
->method('getId')
- ->willReturn('54');
+ ->willReturn(54);
$node->expects($this->once())
->method('getName')
->willReturn('name');
@@ -722,6 +735,9 @@ class SystemMessageTest extends TestCase {
])
->willReturn('absolute-link-owner');
+ $this->filesMetadataCache->expects($this->never())
+ ->method('getMetadataPhotosSizeForFileId');
+
$participant = $this->createMock(Participant::class);
$participant->expects($this->once())
->method('isGuest')
@@ -775,7 +791,7 @@ class SystemMessageTest extends TestCase {
$file = $this->createMock(Node::class);
$file->expects($this->any())
->method('getId')
- ->willReturn('54');
+ ->willReturn(54);
$file->expects($this->once())
->method('getName')
->willReturn('different');
@@ -811,6 +827,9 @@ class SystemMessageTest extends TestCase {
->with($file)
->willReturn(false);
+ $this->filesMetadataCache->expects($this->never())
+ ->method('getMetadataPhotosSizeForFileId');
+
$this->url->expects($this->once())
->method('linkToRouteAbsolute')
->with('files.viewcontroller.showFile', [
diff --git a/tests/php/Controller/ChatControllerTest.php b/tests/php/Controller/ChatControllerTest.php
index e1120a021..a5bbe6da6 100644
--- a/tests/php/Controller/ChatControllerTest.php
+++ b/tests/php/Controller/ChatControllerTest.php
@@ -39,6 +39,7 @@ use OCA\Talk\Service\AvatarService;
use OCA\Talk\Service\ParticipantService;
use OCA\Talk\Service\ReminderService;
use OCA\Talk\Service\SessionService;
+use OCA\Talk\Share\Helper\FilesMetadataCache;
use OCA\Talk\Share\RoomShareProvider;
use OCP\App\IAppManager;
use OCP\AppFramework\Http;
@@ -86,6 +87,8 @@ class ChatControllerTest extends TestCase {
protected $messageParser;
/** @var RoomShareProvider|MockObject */
protected $roomShareProvider;
+ /** @var FilesMetadataCache|MockObject */
+ protected $filesMetadataCache;
/** @var IManager|MockObject */
protected $autoCompleteManager;
/** @var IUserStatusManager|MockObject */
@@ -131,6 +134,7 @@ class ChatControllerTest extends TestCase {
$this->guestManager = $this->createMock(GuestManager::class);
$this->messageParser = $this->createMock(MessageParser::class);
$this->roomShareProvider = $this->createMock(RoomShareProvider::class);
+ $this->filesMetadataCache = $this->createMock(FilesMetadataCache::class);
$this->autoCompleteManager = $this->createMock(IManager::class);
$this->statusManager = $this->createMock(IUserStatusManager::class);
$this->matterbridgeManager = $this->createMock(MatterbridgeManager::class);
@@ -171,6 +175,7 @@ class ChatControllerTest extends TestCase {
$this->guestManager,
$this->messageParser,
$this->roomShareProvider,
+ $this->filesMetadataCache,
$this->autoCompleteManager,
$this->statusManager,
$this->matterbridgeManager,