Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions apps/dav/appinfo/v1/publicwebdav.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
use NCU\Files\NodeAttributes\NodeAttributeContext;
use OC\Files\Filesystem;
use OC\Files\NodeAttributes\NodeAttributeRegistry;
use OC\Files\Storage\Wrapper\DirPermissionsMask;
use OC\Files\Storage\Wrapper\PermissionsMask;
use OC\Files\View;
use OC\OCM\OCMSignatoryManager;
use OCA\DAV\Connector\LegacyPublicAuth;
use OCA\DAV\Connector\Sabre\BearerAuth;
use OCA\DAV\Connector\Sabre\NodeAttributesPlugin;
use OCA\DAV\Connector\Sabre\ServerFactory;
use OCA\DAV\Files\Sharing\FilesDropPlugin;
use OCA\DAV\Files\Sharing\PublicLinkCheckPlugin;
Expand All @@ -27,7 +30,6 @@
use OCP\Files\Storage\IStorage;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\IPreview;
use OCP\IRequest;
use OCP\ISession;
use OCP\ITagManager;
Expand Down Expand Up @@ -79,7 +81,6 @@
Server::get(IMountManager::class),
Server::get(ITagManager::class),
Server::get(IRequest::class),
Server::get(IPreview::class),
$eventDispatcher,
Server::get(IL10nFactory::class)->get('dav')
);
Expand Down Expand Up @@ -159,6 +160,10 @@ function (\Sabre\DAV\Server $server) use (
$filesDropPlugin->enable();
}
$filesDropPlugin->setShare($share);
$server->addPlugin(new NodeAttributesPlugin(
Server::get(NodeAttributeRegistry::class),
static fn (): NodeAttributeContext => NodeAttributeContext::forShare($share),
));

return new View($node->getPath());
});
Expand Down
2 changes: 0 additions & 2 deletions apps/dav/appinfo/v1/webdav.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
use OCP\Files\Mount\IMountManager;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\IPreview;
use OCP\IRequest;
use OCP\ISession;
use OCP\ITagManager;
Expand Down Expand Up @@ -46,7 +45,6 @@
Server::get(IMountManager::class),
Server::get(ITagManager::class),
Server::get(IRequest::class),
Server::get(IPreview::class),
$dispatcher,
Server::get(IL10nFactory::class)->get('dav')
);
Expand Down
9 changes: 7 additions & 2 deletions apps/dav/appinfo/v2/publicremote.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
use NCU\Files\NodeAttributes\NodeAttributeContext;
use OC\Files\Filesystem;
use OC\Files\NodeAttributes\NodeAttributeRegistry;
use OC\Files\Storage\Wrapper\DirPermissionsMask;
use OC\Files\Storage\Wrapper\PermissionsMask;
use OC\Files\View;
use OC\OCM\OCMSignatoryManager;
use OCA\DAV\Connector\Sabre\BearerAuth;
use OCA\DAV\Connector\Sabre\NodeAttributesPlugin;
use OCA\DAV\Connector\Sabre\PublicAuth;
use OCA\DAV\Connector\Sabre\ServerFactory;
use OCA\DAV\Files\Sharing\FilesDropPlugin;
Expand All @@ -31,7 +34,6 @@
use OCP\ICacheFactory;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\IPreview;
use OCP\IRequest;
use OCP\ISession;
use OCP\ITagManager;
Expand Down Expand Up @@ -91,7 +93,6 @@
Server::get(IMountManager::class),
Server::get(ITagManager::class),
$request,
Server::get(IPreview::class),
$eventDispatcher,
$l10nFactory->get('dav'),
);
Expand Down Expand Up @@ -183,6 +184,10 @@
$filesDropPlugin->enable();
}
$filesDropPlugin->setShare($share);
$server->addPlugin(new NodeAttributesPlugin(
Server::get(NodeAttributeRegistry::class),
static fn (): NodeAttributeContext => NodeAttributeContext::forShare($share),
));

$view = new View($node->getPath());
return $view;
Expand Down
4 changes: 4 additions & 0 deletions apps/dav/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,8 @@
'OCA\\DAV\\Connector\\Sabre\\MaintenancePlugin' => $baseDir . '/../lib/Connector/Sabre/MaintenancePlugin.php',
'OCA\\DAV\\Connector\\Sabre\\MtimeSanitizer' => $baseDir . '/../lib/Connector/Sabre/MtimeSanitizer.php',
'OCA\\DAV\\Connector\\Sabre\\Node' => $baseDir . '/../lib/Connector/Sabre/Node.php',
'OCA\\DAV\\Connector\\Sabre\\NodeAttributeListXml' => $baseDir . '/../lib/Connector/Sabre/NodeAttributeListXml.php',
'OCA\\DAV\\Connector\\Sabre\\NodeAttributesPlugin' => $baseDir . '/../lib/Connector/Sabre/NodeAttributesPlugin.php',
'OCA\\DAV\\Connector\\Sabre\\ObjectTree' => $baseDir . '/../lib/Connector/Sabre/ObjectTree.php',
'OCA\\DAV\\Connector\\Sabre\\Principal' => $baseDir . '/../lib/Connector/Sabre/Principal.php',
'OCA\\DAV\\Connector\\Sabre\\PropFindMonitorPlugin' => $baseDir . '/../lib/Connector/Sabre/PropFindMonitorPlugin.php',
Expand Down Expand Up @@ -343,6 +345,8 @@
'OCA\\DAV\\Files\\FileSearchBackend' => $baseDir . '/../lib/Files/FileSearchBackend.php',
'OCA\\DAV\\Files\\FilesHome' => $baseDir . '/../lib/Files/FilesHome.php',
'OCA\\DAV\\Files\\LazySearchBackend' => $baseDir . '/../lib/Files/LazySearchBackend.php',
'OCA\\DAV\\Files\\NodeAttributes\\FavoriteProvider' => $baseDir . '/../lib/Files/NodeAttributes/FavoriteProvider.php',
'OCA\\DAV\\Files\\NodeAttributes\\HasPreviewProvider' => $baseDir . '/../lib/Files/NodeAttributes/HasPreviewProvider.php',
'OCA\\DAV\\Files\\RootCollection' => $baseDir . '/../lib/Files/RootCollection.php',
'OCA\\DAV\\Files\\Sharing\\FilesDropPlugin' => $baseDir . '/../lib/Files/Sharing/FilesDropPlugin.php',
'OCA\\DAV\\Files\\Sharing\\PublicLinkCheckPlugin' => $baseDir . '/../lib/Files/Sharing/PublicLinkCheckPlugin.php',
Expand Down
4 changes: 4 additions & 0 deletions apps/dav/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,8 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Connector\\Sabre\\MaintenancePlugin' => __DIR__ . '/..' . '/../lib/Connector/Sabre/MaintenancePlugin.php',
'OCA\\DAV\\Connector\\Sabre\\MtimeSanitizer' => __DIR__ . '/..' . '/../lib/Connector/Sabre/MtimeSanitizer.php',
'OCA\\DAV\\Connector\\Sabre\\Node' => __DIR__ . '/..' . '/../lib/Connector/Sabre/Node.php',
'OCA\\DAV\\Connector\\Sabre\\NodeAttributeListXml' => __DIR__ . '/..' . '/../lib/Connector/Sabre/NodeAttributeListXml.php',
'OCA\\DAV\\Connector\\Sabre\\NodeAttributesPlugin' => __DIR__ . '/..' . '/../lib/Connector/Sabre/NodeAttributesPlugin.php',
'OCA\\DAV\\Connector\\Sabre\\ObjectTree' => __DIR__ . '/..' . '/../lib/Connector/Sabre/ObjectTree.php',
'OCA\\DAV\\Connector\\Sabre\\Principal' => __DIR__ . '/..' . '/../lib/Connector/Sabre/Principal.php',
'OCA\\DAV\\Connector\\Sabre\\PropFindMonitorPlugin' => __DIR__ . '/..' . '/../lib/Connector/Sabre/PropFindMonitorPlugin.php',
Expand Down Expand Up @@ -358,6 +360,8 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Files\\FileSearchBackend' => __DIR__ . '/..' . '/../lib/Files/FileSearchBackend.php',
'OCA\\DAV\\Files\\FilesHome' => __DIR__ . '/..' . '/../lib/Files/FilesHome.php',
'OCA\\DAV\\Files\\LazySearchBackend' => __DIR__ . '/..' . '/../lib/Files/LazySearchBackend.php',
'OCA\\DAV\\Files\\NodeAttributes\\FavoriteProvider' => __DIR__ . '/..' . '/../lib/Files/NodeAttributes/FavoriteProvider.php',
'OCA\\DAV\\Files\\NodeAttributes\\HasPreviewProvider' => __DIR__ . '/..' . '/../lib/Files/NodeAttributes/HasPreviewProvider.php',
'OCA\\DAV\\Files\\RootCollection' => __DIR__ . '/..' . '/../lib/Files/RootCollection.php',
'OCA\\DAV\\Files\\Sharing\\FilesDropPlugin' => __DIR__ . '/..' . '/../lib/Files/Sharing/FilesDropPlugin.php',
'OCA\\DAV\\Files\\Sharing\\PublicLinkCheckPlugin' => __DIR__ . '/..' . '/../lib/Files/Sharing/PublicLinkCheckPlugin.php',
Expand Down
6 changes: 6 additions & 0 deletions apps/dav/lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
use OCA\DAV\CardDAV\Search\ContactAccountScopedSearchProvider;
use OCA\DAV\CardDAV\SyncService;
use OCA\DAV\ConfigLexicon;
use OCA\DAV\Connector\Sabre\FilesPlugin;
use OCA\DAV\Connector\Sabre\TagsPlugin;
use OCA\DAV\Events\AddressBookCreatedEvent;
use OCA\DAV\Events\AddressBookDeletedEvent;
use OCA\DAV\Events\AddressBookShareUpdatedEvent;
Expand All @@ -47,6 +49,8 @@
use OCA\DAV\Events\SabrePluginAuthInitEvent;
use OCA\DAV\Events\SubscriptionCreatedEvent;
use OCA\DAV\Events\SubscriptionDeletedEvent;
use OCA\DAV\Files\NodeAttributes\FavoriteProvider;
use OCA\DAV\Files\NodeAttributes\HasPreviewProvider;
use OCA\DAV\Listener\ActivityUpdaterListener;
use OCA\DAV\Listener\AddMissingIndicesListener;
use OCA\DAV\Listener\AddressbookListener;
Expand Down Expand Up @@ -128,6 +132,8 @@ public function __construct() {
#[\Override]
public function register(IRegistrationContext $context): void {
$context->registerServiceAlias('CardDAVSyncService', SyncService::class);
$context->registerNodeAttributeProvider(HasPreviewProvider::class, [FilesPlugin::HAS_PREVIEW_PROPERTYNAME]);
$context->registerNodeAttributeProvider(FavoriteProvider::class, [TagsPlugin::FAVORITE_PROPERTYNAME]);
$context->registerService(AppCalendarPlugin::class, function (ContainerInterface $c) {
return new AppCalendarPlugin(
$c->get(ICalendarManager::class),
Expand Down
6 changes: 0 additions & 6 deletions apps/dav/lib/Connector/Sabre/FilesPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
use OCP\FilesMetadata\IFilesMetadataManager;
use OCP\FilesMetadata\Model\IMetadataValueWrapper;
use OCP\IConfig;
use OCP\IPreview;
use OCP\IRequest;
use OCP\IUserSession;
use OCP\L10N\IFactory;
Expand Down Expand Up @@ -84,7 +83,6 @@ class FilesPlugin extends ServerPlugin {
* @param Tree $tree
* @param IConfig $config
* @param IRequest $request
* @param IPreview $previewManager
* @param IUserSession $userSession
* @param bool $isPublic Whether this is public WebDAV. If true, some returned information will be stripped off.
* @param bool $downloadAttachment
Expand All @@ -94,7 +92,6 @@ public function __construct(
private Tree $tree,
private IConfig $config,
private IRequest $request,
private IPreview $previewManager,
private IUserSession $userSession,
private IFilenameValidator $validator,
private IAccountManager $accountManager,
Expand Down Expand Up @@ -397,9 +394,6 @@ public function handleGetProperties(PropFind $propFind, \Sabre\DAV\INode $node)
return null;
});

$propFind->handle(self::HAS_PREVIEW_PROPERTYNAME, function () use ($node) {
return json_encode($this->previewManager->isAvailable($node->getFileInfo()), JSON_THROW_ON_ERROR);
});
$propFind->handle(self::SIZE_PROPERTYNAME, function () use ($node): int|float {
return $node->getSize();
});
Expand Down
35 changes: 35 additions & 0 deletions apps/dav/lib/Connector/Sabre/NodeAttributeListXml.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\DAV\Connector\Sabre;

use Sabre\Xml\Writer;
use Sabre\Xml\XmlSerializable;

/**
* A node attribute list as WebDAV property: one child element per value
*/
class NodeAttributeListXml implements XmlSerializable {
/**
* @param string $childElement child element name in Clark notation
* @param list<bool|int|float|string> $values
*/
public function __construct(
private readonly string $childElement,
private readonly array $values,
) {
}

#[\Override]
public function xmlSerialize(Writer $writer): void {
foreach ($this->values as $value) {
$writer->writeElement($this->childElement, is_bool($value) ? ($value ? 'true' : 'false') : (string)$value);
}
}
}
143 changes: 143 additions & 0 deletions apps/dav/lib/Connector/Sabre/NodeAttributesPlugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\DAV\Connector\Sabre;

use Closure;
use NCU\Files\NodeAttributes\NodeAttributeContext;
use NCU\Files\NodeAttributes\NodeAttributeList;
use OC\Files\NodeAttributes\NodeAttributeRegistry;
use Sabre\DAV\ICollection;
use Sabre\DAV\INode;
use Sabre\DAV\PropFind;
use Sabre\DAV\Server;
use Sabre\DAV\ServerPlugin;

/**
* Serves the attributes of the registered node attribute providers as WebDAV properties
*/
class NodeAttributesPlugin extends ServerPlugin {
/**
* @param Closure(): ?NodeAttributeContext $contextResolver context of the request, null to serve nothing
*/
public function __construct(
private readonly NodeAttributeRegistry $registry,
private readonly Closure $contextResolver,
) {
}

#[\Override]
public function initialize(Server $server): void {
$server->on('preloadCollection', $this->preloadCollection(...));
$server->on('preloadProperties', $this->preloadProperties(...));
$server->on('propFind', $this->propFind(...));
}

private function preloadCollection(PropFind $propFind, ICollection $collection): void {
if (!($collection instanceof Directory)) {
return;
}

$attributes = $this->getRequestedAttributes($propFind);
if ($attributes === []) {
return;
}

$nodes = [$collection->getNode()];
foreach ($collection->getChildren() as $child) {
if ($child instanceof Node) {
$nodes[] = $child->getNode();
}
}
$this->preload($nodes, $attributes);
}

/**
* Preload the attributes of search results
*
* @param INode[] $nodes
* @param string[] $requestProperties
*/
private function preloadProperties(array $nodes, array $requestProperties): void {
$attributes = array_values(array_intersect($requestProperties, $this->registry->getAttributeNames()));
if ($attributes === []) {
return;
}

$fileNodes = [];
foreach ($nodes as $node) {
if ($node instanceof Node) {
$fileNodes[] = $node->getNode();
}
}
$this->preload($fileNodes, $attributes);
}

private function propFind(PropFind $propFind, INode $node): void {
if (!($node instanceof Node)) {
return;
}

$attributes = $this->getRequestedAttributes($propFind);
$context = ($this->contextResolver)();
if ($attributes === [] || $context === null) {
return;
}

$fileNode = $node->getNode();
$this->registry->preload($context, [$fileNode], $attributes);

foreach ($this->registry->getProvidersFor($attributes) as $group) {
$values = $group['provider']->getAttributes($context, $fileNode, $group['attributes']);
foreach ($group['attributes'] as $attribute) {
if (!isset($values[$attribute])) {
continue;
}
$value = $this->toDavValue($attribute, $values[$attribute]);
$propFind->handle($attribute, static fn () => $value);
}
}
}

/**
* Attributes are only served when requested, also for allprop requests like other custom properties
*
* @return list<string>
*/
private function getRequestedAttributes(PropFind $propFind): array {
return array_values(array_intersect($propFind->getRequestedProperties(), $this->registry->getAttributeNames()));
}

/**
* @param list<\OCP\Files\Node> $nodes
* @param list<string> $attributes
*/
private function preload(array $nodes, array $attributes): void {
$context = ($this->contextResolver)();
if ($context === null || $nodes === []) {
return;
}

$this->registry->preload($context, $nodes, $attributes);
}

/**
* Values are typed for JSON, WebDAV carries text that the client parses back
*/
private function toDavValue(string $attribute, bool|int|float|string|NodeAttributeList $value): int|float|string|NodeAttributeListXml {
if (is_bool($value)) {
return $value ? 'true' : 'false';
}
if ($value instanceof NodeAttributeList) {
$namespace = substr($attribute, 0, (int)strrpos($attribute, '}') + 1);
return new NodeAttributeListXml($namespace . $value->getChildName(), $value->getValues());
}
return $value;
}
}
Loading
Loading