Page MenuHomePhorge

D5835.1774855294.diff
No OneTemporary

Authored By
Unknown
Size
10 KB
Referenced Files
None
Subscribers
None

D5835.1774855294.diff

diff --git a/src/app/Backends/Storage.php b/src/app/Backends/Storage.php
--- a/src/app/Backends/Storage.php
+++ b/src/app/Backends/Storage.php
@@ -213,6 +213,10 @@
if ($file->type & Item::TYPE_INCOMPLETE) {
$file->type -= Item::TYPE_INCOMPLETE;
$file->save();
+ } else {
+ // Bump last modification time (needed e.g. for proper WebDAV syncronization/ETag)
+ // Note: We don't use touch() directly on $file because it fails when the object has custom properties
+ Item::where('id', $file->id)->touch();
}
// Update the file type and size information
diff --git a/src/app/Http/Controllers/DAVController.php b/src/app/Http/Controllers/DAVController.php
--- a/src/app/Http/Controllers/DAVController.php
+++ b/src/app/Http/Controllers/DAVController.php
@@ -69,6 +69,7 @@
// Register some plugins
$server->addPlugin(new \Sabre\DAV\Auth\Plugin($auth_backend));
+ $server->addPlugin(new DAV\ServerPlugin());
// Unauthenticated access doesn't work for us since we require credentials to get access to the data in the first place.
// $acl_plugin = new \Sabre\DAVACL\Plugin();
diff --git a/src/app/Http/DAV/Collection.php b/src/app/Http/DAV/Collection.php
--- a/src/app/Http/DAV/Collection.php
+++ b/src/app/Http/DAV/Collection.php
@@ -8,15 +8,18 @@
use Sabre\DAV\Exception;
use Sabre\DAV\ICollection;
use Sabre\DAV\ICopyTarget;
+use Sabre\DAV\IExtendedCollection;
use Sabre\DAV\IMoveTarget;
use Sabre\DAV\INode;
use Sabre\DAV\INodeByPath;
use Sabre\DAV\IProperties;
+use Sabre\DAV\MkCol;
+use Sabre\DAV\Xml\Property\ResourceType;
/**
* Sabre DAV Collection interface implemetation
*/
-class Collection extends Node implements ICollection, ICopyTarget, IMoveTarget, INodeByPath, IProperties
+class Collection extends Node implements ICollection, ICopyTarget, IExtendedCollection, IMoveTarget, INodeByPath, IProperties
{
/**
* Checks if a child-node exists.
@@ -76,6 +79,49 @@
return true;
}
+ /**
+ * Creates a new collection.
+ *
+ * This method will receive a MkCol object with all the information about
+ * the new collection that's being created.
+ *
+ * The MkCol object contains information about the resourceType of the new
+ * collection. If you don't support the specified resourceType, you should
+ * throw Exception\InvalidResourceType.
+ *
+ * The object also contains a list of WebDAV properties for the new
+ * collection.
+ *
+ * You should call the handle() method on this object to specify exactly
+ * which properties you are storing. This allows the system to figure out
+ * exactly which properties you didn't store, which in turn allows other
+ * plugins (such as the propertystorage plugin) to handle storing the
+ * property for you.
+ *
+ * @param string $name
+ *
+ * @throws Exception\InvalidResourceType
+ */
+ public function createExtendedCollection($name, MkCol $mkCol)
+ {
+ $types = $mkCol->getResourceType();
+
+ if (count($types) > 1) {
+ // For now we only support use of 'notebook' in the resourcetype (Kolab Notes)
+ if (in_array('{Kolab:}notebook', $types)) {
+ $type = 'notebook';
+ } else {
+ throw new Exception\InvalidResourceType('The {DAV:}resourcetype you specified is not supported here.');
+ }
+ }
+
+ $collection = $this->createDirectory($name);
+
+ if (!empty($type)) {
+ $collection->setProperty('dav:resourcetype', $type);
+ }
+ }
+
/**
* Creates a new file in the directory
*
@@ -160,6 +206,8 @@
}
DB::commit();
+
+ return $collection;
}
/**
@@ -207,9 +255,10 @@
->select('fs_items.*')
->whereNot('type', '&', Item::TYPE_INCOMPLETE);
- foreach (['name', 'size', 'mimetype'] as $key) {
+ foreach (['name', 'size', 'mimetype', 'dav:resourcetype', 'dav:displayname', 'dav:links', 'dav:categories'] as $key) {
+ $alias = str_replace('dav:', '', $key);
$query->selectRaw('(select value from fs_properties where fs_items.id = fs_properties.item_id'
- . " and fs_properties.key = '{$key}') as {$key}");
+ . " and fs_properties.key = '{$key}') as {$alias}");
}
if ($parent = $this->data) {
@@ -309,6 +358,10 @@
$result['{DAV:}creationdate'] = \Sabre\HTTP\toDate($this->data->created_at);
}
+ if (!empty($this->data->resourcetype)) {
+ $result['{DAV:}resourcetype'] = new ResourceType(['{DAV:}collection', "{Kolab:}{$this->data->resourcetype}"]);
+ }
+
return $result;
}
@@ -361,7 +414,6 @@
{
\Log::debug('[DAV] PROP-PATCH: ' . $this->path);
- // not supported
- // FIXME: Should we throw an exception?
+ // Not implemented
}
}
diff --git a/src/app/Http/DAV/File.php b/src/app/Http/DAV/File.php
--- a/src/app/Http/DAV/File.php
+++ b/src/app/Http/DAV/File.php
@@ -3,7 +3,7 @@
namespace App\Http\DAV;
use App\Backends\Storage;
-use Sabre\DAV\Exception;
+use App\Fs\Item;
use Sabre\DAV\IFile;
use Sabre\DAV\IProperties;
@@ -86,6 +86,18 @@
$result['{DAV:}creationdate'] = \Sabre\HTTP\toDate($this->data->created_at);
}
+ if (!empty($this->data->displayname)) {
+ $result['{DAV:}displayname'] = $this->data->displayname;
+ }
+
+ if (isset($this->data->links)) {
+ $result['{Kolab:}links'] = self::propListOutput(\json_decode($this->data->links), 'link');
+ }
+
+ if (isset($this->data->categories)) {
+ $result['{Kolab:}categories'] = self::propListOutput(\json_decode($this->data->categories), 'category');
+ }
+
return $result;
}
@@ -112,8 +124,62 @@
{
\Log::debug('[DAV] PROP-PATCH: ' . $this->path);
- // not supported
- // FIXME: Should we throw an exception?
+ // Note: Here we register handlers that are executed later by Sabre/DAV
+ $propPatch->handle(
+ // Properties used by Kolab Notes
+ ['{DAV:}displayname', '{Kolab:}links', '{Kolab:}categories'],
+ function ($properties) {
+ return $this->propPatchValidateAndSave($properties);
+ }
+ );
+ }
+
+ /**
+ * Validate PROPPATCH properties
+ */
+ protected function propPatchValidateAndSave($properties): array
+ {
+ $result = [];
+ $updated = false;
+
+ foreach ($properties as $key => $value) {
+ $status = true;
+ $prop_name = null;
+
+ switch ($key) {
+ case '{DAV:}displayname':
+ $prop_name = 'dav:displayname';
+ $status = is_string($value);
+ break;
+ case '{Kolab:}categories':
+ case '{Kolab:}links':
+ $prop_name = 'dav:' . str_replace('{Kolab:}', '', $key);
+ $status = is_array($value);
+ break;
+ }
+
+ if ($status && $prop_name) {
+ if ($value === '' || (is_array($value) && empty($value))) {
+ $value = null;
+ }
+ if (is_array($value)) {
+ $value = json_encode($value);
+ }
+
+ $updated = $updated || $value !== ($this->data->{$prop_name} ?? null);
+ $this->data->setProperty($prop_name, $value);
+ }
+
+ $result[$key] = $status ? 200 : 403; // result to SabreDAV
+ }
+
+ // Bump last modification time (needed e.g. for proper WebDAV syncronization/ETag)
+ // Note: We don't use touch() directly on $file because it fails when the object has custom properties
+ if ($updated) {
+ Item::where('id', $this->data->id)->touch();
+ }
+
+ return $result;
}
/**
diff --git a/src/app/Http/DAV/Locks.php b/src/app/Http/DAV/Locks.php
--- a/src/app/Http/DAV/Locks.php
+++ b/src/app/Http/DAV/Locks.php
@@ -33,6 +33,9 @@
{
\Log::debug('[DAV] GET-LOCKS: ' . $uri);
+ // TODO: On a node delete Sabre invokes this method twice (once before and once after)
+ // so there's a place for some optimization.
+
// Note: We're disabling exceptions here, otherwise it has unwanted effects
// in places where Sabre checks locks on non-existing paths
$ids = Node::resolvePath($uri, true);
diff --git a/src/app/Http/DAV/Node.php b/src/app/Http/DAV/Node.php
--- a/src/app/Http/DAV/Node.php
+++ b/src/app/Http/DAV/Node.php
@@ -268,4 +268,20 @@
// that's why we store all lookup results including `false`.
Context::addHidden('fs:' . $path, $item);
}
+
+ /**
+ * Convert an array into XML property understood by the Sabre XML writer
+ */
+ protected static function propListOutput(array $list, string $item_name): array
+ {
+ foreach ($list as $idx => $item) {
+ $list[$idx] = [
+ 'name' => "{Kolab:}{$item_name}",
+ 'value' => $item,
+ 'properties' => [],
+ ];
+ }
+
+ return $list;
+ }
}
diff --git a/src/app/Http/DAV/ServerPlugin.php b/src/app/Http/DAV/ServerPlugin.php
new file mode 100644
--- /dev/null
+++ b/src/app/Http/DAV/ServerPlugin.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace App\Http\DAV;
+
+use Sabre\DAV\Server;
+use Sabre\Xml\Deserializer;
+use Sabre\Xml\Reader;
+
+/**
+ * A plugin covering Kolab XML extensions.
+ *
+ * Plugins can modifies/extends the Sabre server behaviour.
+ */
+class ServerPlugin extends \Sabre\DAV\ServerPlugin
+{
+ /**
+ * This initializes the plugin.
+ *
+ * This function is called by Sabre\DAV\Server, after addPlugin is called.
+ */
+ public function initialize(Server $server)
+ {
+ // Tell the XML parser how to handle structured Kolab properties
+ $server->xml->elementMap['{Kolab:}links'] = function (Reader $reader) {
+ return Deserializer\repeatingElements($reader, '{Kolab:}link');
+ };
+
+ $server->xml->elementMap['{Kolab:}categories'] = function (Reader $reader) {
+ return Deserializer\repeatingElements($reader, '{Kolab:}category');
+ };
+ }
+}

File Metadata

Mime Type
text/plain
Expires
Mon, Mar 30, 7:21 AM (2 d, 17 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18796047
Default Alt Text
D5835.1774855294.diff (10 KB)

Event Timeline