summaryrefslogtreecommitdiffstats
path: root/lib/private
diff options
context:
space:
mode:
authorVincent Petry <pvince81@owncloud.com>2014-12-15 17:49:24 +0100
committerVincent Petry <pvince81@owncloud.com>2014-12-17 16:50:35 +0100
commit0b3f0716fc8e278a5727885548bfa406004263dc (patch)
treeb0413ceeaa79e69c9a61655baac3bab4278a507d /lib/private
parentbe3d4fd303569a99554dbc6c62ce8992a45c51ad (diff)
downloadnextcloud-server-0b3f0716fc8e278a5727885548bfa406004263dc.tar.gz
nextcloud-server-0b3f0716fc8e278a5727885548bfa406004263dc.zip
Returns and update tags through WebDAV PROPFIND and PROPPATCH
Added oc:tags and oc:favorites in PROPFIND response. It is possible to update them with PROPPATCH. These properties are optional which means they need to be requested explicitly
Diffstat (limited to 'lib/private')
-rw-r--r--lib/private/connector/sabre/directory.php39
-rw-r--r--lib/private/connector/sabre/node.php11
-rw-r--r--lib/private/connector/sabre/server.php7
-rw-r--r--lib/private/connector/sabre/taglist.php102
-rw-r--r--lib/private/connector/sabre/tagsplugin.php289
5 files changed, 441 insertions, 7 deletions
diff --git a/lib/private/connector/sabre/directory.php b/lib/private/connector/sabre/directory.php
index ec5f82f9daa..4bb63839021 100644
--- a/lib/private/connector/sabre/directory.php
+++ b/lib/private/connector/sabre/directory.php
@@ -21,10 +21,19 @@
*
*/
+use OC\Connector\Sabre\TagList;
+
class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node
implements \Sabre\DAV\ICollection, \Sabre\DAV\IQuota {
/**
+ * Cached directory content
+ *
+ * @var \OCP\FileInfo[]
+ */
+ private $dirContent;
+
+ /**
* Creates a new file in the directory
*
* Data will either be supplied as a stream resource, or in certain cases
@@ -133,18 +142,33 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node
}
/**
+ * Return the directory content as fileinfo objects
+ *
+ * @return \OCP\FileInfo[]
+ */
+ public function getDirectoryContent() {
+ }
+
+ /**
* Returns an array with all the child nodes
*
* @return \Sabre\DAV\INode[]
*/
public function getChildren() {
+ if (!is_null($this->dirContent)) {
+ return $this->dirContent;
+ }
+ $folderContent = $this->fileView->getDirectoryContent($this->path);
- $folder_content = $this->fileView->getDirectoryContent($this->path);
+ $properties = array();
$paths = array();
- foreach($folder_content as $info) {
- $paths[] = $this->path.'/'.$info['name'];
- $properties[$this->path.'/'.$info['name']][self::GETETAG_PROPERTYNAME] = '"' . $info['etag'] . '"';
+ foreach($folderContent as $info) {
+ $name = $info->getName();
+ $paths[] = $this->path . '/' . $name;
+ $properties[$this->path.'/' . $name][self::GETETAG_PROPERTYNAME] = '"' . $info->getEtag() . '"';
}
+ // TODO: move this to a beforeGetPropertiesForPath event to pre-cache properties
+ // TODO: only fetch the requested properties
if(count($paths)>0) {
//
// the number of arguments within IN conditions are limited in most databases
@@ -169,12 +193,13 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node
}
$nodes = array();
- foreach($folder_content as $info) {
+ foreach($folderContent as $info) {
$node = $this->getChild($info->getName(), $info);
- $node->setPropertyCache($properties[$this->path.'/'.$info['name']]);
+ $node->setPropertyCache($properties[$this->path . '/' . $info->getName()]);
$nodes[] = $node;
}
- return $nodes;
+ $this->dirContent = $nodes;
+ return $this->dirContent;
}
/**
diff --git a/lib/private/connector/sabre/node.php b/lib/private/connector/sabre/node.php
index a22dc9c5fbe..3173ab8a30f 100644
--- a/lib/private/connector/sabre/node.php
+++ b/lib/private/connector/sabre/node.php
@@ -1,5 +1,7 @@
<?php
+
use Sabre\DAV\URLUtil;
+use OC\Connector\Sabre\TagList;
/**
* ownCloud
@@ -227,6 +229,15 @@ abstract class OC_Connector_Sabre_Node implements \Sabre\DAV\INode, \Sabre\DAV\I
}
/**
+ * Returns the cache's file id
+ *
+ * @return int
+ */
+ public function getId() {
+ return $this->info->getId();
+ }
+
+ /**
* @return string|null
*/
public function getFileId() {
diff --git a/lib/private/connector/sabre/server.php b/lib/private/connector/sabre/server.php
index 137082eea67..a836af2a0b8 100644
--- a/lib/private/connector/sabre/server.php
+++ b/lib/private/connector/sabre/server.php
@@ -144,6 +144,13 @@ class OC_Connector_Sabre_Server extends Sabre\DAV\Server {
$path = rtrim($path,'/');
+ // This event allows people to intercept these requests early on in the
+ // process.
+ //
+ // We're not doing anything with the result, but this can be helpful to
+ // pre-fetch certain expensive live properties.
+ $this->broadCastEvent('beforeGetPropertiesForPath', array($path, $propertyNames, $depth));
+
$returnPropertyList = array();
$parentNode = $this->tree->getNodeForPath($path);
diff --git a/lib/private/connector/sabre/taglist.php b/lib/private/connector/sabre/taglist.php
new file mode 100644
index 00000000000..2b61be3e492
--- /dev/null
+++ b/lib/private/connector/sabre/taglist.php
@@ -0,0 +1,102 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Vincent Petry
+ * @copyright 2014 Vincent Petry <pvince81@owncloud.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\Connector\Sabre;
+
+use Sabre\DAV;
+
+/**
+ * TagList property
+ *
+ * This property contains multiple "tag" elements, each containing a tag name.
+ */
+class TagList extends DAV\Property {
+ const NS_OWNCLOUD = 'http://owncloud.org/ns';
+
+ /**
+ * tags
+ *
+ * @var array
+ */
+ private $tags;
+
+ /**
+ * @param array $tags
+ */
+ public function __construct(array $tags) {
+ $this->tags = $tags;
+ }
+
+ /**
+ * Returns the tags
+ *
+ * @return array
+ */
+ public function getTags() {
+
+ return $this->tags;
+
+ }
+
+ /**
+ * Serializes this property.
+ *
+ * @param DAV\Server $server
+ * @param \DOMElement $dom
+ * @return void
+ */
+ public function serialize(DAV\Server $server,\DOMElement $dom) {
+
+ $prefix = $server->xmlNamespaces[self::NS_OWNCLOUD];
+
+ foreach($this->tags as $tag) {
+
+ $elem = $dom->ownerDocument->createElement($prefix . ':tag');
+ $elem->appendChild($dom->ownerDocument->createTextNode($tag));
+
+ $dom->appendChild($elem);
+ }
+
+ }
+
+ /**
+ * Unserializes this property from a DOM Element
+ *
+ * This method returns an instance of this class.
+ * It will only decode tag values.
+ *
+ * @param \DOMElement $dom
+ * @return DAV\Property\TagList
+ */
+ static function unserialize(\DOMElement $dom) {
+
+ $tags = array();
+ foreach($dom->childNodes as $child) {
+ if (DAV\XMLUtil::toClarkNotation($child)==='{' . self::NS_OWNCLOUD . '}tag') {
+ $tags[] = $child->textContent;
+ }
+ }
+ return new self($tags);
+
+ }
+
+}
diff --git a/lib/private/connector/sabre/tagsplugin.php b/lib/private/connector/sabre/tagsplugin.php
new file mode 100644
index 00000000000..86a66f5a8b5
--- /dev/null
+++ b/lib/private/connector/sabre/tagsplugin.php
@@ -0,0 +1,289 @@
+<?php
+
+namespace OC\Connector\Sabre;
+
+/**
+ * ownCloud
+ *
+ * @author Vincent Petry
+ * @copyright 2014 Vincent Petry <pvince81@owncloud.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+class TagsPlugin extends \Sabre\DAV\ServerPlugin
+{
+
+ // namespace
+ const NS_OWNCLOUD = 'http://owncloud.org/ns';
+ const TAGS_PROPERTYNAME = '{' . self::NS_OWNCLOUD . '}tags';
+ const FAVORITE_PROPERTYNAME = '{' . self::NS_OWNCLOUD . '}favorite';
+ const TAG_FAVORITE = '_$!<Favorite>!$_';
+
+ /**
+ * Reference to main server object
+ *
+ * @var \Sabre\DAV\Server
+ */
+ private $server;
+
+ /**
+ * @var \OCP\ITagManager
+ */
+ private $tagManager;
+
+ /**
+ * @var \OCP\ITags
+ */
+ private $tagger;
+
+ /**
+ * Array of file id to tags array
+ * The null value means the cache wasn't initialized.
+ *
+ * @var array
+ */
+ private $cachedTags;
+
+ /**
+ * @param \OCP\ITagManager $tagManager tag manager
+ */
+ public function __construct(\Sabre\DAV\ObjectTree $objectTree, \OCP\ITagManager $tagManager) {
+ $this->objectTree = $objectTree;
+ $this->tagManager = $tagManager;
+ $this->tagger = null;
+ $this->cachedTags = null;
+ }
+
+ /**
+ * This initializes the plugin.
+ *
+ * This function is called by \Sabre\DAV\Server, after
+ * addPlugin is called.
+ *
+ * This method should set up the required event subscriptions.
+ *
+ * @param \Sabre\DAV\Server $server
+ * @return void
+ */
+ public function initialize(\Sabre\DAV\Server $server) {
+
+ $server->xmlNamespaces[self::NS_OWNCLOUD] = 'oc';
+ $server->propertyMap[self::TAGS_PROPERTYNAME] = 'OC\\Connector\\Sabre\\TagList';
+
+ $this->server = $server;
+ $this->server->subscribeEvent('beforeGetProperties', array($this, 'beforeGetProperties'));
+ $this->server->subscribeEvent('beforeGetPropertiesForPath', array($this, 'beforeGetPropertiesForPath'));
+ $this->server->subscribeEvent('updateProperties', array($this, 'updateProperties'));
+ }
+
+ /**
+ * Searches and removes a value from the given array
+ *
+ * @param array $requestedProps
+ * @param string $propName to remove
+ * @return boolean true if the property was present, false otherwise
+ */
+ private function findAndRemoveProperty(&$requestedProps, $propName) {
+ $index = array_search($propName, $requestedProps);
+ if ($index !== false) {
+ unset($requestedProps[$index]);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns the tagger
+ *
+ * @return \OCP\ITags tagger
+ */
+ private function getTagger() {
+ if (!$this->tagger) {
+ $this->tagger = $this->tagManager->load('files');
+ }
+ return $this->tagger;
+ }
+
+ /**
+ * Returns tags and favorites.
+ *
+ * @param integer $fileId file id
+ * @return array list($tags, $favorite) with $tags as tag array
+ * and $favorite is a boolean whether the file was favorited
+ */
+ private function getTagsAndFav($fileId) {
+ $isFav = false;
+ $tags = $this->getTags($fileId);
+ if ($tags) {
+ $favPos = array_search(self::TAG_FAVORITE, $tags);
+ if ($favPos !== false) {
+ $isFav = true;
+ unset($tags[$favPos]);
+ }
+ }
+ return array($tags, $isFav);
+ }
+
+ /**
+ * Returns tags for the given file id
+ *
+ * @param integer $fileId file id
+ * @return array list of tags for that file
+ */
+ private function getTags($fileId) {
+ if (isset($this->cachedTags[$fileId])) {
+ return $this->cachedTags[$fileId];
+ } else {
+ $tags = $this->getTagger()->getTagsForObjects(array($fileId));
+ if ($tags) {
+ return current($tags);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Updates the tags of the given file id
+ *
+ * @param int $fileId
+ * @param array $tags array of tag strings
+ */
+ private function updateTags($fileId, $tags) {
+ $tagger = $this->getTagger();
+ $currentTags = $this->getTags($fileId);
+
+ $newTags = array_diff($tags, $currentTags);
+ foreach ($newTags as $tag) {
+ if ($tag === self::TAG_FAVORITE) {
+ continue;
+ }
+ $tagger->tagAs($fileId, $tag);
+ }
+ $deletedTags = array_diff($currentTags, $tags);
+ foreach ($deletedTags as $tag) {
+ if ($tag === self::TAG_FAVORITE) {
+ continue;
+ }
+ $tagger->unTag($fileId, $tag);
+ }
+ }
+
+ /**
+ * Pre-fetch tags info
+ *
+ * @param string $path
+ * @param array $requestedProperties
+ * @param integer $depth
+ * @return void
+ */
+ public function beforeGetPropertiesForPath(
+ $path,
+ array $requestedProperties,
+ $depth
+ ) {
+ $node = $this->objectTree->getNodeForPath($path);
+ if (!($node instanceof \OC_Connector_Sabre_Directory)) {
+ return;
+ }
+
+ if ($this->findAndRemoveProperty($requestedProperties, self::TAGS_PROPERTYNAME)
+ || $this->findAndRemoveProperty($requestedProperties, self::FAVORITE_PROPERTYNAME)
+ ) {
+ $fileIds = array();
+ // note: pre-fetching only supported for depth <= 1
+ $folderContent = $node->getChildren();
+ // TODO: refactor somehow with the similar array that is created
+ // in getChildren()
+ foreach ($folderContent as $info) {
+ $fileIds[] = $info->getId();
+ }
+ $tags = $this->getTagger()->getTagsForObjects($fileIds);
+ if ($tags) {
+ $this->cachedTags = $tags;
+ }
+ }
+ }
+
+ /**
+ * Adds tags and favorites properties to the response,
+ * if requested.
+ *
+ * @param string $path
+ * @param \Sabre\DAV\INode $node
+ * @param array $requestedProperties
+ * @param array $returnedProperties
+ * @return void
+ */
+ public function beforeGetProperties(
+ $path,
+ \Sabre\DAV\INode $node,
+ array &$requestedProperties,
+ array &$returnedProperties
+ ) {
+ if (!($node instanceof \OC_Connector_Sabre_Node)) {
+ return;
+ }
+
+ $tags = null;
+ $isFav = null;
+ if ($this->findAndRemoveProperty($requestedProperties, self::TAGS_PROPERTYNAME)) {
+ list($tags, $isFav) = $this->getTagsAndFav($node->getId());
+ $returnedProperties[200][self::TAGS_PROPERTYNAME] = new TagList($tags);
+ }
+ if ($this->findAndRemoveProperty($requestedProperties, self::FAVORITE_PROPERTYNAME)) {
+ if (is_null($tags)) {
+ list($tags, $isFav) = $this->getTagsAndFav($node->getId());
+ }
+ $returnedProperties[200][self::FAVORITE_PROPERTYNAME] = $isFav;
+ }
+ }
+
+ /**
+ * Updates tags and favorites properties, if applicable.
+ *
+ * @param string $path
+ * @param \Sabre\DAV\INode $node
+ * @param array $requestedProperties
+ * @param array $returnedProperties
+ * @return bool success status
+ */
+ public function updateProperties(array &$properties, array &$result, \Sabre\DAV\INode $node) {
+ if (!($node instanceof \OC_Connector_Sabre_Node)) {
+ return;
+ }
+
+ $fileId = $node->getId();
+ if (isset($properties[self::TAGS_PROPERTYNAME])) {
+ $tagsProp = $properties[self::TAGS_PROPERTYNAME];
+ unset($properties[self::TAGS_PROPERTYNAME]);
+ $this->updateTags($fileId, $tagsProp->getTags());
+ $result[200][self::TAGS_PROPERTYNAME] = new TagList($tagsProp->getTags());
+ }
+ if (isset($properties[self::FAVORITE_PROPERTYNAME])) {
+ $favState = $properties[self::FAVORITE_PROPERTYNAME];
+ unset($properties[self::FAVORITE_PROPERTYNAME]);
+ if ((int)$favState === 1 || $favState === 'true') {
+ $favState = true;
+ $this->getTagger()->tagAs($fileId, self::TAG_FAVORITE);
+ } else {
+ $favState = false;
+ $this->getTagger()->unTag($fileId, self::TAG_FAVORITE);
+ }
+ $result[200][self::FAVORITE_PROPERTYNAME] = $favState;
+ }
+ return true;
+ }
+}