From 242bb746b9e93efb52b058f75f9e368d70e3a568 Mon Sep 17 00:00:00 2001 From: Georg Ehrke Date: Mon, 27 Mar 2017 19:15:51 +0200 Subject: OCA\DAV\Files\CustomPropertiesBackend -> OCA\DAV\DAV\CustomPropertiesBackend Signed-off-by: Georg Ehrke --- apps/dav/lib/DAV/CustomPropertiesBackend.php | 291 +++++++++++++++++++++ apps/dav/lib/Files/CustomPropertiesBackend.php | 291 --------------------- apps/dav/lib/Server.php | 2 +- .../tests/unit/DAV/CustomPropertiesBackendTest.php | 170 ++++++++++++ .../unit/Files/CustomPropertiesBackendTest.php | 170 ------------ 5 files changed, 462 insertions(+), 462 deletions(-) create mode 100644 apps/dav/lib/DAV/CustomPropertiesBackend.php delete mode 100644 apps/dav/lib/Files/CustomPropertiesBackend.php create mode 100644 apps/dav/tests/unit/DAV/CustomPropertiesBackendTest.php delete mode 100644 apps/dav/tests/unit/Files/CustomPropertiesBackendTest.php diff --git a/apps/dav/lib/DAV/CustomPropertiesBackend.php b/apps/dav/lib/DAV/CustomPropertiesBackend.php new file mode 100644 index 00000000000..ee0d0407015 --- /dev/null +++ b/apps/dav/lib/DAV/CustomPropertiesBackend.php @@ -0,0 +1,291 @@ + + * + * @author Robin Appelman + * @author Thomas Müller + * @author Georg Ehrke + * + * @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 + * + */ + +namespace OCA\DAV\DAV; + +use OCP\IDBConnection; +use OCP\IUser; +use Sabre\DAV\PropertyStorage\Backend\BackendInterface; +use Sabre\DAV\PropFind; +use Sabre\DAV\PropPatch; +use Sabre\DAV\Tree; + +class CustomPropertiesBackend implements BackendInterface { + + /** + * Ignored properties + * + * @var array + */ + private $ignoredProperties = array( + '{DAV:}getcontentlength', + '{DAV:}getcontenttype', + '{DAV:}getetag', + '{DAV:}quota-used-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 Tree + */ + private $tree; + + /** + * @var IDBConnection + */ + private $connection; + + /** + * @var string + */ + private $user; + + /** + * Properties cache + * + * @var array + */ + private $cache = []; + + /** + * @param Tree $tree node tree + * @param IDBConnection $connection database connection + * @param IUser $user owner of the tree and properties + */ + public function __construct( + Tree $tree, + IDBConnection $connection, + 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) { + + $requestedProps = $propFind->get404Properties(); + + // these might appear + $requestedProps = array_diff( + $requestedProps, + $this->ignoredProperties + ); + + // substr of calendars/ => path is inside the CalDAV component + // two '/' => this a calendar (no calendar-home nor calendar object) + if (substr($path, 0, 10) === 'calendars/' && substr_count($path, '/') === 2) { + $allRequestedProps = $propFind->getRequestedProperties(); + $customPropertiesForShares = [ + '{DAV:}displayname', + '{urn:ietf:params:xml:ns:caldav}calendar-description', + '{urn:ietf:params:xml:ns:caldav}calendar-timezone', + '{http://apple.com/ns/ical/}calendar-order', + '{http://apple.com/ns/ical/}calendar-color', + '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp', + ]; + + foreach ($customPropertiesForShares as $customPropertyForShares) { + if (in_array($customPropertyForShares, $allRequestedProps)) { + $requestedProps[] = $customPropertyForShares; + } + } + } + + if (empty($requestedProps)) { + return; + } + + $props = $this->getProperties($path, $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) { + $propPatch->handleRemaining(function($changedProps) use ($path) { + return $this->updateProperties($path, $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 string $path + * @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($path, array $requestedProperties) { + 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 string $path node for which to update properties + * @param array $properties array of properties to update + * + * @return bool + */ + private function updateProperties($path, $properties) { + + $deleteStatement = 'DELETE FROM `*PREFIX*properties`' . + ' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?'; + + $insertStatement = 'INSERT INTO `*PREFIX*properties`' . + ' (`userid`,`propertypath`,`propertyname`,`propertyvalue`) VALUES(?,?,?,?)'; + + $updateStatement = 'UPDATE `*PREFIX*properties` SET `propertyvalue` = ?' . + ' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?'; + + // TODO: use "insert or update" strategy ? + $existing = $this->getProperties($path, 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)) { + $this->connection->executeUpdate($deleteStatement, + array( + $this->user, + $path, + $propertyName + ) + ); + } + } else { + if (!array_key_exists($propertyName, $existing)) { + $this->connection->executeUpdate($insertStatement, + array( + $this->user, + $path, + $propertyName, + $propertyValue + ) + ); + } else { + $this->connection->executeUpdate($updateStatement, + array( + $propertyValue, + $this->user, + $path, + $propertyName + ) + ); + } + } + } + + $this->connection->commit(); + unset($this->cache[$path]); + + return true; + } + +} diff --git a/apps/dav/lib/Files/CustomPropertiesBackend.php b/apps/dav/lib/Files/CustomPropertiesBackend.php deleted file mode 100644 index 397c7bb22e3..00000000000 --- a/apps/dav/lib/Files/CustomPropertiesBackend.php +++ /dev/null @@ -1,291 +0,0 @@ - - * - * @author Robin Appelman - * @author Thomas Müller - * @author Georg Ehrke - * - * @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 - * - */ - -namespace OCA\DAV\Files; - -use OCP\IDBConnection; -use OCP\IUser; -use Sabre\DAV\PropertyStorage\Backend\BackendInterface; -use Sabre\DAV\PropFind; -use Sabre\DAV\PropPatch; -use Sabre\DAV\Tree; - -class CustomPropertiesBackend implements BackendInterface { - - /** - * Ignored properties - * - * @var array - */ - private $ignoredProperties = array( - '{DAV:}getcontentlength', - '{DAV:}getcontenttype', - '{DAV:}getetag', - '{DAV:}quota-used-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 Tree - */ - private $tree; - - /** - * @var IDBConnection - */ - private $connection; - - /** - * @var string - */ - private $user; - - /** - * Properties cache - * - * @var array - */ - private $cache = []; - - /** - * @param Tree $tree node tree - * @param IDBConnection $connection database connection - * @param IUser $user owner of the tree and properties - */ - public function __construct( - Tree $tree, - IDBConnection $connection, - 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) { - - $requestedProps = $propFind->get404Properties(); - - // these might appear - $requestedProps = array_diff( - $requestedProps, - $this->ignoredProperties - ); - - // substr of calendars/ => path is inside the CalDAV component - // two '/' => this a calendar (no calendar-home nor calendar object) - if (substr($path, 0, 10) === 'calendars/' && substr_count($path, '/') === 2) { - $allRequestedProps = $propFind->getRequestedProperties(); - $customPropertiesForShares = [ - '{DAV:}displayname', - '{urn:ietf:params:xml:ns:caldav}calendar-description', - '{urn:ietf:params:xml:ns:caldav}calendar-timezone', - '{http://apple.com/ns/ical/}calendar-order', - '{http://apple.com/ns/ical/}calendar-color', - '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp', - ]; - - foreach ($customPropertiesForShares as $customPropertyForShares) { - if (in_array($customPropertyForShares, $allRequestedProps)) { - $requestedProps[] = $customPropertyForShares; - } - } - } - - if (empty($requestedProps)) { - return; - } - - $props = $this->getProperties($path, $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) { - $propPatch->handleRemaining(function($changedProps) use ($path) { - return $this->updateProperties($path, $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 string $path - * @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($path, array $requestedProperties) { - 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 string $path node for which to update properties - * @param array $properties array of properties to update - * - * @return bool - */ - private function updateProperties($path, $properties) { - - $deleteStatement = 'DELETE FROM `*PREFIX*properties`' . - ' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?'; - - $insertStatement = 'INSERT INTO `*PREFIX*properties`' . - ' (`userid`,`propertypath`,`propertyname`,`propertyvalue`) VALUES(?,?,?,?)'; - - $updateStatement = 'UPDATE `*PREFIX*properties` SET `propertyvalue` = ?' . - ' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?'; - - // TODO: use "insert or update" strategy ? - $existing = $this->getProperties($path, 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)) { - $this->connection->executeUpdate($deleteStatement, - array( - $this->user, - $path, - $propertyName - ) - ); - } - } else { - if (!array_key_exists($propertyName, $existing)) { - $this->connection->executeUpdate($insertStatement, - array( - $this->user, - $path, - $propertyName, - $propertyValue - ) - ); - } else { - $this->connection->executeUpdate($updateStatement, - array( - $propertyValue, - $this->user, - $path, - $propertyName - ) - ); - } - } - } - - $this->connection->commit(); - unset($this->cache[$path]); - - return true; - } - -} diff --git a/apps/dav/lib/Server.php b/apps/dav/lib/Server.php index 031bc1d3d81..a5c1379eb7e 100644 --- a/apps/dav/lib/Server.php +++ b/apps/dav/lib/Server.php @@ -42,9 +42,9 @@ use OCA\DAV\Connector\Sabre\FilesPlugin; use OCA\DAV\Connector\Sabre\FilesReportPlugin; use OCA\DAV\Connector\Sabre\SharesPlugin; use OCA\DAV\DAV\PublicAuth; +use OCA\DAV\DAV\CustomPropertiesBackend; use OCA\DAV\Connector\Sabre\QuotaPlugin; use OCA\DAV\Files\BrowserErrorPagePlugin; -use OCA\DAV\Files\CustomPropertiesBackend; use OCA\DAV\SystemTag\SystemTagPlugin; use OCP\IRequest; use OCP\SabrePluginEvent; diff --git a/apps/dav/tests/unit/DAV/CustomPropertiesBackendTest.php b/apps/dav/tests/unit/DAV/CustomPropertiesBackendTest.php new file mode 100644 index 00000000000..780324abaa6 --- /dev/null +++ b/apps/dav/tests/unit/DAV/CustomPropertiesBackendTest.php @@ -0,0 +1,170 @@ + + * + * @author Georg Ehrke + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see . + * + */ +namespace OCA\DAV\Tests\DAV; + +use OCA\DAV\DAV\CustomPropertiesBackend; +use OCP\IDBConnection; +use OCP\IUser; +use Sabre\DAV\PropFind; +use Sabre\DAV\PropPatch; +use Sabre\DAV\Tree; +use Test\TestCase; + +class CustomPropertiesBackendTest extends TestCase { + + /** @var Tree | \PHPUnit_Framework_MockObject_MockObject */ + private $tree; + + /** @var IDBConnection | \PHPUnit_Framework_MockObject_MockObject */ + private $dbConnection; + + /** @var IUser | \PHPUnit_Framework_MockObject_MockObject */ + private $user; + + /** @var CustomPropertiesBackend | \PHPUnit_Framework_MockObject_MockObject */ + private $backend; + + public function setUp() { + parent::setUp(); + + $this->tree = $this->createMock(Tree::class); + $this->dbConnection = $this->createMock(IDBConnection::class); + $this->user = $this->createMock(IUser::class); + $this->user->expects($this->once()) + ->method('getUID') + ->with() + ->will($this->returnValue('dummy_user_42')); + + $this->backend = new CustomPropertiesBackend($this->tree, + $this->dbConnection, $this->user); + } + + public function testPropFindNoDbCalls() { + $propFind = $this->createMock(PropFind::class); + $propFind->expects($this->at(0)) + ->method('get404Properties') + ->with() + ->will($this->returnValue([ + '{http://owncloud.org/ns}permissions', + '{http://owncloud.org/ns}downloadURL', + '{http://owncloud.org/ns}dDC', + '{http://owncloud.org/ns}size', + ])); + + $this->dbConnection->expects($this->never()) + ->method($this->anything()); + + $this->backend->propFind('foo_bar_path_1337_0', $propFind); + } + + public function testPropFindCalendarCall() { + $propFind = $this->createMock(PropFind::class); + $propFind->expects($this->at(0)) + ->method('get404Properties') + ->with() + ->will($this->returnValue([ + '{DAV:}getcontentlength', + '{DAV:}getcontenttype', + '{DAV:}getetag', + '{abc}def' + ])); + + $propFind->expects($this->at(1)) + ->method('getRequestedProperties') + ->with() + ->will($this->returnValue([ + '{DAV:}getcontentlength', + '{DAV:}getcontenttype', + '{DAV:}getetag', + '{DAV:}displayname', + '{urn:ietf:params:xml:ns:caldav}calendar-description', + '{urn:ietf:params:xml:ns:caldav}calendar-timezone', + '{abc}def' + ])); + + $statement = $this->createMock('\Doctrine\DBAL\Driver\Statement'); + $this->dbConnection->expects($this->once()) + ->method('executeQuery') + ->with('SELECT * FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` in (?)', + ['dummy_user_42', 'calendars/foo/bar_path_1337_0', [ + 3 => '{abc}def', + 4 => '{DAV:}displayname', + 5 => '{urn:ietf:params:xml:ns:caldav}calendar-description', + 6 => '{urn:ietf:params:xml:ns:caldav}calendar-timezone']], + [null, null, 102]) + ->will($this->returnValue($statement)); + + $this->backend->propFind('calendars/foo/bar_path_1337_0', $propFind); + } + + /** + * @dataProvider propPatchProvider + */ + public function testPropPatch($path, $propPatch) { + $propPatch->expects($this->once()) + ->method('handleRemaining'); + + $this->backend->propPatch($path, $propPatch); + } + + public function propPatchProvider() { + $propPatchMock = $this->createMock(PropPatch::class); + return [ + ['foo_bar_path_1337', $propPatchMock], + ]; + } + + public function testDelete() { + $statement = $this->createMock('\Doctrine\DBAL\Driver\Statement'); + $statement->expects($this->at(0)) + ->method('execute') + ->with(['dummy_user_42', 'foo_bar_path_1337']); + $statement->expects($this->at(1)) + ->method('closeCursor') + ->with(); + + $this->dbConnection->expects($this->at(0)) + ->method('prepare') + ->with('DELETE FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ?') + ->will($this->returnValue($statement)); + + $this->backend->delete('foo_bar_path_1337'); + } + + public function testMove() { + $statement = $this->createMock('\Doctrine\DBAL\Driver\Statement'); + $statement->expects($this->at(0)) + ->method('execute') + ->with(['bar_foo_path_7331', 'dummy_user_42', 'foo_bar_path_1337']); + $statement->expects($this->at(1)) + ->method('closeCursor') + ->with(); + + $this->dbConnection->expects($this->at(0)) + ->method('prepare') + ->with('UPDATE `*PREFIX*properties` SET `propertypath` = ? WHERE `userid` = ? AND `propertypath` = ?') + ->will($this->returnValue($statement)); + + $this->backend->move('foo_bar_path_1337', 'bar_foo_path_7331'); + } +} diff --git a/apps/dav/tests/unit/Files/CustomPropertiesBackendTest.php b/apps/dav/tests/unit/Files/CustomPropertiesBackendTest.php deleted file mode 100644 index 4f3e22aea6f..00000000000 --- a/apps/dav/tests/unit/Files/CustomPropertiesBackendTest.php +++ /dev/null @@ -1,170 +0,0 @@ - - * - * @author Georg Ehrke - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * 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 - * along with this program. If not, see . - * - */ -namespace OCA\DAV\Tests\Files; - -use OCA\DAV\Files\CustomPropertiesBackend; -use OCP\IDBConnection; -use OCP\IUser; -use Sabre\DAV\PropFind; -use Sabre\DAV\PropPatch; -use Sabre\DAV\Tree; -use Test\TestCase; - -class CustomPropertiesBackendTest extends TestCase { - - /** @var Tree | \PHPUnit_Framework_MockObject_MockObject */ - private $tree; - - /** @var IDBConnection | \PHPUnit_Framework_MockObject_MockObject */ - private $dbConnection; - - /** @var IUser | \PHPUnit_Framework_MockObject_MockObject */ - private $user; - - /** @var CustomPropertiesBackend | \PHPUnit_Framework_MockObject_MockObject */ - private $backend; - - public function setUp() { - parent::setUp(); - - $this->tree = $this->createMock(Tree::class); - $this->dbConnection = $this->createMock(IDBConnection::class); - $this->user = $this->createMock(IUser::class); - $this->user->expects($this->once()) - ->method('getUID') - ->with() - ->will($this->returnValue('dummy_user_42')); - - $this->backend = new CustomPropertiesBackend($this->tree, - $this->dbConnection, $this->user); - } - - public function testPropFindNoDbCalls() { - $propFind = $this->createMock(PropFind::class); - $propFind->expects($this->at(0)) - ->method('get404Properties') - ->with() - ->will($this->returnValue([ - '{http://owncloud.org/ns}permissions', - '{http://owncloud.org/ns}downloadURL', - '{http://owncloud.org/ns}dDC', - '{http://owncloud.org/ns}size', - ])); - - $this->dbConnection->expects($this->never()) - ->method($this->anything()); - - $this->backend->propFind('foo_bar_path_1337_0', $propFind); - } - - public function testPropFindCalendarCall() { - $propFind = $this->createMock(PropFind::class); - $propFind->expects($this->at(0)) - ->method('get404Properties') - ->with() - ->will($this->returnValue([ - '{DAV:}getcontentlength', - '{DAV:}getcontenttype', - '{DAV:}getetag', - '{abc}def' - ])); - - $propFind->expects($this->at(1)) - ->method('getRequestedProperties') - ->with() - ->will($this->returnValue([ - '{DAV:}getcontentlength', - '{DAV:}getcontenttype', - '{DAV:}getetag', - '{DAV:}displayname', - '{urn:ietf:params:xml:ns:caldav}calendar-description', - '{urn:ietf:params:xml:ns:caldav}calendar-timezone', - '{abc}def' - ])); - - $statement = $this->createMock('\Doctrine\DBAL\Driver\Statement'); - $this->dbConnection->expects($this->once()) - ->method('executeQuery') - ->with('SELECT * FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` in (?)', - ['dummy_user_42', 'calendars/foo/bar_path_1337_0', [ - 3 => '{abc}def', - 4 => '{DAV:}displayname', - 5 => '{urn:ietf:params:xml:ns:caldav}calendar-description', - 6 => '{urn:ietf:params:xml:ns:caldav}calendar-timezone']], - [null, null, 102]) - ->will($this->returnValue($statement)); - - $this->backend->propFind('calendars/foo/bar_path_1337_0', $propFind); - } - - /** - * @dataProvider propPatchProvider - */ - public function testPropPatch($path, $propPatch) { - $propPatch->expects($this->once()) - ->method('handleRemaining'); - - $this->backend->propPatch($path, $propPatch); - } - - public function propPatchProvider() { - $propPatchMock = $this->createMock(PropPatch::class); - return [ - ['foo_bar_path_1337', $propPatchMock], - ]; - } - - public function testDelete() { - $statement = $this->createMock('\Doctrine\DBAL\Driver\Statement'); - $statement->expects($this->at(0)) - ->method('execute') - ->with(['dummy_user_42', 'foo_bar_path_1337']); - $statement->expects($this->at(1)) - ->method('closeCursor') - ->with(); - - $this->dbConnection->expects($this->at(0)) - ->method('prepare') - ->with('DELETE FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ?') - ->will($this->returnValue($statement)); - - $this->backend->delete('foo_bar_path_1337'); - } - - public function testMove() { - $statement = $this->createMock('\Doctrine\DBAL\Driver\Statement'); - $statement->expects($this->at(0)) - ->method('execute') - ->with(['bar_foo_path_7331', 'dummy_user_42', 'foo_bar_path_1337']); - $statement->expects($this->at(1)) - ->method('closeCursor') - ->with(); - - $this->dbConnection->expects($this->at(0)) - ->method('prepare') - ->with('UPDATE `*PREFIX*properties` SET `propertypath` = ? WHERE `userid` = ? AND `propertypath` = ?') - ->will($this->returnValue($statement)); - - $this->backend->move('foo_bar_path_1337', 'bar_foo_path_7331'); - } -} -- cgit v1.2.3