diff options
author | Vincent Petry <pvince81@owncloud.com> | 2014-12-15 17:49:24 +0100 |
---|---|---|
committer | Vincent Petry <pvince81@owncloud.com> | 2014-12-17 16:50:35 +0100 |
commit | 0b3f0716fc8e278a5727885548bfa406004263dc (patch) | |
tree | b0413ceeaa79e69c9a61655baac3bab4278a507d /lib/private | |
parent | be3d4fd303569a99554dbc6c62ce8992a45c51ad (diff) | |
download | nextcloud-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.php | 39 | ||||
-rw-r--r-- | lib/private/connector/sabre/node.php | 11 | ||||
-rw-r--r-- | lib/private/connector/sabre/server.php | 7 | ||||
-rw-r--r-- | lib/private/connector/sabre/taglist.php | 102 | ||||
-rw-r--r-- | lib/private/connector/sabre/tagsplugin.php | 289 |
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; + } +} |