diff options
Diffstat (limited to 'apps')
-rw-r--r-- | apps/dav/lib/Connector/Sabre/FilesPlugin.php | 9 | ||||
-rw-r--r-- | apps/dav/lib/Files/FileSearchBackend.php | 102 | ||||
-rw-r--r-- | apps/dav/lib/Server.php | 5 | ||||
-rw-r--r-- | apps/dav/tests/unit/Files/FileSearchBackendTest.php | 5 | ||||
-rw-r--r-- | apps/files/lib/Command/Scan.php | 15 | ||||
-rw-r--r-- | apps/files_trashbin/lib/Trash/TrashItem.php | 10 |
6 files changed, 98 insertions, 48 deletions
diff --git a/apps/dav/lib/Connector/Sabre/FilesPlugin.php b/apps/dav/lib/Connector/Sabre/FilesPlugin.php index 709a4cd68ed..9319327d094 100644 --- a/apps/dav/lib/Connector/Sabre/FilesPlugin.php +++ b/apps/dav/lib/Connector/Sabre/FilesPlugin.php @@ -7,6 +7,7 @@ * @author Christoph Wurst <christoph@winzerhof-wurst.at> * @author Joas Schilling <coding@schilljs.com> * @author Lukas Reschke <lukas@statuscode.ch> + * @author Maxence Lange <maxence@artificial-owl.com> * @author Michael Jobst <mjobst+github@tecratech.de> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> @@ -49,8 +50,8 @@ use Sabre\DAV\IFile; use Sabre\DAV\INode; use Sabre\DAV\PropFind; use Sabre\DAV\PropPatch; -use Sabre\DAV\ServerPlugin; use Sabre\DAV\Server; +use Sabre\DAV\ServerPlugin; use Sabre\DAV\Tree; use Sabre\HTTP\RequestInterface; use Sabre\HTTP\ResponseInterface; @@ -84,6 +85,7 @@ class FilesPlugin extends ServerPlugin { public const SHARE_NOTE = '{http://nextcloud.org/ns}note'; public const SUBFOLDER_COUNT_PROPERTYNAME = '{http://nextcloud.org/ns}contained-folder-count'; public const SUBFILE_COUNT_PROPERTYNAME = '{http://nextcloud.org/ns}contained-file-count'; + public const FILE_METADATA_PREFIX = '{http://nextcloud.org/ns}metadata-'; public const FILE_METADATA_SIZE = '{http://nextcloud.org/ns}file-metadata-size'; public const FILE_METADATA_GPS = '{http://nextcloud.org/ns}file-metadata-gps'; @@ -389,6 +391,11 @@ class FilesPlugin extends ServerPlugin { $propFind->handle(self::CREATION_TIME_PROPERTYNAME, function () use ($node) { return $node->getFileInfo()->getCreationTime(); }); + + foreach ($node->getFileInfo()->getMetadata() as $metadataKey => $metadataValue) { + $propFind->handle(self::FILE_METADATA_PREFIX . $metadataKey, $metadataValue); + } + /** * Return file/folder name as displayname. The primary reason to * implement it this way is to avoid costly fallback to diff --git a/apps/dav/lib/Files/FileSearchBackend.php b/apps/dav/lib/Files/FileSearchBackend.php index 524f90e6623..658ad34ec32 100644 --- a/apps/dav/lib/Files/FileSearchBackend.php +++ b/apps/dav/lib/Files/FileSearchBackend.php @@ -4,6 +4,7 @@ * * @author Christian <16852529+cviereck@users.noreply.github.com> * @author Christoph Wurst <christoph@winzerhof-wurst.at> + * @author Maxence Lange <maxence@artificial-owl.com> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @@ -42,6 +43,9 @@ use OCP\Files\Node; use OCP\Files\Search\ISearchOperator; use OCP\Files\Search\ISearchOrder; use OCP\Files\Search\ISearchQuery; +use OCP\FilesMetadata\IFilesMetadataManager; +use OCP\FilesMetadata\Model\IMetadataQuery; +use OCP\FilesMetadata\Model\IMetadataValueWrapper; use OCP\IUser; use OCP\Share\IManager; use Sabre\DAV\Exception\NotFound; @@ -57,37 +61,14 @@ use SearchDAV\Query\Query; class FileSearchBackend implements ISearchBackend { public const OPERATOR_LIMIT = 100; - /** @var CachingTree */ - private $tree; - - /** @var IUser */ - private $user; - - /** @var IRootFolder */ - private $rootFolder; - - /** @var IManager */ - private $shareManager; - - /** @var View */ - private $view; - - /** - * FileSearchBackend constructor. - * - * @param CachingTree $tree - * @param IUser $user - * @param IRootFolder $rootFolder - * @param IManager $shareManager - * @param View $view - * @internal param IRootFolder $rootFolder - */ - public function __construct(CachingTree $tree, IUser $user, IRootFolder $rootFolder, IManager $shareManager, View $view) { - $this->tree = $tree; - $this->user = $user; - $this->rootFolder = $rootFolder; - $this->shareManager = $shareManager; - $this->view = $view; + public function __construct( + private CachingTree $tree, + private IUser $user, + private IRootFolder $rootFolder, + private IManager $shareManager, + private View $view, + private IFilesMetadataManager $filesMetadataManager, + ) { } /** @@ -115,7 +96,7 @@ class FileSearchBackend implements ISearchBackend { // all valid scopes support the same schema //todo dynamically load all propfind properties that are supported - return [ + $props = [ // queryable properties new SearchPropertyDefinition('{DAV:}displayname', true, true, true), new SearchPropertyDefinition('{DAV:}getcontenttype', true, true, true), @@ -137,6 +118,33 @@ class FileSearchBackend implements ISearchBackend { new SearchPropertyDefinition(FilesPlugin::FILE_METADATA_SIZE, true, false, false, SearchPropertyDefinition::DATATYPE_STRING), new SearchPropertyDefinition(FilesPlugin::FILEID_PROPERTYNAME, true, false, false, SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER), ]; + + return array_merge($props, $this->getPropertyDefinitionsForMetadata()); + } + + + private function getPropertyDefinitionsForMetadata(): array { + $metadataProps = []; + $metadata = $this->filesMetadataManager->getKnownMetadata(); + $indexes = $metadata->getIndexes(); + foreach ($metadata->getKeys() as $key) { + $isIndex = in_array($key, $indexes); + $type = match ($metadata->getType($key)) { + IMetadataValueWrapper::TYPE_INT => SearchPropertyDefinition::DATATYPE_INTEGER, + IMetadataValueWrapper::TYPE_FLOAT => SearchPropertyDefinition::DATATYPE_DECIMAL, + IMetadataValueWrapper::TYPE_BOOL => SearchPropertyDefinition::DATATYPE_BOOLEAN, + default => SearchPropertyDefinition::DATATYPE_STRING + }; + $metadataProps[] = new SearchPropertyDefinition( + FilesPlugin::FILE_METADATA_PREFIX . $key, + true, + $isIndex, + $isIndex, + $type + ); + } + + return $metadataProps; } /** @@ -300,11 +308,20 @@ class FileSearchBackend implements ISearchBackend { /** * @param Query $query + * * @return ISearchQuery */ private function transformQuery(Query $query): ISearchQuery { + $orders = array_map(function (Order $order): ISearchOrder { + $direction = $order->order === Order::ASC ? ISearchOrder::DIRECTION_ASCENDING : ISearchOrder::DIRECTION_DESCENDING; + if (str_starts_with($order->property->name, FilesPlugin::FILE_METADATA_PREFIX)) { + return new SearchOrder($direction, substr($order->property->name, strlen(FilesPlugin::FILE_METADATA_PREFIX)), IMetadataQuery::EXTRA); + } else { + return new SearchOrder($direction, $this->mapPropertyNameToColumn($order->property)); + } + }, $query->orderBy); + $limit = $query->limit; - $orders = array_map([$this, 'mapSearchOrder'], $query->orderBy); $offset = $limit->firstResult; $limitHome = false; @@ -353,14 +370,6 @@ class FileSearchBackend implements ISearchBackend { } /** - * @param Order $order - * @return ISearchOrder - */ - private function mapSearchOrder(Order $order) { - return new SearchOrder($order->order === Order::ASC ? ISearchOrder::DIRECTION_ASCENDING : ISearchOrder::DIRECTION_DESCENDING, $this->mapPropertyNameToColumn($order->property)); - } - - /** * @param Operator $operator * @return ISearchOperator */ @@ -387,7 +396,16 @@ class FileSearchBackend implements ISearchBackend { if (!($operator->arguments[1] instanceof Literal)) { throw new \InvalidArgumentException('Invalid argument 2 for ' . $trimmedType . ' operation, expected literal'); } - return new SearchComparison($trimmedType, $this->mapPropertyNameToColumn($operator->arguments[0]), $this->castValue($operator->arguments[0], $operator->arguments[1]->value)); + + $property = $operator->arguments[0]; + $value = $this->castValue($property, $operator->arguments[1]->value); + if (str_starts_with($property->name, FilesPlugin::FILE_METADATA_PREFIX)) { + return new SearchComparison($trimmedType, substr($property->name, strlen(FilesPlugin::FILE_METADATA_PREFIX)), $value, IMetadataQuery::EXTRA); + } else { + return new SearchComparison($trimmedType, $this->mapPropertyNameToColumn($property), $value); + } + + // no break case Operator::OPERATION_IS_COLLECTION: return new SearchComparison('eq', 'mimetype', ICacheEntry::DIRECTORY_MIMETYPE); default: diff --git a/apps/dav/lib/Server.php b/apps/dav/lib/Server.php index 603e015fca9..15b97d028cc 100644 --- a/apps/dav/lib/Server.php +++ b/apps/dav/lib/Server.php @@ -11,6 +11,7 @@ * @author Joas Schilling <coding@schilljs.com> * @author John Molakvoæ <skjnldsv@protonmail.com> * @author Lukas Reschke <lukas@statuscode.ch> + * @author Maxence Lange <maxence@artificial-owl.com> * @author Morris Jobke <hey@morrisjobke.de> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> @@ -76,6 +77,7 @@ use OCA\DAV\Upload\ChunkingV2Plugin; use OCP\AppFramework\Http\Response; use OCP\Diagnostics\IEventLogger; use OCP\EventDispatcher\IEventDispatcher; +use OCP\FilesMetadata\IFilesMetadataManager; use OCP\ICacheFactory; use OCP\IRequest; use OCP\Profiler\IProfiler; @@ -316,7 +318,8 @@ class Server { $user, \OC::$server->getRootFolder(), \OC::$server->getShareManager(), - $view + $view, + \OCP\Server::get(IFilesMetadataManager::class) )); $this->server->addPlugin( new BulkUploadPlugin( diff --git a/apps/dav/tests/unit/Files/FileSearchBackendTest.php b/apps/dav/tests/unit/Files/FileSearchBackendTest.php index 715130d2fae..ea841140201 100644 --- a/apps/dav/tests/unit/Files/FileSearchBackendTest.php +++ b/apps/dav/tests/unit/Files/FileSearchBackendTest.php @@ -41,6 +41,7 @@ use OCP\Files\IRootFolder; use OCP\Files\Search\ISearchBinaryOperator; use OCP\Files\Search\ISearchComparison; use OCP\Files\Search\ISearchQuery; +use OCP\FilesMetadata\IFilesMetadataManager; use OCP\IUser; use OCP\Share\IManager; use SearchDAV\Backend\SearchPropertyDefinition; @@ -114,7 +115,9 @@ class FileSearchBackendTest extends TestCase { ->method('get') ->willReturn($this->searchFolder); - $this->search = new FileSearchBackend($this->tree, $this->user, $this->rootFolder, $this->shareManager, $this->view); + $filesMetadataManager = $this->createMock(IFilesMetadataManager::class); + + $this->search = new FileSearchBackend($this->tree, $this->user, $this->rootFolder, $this->shareManager, $this->view, $filesMetadataManager); } public function testSearchFilename(): void { diff --git a/apps/files/lib/Command/Scan.php b/apps/files/lib/Command/Scan.php index f5ac3627196..b1fc25bfe9b 100644 --- a/apps/files/lib/Command/Scan.php +++ b/apps/files/lib/Command/Scan.php @@ -11,6 +11,7 @@ * @author Joel S <joel.devbox@protonmail.com> * @author Jörn Friedrich Dreyer <jfd@butonic.de> * @author martin.mattel@diemattels.at <martin.mattel@diemattels.at> + * @author Maxence Lange <maxence@artificial-owl.com> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> @@ -37,17 +38,19 @@ use OC\Core\Command\Base; use OC\Core\Command\InterruptedException; use OC\DB\Connection; use OC\DB\ConnectionAdapter; +use OC\FilesMetadata\FilesMetadataManager; +use OC\ForbiddenException; +use OC\Metadata\MetadataManager; +use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Events\FileCacheUpdated; use OCP\Files\Events\NodeAddedToCache; use OCP\Files\Events\NodeRemovedFromCache; use OCP\Files\File; -use OC\ForbiddenException; -use OC\Metadata\MetadataManager; -use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\IRootFolder; use OCP\Files\Mount\IMountPoint; use OCP\Files\NotFoundException; use OCP\Files\StorageNotAvailableException; +use OCP\FilesMetadata\IFilesMetadataManager; use OCP\IUserManager; use Psr\Log\LoggerInterface; use Symfony\Component\Console\Helper\Table; @@ -69,6 +72,7 @@ class Scan extends Base { private IUserManager $userManager, private IRootFolder $rootFolder, private MetadataManager $metadataManager, + private FilesMetadataManager $filesMetadataManager, private IEventDispatcher $eventDispatcher, private LoggerInterface $logger, ) { @@ -140,6 +144,11 @@ class Scan extends Base { if ($node instanceof File) { $this->metadataManager->generateMetadata($node, false); } + + $this->filesMetadataManager->refreshMetadata( + $node, + IFilesMetadataManager::PROCESS_LIVE | IFilesMetadataManager::PROCESS_BACKGROUND + ); } }); diff --git a/apps/files_trashbin/lib/Trash/TrashItem.php b/apps/files_trashbin/lib/Trash/TrashItem.php index 3bfc905d3a1..99b2d3a1a2c 100644 --- a/apps/files_trashbin/lib/Trash/TrashItem.php +++ b/apps/files_trashbin/lib/Trash/TrashItem.php @@ -2,6 +2,7 @@ /** * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl> * + * @author Maxence Lange <maxence@artificial-owl.com> * @author Robin Appelman <robin@icewind.nl> * * @license GNU AGPL version 3 or any later version @@ -23,6 +24,7 @@ namespace OCA\Files_Trashbin\Trash; use OCP\Files\FileInfo; +use OCP\FilesMetadata\Model\IFilesMetadata; use OCP\IUser; class TrashItem implements ITrashItem { @@ -190,4 +192,12 @@ class TrashItem implements ITrashItem { public function getParentId(): int { return $this->fileInfo->getParentId(); } + + /** + * @inheritDoc + * @return array<string, int|string|bool|float|string[]|int[]> + */ + public function getMetadata(): array { + return $this->fileInfo->getMetadata(); + } } |