aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorLouis Chemineau <louis@chmn.me>2023-11-08 12:35:01 +0100
committerLouis Chemineau <louis@chmn.me>2023-11-08 16:23:53 +0100
commitd3a313f192c090d026bac1fd1a8aed718d54c634 (patch)
treea4860e713479eb045fcb66fb19161f52a44110d8 /lib
parent9285fe04ff277088bc06eda264712d3a164539a9 (diff)
downloadnextcloud-server-d3a313f192c090d026bac1fd1a8aed718d54c634.tar.gz
nextcloud-server-d3a313f192c090d026bac1fd1a8aed718d54c634.zip
Support getting and setting metadata in DAV requests
Signed-off-by: Louis Chemineau <louis@chmn.me>
Diffstat (limited to 'lib')
-rw-r--r--lib/composer/composer/autoload_classmap.php8
-rw-r--r--lib/composer/composer/autoload_static.php8
-rw-r--r--lib/private/Files/Cache/Cache.php11
-rw-r--r--lib/private/Files/Cache/CacheQueryBuilder.php15
-rw-r--r--lib/private/Files/Cache/QuerySearchHelper.php20
-rw-r--r--lib/private/FilesMetadata/FilesMetadataManager.php15
-rw-r--r--lib/private/FilesMetadata/Listener/MetadataUpdate.php2
-rw-r--r--lib/private/FilesMetadata/Model/FilesMetadata.php8
-rw-r--r--lib/private/FilesMetadata/Service/IndexRequestService.php2
-rw-r--r--lib/private/Metadata/Capabilities.php42
-rw-r--r--lib/private/Metadata/FileEventListener.php108
-rw-r--r--lib/private/Metadata/FileMetadata.php51
-rw-r--r--lib/private/Metadata/FileMetadataMapper.php177
-rw-r--r--lib/private/Metadata/IMetadataManager.php35
-rw-r--r--lib/private/Metadata/IMetadataProvider.php41
-rw-r--r--lib/private/Metadata/MetadataManager.php95
-rw-r--r--lib/private/Metadata/Provider/ExifProvider.php139
-rw-r--r--lib/private/Server.php8
-rw-r--r--lib/public/FilesMetadata/IFilesMetadataManager.php3
-rw-r--r--lib/public/FilesMetadata/Model/IFilesMetadata.php4
20 files changed, 48 insertions, 744 deletions
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php
index aaba8312c44..1a55209c286 100644
--- a/lib/composer/composer/autoload_classmap.php
+++ b/lib/composer/composer/autoload_classmap.php
@@ -1496,14 +1496,6 @@ return array(
'OC\\Memcache\\Redis' => $baseDir . '/lib/private/Memcache/Redis.php',
'OC\\Memcache\\WithLocalCache' => $baseDir . '/lib/private/Memcache/WithLocalCache.php',
'OC\\MemoryInfo' => $baseDir . '/lib/private/MemoryInfo.php',
- 'OC\\Metadata\\Capabilities' => $baseDir . '/lib/private/Metadata/Capabilities.php',
- 'OC\\Metadata\\FileEventListener' => $baseDir . '/lib/private/Metadata/FileEventListener.php',
- 'OC\\Metadata\\FileMetadata' => $baseDir . '/lib/private/Metadata/FileMetadata.php',
- 'OC\\Metadata\\FileMetadataMapper' => $baseDir . '/lib/private/Metadata/FileMetadataMapper.php',
- 'OC\\Metadata\\IMetadataManager' => $baseDir . '/lib/private/Metadata/IMetadataManager.php',
- 'OC\\Metadata\\IMetadataProvider' => $baseDir . '/lib/private/Metadata/IMetadataProvider.php',
- 'OC\\Metadata\\MetadataManager' => $baseDir . '/lib/private/Metadata/MetadataManager.php',
- 'OC\\Metadata\\Provider\\ExifProvider' => $baseDir . '/lib/private/Metadata/Provider/ExifProvider.php',
'OC\\Migration\\BackgroundRepair' => $baseDir . '/lib/private/Migration/BackgroundRepair.php',
'OC\\Migration\\ConsoleOutput' => $baseDir . '/lib/private/Migration/ConsoleOutput.php',
'OC\\Migration\\SimpleOutput' => $baseDir . '/lib/private/Migration/SimpleOutput.php',
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index 6c341797b79..bebf5be91bd 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.php
@@ -1529,14 +1529,6 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Memcache\\Redis' => __DIR__ . '/../../..' . '/lib/private/Memcache/Redis.php',
'OC\\Memcache\\WithLocalCache' => __DIR__ . '/../../..' . '/lib/private/Memcache/WithLocalCache.php',
'OC\\MemoryInfo' => __DIR__ . '/../../..' . '/lib/private/MemoryInfo.php',
- 'OC\\Metadata\\Capabilities' => __DIR__ . '/../../..' . '/lib/private/Metadata/Capabilities.php',
- 'OC\\Metadata\\FileEventListener' => __DIR__ . '/../../..' . '/lib/private/Metadata/FileEventListener.php',
- 'OC\\Metadata\\FileMetadata' => __DIR__ . '/../../..' . '/lib/private/Metadata/FileMetadata.php',
- 'OC\\Metadata\\FileMetadataMapper' => __DIR__ . '/../../..' . '/lib/private/Metadata/FileMetadataMapper.php',
- 'OC\\Metadata\\IMetadataManager' => __DIR__ . '/../../..' . '/lib/private/Metadata/IMetadataManager.php',
- 'OC\\Metadata\\IMetadataProvider' => __DIR__ . '/../../..' . '/lib/private/Metadata/IMetadataProvider.php',
- 'OC\\Metadata\\MetadataManager' => __DIR__ . '/../../..' . '/lib/private/Metadata/MetadataManager.php',
- 'OC\\Metadata\\Provider\\ExifProvider' => __DIR__ . '/../../..' . '/lib/private/Metadata/Provider/ExifProvider.php',
'OC\\Migration\\BackgroundRepair' => __DIR__ . '/../../..' . '/lib/private/Migration/BackgroundRepair.php',
'OC\\Migration\\ConsoleOutput' => __DIR__ . '/../../..' . '/lib/private/Migration/ConsoleOutput.php',
'OC\\Migration\\SimpleOutput' => __DIR__ . '/../../..' . '/lib/private/Migration/SimpleOutput.php',
diff --git a/lib/private/Files/Cache/Cache.php b/lib/private/Files/Cache/Cache.php
index 67d01bb6999..240f02b3fba 100644
--- a/lib/private/Files/Cache/Cache.php
+++ b/lib/private/Files/Cache/Cache.php
@@ -59,6 +59,7 @@ use OCP\Files\Search\ISearchComparison;
use OCP\Files\Search\ISearchOperator;
use OCP\Files\Search\ISearchQuery;
use OCP\Files\Storage\IStorage;
+use OCP\FilesMetadata\IFilesMetadataManager;
use OCP\IDBConnection;
use OCP\Util;
use Psr\Log\LoggerInterface;
@@ -132,7 +133,8 @@ class Cache implements ICache {
return new CacheQueryBuilder(
$this->connection,
\OC::$server->getSystemConfig(),
- \OC::$server->get(LoggerInterface::class)
+ \OC::$server->get(LoggerInterface::class),
+ \OC::$server->get(IFilesMetadataManager::class),
);
}
@@ -154,6 +156,7 @@ class Cache implements ICache {
public function get($file) {
$query = $this->getQueryBuilder();
$query->selectFileCache();
+ $metadataQuery = $query->selectMetadata();
if (is_string($file) || $file == '') {
// normalize file
@@ -175,6 +178,7 @@ class Cache implements ICache {
} elseif (!$data) {
return $data;
} else {
+ $data['metadata'] = $metadataQuery->extractMetadata($data)->asArray();
return self::cacheEntryFromData($data, $this->mimetypeLoader);
}
}
@@ -239,11 +243,14 @@ class Cache implements ICache {
->whereParent($fileId)
->orderBy('name', 'ASC');
+ $metadataQuery = $query->selectMetadata();
+
$result = $query->execute();
$files = $result->fetchAll();
$result->closeCursor();
- return array_map(function (array $data) {
+ return array_map(function (array $data) use ($metadataQuery) {
+ $data['metadata'] = $metadataQuery->extractMetadata($data)->asArray();
return self::cacheEntryFromData($data, $this->mimetypeLoader);
}, $files);
}
diff --git a/lib/private/Files/Cache/CacheQueryBuilder.php b/lib/private/Files/Cache/CacheQueryBuilder.php
index f799d6aa72e..27f66e63e7b 100644
--- a/lib/private/Files/Cache/CacheQueryBuilder.php
+++ b/lib/private/Files/Cache/CacheQueryBuilder.php
@@ -29,6 +29,8 @@ namespace OC\Files\Cache;
use OC\DB\QueryBuilder\QueryBuilder;
use OC\SystemConfig;
use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\FilesMetadata\IFilesMetadataManager;
+use OCP\FilesMetadata\Model\IMetadataQuery;
use OCP\IDBConnection;
use Psr\Log\LoggerInterface;
@@ -38,7 +40,12 @@ use Psr\Log\LoggerInterface;
class CacheQueryBuilder extends QueryBuilder {
private ?string $alias = null;
- public function __construct(IDBConnection $connection, SystemConfig $systemConfig, LoggerInterface $logger) {
+ public function __construct(
+ IDBConnection $connection,
+ SystemConfig $systemConfig,
+ LoggerInterface $logger,
+ private IFilesMetadataManager $filesMetadataManager,
+ ) {
parent::__construct($connection, $systemConfig, $logger);
}
@@ -127,4 +134,10 @@ class CacheQueryBuilder extends QueryBuilder {
return $this;
}
+
+ public function selectMetadata(): IMetadataQuery {
+ $metadataQuery = $this->filesMetadataManager->getMetadataQuery($this, $this->alias, 'fileid');
+ $metadataQuery->retrieveMetadata();
+ return $metadataQuery;
+ }
}
diff --git a/lib/private/Files/Cache/QuerySearchHelper.php b/lib/private/Files/Cache/QuerySearchHelper.php
index ca54133a243..f8e5d1608f7 100644
--- a/lib/private/Files/Cache/QuerySearchHelper.php
+++ b/lib/private/Files/Cache/QuerySearchHelper.php
@@ -62,7 +62,8 @@ class QuerySearchHelper {
return new CacheQueryBuilder(
$this->connection,
$this->systemConfig,
- $this->logger
+ $this->logger,
+ $this->filesMetadataManager,
);
}
@@ -133,20 +134,6 @@ class QuerySearchHelper {
));
}
-
- /**
- * left join metadata and its indexes to the filecache table
- *
- * @param CacheQueryBuilder $query
- *
- * @return IMetadataQuery
- */
- protected function equipQueryForMetadata(CacheQueryBuilder $query): IMetadataQuery {
- $metadataQuery = $this->filesMetadataManager->getMetadataQuery($query, 'file', 'fileid');
- $metadataQuery->retrieveMetadata();
- return $metadataQuery;
- }
-
/**
* Perform a file system search in multiple caches
*
@@ -186,7 +173,8 @@ class QuerySearchHelper {
$this->equipQueryForDavTags($query, $this->requireUser($searchQuery));
}
- $metadataQuery = $this->equipQueryForMetadata($query);
+ $metadataQuery = $query->selectMetadata();
+
$this->applySearchConstraints($query, $searchQuery, $caches, $metadataQuery);
$result = $query->execute();
diff --git a/lib/private/FilesMetadata/FilesMetadataManager.php b/lib/private/FilesMetadata/FilesMetadataManager.php
index 54310f934d7..b4c91c3836a 100644
--- a/lib/private/FilesMetadata/FilesMetadataManager.php
+++ b/lib/private/FilesMetadata/FilesMetadataManager.php
@@ -38,7 +38,6 @@ use OCP\DB\Exception;
use OCP\DB\Exception as DBException;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\EventDispatcher\IEventDispatcher;
-use OCP\Files\Events\Node\NodeCreatedEvent;
use OCP\Files\Events\Node\NodeDeletedEvent;
use OCP\Files\Events\Node\NodeWrittenEvent;
use OCP\Files\InvalidPathException;
@@ -124,14 +123,23 @@ class FilesMetadataManager implements IFilesMetadataManager {
/**
* @param int $fileId file id
+ * @param boolean $generate Generate if metadata does not exists
*
* @inheritDoc
* @return IFilesMetadata
* @throws FilesMetadataNotFoundException if not found
* @since 28.0.0
*/
- public function getMetadata(int $fileId): IFilesMetadata {
- return $this->metadataRequestService->getMetadataFromFileId($fileId);
+ public function getMetadata(int $fileId, bool $generate = false): IFilesMetadata {
+ try {
+ return $this->metadataRequestService->getMetadataFromFileId($fileId);
+ } catch (FilesMetadataNotFoundException $ex) {
+ if ($generate) {
+ return new FilesMetadata($fileId);
+ }
+
+ throw $ex;
+ }
}
/**
@@ -274,7 +282,6 @@ class FilesMetadataManager implements IFilesMetadataManager {
* @param IEventDispatcher $eventDispatcher
*/
public static function loadListeners(IEventDispatcher $eventDispatcher): void {
- $eventDispatcher->addServiceListener(NodeCreatedEvent::class, MetadataUpdate::class);
$eventDispatcher->addServiceListener(NodeWrittenEvent::class, MetadataUpdate::class);
$eventDispatcher->addServiceListener(NodeDeletedEvent::class, MetadataDelete::class);
}
diff --git a/lib/private/FilesMetadata/Listener/MetadataUpdate.php b/lib/private/FilesMetadata/Listener/MetadataUpdate.php
index 395a852e9e3..9848f079882 100644
--- a/lib/private/FilesMetadata/Listener/MetadataUpdate.php
+++ b/lib/private/FilesMetadata/Listener/MetadataUpdate.php
@@ -51,7 +51,7 @@ class MetadataUpdate implements IEventListener {
* @param Event $event
*/
public function handle(Event $event): void {
- if (!($event instanceof NodeCreatedEvent) && !($event instanceof NodeWrittenEvent)) {
+ if (!($event instanceof NodeWrittenEvent)) {
return;
}
diff --git a/lib/private/FilesMetadata/Model/FilesMetadata.php b/lib/private/FilesMetadata/Model/FilesMetadata.php
index a94c7a9b6ff..b10de55579c 100644
--- a/lib/private/FilesMetadata/Model/FilesMetadata.php
+++ b/lib/private/FilesMetadata/Model/FilesMetadata.php
@@ -133,7 +133,7 @@ class FilesMetadata implements IFilesMetadata {
* @throws FilesMetadataTypeException
* @since 28.0.0
*/
- public function get(string $key): string {
+ public function getString(string $key): string {
if (!array_key_exists($key, $this->metadata)) {
throw new FilesMetadataNotFoundException();
}
@@ -276,10 +276,10 @@ class FilesMetadata implements IFilesMetadata {
* @throws FilesMetadataKeyFormatException
* @since 28.0.0
*/
- public function set(string $key, string $value, bool $index = false): IFilesMetadata {
+ public function setString(string $key, string $value, bool $index = false): IFilesMetadata {
$this->confirmKeyFormat($key);
try {
- if ($this->get($key) === $value && $index === $this->isIndex($key)) {
+ if ($this->getString($key) === $value && $index === $this->isIndex($key)) {
return $this; // we ignore if value and index have not changed
}
} catch (FilesMetadataNotFoundException|FilesMetadataTypeException $e) {
@@ -506,7 +506,7 @@ class FilesMetadata implements IFilesMetadata {
return;
}
- throw new FilesMetadataKeyFormatException('key can only contains alphanumerical characters, and dash (-)');
+ throw new FilesMetadataKeyFormatException('key can only contains alphanumerical characters, and dash (-, _)');
}
/**
diff --git a/lib/private/FilesMetadata/Service/IndexRequestService.php b/lib/private/FilesMetadata/Service/IndexRequestService.php
index 6530dabd1c3..2a23e2c9a67 100644
--- a/lib/private/FilesMetadata/Service/IndexRequestService.php
+++ b/lib/private/FilesMetadata/Service/IndexRequestService.php
@@ -73,7 +73,7 @@ class IndexRequestService {
try {
$this->dropIndex($fileId, $key);
match ($metadataType) {
- IMetadataValueWrapper::TYPE_STRING => $this->insertIndexString($fileId, $key, $filesMetadata->get($key)),
+ IMetadataValueWrapper::TYPE_STRING => $this->insertIndexString($fileId, $key, $filesMetadata->getString($key)),
IMetadataValueWrapper::TYPE_INT => $this->insertIndexInt($fileId, $key, $filesMetadata->getInt($key)),
IMetadataValueWrapper::TYPE_BOOL => $this->insertIndexBool($fileId, $key, $filesMetadata->getBool($key)),
IMetadataValueWrapper::TYPE_STRING_LIST => $this->insertIndexStringList($fileId, $key, $filesMetadata->getStringList($key)),
diff --git a/lib/private/Metadata/Capabilities.php b/lib/private/Metadata/Capabilities.php
deleted file mode 100644
index d8b0b82377e..00000000000
--- a/lib/private/Metadata/Capabilities.php
+++ /dev/null
@@ -1,42 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-/**
- * @copyright Copyright 2022 Carl Schwan <carl@carlschwan.eu>
- * @license AGPL-3.0-or-later
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
- */
-
-namespace OC\Metadata;
-
-use OCP\Capabilities\IPublicCapability;
-use OCP\IConfig;
-
-class Capabilities implements IPublicCapability {
- public function __construct(
- private IMetadataManager $manager,
- private IConfig $config,
- ) {
- }
-
- public function getCapabilities(): array {
- if ($this->config->getSystemValueBool('enable_file_metadata', true)) {
- return ['metadataAvailable' => $this->manager->getCapabilities()];
- }
-
- return [];
- }
-}
diff --git a/lib/private/Metadata/FileEventListener.php b/lib/private/Metadata/FileEventListener.php
deleted file mode 100644
index 5a1882ac0e4..00000000000
--- a/lib/private/Metadata/FileEventListener.php
+++ /dev/null
@@ -1,108 +0,0 @@
-<?php
-
-declare(strict_types=1);
-/**
- * @copyright Copyright 2022 Carl Schwan <carl@carlschwan.eu>
- * @license AGPL-3.0-or-later
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
- */
-
-namespace OC\Metadata;
-
-use OC\Files\Filesystem;
-use OCP\EventDispatcher\Event;
-use OCP\EventDispatcher\IEventListener;
-use OCP\Files\Events\Node\NodeDeletedEvent;
-use OCP\Files\Events\Node\NodeWrittenEvent;
-use OCP\Files\Events\NodeRemovedFromCache;
-use OCP\Files\File;
-use OCP\Files\Node;
-use OCP\Files\NotFoundException;
-use OCP\Files\FileInfo;
-use Psr\Log\LoggerInterface;
-
-/**
- * @template-implements IEventListener<NodeRemovedFromCache>
- * @template-implements IEventListener<NodeDeletedEvent>
- * @template-implements IEventListener<NodeWrittenEvent>
- */
-class FileEventListener implements IEventListener {
- public function __construct(
- private IMetadataManager $manager,
- private LoggerInterface $logger,
- ) {
- }
-
- private function shouldExtractMetadata(Node $node): bool {
- try {
- if ($node->getMimetype() === 'httpd/unix-directory') {
- return false;
- }
- } catch (NotFoundException $e) {
- return false;
- }
- if ($node->getSize(false) <= 0) {
- return false;
- }
-
- $path = $node->getPath();
- return $this->isCorrectPath($path);
- }
-
- private function isCorrectPath(string $path): bool {
- // TODO make this more dynamic, we have the same issue in other places
- return !str_starts_with($path, 'appdata_') && !str_starts_with($path, 'files_versions/') && !str_starts_with($path, 'files_trashbin/');
- }
-
- public function handle(Event $event): void {
- if ($event instanceof NodeRemovedFromCache) {
- if (!$this->isCorrectPath($event->getPath())) {
- // Don't listen to paths for which we don't extract metadata
- return;
- }
- $view = Filesystem::getView();
- if (!$view) {
- // Should not happen since a scan in the user folder should setup
- // the file system.
- $e = new \Exception(); // don't trigger, just get backtrace
- $this->logger->error('Detecting deletion of a file with possible metadata but file system setup is not setup', [
- 'exception' => $e,
- 'app' => 'metadata'
- ]);
- return;
- }
- $info = $view->getFileInfo($event->getPath());
- if ($info && $info->getType() === FileInfo::TYPE_FILE) {
- $this->manager->clearMetadata($info->getId());
- }
- }
-
- if ($event instanceof NodeDeletedEvent) {
- $node = $event->getNode();
- if ($this->shouldExtractMetadata($node)) {
- /** @var File $node */
- $this->manager->clearMetadata($event->getNode()->getId());
- }
- }
-
- if ($event instanceof NodeWrittenEvent) {
- $node = $event->getNode();
- if ($this->shouldExtractMetadata($node)) {
- /** @var File $node */
- $this->manager->generateMetadata($event->getNode(), false);
- }
- }
- }
-}
diff --git a/lib/private/Metadata/FileMetadata.php b/lib/private/Metadata/FileMetadata.php
deleted file mode 100644
index a9808a86998..00000000000
--- a/lib/private/Metadata/FileMetadata.php
+++ /dev/null
@@ -1,51 +0,0 @@
-<?php
-
-declare(strict_types=1);
-/**
- * @copyright Copyright 2022 Carl Schwan <carl@carlschwan.eu>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
- */
-
-namespace OC\Metadata;
-
-use OCP\AppFramework\Db\Entity;
-use OCP\DB\Types;
-
-/**
- * @method string getGroupName()
- * @method void setGroupName(string $groupName)
- * @method string getValue()
- * @method void setValue(string $value)
- * @see \OC\Core\Migrations\Version240000Date20220404230027
- */
-class FileMetadata extends Entity {
- protected ?string $groupName = null;
- protected ?string $value = null;
-
- public function __construct() {
- $this->addType('groupName', 'string');
- $this->addType('value', Types::STRING);
- }
-
- public function getDecodedValue(): array {
- return json_decode($this->getValue(), true) ?? [];
- }
-
- public function setArrayAsValue(array $value): void {
- $this->setValue(json_encode($value, JSON_THROW_ON_ERROR));
- }
-}
diff --git a/lib/private/Metadata/FileMetadataMapper.php b/lib/private/Metadata/FileMetadataMapper.php
deleted file mode 100644
index 003ab13126e..00000000000
--- a/lib/private/Metadata/FileMetadataMapper.php
+++ /dev/null
@@ -1,177 +0,0 @@
-<?php
-
-declare(strict_types=1);
-/**
- * @copyright Copyright 2022 Carl Schwan <carl@carlschwan.eu>
- * @copyright Copyright 2022 Louis Chmn <louis@chmn.me>
- * @license AGPL-3.0-or-later
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
- */
-
-namespace OC\Metadata;
-
-use OCP\AppFramework\Db\DoesNotExistException;
-use OCP\AppFramework\Db\MultipleObjectsReturnedException;
-use OCP\AppFramework\Db\QBMapper;
-use OCP\AppFramework\Db\Entity;
-use OCP\DB\Exception;
-use OCP\DB\QueryBuilder\IQueryBuilder;
-use OCP\IDBConnection;
-
-/**
- * @template-extends QBMapper<FileMetadata>
- */
-class FileMetadataMapper extends QBMapper {
- public function __construct(IDBConnection $db) {
- parent::__construct($db, 'file_metadata', FileMetadata::class);
- }
-
- /**
- * @return FileMetadata[]
- * @throws Exception
- */
- public function findForFile(int $fileId): array {
- $qb = $this->db->getQueryBuilder();
- $qb->select('*')
- ->from($this->getTableName())
- ->where($qb->expr()->eq('id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)));
-
- return $this->findEntities($qb);
- }
-
- /**
- * @throws DoesNotExistException
- * @throws MultipleObjectsReturnedException
- * @throws Exception
- */
- public function findForGroupForFile(int $fileId, string $groupName): FileMetadata {
- $qb = $this->db->getQueryBuilder();
- $qb->select('*')
- ->from($this->getTableName())
- ->where($qb->expr()->eq('id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)))
- ->andWhere($qb->expr()->eq('group_name', $qb->createNamedParameter($groupName, IQueryBuilder::PARAM_STR)));
-
- return $this->findEntity($qb);
- }
-
- /**
- * @return array<int, FileMetadata>
- * @throws Exception
- */
- public function findForGroupForFiles(array $fileIds, string $groupName): array {
- $qb = $this->db->getQueryBuilder();
- $qb->select('*')
- ->from($this->getTableName())
- ->where($qb->expr()->in('id', $qb->createParameter('fileIds')))
- ->andWhere($qb->expr()->eq('group_name', $qb->createNamedParameter($groupName, IQueryBuilder::PARAM_STR)));
-
- $metadata = [];
- foreach (array_chunk($fileIds, 1000) as $fileIdsChunk) {
- $qb->setParameter('fileIds', $fileIdsChunk, IQueryBuilder::PARAM_INT_ARRAY);
- /** @var FileMetadata[] $rawEntities */
- $rawEntities = $this->findEntities($qb);
- foreach ($rawEntities as $entity) {
- $metadata[$entity->getId()] = $entity;
- }
- }
-
- foreach ($fileIds as $id) {
- if (isset($metadata[$id])) {
- continue;
- }
- $empty = new FileMetadata();
- $empty->setValue('');
- $empty->setGroupName($groupName);
- $empty->setId($id);
- $metadata[$id] = $empty;
- }
- return $metadata;
- }
-
- public function clear(int $fileId): void {
- $qb = $this->db->getQueryBuilder();
- $qb->delete($this->getTableName())
- ->where($qb->expr()->eq('id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)));
-
- $qb->executeStatement();
- }
-
- /**
- * Updates an entry in the db from an entity
- *
- * @param FileMetadata $entity the entity that should be created
- * @return FileMetadata the saved entity with the set id
- * @throws Exception
- * @throws \InvalidArgumentException if entity has no id
- */
- public function update(Entity $entity): FileMetadata {
- if (!($entity instanceof FileMetadata)) {
- throw new \Exception("Entity should be a FileMetadata entity");
- }
-
- // entity needs an id
- $id = $entity->getId();
- if ($id === null) {
- throw new \InvalidArgumentException('Entity which should be updated has no id');
- }
-
- // entity needs an group_name
- $groupName = $entity->getGroupName();
- if ($groupName === null) {
- throw new \InvalidArgumentException('Entity which should be updated has no group_name');
- }
-
- $idType = $this->getParameterTypeForProperty($entity, 'id');
- $groupNameType = $this->getParameterTypeForProperty($entity, 'groupName');
- $value = $entity->getValue();
- $valueType = $this->getParameterTypeForProperty($entity, 'value');
-
- $qb = $this->db->getQueryBuilder();
-
- $qb->update($this->tableName)
- ->set('value', $qb->createNamedParameter($value, $valueType))
- ->where($qb->expr()->eq('id', $qb->createNamedParameter($id, $idType)))
- ->andWhere($qb->expr()->eq('group_name', $qb->createNamedParameter($groupName, $groupNameType)))
- ->executeStatement();
-
- return $entity;
- }
-
- /**
- * Override the insertOrUpdate as we could be in a transaction in which case we can not afford on error.
- *
- * @param FileMetadata $entity the entity that should be created/updated
- * @return FileMetadata the saved entity with the (new) id
- * @throws Exception
- * @throws \InvalidArgumentException if entity has no id
- */
- public function insertOrUpdate(Entity $entity): FileMetadata {
- try {
- $existingEntity = $this->findForGroupForFile($entity->getId(), $entity->getGroupName());
- } catch (\Throwable) {
- $existingEntity = null;
- }
-
- if ($existingEntity !== null) {
- if ($entity->getValue() !== $existingEntity->getValue()) {
- return $this->update($entity);
- } else {
- return $existingEntity;
- }
- } else {
- return parent::insertOrUpdate($entity);
- }
- }
-}
diff --git a/lib/private/Metadata/IMetadataManager.php b/lib/private/Metadata/IMetadataManager.php
deleted file mode 100644
index fa0bcc22801..00000000000
--- a/lib/private/Metadata/IMetadataManager.php
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace OC\Metadata;
-
-use OCP\Files\File;
-
-/**
- * Interface to manage additional metadata for files
- */
-interface IMetadataManager {
- /**
- * @param class-string<IMetadataProvider> $className
- */
- public function registerProvider(string $className): void;
-
- /**
- * Generate the metadata for one file
- */
- public function generateMetadata(File $file, bool $checkExisting = false): void;
-
- /**
- * Clear the metadata for one file
- */
- public function clearMetadata(int $fileId): void;
-
- /** @return array<int, FileMetadata> */
- public function fetchMetadataFor(string $group, array $fileIds): array;
-
- /**
- * Get the capabilities as an array of mimetype regex to the type provided
- */
- public function getCapabilities(): array;
-}
diff --git a/lib/private/Metadata/IMetadataProvider.php b/lib/private/Metadata/IMetadataProvider.php
deleted file mode 100644
index 7cbe102a538..00000000000
--- a/lib/private/Metadata/IMetadataProvider.php
+++ /dev/null
@@ -1,41 +0,0 @@
-<?php
-
-namespace OC\Metadata;
-
-use OCP\Files\File;
-
-/**
- * Interface for the metadata providers. If you want an application to provide
- * some metadata, you can use this to store them.
- */
-interface IMetadataProvider {
- /**
- * The list of groups that this metadata provider is able to provide.
- *
- * @return string[]
- */
- public static function groupsProvided(): array;
-
- /**
- * Check if the metadata provider is available. A metadata provider might be
- * unavailable due to a php extension not being installed.
- */
- public static function isAvailable(): bool;
-
- /**
- * Get the mimetypes supported as a regex.
- */
- public static function getMimetypesSupported(): string;
-
- /**
- * Execute the extraction on the specified file. The metadata should be
- * grouped by metadata
- *
- * Each group should be json serializable and the string representation
- * shouldn't be longer than 4000 characters.
- *
- * @param File $file The file to extract the metadata from
- * @param array<string, FileMetadata> An array containing all the metadata fetched.
- */
- public function execute(File $file): array;
-}
diff --git a/lib/private/Metadata/MetadataManager.php b/lib/private/Metadata/MetadataManager.php
deleted file mode 100644
index ef9f9200df7..00000000000
--- a/lib/private/Metadata/MetadataManager.php
+++ /dev/null
@@ -1,95 +0,0 @@
-<?php
-/**
- * @copyright Copyright 2022 Carl Schwan <carl@carlschwan.eu>
- * @license AGPL-3.0-or-later
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
- */
-
-namespace OC\Metadata;
-
-use OC\Metadata\Provider\ExifProvider;
-use OCP\Files\File;
-
-class MetadataManager implements IMetadataManager {
- /** @var array<string, IMetadataProvider> */
- private array $providers = [];
- private array $providerClasses = [];
-
- public function __construct(
- private FileMetadataMapper $fileMetadataMapper,
- ) {
- // TODO move to another place, where?
- $this->registerProvider(ExifProvider::class);
- }
-
- /**
- * @param class-string<IMetadataProvider> $className
- */
- public function registerProvider(string $className):void {
- if (in_array($className, $this->providerClasses)) {
- return;
- }
-
- if (call_user_func([$className, 'isAvailable'])) {
- $this->providers[call_user_func([$className, 'getMimetypesSupported'])] = \OC::$server->get($className);
- }
- }
-
- public function generateMetadata(File $file, bool $checkExisting = false): void {
- $existingMetadataGroups = [];
-
- if ($checkExisting) {
- $existingMetadata = $this->fileMetadataMapper->findForFile($file->getId());
- foreach ($existingMetadata as $metadata) {
- $existingMetadataGroups[] = $metadata->getGroupName();
- }
- }
-
- foreach ($this->providers as $supportedMimetype => $provider) {
- if (preg_match($supportedMimetype, $file->getMimeType())) {
- if (count(array_diff($provider::groupsProvided(), $existingMetadataGroups)) > 0) {
- $metaDataGroup = $provider->execute($file);
- foreach ($metaDataGroup as $group => $metadata) {
- $this->fileMetadataMapper->insertOrUpdate($metadata);
- }
- }
- }
- }
- }
-
- public function clearMetadata(int $fileId): void {
- $this->fileMetadataMapper->clear($fileId);
- }
-
- /**
- * @return array<int, FileMetadata>
- */
- public function fetchMetadataFor(string $group, array $fileIds): array {
- return $this->fileMetadataMapper->findForGroupForFiles($fileIds, $group);
- }
-
- public function getCapabilities(): array {
- $capabilities = [];
- foreach ($this->providers as $supportedMimetype => $provider) {
- foreach ($provider::groupsProvided() as $group) {
- if (isset($capabilities[$group])) {
- $capabilities[$group][] = $supportedMimetype;
- }
- $capabilities[$group] = [$supportedMimetype];
- }
- }
- return $capabilities;
- }
-}
diff --git a/lib/private/Metadata/Provider/ExifProvider.php b/lib/private/Metadata/Provider/ExifProvider.php
deleted file mode 100644
index 26edc6e01a0..00000000000
--- a/lib/private/Metadata/Provider/ExifProvider.php
+++ /dev/null
@@ -1,139 +0,0 @@
-<?php
-
-declare(strict_types=1);
-/**
- * @copyright Copyright 2022 Carl Schwan <carl@carlschwan.eu>
- * @copyright Copyright 2022 Louis Chmn <louis@chmn.me>
- * @license AGPL-3.0-or-later
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
- */
-
-namespace OC\Metadata\Provider;
-
-use OC\Metadata\FileMetadata;
-use OC\Metadata\IMetadataProvider;
-use OCP\Files\File;
-use Psr\Log\LoggerInterface;
-
-class ExifProvider implements IMetadataProvider {
- public function __construct(
- private LoggerInterface $logger,
- ) {
- }
-
- public static function groupsProvided(): array {
- return ['size', 'gps'];
- }
-
- public static function isAvailable(): bool {
- return extension_loaded('exif');
- }
-
- /** @return array{'gps'?: FileMetadata, 'size'?: FileMetadata} */
- public function execute(File $file): array {
- $exifData = [];
- $fileDescriptor = $file->fopen('rb');
-
- if ($fileDescriptor === false) {
- return [];
- }
-
- $data = null;
- try {
- // Needed to make reading exif data reliable.
- // This is to trigger this condition: https://github.com/php/php-src/blob/d64aa6f646a7b5e58359dc79479860164239580a/main/streams/streams.c#L710
- // But I don't understand why 1 as a special meaning.
- // Revert right after reading the exif data.
- $oldBufferSize = stream_set_chunk_size($fileDescriptor, 1);
- $data = @exif_read_data($fileDescriptor, 'ANY_TAG', true);
- stream_set_chunk_size($fileDescriptor, $oldBufferSize);
- } catch (\Exception $ex) {
- $this->logger->info("Couldn't extract metadata for ".$file->getId(), ['exception' => $ex]);
- }
-
- $size = new FileMetadata();
- $size->setGroupName('size');
- $size->setId($file->getId());
- $size->setArrayAsValue([]);
-
- if (!$data) {
- $sizeResult = getimagesizefromstring($file->getContent());
- if ($sizeResult !== false) {
- $size->setArrayAsValue([
- 'width' => $sizeResult[0],
- 'height' => $sizeResult[1],
- ]);
-
- $exifData['size'] = $size;
- }
- } elseif (array_key_exists('COMPUTED', $data)) {
- if (array_key_exists('Width', $data['COMPUTED']) && array_key_exists('Height', $data['COMPUTED'])) {
- $size->setArrayAsValue([
- 'width' => $data['COMPUTED']['Width'],
- 'height' => $data['COMPUTED']['Height'],
- ]);
-
- $exifData['size'] = $size;
- }
- }
-
- if ($data && array_key_exists('GPS', $data)
- && array_key_exists('GPSLatitude', $data['GPS']) && array_key_exists('GPSLatitudeRef', $data['GPS'])
- && array_key_exists('GPSLongitude', $data['GPS']) && array_key_exists('GPSLongitudeRef', $data['GPS'])
- ) {
- $gps = new FileMetadata();
- $gps->setGroupName('gps');
- $gps->setId($file->getId());
- $gps->setArrayAsValue([
- 'latitude' => $this->gpsDegreesToDecimal($data['GPS']['GPSLatitude'], $data['GPS']['GPSLatitudeRef']),
- 'longitude' => $this->gpsDegreesToDecimal($data['GPS']['GPSLongitude'], $data['GPS']['GPSLongitudeRef']),
- ]);
-
- $exifData['gps'] = $gps;
- }
-
- return $exifData;
- }
-
- public static function getMimetypesSupported(): string {
- return '/image\/(png|jpeg|heif|webp|tiff)/';
- }
-
- /**
- * @param array|string $coordinates
- */
- private static function gpsDegreesToDecimal($coordinates, ?string $hemisphere): float {
- if (is_string($coordinates)) {
- $coordinates = array_map("trim", explode(",", $coordinates));
- }
-
- if (count($coordinates) !== 3) {
- throw new \Exception('Invalid coordinate format: ' . json_encode($coordinates));
- }
-
- [$degrees, $minutes, $seconds] = array_map(function (string $rawDegree) {
- $parts = explode('/', $rawDegree);
-
- if ($parts[1] === '0') {
- return 0;
- }
-
- return floatval($parts[0]) / floatval($parts[1] ?? 1);
- }, $coordinates);
-
- $sign = ($hemisphere === 'W' || $hemisphere === 'S') ? -1 : 1;
- return $sign * ($degrees + $minutes / 60 + $seconds / 3600);
- }
-}
diff --git a/lib/private/Server.php b/lib/private/Server.php
index 31e4a9f79e2..80f9ba8bdd1 100644
--- a/lib/private/Server.php
+++ b/lib/private/Server.php
@@ -121,9 +121,6 @@ use OC\Log\PsrLoggerAdapter;
use OC\Mail\Mailer;
use OC\Memcache\ArrayCache;
use OC\Memcache\Factory;
-use OC\Metadata\Capabilities as MetadataCapabilities;
-use OC\Metadata\IMetadataManager;
-use OC\Metadata\MetadataManager;
use OC\Notification\Manager;
use OC\OCM\Model\OCMProvider;
use OC\OCM\OCMDiscoveryService;
@@ -1136,9 +1133,6 @@ class Server extends ServerContainer implements IServerContainer {
$manager->registerCapability(function () use ($c) {
return $c->get(\OC\Security\Bruteforce\Capabilities::class);
});
- $manager->registerCapability(function () use ($c) {
- return $c->get(MetadataCapabilities::class);
- });
return $manager;
});
/** @deprecated 19.0.0 */
@@ -1415,8 +1409,6 @@ class Server extends ServerContainer implements IServerContainer {
$this->registerAlias(IBroker::class, Broker::class);
- $this->registerAlias(IMetadataManager::class, MetadataManager::class);
-
$this->registerAlias(\OCP\Files\AppData\IAppDataFactory::class, \OC\Files\AppData\Factory::class);
$this->registerAlias(IBinaryFinder::class, BinaryFinder::class);
diff --git a/lib/public/FilesMetadata/IFilesMetadataManager.php b/lib/public/FilesMetadata/IFilesMetadataManager.php
index 1cd0fcb4125..61494cac371 100644
--- a/lib/public/FilesMetadata/IFilesMetadataManager.php
+++ b/lib/public/FilesMetadata/IFilesMetadataManager.php
@@ -69,12 +69,13 @@ interface IFilesMetadataManager {
* returns metadata from a file id
*
* @param int $fileId file id
+ * @param boolean $generate Generate if metadata does not exist
*
* @return IFilesMetadata
* @throws FilesMetadataNotFoundException if not found
* @since 28.0.0
*/
- public function getMetadata(int $fileId): IFilesMetadata;
+ public function getMetadata(int $fileId, bool $generate = false): IFilesMetadata;
/**
* save metadata to database and refresh indexes.
diff --git a/lib/public/FilesMetadata/Model/IFilesMetadata.php b/lib/public/FilesMetadata/Model/IFilesMetadata.php
index ca1cc898f74..e5b6ff1bce0 100644
--- a/lib/public/FilesMetadata/Model/IFilesMetadata.php
+++ b/lib/public/FilesMetadata/Model/IFilesMetadata.php
@@ -120,7 +120,7 @@ interface IFilesMetadata extends JsonSerializable {
* @throws FilesMetadataTypeException
* @since 28.0.0
*/
- public function get(string $key): string;
+ public function getString(string $key): string;
/**
* returns int value for a metadata key
@@ -222,7 +222,7 @@ interface IFilesMetadata extends JsonSerializable {
* @return self
* @since 28.0.0
*/
- public function set(string $key, string $value, bool $index = false): self;
+ public function setString(string $key, string $value, bool $index = false): self;
/**
* set a metadata key/value pair for int value