Skip to content

Commit 7817845

Browse files
committed
Add a metadata service to store file metadata
Signed-off-by: Carl Schwan <carl@carlschwan.eu>
1 parent cd95fce commit 7817845

39 files changed

+897
-55
lines changed

apps/dav/composer/autoload.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
// autoload.php @generated by Composer
44

5+
if (PHP_VERSION_ID < 50600) {
6+
echo 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
7+
exit(1);
8+
}
9+
510
require_once __DIR__ . '/composer/autoload_real.php';
611

712
return ComposerAutoloaderInitDAV::getLoader();

apps/dav/composer/composer/autoload_real.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public static function getLoader()
2727
spl_autoload_unregister(array('ComposerAutoloaderInitDAV', 'loadClassLoader'));
2828

2929
require __DIR__ . '/autoload_static.php';
30-
\Composer\Autoload\ComposerStaticInitDAV::getInitializer($loader)();
30+
call_user_func(\Composer\Autoload\ComposerStaticInitDAV::getInitializer($loader));
3131

3232
$loader->setClassMapAuthoritative(true);
3333
$loader->register(true);

apps/dav/composer/composer/installed.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
'type' => 'library',
66
'install_path' => __DIR__ . '/../',
77
'aliases' => array(),
8-
'reference' => 'e2c675724fc4ea50f1275bf0027b96f277c32578',
8+
'reference' => '9586920c0ec4016864a2219e838fb272127822d8',
99
'name' => '__root__',
1010
'dev' => false,
1111
),
@@ -16,7 +16,7 @@
1616
'type' => 'library',
1717
'install_path' => __DIR__ . '/../',
1818
'aliases' => array(),
19-
'reference' => 'e2c675724fc4ea50f1275bf0027b96f277c32578',
19+
'reference' => '9586920c0ec4016864a2219e838fb272127822d8',
2020
'dev_requirement' => false,
2121
),
2222
),

apps/dav/lib/Connector/Sabre/Directory.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434

3535
use OC\Files\Mount\MoveableMount;
3636
use OC\Files\View;
37+
use OC\Metadata\FileMetadata;
38+
use OC\Metadata\MetadataGroup;
3739
use OCA\DAV\Connector\Sabre\Exception\FileLocked;
3840
use OCA\DAV\Connector\Sabre\Exception\Forbidden;
3941
use OCA\DAV\Connector\Sabre\Exception\InvalidPath;
@@ -73,6 +75,9 @@ class Directory extends \OCA\DAV\Connector\Sabre\Node implements \Sabre\DAV\ICol
7375
*/
7476
private $tree;
7577

78+
/** @var array<string, array<int, FileMetadata>> */
79+
private array $metadata = [];
80+
7681
/**
7782
* Sets up the node, expects a full path name
7883
*

apps/dav/lib/Connector/Sabre/File.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
use OC\Files\Filesystem;
4444
use OC\Files\Stream\HashWrapper;
4545
use OC\Files\View;
46+
use OC\Metadata\FileMetadata;
4647
use OCA\DAV\AppInfo\Application;
4748
use OCA\DAV\Connector\Sabre\Exception\EntityTooLarge;
4849
use OCA\DAV\Connector\Sabre\Exception\FileLocked;
@@ -80,6 +81,9 @@ class File extends Node implements IFile {
8081

8182
protected IL10N $l10n;
8283

84+
/** @var array<string, FileMetadata> */
85+
private array $metadata = [];
86+
8387
/**
8488
* Sets up the node, expects a full path name
8589
*
@@ -757,4 +761,16 @@ public function hash(string $type) {
757761
public function getNode(): \OCP\Files\File {
758762
return $this->node;
759763
}
764+
765+
public function getMetadata(string $group): FileMetadata {
766+
return $this->metadata[$group];
767+
}
768+
769+
public function setMetadata(string $group, FileMetadata $metadata): void {
770+
$this->metadata[$group] = $metadata;
771+
}
772+
773+
public function hasMetadata(string $group) {
774+
return array_key_exists($group, $this->metadata);
775+
}
760776
}

apps/dav/lib/Connector/Sabre/FilesPlugin.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,15 @@
3434
namespace OCA\DAV\Connector\Sabre;
3535

3636
use OC\AppFramework\Http\Request;
37+
use OC\Metadata\IMetadataManager;
3738
use OCP\Constants;
3839
use OCP\Files\ForbiddenException;
3940
use OCP\Files\StorageNotAvailableException;
4041
use OCP\IConfig;
4142
use OCP\IPreview;
4243
use OCP\IRequest;
4344
use OCP\IUserSession;
45+
use Psr\Log\LoggerInterface;
4446
use Sabre\DAV\Exception\Forbidden;
4547
use Sabre\DAV\Exception\NotFound;
4648
use Sabre\DAV\IFile;
@@ -50,6 +52,7 @@
5052
use Sabre\DAV\Tree;
5153
use Sabre\HTTP\RequestInterface;
5254
use Sabre\HTTP\ResponseInterface;
55+
use Sabre\Uri;
5356

5457
class FilesPlugin extends ServerPlugin {
5558

@@ -79,6 +82,7 @@ class FilesPlugin extends ServerPlugin {
7982
public const SHARE_NOTE = '{http://nextcloud.org/ns}note';
8083
public const SUBFOLDER_COUNT_PROPERTYNAME = '{http://nextcloud.org/ns}contained-folder-count';
8184
public const SUBFILE_COUNT_PROPERTYNAME = '{http://nextcloud.org/ns}contained-file-count';
85+
public const FILE_METADATA_SIZE = '{http://nextcloud.org/ns}file-metadata-size';
8286

8387
/**
8488
* Reference to main server object
@@ -436,6 +440,29 @@ public function handleGetProperties(PropFind $propFind, \Sabre\DAV\INode $node)
436440
$propFind->handle(self::UPLOAD_TIME_PROPERTYNAME, function () use ($node) {
437441
return $node->getFileInfo()->getUploadTime();
438442
});
443+
444+
if ($this->config->getSystemValueBool('enable_file_metadata', true)) {
445+
$propFind->handle(self::FILE_METADATA_SIZE, function () use ($node) {
446+
if (!str_starts_with($node->getFileInfo()->getMimetype(), 'image')) {
447+
return json_encode([]);
448+
}
449+
450+
if ($node->hasMetadata('size')) {
451+
$sizeMetadata = $node->getMetadata('size');
452+
} else {
453+
// This code path should not be called since we try to preload
454+
// the metadata when loading the folder or the search results
455+
// in one go
456+
$metadataManager = \OC::$server->get(IMetadataManager::class);
457+
$sizeMetadata = $metadataManager->fetchMetadataFor('size', [$node->getId()])[$node->getId()];
458+
459+
// TODO would be nice to display this in the profiler...
460+
\OC::$server->get(LoggerInterface::class)->warning('Inefficient fetching of metadata');
461+
}
462+
463+
return json_encode($sizeMetadata->getMetadata());
464+
});
465+
}
439466
}
440467

441468
if ($node instanceof Directory) {
@@ -448,6 +475,32 @@ public function handleGetProperties(PropFind $propFind, \Sabre\DAV\INode $node)
448475
});
449476

450477
$requestProperties = $propFind->getRequestedProperties();
478+
479+
// TODO detect dynamically which metadata groups are requested and
480+
// preload all of them and not just size
481+
if ($this->config->getSystemValueBool('enable_file_metadata', true)
482+
&& in_array(self::FILE_METADATA_SIZE, $requestProperties, true)) {
483+
// Preloading of the metadata
484+
$fileIds = [];
485+
foreach ($node->getChildren() as $child) {
486+
/** @var \OCP\Files\Node|Node $child */
487+
if (str_starts_with($child->getFileInfo()->getMimeType(), 'image/')) {
488+
/** @var File $child */
489+
$fileIds[] = $child->getFileInfo()->getId();
490+
}
491+
}
492+
/** @var IMetaDataManager $metadataManager */
493+
$metadataManager = \OC::$server->get(IMetadataManager::class);
494+
$preloadedMetadata = $metadataManager->fetchMetadataFor('size', $fileIds);
495+
foreach ($node->getChildren() as $child) {
496+
/** @var \OCP\Files\Node|Node $child */
497+
if (str_starts_with($child->getFileInfo()->getMimeType(), 'image')) {
498+
/** @var File $child */
499+
$child->setMetadata('size', $preloadedMetadata[$child->getFileInfo()->getId()]);
500+
}
501+
}
502+
}
503+
451504
if (in_array(self::SUBFILE_COUNT_PROPERTYNAME, $requestProperties, true)
452505
|| in_array(self::SUBFOLDER_COUNT_PROPERTYNAME, $requestProperties, true)) {
453506
$nbFiles = 0;

apps/dav/lib/Files/FileSearchBackend.php

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
use OC\Files\Search\SearchOrder;
3131
use OC\Files\Search\SearchQuery;
3232
use OC\Files\View;
33+
use OC\Metadata\IMetadataManager;
3334
use OCA\DAV\Connector\Sabre\CachingTree;
3435
use OCA\DAV\Connector\Sabre\Directory;
3536
use OCA\DAV\Connector\Sabre\FilesPlugin;
@@ -44,6 +45,7 @@
4445
use OCP\IUser;
4546
use OCP\Share\IManager;
4647
use Sabre\DAV\Exception\NotFound;
48+
use Sabre\DAV\INode;
4749
use SearchDAV\Backend\ISearchBackend;
4850
use SearchDAV\Backend\SearchPropertyDefinition;
4951
use SearchDAV\Backend\SearchResult;
@@ -88,14 +90,12 @@ public function __construct(CachingTree $tree, IUser $user, IRootFolder $rootFol
8890

8991
/**
9092
* Search endpoint will be remote.php/dav
91-
*
92-
* @return string
9393
*/
94-
public function getArbiterPath() {
94+
public function getArbiterPath(): string {
9595
return '';
9696
}
9797

98-
public function isValidScope($href, $depth, $path) {
98+
public function isValidScope(string $href, $depth, ?string $path): bool {
9999
// only allow scopes inside the dav server
100100
if (is_null($path)) {
101101
return false;
@@ -109,7 +109,7 @@ public function isValidScope($href, $depth, $path) {
109109
}
110110
}
111111

112-
public function getPropertyDefinitionsForScope($href, $path) {
112+
public function getPropertyDefinitionsForScope(string $href, ?string $path): array {
113113
// all valid scopes support the same schema
114114

115115
//todo dynamically load all propfind properties that are supported
@@ -132,15 +132,44 @@ public function getPropertyDefinitionsForScope($href, $path) {
132132
new SearchPropertyDefinition(FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME, false, true, false),
133133
new SearchPropertyDefinition(FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME, false, true, false),
134134
new SearchPropertyDefinition(FilesPlugin::HAS_PREVIEW_PROPERTYNAME, false, true, false, SearchPropertyDefinition::DATATYPE_BOOLEAN),
135+
new SearchPropertyDefinition(FilesPlugin::FILE_METADATA_SIZE, false, true, false, SearchPropertyDefinition::DATATYPE_STRING),
135136
new SearchPropertyDefinition(FilesPlugin::FILEID_PROPERTYNAME, false, true, false, SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER),
136137
];
137138
}
138139

140+
/**
141+
* @param INode[] $nodes
142+
* @param string[] $requestProperties
143+
*/
144+
public function preloadPropertyFor(array $nodes, array $requestProperties): void {
145+
if (in_array(FilesPlugin::FILE_METADATA_SIZE, $requestProperties, true)) {
146+
// Preloading of the metadata
147+
$fileIds = [];
148+
foreach ($nodes as $node) {
149+
/** @var \OCP\Files\Node|\OCA\DAV\Connector\Sabre\Node $node */
150+
if (str_starts_with($node->getFileInfo()->getMimeType(), 'image/')) {
151+
/** @var \OCA\DAV\Connector\Sabre\File $node */
152+
$fileIds[] = $node->getFileInfo()->getId();
153+
}
154+
}
155+
/** @var IMetaDataManager $metadataManager */
156+
$metadataManager = \OC::$server->get(IMetadataManager::class);
157+
$preloadedMetadata = $metadataManager->fetchMetadataFor('size', $fileIds);
158+
foreach ($nodes as $node) {
159+
/** @var \OCP\Files\Node|\OCA\DAV\Connector\Sabre\Node $node */
160+
if (str_starts_with($node->getFileInfo()->getMimeType(), 'image/')) {
161+
/** @var \OCA\DAV\Connector\Sabre\File $node */
162+
$node->setMetadata('size', $preloadedMetadata[$node->getFileInfo()->getId()]);
163+
}
164+
}
165+
}
166+
}
167+
139168
/**
140169
* @param Query $search
141170
* @return SearchResult[]
142171
*/
143-
public function search(Query $search) {
172+
public function search(Query $search): array {
144173
if (count($search->from) !== 1) {
145174
throw new \InvalidArgumentException('Searching more than one folder is not supported');
146175
}

apps/dav/lib/Files/LazySearchBackend.php

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
*/
2323
namespace OCA\DAV\Files;
2424

25+
use Sabre\DAV\INode;
2526
use SearchDAV\Backend\ISearchBackend;
2627
use SearchDAV\Query\Query;
2728

@@ -35,35 +36,38 @@ public function setBackend(ISearchBackend $backend) {
3536
$this->backend = $backend;
3637
}
3738

38-
public function getArbiterPath() {
39+
public function getArbiterPath(): string {
3940
if ($this->backend) {
4041
return $this->backend->getArbiterPath();
4142
} else {
4243
return '';
4344
}
4445
}
4546

46-
public function isValidScope($href, $depth, $path) {
47+
public function isValidScope(string $href, $depth, ?string $path): bool {
4748
if ($this->backend) {
4849
return $this->backend->getArbiterPath();
49-
} else {
50-
return false;
5150
}
51+
return false;
5252
}
5353

54-
public function getPropertyDefinitionsForScope($href, $path) {
54+
public function getPropertyDefinitionsForScope(string $href, ?String $path): array {
5555
if ($this->backend) {
5656
return $this->backend->getPropertyDefinitionsForScope($href, $path);
57-
} else {
58-
return [];
5957
}
58+
return [];
6059
}
6160

62-
public function search(Query $query) {
61+
public function search(Query $query): array {
6362
if ($this->backend) {
6463
return $this->backend->search($query);
65-
} else {
66-
return [];
64+
}
65+
return [];
66+
}
67+
68+
public function preloadPropertyFor(array $nodes, array $requestProperties): void {
69+
if ($this->backend) {
70+
$this->backend->preloadPropertyFor($nodes, $requestProperties);
6771
}
6872
}
6973
}

build/psalm-baseline.xml

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -826,10 +826,6 @@
826826
</InvalidScalarArgument>
827827
</file>
828828
<file src="apps/dav/lib/Files/FileSearchBackend.php">
829-
<InvalidArgument occurrences="2">
830-
<code>$argument</code>
831-
<code>$operator-&gt;arguments</code>
832-
</InvalidArgument>
833829
<InvalidReturnStatement occurrences="1">
834830
<code>$value</code>
835831
</InvalidReturnStatement>
@@ -839,9 +835,6 @@
839835
<ParamNameMismatch occurrences="1">
840836
<code>$search</code>
841837
</ParamNameMismatch>
842-
<UndefinedDocblockClass occurrences="1">
843-
<code>$operator-&gt;arguments[0]-&gt;name</code>
844-
</UndefinedDocblockClass>
845838
<UndefinedPropertyFetch occurrences="1">
846839
<code>$operator-&gt;arguments[0]-&gt;name</code>
847840
</UndefinedPropertyFetch>
@@ -855,9 +848,7 @@
855848
<InvalidReturnStatement occurrences="1">
856849
<code>$this-&gt;backend-&gt;getArbiterPath()</code>
857850
</InvalidReturnStatement>
858-
<InvalidReturnType occurrences="1">
859-
<code>isValidScope</code>
860-
</InvalidReturnType>
851+
<InvalidReturnType occurrences="1"/>
861852
</file>
862853
<file src="apps/dav/lib/Files/RootCollection.php">
863854
<UndefinedFunction occurrences="1">

0 commit comments

Comments
 (0)