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
This commit is contained in:
parent
be3d4fd303
commit
0b3f0716fc
|
@ -53,6 +53,7 @@ $server->subscribeEvent('beforeMethod', function () use ($server, $objectTree) {
|
||||||
$rootDir = new OC_Connector_Sabre_Directory($view, $rootInfo);
|
$rootDir = new OC_Connector_Sabre_Directory($view, $rootInfo);
|
||||||
$objectTree->init($rootDir, $view, $mountManager);
|
$objectTree->init($rootDir, $view, $mountManager);
|
||||||
|
|
||||||
|
$server->addPlugin(new \OC\Connector\Sabre\TagsPlugin($objectTree, \OC::$server->getTagManager()));
|
||||||
$server->addPlugin(new OC_Connector_Sabre_QuotaPlugin($view));
|
$server->addPlugin(new OC_Connector_Sabre_QuotaPlugin($view));
|
||||||
}, 30); // priority 30: after auth (10) and acl(20), before lock(50) and handling the request
|
}, 30); // priority 30: after auth (10) and acl(20), before lock(50) and handling the request
|
||||||
|
|
||||||
|
|
|
@ -21,9 +21,18 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use OC\Connector\Sabre\TagList;
|
||||||
|
|
||||||
class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node
|
class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node
|
||||||
implements \Sabre\DAV\ICollection, \Sabre\DAV\IQuota {
|
implements \Sabre\DAV\ICollection, \Sabre\DAV\IQuota {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cached directory content
|
||||||
|
*
|
||||||
|
* @var \OCP\FileInfo[]
|
||||||
|
*/
|
||||||
|
private $dirContent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new file in the directory
|
* Creates a new file in the directory
|
||||||
*
|
*
|
||||||
|
@ -132,19 +141,34 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node
|
||||||
return $node;
|
return $node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the directory content as fileinfo objects
|
||||||
|
*
|
||||||
|
* @return \OCP\FileInfo[]
|
||||||
|
*/
|
||||||
|
public function getDirectoryContent() {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an array with all the child nodes
|
* Returns an array with all the child nodes
|
||||||
*
|
*
|
||||||
* @return \Sabre\DAV\INode[]
|
* @return \Sabre\DAV\INode[]
|
||||||
*/
|
*/
|
||||||
public function getChildren() {
|
public function getChildren() {
|
||||||
|
if (!is_null($this->dirContent)) {
|
||||||
$folder_content = $this->fileView->getDirectoryContent($this->path);
|
return $this->dirContent;
|
||||||
$paths = array();
|
|
||||||
foreach($folder_content as $info) {
|
|
||||||
$paths[] = $this->path.'/'.$info['name'];
|
|
||||||
$properties[$this->path.'/'.$info['name']][self::GETETAG_PROPERTYNAME] = '"' . $info['etag'] . '"';
|
|
||||||
}
|
}
|
||||||
|
$folderContent = $this->fileView->getDirectoryContent($this->path);
|
||||||
|
|
||||||
|
$properties = array();
|
||||||
|
$paths = array();
|
||||||
|
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) {
|
if(count($paths)>0) {
|
||||||
//
|
//
|
||||||
// the number of arguments within IN conditions are limited in most databases
|
// 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();
|
$nodes = array();
|
||||||
foreach($folder_content as $info) {
|
foreach($folderContent as $info) {
|
||||||
$node = $this->getChild($info->getName(), $info);
|
$node = $this->getChild($info->getName(), $info);
|
||||||
$node->setPropertyCache($properties[$this->path.'/'.$info['name']]);
|
$node->setPropertyCache($properties[$this->path . '/' . $info->getName()]);
|
||||||
$nodes[] = $node;
|
$nodes[] = $node;
|
||||||
}
|
}
|
||||||
return $nodes;
|
$this->dirContent = $nodes;
|
||||||
|
return $this->dirContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Sabre\DAV\URLUtil;
|
use Sabre\DAV\URLUtil;
|
||||||
|
use OC\Connector\Sabre\TagList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ownCloud
|
* ownCloud
|
||||||
|
@ -226,6 +228,15 @@ abstract class OC_Connector_Sabre_Node implements \Sabre\DAV\INode, \Sabre\DAV\I
|
||||||
return $props;
|
return $props;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the cache's file id
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getId() {
|
||||||
|
return $this->info->getId();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string|null
|
* @return string|null
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -144,6 +144,13 @@ class OC_Connector_Sabre_Server extends Sabre\DAV\Server {
|
||||||
|
|
||||||
$path = rtrim($path,'/');
|
$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();
|
$returnPropertyList = array();
|
||||||
|
|
||||||
$parentNode = $this->tree->getNodeForPath($path);
|
$parentNode = $this->tree->getNodeForPath($path);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -101,4 +101,58 @@ class Test_OC_Connector_Sabre_Directory extends \Test\TestCase {
|
||||||
$dir = $this->getRootDir();
|
$dir = $this->getRootDir();
|
||||||
$dir->delete();
|
$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)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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]));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue