summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMorris Jobke <hey@morrisjobke.de>2014-12-19 09:33:28 +0100
committerMorris Jobke <hey@morrisjobke.de>2014-12-19 09:33:28 +0100
commit39d6ddd38a05127fe0003e664ffd4ab5603706b7 (patch)
tree997adaae4a7b05ded4ebbd285f78160d03cb7f9b
parent028b0efd5c2d00d70aea55ef98c0a2615733bfbd (diff)
parent6224e29f25931c175fca23148fa491e182de8348 (diff)
downloadnextcloud-server-39d6ddd38a05127fe0003e664ffd4ab5603706b7.tar.gz
nextcloud-server-39d6ddd38a05127fe0003e664ffd4ab5603706b7.zip
Merge pull request #12865 from owncloud/files-tags-webdav
Returns tags through WebDAV
-rw-r--r--apps/files/appinfo/remote.php1
-rw-r--r--lib/private/connector/sabre/directory.php29
-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
-rw-r--r--tests/lib/connector/sabre/directory.php54
-rw-r--r--tests/lib/connector/sabre/tagsplugin.php314
8 files changed, 800 insertions, 7 deletions
diff --git a/apps/files/appinfo/remote.php b/apps/files/appinfo/remote.php
index 26bef966f79..c622f083958 100644
--- a/apps/files/appinfo/remote.php
+++ b/apps/files/appinfo/remote.php
@@ -53,6 +53,7 @@ $server->subscribeEvent('beforeMethod', function () use ($server, $objectTree) {
$rootDir = new OC_Connector_Sabre_Directory($view, $rootInfo);
$objectTree->init($rootDir, $view, $mountManager);
+ $server->addPlugin(new \OC\Connector\Sabre\TagsPlugin($objectTree, \OC::$server->getTagManager()));
$server->addPlugin(new OC_Connector_Sabre_QuotaPlugin($view));
}, 30); // priority 30: after auth (10) and acl(20), before lock(50) and handling the request
diff --git a/lib/private/connector/sabre/directory.php b/lib/private/connector/sabre/directory.php
index bbe0f3452a7..c878e5ee4b4 100644
--- a/lib/private/connector/sabre/directory.php
+++ b/lib/private/connector/sabre/directory.php
@@ -25,6 +25,13 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node
implements \Sabre\DAV\ICollection, \Sabre\DAV\IQuota {
/**
+ * Cached directory content
+ *
+ * @var \OCP\Files\FileInfo[]
+ */
+ private $dirContent;
+
+ /**
* Creates a new file in the directory
*
* Data will either be supplied as a stream resource, or in certain cases
@@ -138,13 +145,20 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node
* @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 +183,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..56cab393fea
--- /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 \OC\Connector\Sabre\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..dd0b5172bd6
--- /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 = '{http://owncloud.org/ns}tags';
+ const FAVORITE_PROPERTYNAME = '{http://owncloud.org/ns}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;
+ }
+}
diff --git a/tests/lib/connector/sabre/directory.php b/tests/lib/connector/sabre/directory.php
index d8dca35cd71..e9bfea81b77 100644
--- a/tests/lib/connector/sabre/directory.php
+++ b/tests/lib/connector/sabre/directory.php
@@ -101,4 +101,58 @@ class Test_OC_Connector_Sabre_Directory extends \Test\TestCase {
$dir = $this->getRootDir();
$dir->delete();
}
+
+ public function testGetChildren() {
+ $info1 = $this->getMockBuilder('OC\Files\FileInfo')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $info2 = $this->getMockBuilder('OC\Files\FileInfo')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $info1->expects($this->any())
+ ->method('getName')
+ ->will($this->returnValue('first'));
+ $info1->expects($this->any())
+ ->method('getEtag')
+ ->will($this->returnValue('abc'));
+ $info2->expects($this->any())
+ ->method('getName')
+ ->will($this->returnValue('second'));
+ $info2->expects($this->any())
+ ->method('getEtag')
+ ->will($this->returnValue('def'));
+
+ $this->view->expects($this->once())
+ ->method('getDirectoryContent')
+ ->with('')
+ ->will($this->returnValue(array($info1, $info2)));
+
+ $this->view->expects($this->any())
+ ->method('getRelativePath')
+ ->will($this->returnValue(''));
+
+ $dir = new OC_Connector_Sabre_Directory($this->view, $this->info);
+ $nodes = $dir->getChildren();
+
+ $this->assertEquals(2, count($nodes));
+
+ // calling a second time just returns the cached values,
+ // does not call getDirectoryContents again
+ $nodes = $dir->getChildren();
+
+ $properties = array('testprop', OC_Connector_Sabre_Node::GETETAG_PROPERTYNAME);
+ $this->assertEquals(2, count($nodes));
+ $this->assertEquals(
+ array(
+ OC_Connector_Sabre_Node::GETETAG_PROPERTYNAME => '"abc"'
+ ),
+ $nodes[0]->getProperties($properties)
+ );
+ $this->assertEquals(
+ array(
+ OC_Connector_Sabre_Node::GETETAG_PROPERTYNAME => '"def"'
+ ),
+ $nodes[1]->getProperties($properties)
+ );
+ }
}
diff --git a/tests/lib/connector/sabre/tagsplugin.php b/tests/lib/connector/sabre/tagsplugin.php
new file mode 100644
index 00000000000..2afea061ec3
--- /dev/null
+++ b/tests/lib/connector/sabre/tagsplugin.php
@@ -0,0 +1,314 @@
+<?php
+
+namespace Tests\Connector\Sabre;
+
+/**
+ * Copyright (c) 2014 Vincent Petry <pvince81@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+class TagsPlugin extends \Test\TestCase {
+
+ const TAGS_PROPERTYNAME = \OC\Connector\Sabre\TagsPlugin::TAGS_PROPERTYNAME;
+ const FAVORITE_PROPERTYNAME = \OC\Connector\Sabre\TagsPlugin::FAVORITE_PROPERTYNAME;
+ const TAG_FAVORITE = \OC\Connector\Sabre\TagsPlugin::TAG_FAVORITE;
+
+ /**
+ * @var \Sabre\DAV\Server
+ */
+ private $server;
+
+ /**
+ * @var \Sabre\DAV\ObjectTree
+ */
+ private $tree;
+
+ /**
+ * @var \OCP\ITagManager
+ */
+ private $tagManager;
+
+ /**
+ * @var \OCP\ITags
+ */
+ private $tagger;
+
+ /**
+ * @var \OC\Connector\Sabre\TagsPlugin
+ */
+ private $plugin;
+
+ public function setUp() {
+ parent::setUp();
+ $this->server = new \Sabre\DAV\Server();
+ $this->tree = $this->getMockBuilder('\Sabre\DAV\ObjectTree')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->tagger = $this->getMock('\OCP\ITags');
+ $this->tagManager = $this->getMock('\OCP\ITagManager');
+ $this->tagManager->expects($this->any())
+ ->method('load')
+ ->with('files')
+ ->will($this->returnValue($this->tagger));
+ $this->plugin = new \OC\Connector\Sabre\TagsPlugin($this->tree, $this->tagManager);
+ $this->plugin->initialize($this->server);
+ }
+
+ /**
+ * @dataProvider tagsGetPropertiesDataProvider
+ */
+ public function testGetProperties($tags, $requestedProperties, $expectedProperties) {
+ $node = $this->getMockBuilder('\OC_Connector_Sabre_Node')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $node->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue(123));
+
+ $expectedCallCount = 0;
+ if (count($requestedProperties) > 0) {
+ $expectedCallCount = 1;
+ }
+
+ $this->tagger->expects($this->exactly($expectedCallCount))
+ ->method('getTagsForObjects')
+ ->with($this->equalTo(array(123)))
+ ->will($this->returnValue(array(123 => $tags)));
+
+ $returnedProperties = array();
+
+ $this->plugin->beforeGetProperties(
+ '',
+ $node,
+ $requestedProperties,
+ $returnedProperties
+ );
+
+ $this->assertEquals($expectedProperties, $returnedProperties);
+ }
+
+ /**
+ * @dataProvider tagsGetPropertiesDataProvider
+ */
+ public function testPreloadThenGetProperties($tags, $requestedProperties, $expectedProperties) {
+ $node1 = $this->getMockBuilder('\OC_Connector_Sabre_File')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $node1->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue(111));
+ $node2 = $this->getMockBuilder('\OC_Connector_Sabre_File')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $node2->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue(222));
+
+ $expectedCallCount = 0;
+ if (count($requestedProperties) > 0) {
+ // this guarantees that getTagsForObjects
+ // is only called once and then the tags
+ // are cached
+ $expectedCallCount = 1;
+ }
+
+ $node = $this->getMockBuilder('\OC_Connector_Sabre_Directory')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $node->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue(123));
+ $node->expects($this->exactly($expectedCallCount))
+ ->method('getChildren')
+ ->will($this->returnValue(array($node1, $node2)));
+
+ $this->tree->expects($this->once())
+ ->method('getNodeForPath')
+ ->with('/subdir')
+ ->will($this->returnValue($node));
+
+ $this->tagger->expects($this->exactly($expectedCallCount))
+ ->method('getTagsForObjects')
+ ->with($this->equalTo(array(111, 222)))
+ ->will($this->returnValue(
+ array(
+ 111 => $tags,
+ 123 => $tags
+ )
+ ));
+
+ $returnedProperties = array();
+
+ $this->plugin->beforeGetPropertiesForPath(
+ '/subdir',
+ $requestedProperties,
+ 1
+ );
+
+ $this->plugin->beforeGetProperties(
+ '/subdir/test.txt',
+ $node1,
+ $requestedProperties,
+ $returnedProperties
+ );
+
+ $this->assertEquals($expectedProperties, $returnedProperties);
+ }
+
+ function tagsGetPropertiesDataProvider() {
+ return array(
+ // request both, receive both
+ array(
+ array('tag1', 'tag2', self::TAG_FAVORITE),
+ array(self::TAGS_PROPERTYNAME, self::FAVORITE_PROPERTYNAME),
+ array(
+ 200 => array(
+ self::TAGS_PROPERTYNAME => new \OC\Connector\Sabre\TagList(array('tag1', 'tag2')),
+ self::FAVORITE_PROPERTYNAME => true,
+ )
+ )
+ ),
+ // request tags alone
+ array(
+ array('tag1', 'tag2', self::TAG_FAVORITE),
+ array(self::TAGS_PROPERTYNAME),
+ array(
+ 200 => array(
+ self::TAGS_PROPERTYNAME => new \OC\Connector\Sabre\TagList(array('tag1', 'tag2')),
+ )
+ )
+ ),
+ // request fav alone
+ array(
+ array('tag1', 'tag2', self::TAG_FAVORITE),
+ array(self::FAVORITE_PROPERTYNAME),
+ array(
+ 200 => array(
+ self::FAVORITE_PROPERTYNAME => true,
+ )
+ )
+ ),
+ // request none
+ array(
+ array('tag1', 'tag2', self::TAG_FAVORITE),
+ array(),
+ array(),
+ ),
+ // request both with none set, receive both
+ array(
+ array(),
+ array(self::TAGS_PROPERTYNAME, self::FAVORITE_PROPERTYNAME),
+ array(
+ 200 => array(
+ self::TAGS_PROPERTYNAME => new \OC\Connector\Sabre\TagList(array()),
+ self::FAVORITE_PROPERTYNAME => false,
+ )
+ )
+ ),
+ );
+ }
+
+ public function testUpdateTags() {
+ // this test will replace the existing tags "tagremove" with "tag1" and "tag2"
+ // and keep "tagkeep"
+ $node = $this->getMockBuilder('\OC_Connector_Sabre_Node')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $node->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue(123));
+
+ $this->tagger->expects($this->at(0))
+ ->method('getTagsForObjects')
+ ->with($this->equalTo(array(123)))
+ ->will($this->returnValue(array(123 => array('tagkeep', 'tagremove', self::TAG_FAVORITE))));
+
+ // then tag as tag1 and tag2
+ $this->tagger->expects($this->at(1))
+ ->method('tagAs')
+ ->with(123, 'tag1');
+ $this->tagger->expects($this->at(2))
+ ->method('tagAs')
+ ->with(123, 'tag2');
+
+ // it will untag tag3
+ $this->tagger->expects($this->at(3))
+ ->method('unTag')
+ ->with(123, 'tagremove');
+
+ // properties to set
+ $properties = array(
+ self::TAGS_PROPERTYNAME => new \OC\Connector\Sabre\TagList(array('tag1', 'tag2', 'tagkeep'))
+ );
+ $result = array();
+
+ $this->plugin->updateProperties(
+ $properties,
+ $result,
+ $node
+ );
+
+ // all requested properties removed, as they were processed already
+ $this->assertEmpty($properties);
+
+ $this->assertEquals(
+ new \OC\Connector\Sabre\TagList(array('tag1', 'tag2', 'tagkeep')),
+ $result[200][self::TAGS_PROPERTYNAME]
+ );
+ $this->assertFalse(isset($result[200][self::FAVORITE_PROPERTYNAME]));
+ }
+
+ public function testUpdateFav() {
+ // this test will replace the existing tags "tagremove" with "tag1" and "tag2"
+ // and keep "tagkeep"
+ $node = $this->getMockBuilder('\OC_Connector_Sabre_Node')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $node->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue(123));
+
+ // set favorite tag
+ $this->tagger->expects($this->once())
+ ->method('tagAs')
+ ->with(123, self::TAG_FAVORITE);
+
+ // properties to set
+ $properties = array(
+ self::FAVORITE_PROPERTYNAME => true
+ );
+ $result = array();
+ $this->plugin->updateProperties(
+ $properties,
+ $result,
+ $node
+ );
+
+ // all requested properties removed, as they were processed already
+ $this->assertEmpty($properties);
+
+ $this->assertTrue($result[200][self::FAVORITE_PROPERTYNAME]);
+ $this->assertFalse(isset($result[200][self::TAGS_PROPERTYNAME]));
+
+ // unfavorite now
+ // set favorite tag
+ $this->tagger->expects($this->once())
+ ->method('unTag')
+ ->with(123, self::TAG_FAVORITE);
+
+ $properties = array(
+ self::FAVORITE_PROPERTYNAME => false
+ );
+ $result = array();
+ $this->plugin->updateProperties(
+ $properties,
+ $result,
+ $node
+ );
+
+ $this->assertFalse($result[200][self::FAVORITE_PROPERTYNAME]);
+ $this->assertFalse(isset($result[200][self::TAGS_PROPERTYNAME]));
+ }
+
+}