- VObject fixes for Sabre\VObject 3.3 - Remove VObject property workarounds - Added prefetching for tags in sabre tags plugin - Moved oc_properties logic to separate PropertyStorage backend (WIP) - Fixed Sabre connector namespaces - Improved files plugin to handle props on-demand - Moved allowed props from server class to files plugin - Fixed tags caching for files that are known to have no tags (less queries) - Added/fixed unit tests for Sabre FilesPlugin, TagsPlugin - Replace OC\Connector\Sabre\Request with direct call to httpRequest->setUrl() - Fix exception detection in DAV client when using Sabre\DAV\Client - Added setETag() on Node instead of using the static FileSystem - Also preload tags/props when depth is infinityremotes/origin/log-external-deletes
parent
66e3211fd8
commit
9f6dcb9d3e
@ -1 +1 @@ |
||||
Subproject commit 59f092231c6036838746262a4db80997908bb06f |
||||
Subproject commit 588b1308f4abf58acb3bb8519f6952d9890cca89 |
@ -0,0 +1,347 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* ownCloud |
||||
* |
||||
* @author Vincent Petry |
||||
* @copyright 2015 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\PropFind; |
||||
use \Sabre\DAV\PropPatch; |
||||
use \Sabre\HTTP\RequestInterface; |
||||
use \Sabre\HTTP\ResponseInterface; |
||||
|
||||
class CustomPropertiesBackend implements \Sabre\DAV\PropertyStorage\Backend\BackendInterface { |
||||
|
||||
/** |
||||
* Ignored properties |
||||
* |
||||
* @var array |
||||
*/ |
||||
private $ignoredProperties = array( |
||||
'{DAV:}getcontentlength', |
||||
'{DAV:}getcontenttype', |
||||
'{DAV:}getetag', |
||||
'{DAV:}quota-used-bytes', |
||||
'{DAV:}quota-available-bytes', |
||||
'{DAV:}quota-available-bytes', |
||||
'{http://owncloud.org/ns}permissions', |
||||
'{http://owncloud.org/ns}downloadURL', |
||||
'{http://owncloud.org/ns}dDC', |
||||
'{http://owncloud.org/ns}size', |
||||
); |
||||
|
||||
/** |
||||
* @var \Sabre\DAV\Tree |
||||
*/ |
||||
private $tree; |
||||
|
||||
/** |
||||
* @var \OCP\IDBConnection |
||||
*/ |
||||
private $connection; |
||||
|
||||
/** |
||||
* @var \OCP\IUser |
||||
*/ |
||||
private $user; |
||||
|
||||
/** |
||||
* Properties cache |
||||
* |
||||
* @var array |
||||
*/ |
||||
private $cache = []; |
||||
|
||||
/** |
||||
* @param \Sabre\DAV\Tree $tree node tree |
||||
* @param \OCP\IDBConnection $connection database connection |
||||
* @param \OCP\IUser $user owner of the tree and properties |
||||
*/ |
||||
public function __construct( |
||||
\Sabre\DAV\Tree $tree, |
||||
\OCP\IDBConnection $connection, |
||||
\OCP\IUser $user) { |
||||
$this->tree = $tree; |
||||
$this->connection = $connection; |
||||
$this->user = $user->getUID(); |
||||
} |
||||
|
||||
/** |
||||
* Fetches properties for a path. |
||||
* |
||||
* @param string $path |
||||
* @param PropFind $propFind |
||||
* @return void |
||||
*/ |
||||
public function propFind($path, PropFind $propFind) { |
||||
$node = $this->tree->getNodeForPath($path); |
||||
if (!($node instanceof \OC\Connector\Sabre\Node)) { |
||||
return; |
||||
} |
||||
|
||||
$requestedProps = $propFind->get404Properties(); |
||||
|
||||
// these might appear |
||||
$requestedProps = array_diff( |
||||
$requestedProps, |
||||
$this->ignoredProperties |
||||
); |
||||
|
||||
if (empty($requestedProps)) { |
||||
return; |
||||
} |
||||
|
||||
if ($node instanceof \OC\Connector\Sabre\Directory |
||||
&& $propFind->getDepth() !== 0 |
||||
) { |
||||
// note: pre-fetching only supported for depth <= 1 |
||||
$this->loadChildrenProperties($node, $requestedProps); |
||||
} |
||||
|
||||
$props = $this->getProperties($node, $requestedProps); |
||||
foreach ($props as $propName => $propValue) { |
||||
$propFind->set($propName, $propValue); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Updates properties for a path |
||||
* |
||||
* @param string $path |
||||
* @param PropPatch $propPatch |
||||
* |
||||
* @return void |
||||
*/ |
||||
public function propPatch($path, PropPatch $propPatch) { |
||||
$node = $this->tree->getNodeForPath($path); |
||||
if (!($node instanceof \OC\Connector\Sabre\Node)) { |
||||
return; |
||||
} |
||||
|
||||
$propPatch->handleRemaining(function($changedProps) use ($node) { |
||||
return $this->updateProperties($node, $changedProps); |
||||
}); |
||||
} |
||||
|
||||
/** |
||||
* This method is called after a node is deleted. |
||||
* |
||||
* @param string $path path of node for which to delete properties |
||||
*/ |
||||
public function delete($path) { |
||||
$statement = $this->connection->prepare( |
||||
'DELETE FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ?' |
||||
); |
||||
$statement->execute(array($this->user, '/' . $path)); |
||||
$statement->closeCursor(); |
||||
|
||||
unset($this->cache[$path]); |
||||
} |
||||
|
||||
/** |
||||
* This method is called after a successful MOVE |
||||
* |
||||
* @param string $source |
||||
* @param string $destination |
||||
* |
||||
* @return void |
||||
*/ |
||||
public function move($source, $destination) { |
||||
$statement = $this->connection->prepare( |
||||
'UPDATE `*PREFIX*properties` SET `propertypath` = ?' . |
||||
' WHERE `userid` = ? AND `propertypath` = ?' |
||||
); |
||||
$statement->execute(array('/' . $destination, $this->user, '/' . $source)); |
||||
$statement->closeCursor(); |
||||
} |
||||
|
||||
/** |
||||
* Returns a list of properties for this nodes.; |
||||
* @param \OC\Connector\Sabre\Node $node |
||||
* @param array $requestedProperties requested properties or empty array for "all" |
||||
* @return array |
||||
* @note The properties list is a list of propertynames the client |
||||
* requested, encoded as xmlnamespace#tagName, for example: |
||||
* http://www.example.org/namespace#author If the array is empty, all |
||||
* properties should be returned |
||||
*/ |
||||
private function getProperties(\OC\Connector\Sabre\Node $node, array $requestedProperties) { |
||||
$path = $node->getPath(); |
||||
if (isset($this->cache[$path])) { |
||||
return $this->cache[$path]; |
||||
} |
||||
|
||||
// TODO: chunking if more than 1000 properties |
||||
$sql = 'SELECT * FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ?'; |
||||
|
||||
$whereValues = array($this->user, $path); |
||||
$whereTypes = array(null, null); |
||||
|
||||
if (!empty($requestedProperties)) { |
||||
// request only a subset |
||||
$sql .= ' AND `propertyname` in (?)'; |
||||
$whereValues[] = $requestedProperties; |
||||
$whereTypes[] = \Doctrine\DBAL\Connection::PARAM_STR_ARRAY; |
||||
} |
||||
|
||||
$result = $this->connection->executeQuery( |
||||
$sql, |
||||
$whereValues, |
||||
$whereTypes |
||||
); |
||||
|
||||
$props = []; |
||||
while ($row = $result->fetch()) { |
||||
$props[$row['propertyname']] = $row['propertyvalue']; |
||||
} |
||||
|
||||
$result->closeCursor(); |
||||
|
||||
$this->cache[$path] = $props; |
||||
return $props; |
||||
} |
||||
|
||||
/** |
||||
* Update properties |
||||
* |
||||
* @param \OC\Connector\Sabre\Node $node node for which to update properties |
||||
* @param array $properties array of properties to update |
||||
* |
||||
* @return bool |
||||
*/ |
||||
private function updateProperties($node, $properties) { |
||||
$path = $node->getPath(); |
||||
|
||||
$deleteStatement = $this->connection->prepare( |
||||
'DELETE FROM `*PREFIX*properties`' . |
||||
' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?' |
||||
); |
||||
|
||||
$insertStatement = $this->connection->prepare( |
||||
'INSERT INTO `*PREFIX*properties`' . |
||||
' (`userid`,`propertypath`,`propertyname`,`propertyvalue`) VALUES(?,?,?,?)' |
||||
); |
||||
|
||||
$updateStatement = $this->connection->prepare( |
||||
'UPDATE `*PREFIX*properties` SET `propertyvalue` = ?' . |
||||
' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?' |
||||
); |
||||
|
||||
// TODO: use "insert or update" strategy ? |
||||
$existing = $this->getProperties($node, array()); |
||||
$this->connection->beginTransaction(); |
||||
foreach ($properties as $propertyName => $propertyValue) { |
||||
// If it was null, we need to delete the property |
||||
if (is_null($propertyValue)) { |
||||
if (array_key_exists($propertyName, $existing)) { |
||||
$deleteStatement->execute( |
||||
array( |
||||
$this->user, |
||||
$path, |
||||
$propertyName |
||||
) |
||||
); |
||||
$deleteStatement->closeCursor(); |
||||
} |
||||
} else { |
||||
if (!array_key_exists($propertyName, $existing)) { |
||||
$insertStatement->execute( |
||||
array( |
||||
$this->user, |
||||
$path, |
||||
$propertyName, |
||||
$propertyValue |
||||
) |
||||
); |
||||
$insertStatement->closeCursor(); |
||||
} else { |
||||
$updateStatement->execute( |
||||
array( |
||||
$propertyValue, |
||||
$this->user, |
||||
$path, |
||||
$propertyName |
||||
) |
||||
); |
||||
$updateStatement->closeCursor(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
$this->connection->commit(); |
||||
unset($this->cache[$path]); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* Bulk load properties for directory children |
||||
* |
||||
* @param \OC\Connector\Sabre\Directory $node |
||||
* @param array $requestedProperties requested properties |
||||
* |
||||
* @return void |
||||
*/ |
||||
private function loadChildrenProperties(\OC\Connector\Sabre\Directory $node, $requestedProperties) { |
||||
$path = $node->getPath(); |
||||
if (isset($this->cache[$path])) { |
||||
// we already loaded them at some point |
||||
return; |
||||
} |
||||
|
||||
$childNodes = $node->getChildren(); |
||||
// pre-fill cache |
||||
foreach ($childNodes as $childNode) { |
||||
$this->cache[$childNode->getPath()] = []; |
||||
} |
||||
|
||||
$sql = 'SELECT * FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` LIKE ?'; |
||||
$sql .= ' AND `propertyname` in (?) ORDER BY `propertypath`, `propertyname`'; |
||||
|
||||
$result = $this->connection->executeQuery( |
||||
$sql, |
||||
array($this->user, rtrim($path, '/') . '/%', $requestedProperties), |
||||
array(null, null, \Doctrine\DBAL\Connection::PARAM_STR_ARRAY) |
||||
); |
||||
|
||||
$props = []; |
||||
$oldPath = null; |
||||
$props = []; |
||||
while ($row = $result->fetch()) { |
||||
$path = $row['propertypath']; |
||||
if ($oldPath !== $path) { |
||||
// save previously gathered props |
||||
$this->cache[$oldPath] = $props; |
||||
$oldPath = $path; |
||||
// prepare props for next path |
||||
$props = []; |
||||
} |
||||
$props[$row['propertyname']] = $row['propertyvalue']; |
||||
} |
||||
if (!is_null($oldPath)) { |
||||
// save props from last run |
||||
$this->cache[$oldPath] = $props; |
||||
} |
||||
|
||||
$result->closeCursor(); |
||||
} |
||||
|
||||
} |
@ -1,50 +0,0 @@ |
||||
<?php |
||||
/** |
||||
* @author Bart Visscher <bartv@thisnet.nl> |
||||
* @author Lukas Reschke <lukas@owncloud.com> |
||||
* @author Stefan Herbrechtsmeier <stefan@herbrechtsmeier.net> |
||||
* @author Thomas Müller <thomas.mueller@tmit.eu> |
||||
* |
||||
* @copyright Copyright (c) 2015, ownCloud, Inc. |
||||
* @license AGPL-3.0 |
||||
* |
||||
* This code is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License, version 3, |
||||
* as published by the Free Software Foundation. |
||||
* |
||||
* This program 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, version 3, |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/> |
||||
* |
||||
*/ |
||||
class OC_Connector_Sabre_Request extends \Sabre\HTTP\Request { |
||||
/** |
||||
* Returns the requested uri |
||||
* |
||||
* @return string |
||||
*/ |
||||
public function getUri() { |
||||
return \OC::$server->getRequest()->getRequestUri(); |
||||
} |
||||
|
||||
/** |
||||
* Returns a specific item from the _SERVER array. |
||||
* |
||||
* Do not rely on this feature, it is for internal use only. |
||||
* |
||||
* @param string $field |
||||
* @return string |
||||
*/ |
||||
public function getRawServerValue($field) { |
||||
if($field == 'REQUEST_URI') { |
||||
return $this->getUri(); |
||||
} |
||||
else{ |
||||
return isset($this->_SERVER[$field])?$this->_SERVER[$field]:null; |
||||
} |
||||
} |
||||
} |
@ -1,69 +0,0 @@ |
||||
<?php |
||||
/** |
||||
* @author Christopher Schäpers <kondou@ts.unde.re> |
||||
* @author Thomas Tanghus <thomas@tanghus.net> |
||||
* |
||||
* @copyright Copyright (c) 2015, ownCloud, Inc. |
||||
* @license AGPL-3.0 |
||||
* |
||||
* This code is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License, version 3, |
||||
* as published by the Free Software Foundation. |
||||
* |
||||
* This program 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, version 3, |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/> |
||||
* |
||||
*/ |
||||
namespace OC\VObject; |
||||
|
||||
/** |
||||
* This class overrides \Sabre\VObject\Property::serialize() to not |
||||
* double escape commas and semi-colons in compound properties. |
||||
*/ |
||||
class CompoundProperty extends \Sabre\VObject\Property\Compound { |
||||
|
||||
/** |
||||
* Turns the object back into a serialized blob. |
||||
* |
||||
* @return string |
||||
*/ |
||||
public function serialize() { |
||||
|
||||
$str = $this->name; |
||||
if ($this->group) { |
||||
$str = $this->group . '.' . $this->name; |
||||
} |
||||
|
||||
foreach($this->parameters as $param) { |
||||
$str.=';' . $param->serialize(); |
||||
} |
||||
$src = array( |
||||
"\n", |
||||
); |
||||
$out = array( |
||||
'\n', |
||||
); |
||||
$str.=':' . str_replace($src, $out, $this->value); |
||||
|
||||
$out = ''; |
||||
while(strlen($str) > 0) { |
||||
if (strlen($str) > 75) { |
||||
$out .= mb_strcut($str, 0, 75, 'utf-8') . "\r\n"; |
||||
$str = ' ' . mb_strcut($str, 75, strlen($str), 'utf-8'); |
||||
} else { |
||||
$out .= $str . "\r\n"; |
||||
$str = ''; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
return $out; |
||||
|
||||
} |
||||
|
||||
} |
@ -1,77 +0,0 @@ |
||||
<?php |
||||
/** |
||||
* @author Christopher Schäpers <kondou@ts.unde.re> |
||||
* @author Thomas Tanghus <thomas@tanghus.net> |
||||
* |
||||
* @copyright Copyright (c) 2015, ownCloud, Inc. |
||||
* @license AGPL-3.0 |
||||
* |
||||
* This code is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License, version 3, |
||||
* as published by the Free Software Foundation. |
||||
* |
||||
* This program 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, version 3, |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/> |
||||
* |
||||
*/ |
||||
namespace OC\VObject; |
||||
|
||||
/** |
||||
* This class overrides \Sabre\VObject\Property::serialize() properly |
||||
* escape commas and semi-colons in string properties. |
||||
*/ |
||||
class StringProperty extends \Sabre\VObject\Property { |
||||
|
||||
/** |
||||
* Turns the object back into a serialized blob. |
||||
* |
||||
* @return string |
||||
*/ |
||||
public function serialize() { |
||||
|
||||
$str = $this->name; |
||||
if ($this->group) { |
||||
$str = $this->group . '.' . $this->name; |
||||
} |
||||
|
||||
foreach($this->parameters as $param) { |
||||
$str.=';' . $param->serialize(); |
||||
} |
||||
|
||||
$src = array( |
||||
'\\', |
||||
"\n", |
||||
';', |
||||
',', |
||||
); |
||||
$out = array( |
||||
'\\\\', |
||||
'\n', |
||||
'\;', |
||||
'\,', |
||||
); |
||||
$value = strtr($this->value, array('\,' => ',', '\;' => ';', '\\\\' => '\\')); |
||||
$str.=':' . str_replace($src, $out, $value); |
||||
|
||||
$out = ''; |
||||
while(strlen($str) > 0) { |
||||
if (strlen($str) > 75) { |
||||
$out .= mb_strcut($str, 0, 75, 'utf-8') . "\r\n"; |
||||
$str = ' ' . mb_strcut($str, 75, strlen($str), 'utf-8'); |
||||
} else { |
||||
$out .= $str . "\r\n"; |
||||
$str = ''; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
return $out; |
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,248 @@ |
||||
<?php |
||||
|
||||
namespace Tests\Connector\Sabre; |
||||
|
||||
/** |
||||
* Copyright (c) 2015 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 CustomPropertiesBackend extends \Test\TestCase { |
||||
|
||||
/** |
||||
* @var \Sabre\DAV\Server |
||||
*/ |
||||
private $server; |
||||
|
||||
/** |
||||
* @var \Sabre\DAV\ObjectTree |
||||
*/ |
||||
private $tree; |
||||
|
||||
/** |
||||
* @var \OC\Connector\Sabre\CustomPropertiesBackend |
||||
*/ |
||||
private $plugin; |
||||
|
||||
/** |
||||
* @var \OCP\IUser |
||||
*/ |
||||
private $user; |
||||
|
||||
public function setUp() { |
||||
parent::setUp(); |
||||
$this->server = new \Sabre\DAV\Server(); |
||||
$this->tree = $this->getMockBuilder('\Sabre\DAV\Tree') |
||||
->disableOriginalConstructor() |
||||
->getMock(); |
||||
|
||||
$userId = $this->getUniqueID('testcustompropertiesuser'); |
||||
|
||||
$this->user = $this->getMock('\OCP\IUser'); |
||||
$this->user->expects($this->any()) |
||||
->method('getUID') |
||||
->will($this->returnValue($userId)); |
||||
|
||||
$this->plugin = new \OC\Connector\Sabre\CustomPropertiesBackend( |
||||
$this->tree, |
||||
\OC::$server->getDatabaseConnection(), |
||||
$this->user |
||||
); |
||||
} |
||||
|
||||
public function tearDown() { |
||||
$connection = \OC::$server->getDatabaseConnection(); |
||||
$deleteStatement = $connection->prepare( |
||||
'DELETE FROM `*PREFIX*properties`' . |
||||
' WHERE `userid` = ?' |
||||
); |
||||
$deleteStatement->execute( |
||||
array( |
||||
$this->user->getUID(), |
||||
) |
||||
); |
||||
$deleteStatement->closeCursor(); |
||||
} |
||||
|
||||
private function createTestNode($class) { |
||||
$node = $this->getMockBuilder($class) |
||||
->disableOriginalConstructor() |
||||
->getMock(); |
||||
$node->expects($this->any()) |
||||
->method('getId') |
||||
->will($this->returnValue(123)); |
||||
|
||||
$node->expects($this->any()) |
||||
->method('getPath') |
||||
->will($this->returnValue('/dummypath')); |
||||
|
||||
return $node; |
||||
} |
||||
|
||||
private function applyDefaultProps($path = '/dummypath') { |
||||
// properties to set |
||||
$propPatch = new \Sabre\DAV\PropPatch(array( |
||||
'customprop' => 'value1', |
||||
'customprop2' => 'value2', |
||||
)); |
||||
|
||||
$this->plugin->propPatch( |
||||
$path, |
||||
$propPatch |
||||
); |
||||
|
||||
$propPatch->commit(); |
||||
|
||||
$this->assertEmpty($propPatch->getRemainingMutations()); |
||||
|
||||
$result = $propPatch->getResult(); |
||||
$this->assertEquals(200, $result['customprop']); |
||||
$this->assertEquals(200, $result['customprop2']); |
||||
} |
||||
|
||||
/** |
||||
* Test setting/getting properties |
||||
*/ |
||||
public function testSetGetPropertiesForFile() { |
||||
$node = $this->createTestNode('\OC\Connector\Sabre\File'); |
||||
$this->tree->expects($this->any()) |
||||
->method('getNodeForPath') |
||||
->with('/dummypath') |
||||
->will($this->returnValue($node)); |
||||
|
||||
$this->applyDefaultProps(); |
||||
|
||||
$propFind = new \Sabre\DAV\PropFind( |
||||
'/dummypath', |
||||
array( |
||||
'customprop', |
||||
'customprop2', |
||||
'unsetprop', |
||||
), |
||||
0 |
||||
); |
||||
|
||||
$this->plugin->propFind( |
||||
'/dummypath', |
||||
$propFind |
||||
); |
||||
|
||||
$this->assertEquals('value1', $propFind->get('customprop')); |
||||
$this->assertEquals('value2', $propFind->get('customprop2')); |
||||
$this->assertEquals(array('unsetprop'), $propFind->get404Properties()); |
||||
} |
||||
|
||||
/** |
||||
* Test getting properties from directory |
||||
*/ |
||||
public function testGetPropertiesForDirectory() { |
||||
$rootNode = $this->createTestNode('\OC\Connector\Sabre\Directory'); |
||||
|
||||
$nodeSub = $this->getMockBuilder('\OC\Connector\Sabre\File') |
||||
->disableOriginalConstructor() |
||||
->getMock(); |
||||
$nodeSub->expects($this->any()) |
||||
->method('getId') |
||||
->will($this->returnValue(456)); |
||||
|
||||
$nodeSub->expects($this->any()) |
||||
->method('getPath') |
||||
->will($this->returnValue('/dummypath/test.txt')); |
||||
|
||||
$rootNode->expects($this->once()) |
||||
->method('getChildren') |
||||
->will($this->returnValue(array($nodeSub))); |
||||
|
||||
$this->tree->expects($this->at(0)) |
||||
->method('getNodeForPath') |
||||
->with('/dummypath') |
||||
->will($this->returnValue($rootNode)); |
||||
|
||||
$this->tree->expects($this->at(1)) |
||||
->method('getNodeForPath') |
||||
->with('/dummypath/test.txt') |
||||
->will($this->returnValue($nodeSub)); |
||||
|
||||
$this->tree->expects($this->at(2)) |
||||
->method('getNodeForPath') |
||||
->with('/dummypath') |
||||
->will($this->returnValue($rootNode)); |
||||
|
||||
$this->tree->expects($this->at(3)) |
||||
->method('getNodeForPath') |
||||
->with('/dummypath/test.txt') |
||||
->will($this->returnValue($nodeSub)); |
||||
|
||||
$this->applyDefaultProps('/dummypath'); |
||||
$this->applyDefaultProps('/dummypath/test.txt'); |
||||
|
||||
$propNames = array( |
||||
'customprop', |
||||
'customprop2', |
||||
'unsetprop', |
||||
); |
||||
|
||||
$propFindRoot = new \Sabre\DAV\PropFind( |
||||
'/dummypath', |
||||
$propNames, |
||||
1 |
||||
); |
||||
|
||||
$propFindSub = new \Sabre\DAV\PropFind( |
||||
'/dummypath/test.txt', |
||||
$propNames, |
||||
0 |
||||
); |
||||
|
||||
$this->plugin->propFind( |
||||
'/dummypath', |
||||
$propFindRoot |
||||
); |
||||
|
||||
$this->plugin->propFind( |
||||
'/dummypath/test.txt', |
||||
$propFindSub |
||||
); |
||||
|
||||
// TODO: find a way to assert that no additional SQL queries were |
||||
// run while doing the second propFind |
||||
|
||||
$this->assertEquals('value1', $propFindRoot->get('customprop')); |
||||
$this->assertEquals('value2', $propFindRoot->get('customprop2')); |
||||
$this->assertEquals(array('unsetprop'), $propFindRoot->get404Properties()); |
||||
|
||||
$this->assertEquals('value1', $propFindSub->get('customprop')); |
||||
$this->assertEquals('value2', $propFindSub->get('customprop2')); |
||||
$this->assertEquals(array('unsetprop'), $propFindSub->get404Properties()); |
||||
} |
||||
|
||||
/** |
||||
* Test delete property |
||||
*/ |
||||
public function testDeleteProperty() { |
||||
$node = $this->createTestNode('\OC\Connector\Sabre\File'); |
||||
$this->tree->expects($this->any()) |
||||
->method('getNodeForPath') |
||||
->with('/dummypath') |
||||
->will($this->returnValue($node)); |
||||
|
||||
$this->applyDefaultProps(); |
||||
|
||||
$propPatch = new \Sabre\DAV\PropPatch(array( |
||||
'customprop' => null, |
||||
)); |
||||
|
||||
$this->plugin->propPatch( |
||||
'/dummypath', |
||||
$propPatch |
||||
); |
||||
|
||||
$propPatch->commit(); |
||||
|
||||
$this->assertEmpty($propPatch->getRemainingMutations()); |
||||
|
||||
$result = $propPatch->getResult(); |
||||
$this->assertEquals(204, $result['customprop']); |
||||
} |
||||
} |
@ -0,0 +1,174 @@ |
||||
<?php |
||||
|
||||
namespace Tests\Connector\Sabre; |
||||
|
||||
/** |
||||
* Copyright (c) 2015 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 FilesPlugin extends \Test\TestCase { |
||||
const GETETAG_PROPERTYNAME = \OC\Connector\Sabre\FilesPlugin::GETETAG_PROPERTYNAME; |
||||
const FILEID_PROPERTYNAME = \OC\Connector\Sabre\FilesPlugin::FILEID_PROPERTYNAME; |
||||
const SIZE_PROPERTYNAME = \OC\Connector\Sabre\FilesPlugin::SIZE_PROPERTYNAME; |
||||
const PERMISSIONS_PROPERTYNAME = \OC\Connector\Sabre\FilesPlugin::PERMISSIONS_PROPERTYNAME; |
||||
const GETLASTMODIFIED_PROPERTYNAME = \OC\Connector\Sabre\FilesPlugin::GETLASTMODIFIED_PROPERTYNAME; |
||||
const DOWNLOADURL_PROPERTYNAME = \OC\Connector\Sabre\FilesPlugin::DOWNLOADURL_PROPERTYNAME; |
||||
|
||||
/** |
||||
* @var \Sabre\DAV\Server |
||||
*/ |
||||
private $server; |
||||
|
||||
/** |
||||
* @var \Sabre\DAV\ObjectTree |
||||
*/ |
||||
private $tree; |
||||
|
||||
/** |
||||
* @var \OC\Connector\Sabre\FilesPlugin |
||||
*/ |
||||
private $plugin; |
||||
|
||||
public function setUp() { |
||||
parent::setUp(); |
||||
$this->server = new \Sabre\DAV\Server(); |
||||
$this->tree = $this->getMockBuilder('\Sabre\DAV\Tree') |
||||
->disableOriginalConstructor() |
||||
->getMock(); |
||||
$this->plugin = new \OC\Connector\Sabre\FilesPlugin($this->tree); |
||||
$this->plugin->initialize($this->server); |
||||
} |
||||
|
||||
private function createTestNode($class) { |
||||
$node = $this->getMockBuilder($class) |
||||
->disableOriginalConstructor() |
||||
->getMock(); |
||||
$node->expects($this->any()) |
||||
->method('getId') |
||||
->will($this->returnValue(123)); |
||||
|
||||
$this->tree->expects($this->any()) |
||||
->method('getNodeForPath') |
||||
->with('/dummypath') |
||||
->will($this->returnValue($node)); |
||||
|
||||
$node->expects($this->any()) |
||||
->method('getFileId') |
||||
->will($this->returnValue(123)); |
||||
$node->expects($this->any()) |
||||
->method('getEtag') |
||||
->will($this->returnValue('"abc"')); |
||||
$node->expects($this->any()) |
||||
->method('getDavPermissions') |
||||
->will($this->returnValue('R')); |
||||
|
||||
return $node; |
||||
} |
||||
|
||||
/** |
||||
*/ |
||||
public function testGetPropertiesForFile() { |
||||
$node = $this->createTestNode('\OC\Connector\Sabre\File'); |
||||
|
||||
$propFind = new \Sabre\DAV\PropFind( |
||||
'/dummyPath', |
||||
array( |
||||
self::GETETAG_PROPERTYNAME, |
||||
self::FILEID_PROPERTYNAME, |
||||
self::SIZE_PROPERTYNAME, |
||||
self::PERMISSIONS_PROPERTYNAME, |
||||
self::DOWNLOADURL_PROPERTYNAME, |
||||
), |
||||
0 |
||||
); |
||||
|
||||
$node->expects($this->once()) |
||||
->method('getDirectDownload') |
||||
->will($this->returnValue(array('url' => 'http://example.com/'))); |
||||
$node->expects($this->never()) |
||||
->method('getSize'); |
||||
|
||||
$this->plugin->handleGetProperties( |
||||
$propFind, |
||||
$node |
||||
); |
||||
|
||||
$this->assertEquals('"abc"', $propFind->get(self::GETETAG_PROPERTYNAME)); |
||||
$this->assertEquals(123, $propFind->get(self::FILEID_PROPERTYNAME)); |
||||
$this->assertEquals(null, $propFind->get(self::SIZE_PROPERTYNAME)); |
||||
$this->assertEquals('R', $propFind->get(self::PERMISSIONS_PROPERTYNAME)); |
||||
$this->assertEquals('http://example.com/', $propFind->get(self::DOWNLOADURL_PROPERTYNAME)); |
||||
$this->assertEquals(array(self::SIZE_PROPERTYNAME), $propFind->get404Properties()); |
||||
} |
||||
|
||||
public function testGetPropertiesForDirectory() { |
||||
$node = $this->createTestNode('\OC\Connector\Sabre\Directory'); |
||||
|
||||
$propFind = new \Sabre\DAV\PropFind( |
||||
'/dummyPath', |
||||
array( |
||||
self::GETETAG_PROPERTYNAME, |
||||
self::FILEID_PROPERTYNAME, |
||||
self::SIZE_PROPERTYNAME, |
||||
self::PERMISSIONS_PROPERTYNAME, |
||||
self::DOWNLOADURL_PROPERTYNAME, |
||||
), |
||||
0 |
||||
); |
||||
|
||||
$node->expects($this->never()) |
||||
->method('getDirectDownload'); |
||||
$node->expects($this->once()) |
||||
->method('getSize') |
||||
->will($this->returnValue(1025)); |
||||
|
||||
$this->plugin->handleGetProperties( |
||||
$propFind, |
||||
$node |
||||
); |
||||
|
||||
$this->assertEquals('"abc"', $propFind->get(self::GETETAG_PROPERTYNAME)); |
||||
$this->assertEquals(123, $propFind->get(self::FILEID_PROPERTYNAME)); |
||||
$this->assertEquals(1025, $propFind->get(self::SIZE_PROPERTYNAME)); |
||||
$this->assertEquals('R', $propFind->get(self::PERMISSIONS_PROPERTYNAME)); |
||||
$this->assertEquals(null, $propFind->get(self::DOWNLOADURL_PROPERTYNAME)); |
||||
$this->assertEquals(array(self::DOWNLOADURL_PROPERTYNAME), $propFind->get404Properties()); |
||||
} |
||||
|
||||
public function testUpdateProps() { |
||||
$node = $this->createTestNode('\OC\Connector\Sabre\File'); |
||||
|
||||
$testDate = 'Fri, 13 Feb 2015 00:01:02 GMT'; |
||||
|
||||
$node->expects($this->once()) |
||||
->method('touch') |
||||
->with($testDate); |
||||
|
||||
$node->expects($this->once()) |
||||
->method('setEtag') |
||||
->with('newetag') |
||||
->will($this->returnValue(true)); |
||||
|
||||
// properties to set |
||||
$propPatch = new \Sabre\DAV\PropPatch(array( |
||||
self::GETETAG_PROPERTYNAME => 'newetag', |
||||
self::GETLASTMODIFIED_PROPERTYNAME => $testDate |
||||
)); |
||||
|
||||
$this->plugin->handleUpdateProperties( |
||||
'/dummypath', |
||||
$propPatch |
||||
); |
||||
|
||||
$propPatch->commit(); |
||||
|
||||
$this->assertEmpty($propPatch->getRemainingMutations()); |
||||
|
||||
$result = $propPatch->getResult(); |
||||
$this->assertEquals(200, $result[self::GETLASTMODIFIED_PROPERTYNAME]); |
||||
$this->assertEquals(200, $result[self::GETETAG_PROPERTYNAME]); |
||||
} |
||||
|
||||
} |
@ -1,40 +0,0 @@ |
||||
<?php |
||||
/** |
||||
* Copyright (c) 2013 Thomas Tanghus (thomas@tanghus.net) |
||||
* This file is licensed under the Affero General Public License version 3 or |
||||
* later. |
||||
* See the COPYING-README file. |
||||
*/ |
||||
|
||||
class Test_VObject extends \Test\TestCase { |
||||
|
||||
protected function setUp() { |
||||
parent::setUp(); |
||||
|
||||
Sabre\VObject\Property::$classMap['SUMMARY'] = 'OC\VObject\StringProperty'; |
||||
Sabre\VObject\Property::$classMap['ORG'] = 'OC\VObject\CompoundProperty'; |
||||
} |
||||
|
||||
function testStringProperty() { |
||||
$property = Sabre\VObject\Property::create('SUMMARY', 'Escape;this,please'); |
||||
$this->assertEquals("SUMMARY:Escape\;this\,please\r\n", $property->serialize()); |
||||
} |
||||
|
||||
function testCompoundProperty() { |
||||
|
||||
$arr = array( |
||||
'ABC, Inc.', |
||||
'North American Division', |
||||
'Marketing;Sales', |
||||
); |
||||
|
||||
$property = Sabre\VObject\Property::create('ORG'); |
||||
$property->setParts($arr); |
||||
|
||||
$this->assertEquals('ABC\, Inc.;North American Division;Marketing\;Sales', $property->value); |
||||
$this->assertEquals('ORG:ABC\, Inc.;North American Division;Marketing\;Sales' . "\r\n", $property->serialize()); |
||||
$this->assertEquals(3, count($property->getParts())); |
||||
$parts = $property->getParts(); |
||||
$this->assertEquals('Marketing;Sales', $parts[2]); |
||||
} |
||||
} |
Loading…
Reference in new issue