Browse Source
Add webdav property for share info in PROPFIND response
remotes/origin/files-consistentpermsformountpoint
Add webdav property for share info in PROPFIND response
remotes/origin/files-consistentpermsformountpoint
7 changed files with 608 additions and 45 deletions
-
6apps/dav/lib/connector/sabre/serverfactory.php
-
197apps/dav/lib/connector/sabre/sharesplugin.php
-
87apps/dav/lib/connector/sabre/sharetypelist.php
-
263apps/dav/tests/unit/connector/sabre/sharesplugin.php
-
57apps/files_sharing/js/share.js
-
10apps/files_sharing/js/sharedfilelist.js
-
33apps/files_sharing/tests/js/shareSpec.js
@ -0,0 +1,197 @@ |
|||
<?php |
|||
/** |
|||
* @author Vincent Petry <pvince81@owncloud.com> |
|||
* |
|||
* @copyright Copyright (c) 2016, 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 OCA\DAV\Connector\Sabre; |
|||
|
|||
/** |
|||
* ownCloud |
|||
* |
|||
* @author Vincent Petry |
|||
* @copyright 2016 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/>. |
|||
* |
|||
*/ |
|||
|
|||
use \Sabre\DAV\PropFind; |
|||
use \Sabre\DAV\PropPatch; |
|||
use OCP\IUserSession; |
|||
use OCP\Share\IShare; |
|||
use OCA\DAV\Connector\Sabre\ShareTypeList; |
|||
|
|||
/** |
|||
* Sabre Plugin to provide share-related properties |
|||
*/ |
|||
class SharesPlugin extends \Sabre\DAV\ServerPlugin { |
|||
|
|||
const NS_OWNCLOUD = 'http://owncloud.org/ns'; |
|||
const SHARETYPES_PROPERTYNAME = '{http://owncloud.org/ns}share-types'; |
|||
|
|||
/** |
|||
* Reference to main server object |
|||
* |
|||
* @var \Sabre\DAV\Server |
|||
*/ |
|||
private $server; |
|||
|
|||
/** |
|||
* @var \OCP\Share\IManager |
|||
*/ |
|||
private $shareManager; |
|||
|
|||
/** |
|||
* @var \Sabre\DAV\Tree |
|||
*/ |
|||
private $tree; |
|||
|
|||
/** |
|||
* @var string |
|||
*/ |
|||
private $userId; |
|||
|
|||
/** |
|||
* @var \OCP\Files\Folder |
|||
*/ |
|||
private $userFolder; |
|||
|
|||
/** |
|||
* @var IShare[] |
|||
*/ |
|||
private $cachedShareTypes; |
|||
|
|||
/** |
|||
* @param \Sabre\DAV\Tree $tree tree |
|||
* @param IUserSession $userSession user session |
|||
* @param \OCP\Files\Folder $userFolder user home folder |
|||
* @param \OCP\Share\IManager $shareManager share manager |
|||
*/ |
|||
public function __construct( |
|||
\Sabre\DAV\Tree $tree, |
|||
IUserSession $userSession, |
|||
\OCP\Files\Folder $userFolder, |
|||
\OCP\Share\IManager $shareManager |
|||
) { |
|||
$this->tree = $tree; |
|||
$this->shareManager = $shareManager; |
|||
$this->userFolder = $userFolder; |
|||
$this->userId = $userSession->getUser()->getUID(); |
|||
$this->cachedShareTypes = []; |
|||
} |
|||
|
|||
/** |
|||
* 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 |
|||
*/ |
|||
public function initialize(\Sabre\DAV\Server $server) { |
|||
$server->xml->namespacesMap[self::NS_OWNCLOUD] = 'oc'; |
|||
$server->xml->elementMap[self::SHARETYPES_PROPERTYNAME] = 'OCA\\DAV\\Connector\\Sabre\\ShareTypeList'; |
|||
$server->protectedProperties[] = self::SHARETYPES_PROPERTYNAME; |
|||
|
|||
$this->server = $server; |
|||
$this->server->on('propFind', array($this, 'handleGetProperties')); |
|||
} |
|||
|
|||
/** |
|||
* Return a list of share types for outgoing shares |
|||
* |
|||
* @param \OCP\Files\Node $node file node |
|||
* |
|||
* @return int[] array of share types |
|||
*/ |
|||
private function getShareTypes(\OCP\Files\Node $node) { |
|||
$shareTypes = []; |
|||
$requestedShareTypes = [ |
|||
\OCP\Share::SHARE_TYPE_USER, |
|||
\OCP\Share::SHARE_TYPE_GROUP, |
|||
\OCP\Share::SHARE_TYPE_LINK |
|||
]; |
|||
foreach ($requestedShareTypes as $requestedShareType) { |
|||
// one of each type is enough to find out about the types
|
|||
$shares = $this->shareManager->getSharesBy( |
|||
$this->userId, |
|||
$requestedShareType, |
|||
$node, |
|||
false, |
|||
1 |
|||
); |
|||
if (!empty($shares)) { |
|||
$shareTypes[] = $requestedShareType; |
|||
} |
|||
} |
|||
return $shareTypes; |
|||
} |
|||
|
|||
/** |
|||
* Adds shares to propfind response |
|||
* |
|||
* @param PropFind $propFind propfind object |
|||
* @param \Sabre\DAV\INode $sabreNode sabre node |
|||
*/ |
|||
public function handleGetProperties( |
|||
PropFind $propFind, |
|||
\Sabre\DAV\INode $sabreNode |
|||
) { |
|||
if (!($sabreNode instanceof \OCA\DAV\Connector\Sabre\Node)) { |
|||
return; |
|||
} |
|||
|
|||
// need prefetch ?
|
|||
if ($sabreNode instanceof \OCA\DAV\Connector\Sabre\Directory |
|||
&& $propFind->getDepth() !== 0 |
|||
&& !is_null($propFind->getStatus(self::SHARETYPES_PROPERTYNAME)) |
|||
) { |
|||
$folderNode = $this->userFolder->get($propFind->getPath()); |
|||
$children = $folderNode->getDirectoryListing(); |
|||
|
|||
$this->cachedShareTypes[$folderNode->getId()] = $this->getShareTypes($folderNode); |
|||
foreach ($children as $childNode) { |
|||
$this->cachedShareTypes[$childNode->getId()] = $this->getShareTypes($childNode); |
|||
} |
|||
} |
|||
|
|||
$propFind->handle(self::SHARETYPES_PROPERTYNAME, function() use ($sabreNode) { |
|||
if (isset($this->cachedShareTypes[$sabreNode->getId()])) { |
|||
$shareTypes = $this->cachedShareTypes[$sabreNode->getId()]; |
|||
} else { |
|||
$node = $this->userFolder->get($sabreNode->getPath()); |
|||
$shareTypes = $this->getShareTypes($node); |
|||
} |
|||
|
|||
return new ShareTypeList($shareTypes); |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,87 @@ |
|||
<?php |
|||
/** |
|||
* @author Vincent Petry <pvince81@owncloud.com> |
|||
* |
|||
* @copyright Copyright (c) 2016, 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 OCA\DAV\Connector\Sabre; |
|||
|
|||
use Sabre\Xml\Element; |
|||
use Sabre\Xml\Reader; |
|||
use Sabre\Xml\Writer; |
|||
|
|||
/** |
|||
* ShareTypeList property |
|||
* |
|||
* This property contains multiple "share-type" elements, each containing a share type. |
|||
*/ |
|||
class ShareTypeList implements Element { |
|||
const NS_OWNCLOUD = 'http://owncloud.org/ns'; |
|||
|
|||
/** |
|||
* Share types |
|||
* |
|||
* @var int[] |
|||
*/ |
|||
private $shareTypes; |
|||
|
|||
/** |
|||
* @param int[] $shareTypes |
|||
*/ |
|||
public function __construct($shareTypes) { |
|||
$this->shareTypes = $shareTypes; |
|||
} |
|||
|
|||
/** |
|||
* Returns the share types |
|||
* |
|||
* @return int[] |
|||
*/ |
|||
public function getShareTypes() { |
|||
return $this->shareTypes; |
|||
} |
|||
|
|||
/** |
|||
* The deserialize method is called during xml parsing. |
|||
* |
|||
* @param Reader $reader |
|||
* @return mixed |
|||
*/ |
|||
static function xmlDeserialize(Reader $reader) { |
|||
$shareTypes = []; |
|||
|
|||
foreach ($reader->parseInnerTree() as $elem) { |
|||
if ($elem['name'] === '{' . self::NS_OWNCLOUD . '}share-type') { |
|||
$shareTypes[] = (int)$elem['value']; |
|||
} |
|||
} |
|||
return new self($shareTypes); |
|||
} |
|||
|
|||
/** |
|||
* The xmlSerialize metod is called during xml writing. |
|||
* |
|||
* @param Writer $writer |
|||
* @return void |
|||
*/ |
|||
function xmlSerialize(Writer $writer) { |
|||
foreach ($this->shareTypes as $shareType) { |
|||
$writer->writeElement('{' . self::NS_OWNCLOUD . '}share-type', $shareType); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,263 @@ |
|||
<?php |
|||
/** |
|||
* @author Vincent Petry <pvince81@owncloud.com> |
|||
* |
|||
* @copyright Copyright (c) 2016, 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 OCA\DAV\Tests\Unit\Connector\Sabre; |
|||
|
|||
/** |
|||
* Copyright (c) 2016 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 SharesPlugin extends \Test\TestCase { |
|||
|
|||
const SHARETYPES_PROPERTYNAME = \OCA\DAV\Connector\Sabre\SharesPlugin::SHARETYPES_PROPERTYNAME; |
|||
|
|||
/** |
|||
* @var \Sabre\DAV\Server |
|||
*/ |
|||
private $server; |
|||
|
|||
/** |
|||
* @var \Sabre\DAV\Tree |
|||
*/ |
|||
private $tree; |
|||
|
|||
/** |
|||
* @var \OCP\Share\IManager |
|||
*/ |
|||
private $shareManager; |
|||
|
|||
/** |
|||
* @var \OCP\Files\Folder |
|||
*/ |
|||
private $userFolder; |
|||
|
|||
/** |
|||
* @var \OCA\DAV\Connector\Sabre\SharesPlugin |
|||
*/ |
|||
private $plugin; |
|||
|
|||
public function setUp() { |
|||
parent::setUp(); |
|||
$this->server = new \Sabre\DAV\Server(); |
|||
$this->tree = $this->getMockBuilder('\Sabre\DAV\Tree') |
|||
->disableOriginalConstructor() |
|||
->getMock(); |
|||
$this->shareManager = $this->getMock('\OCP\Share\IManager'); |
|||
$user = $this->getMock('\OCP\IUser'); |
|||
$user->expects($this->once()) |
|||
->method('getUID') |
|||
->will($this->returnValue('user1')); |
|||
$userSession = $this->getMock('\OCP\IUserSession'); |
|||
$userSession->expects($this->once()) |
|||
->method('getUser') |
|||
->will($this->returnValue($user)); |
|||
|
|||
$this->userFolder = $this->getMock('\OCP\Files\Folder'); |
|||
|
|||
$this->plugin = new \OCA\DAV\Connector\Sabre\SharesPlugin( |
|||
$this->tree, |
|||
$userSession, |
|||
$this->userFolder, |
|||
$this->shareManager |
|||
); |
|||
$this->plugin->initialize($this->server); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider sharesGetPropertiesDataProvider |
|||
*/ |
|||
public function testGetProperties($shareTypes) { |
|||
$sabreNode = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Node') |
|||
->disableOriginalConstructor() |
|||
->getMock(); |
|||
$sabreNode->expects($this->any()) |
|||
->method('getId') |
|||
->will($this->returnValue(123)); |
|||
$sabreNode->expects($this->once()) |
|||
->method('getPath') |
|||
->will($this->returnValue('/subdir')); |
|||
|
|||
// node API nodes
|
|||
$node = $this->getMock('\OCP\Files\Folder'); |
|||
|
|||
$this->userFolder->expects($this->once()) |
|||
->method('get') |
|||
->with('/subdir') |
|||
->will($this->returnValue($node)); |
|||
|
|||
$this->shareManager->expects($this->any()) |
|||
->method('getSharesBy') |
|||
->with( |
|||
$this->equalTo('user1'), |
|||
$this->anything(), |
|||
$this->anything(), |
|||
$this->equalTo(false), |
|||
$this->equalTo(1) |
|||
) |
|||
->will($this->returnCallback(function($userId, $requestedShareType, $node, $flag, $limit) use ($shareTypes){ |
|||
if (in_array($requestedShareType, $shareTypes)) { |
|||
return ['dummyshare']; |
|||
} |
|||
return []; |
|||
})); |
|||
|
|||
$propFind = new \Sabre\DAV\PropFind( |
|||
'/dummyPath', |
|||
[self::SHARETYPES_PROPERTYNAME], |
|||
0 |
|||
); |
|||
|
|||
$this->plugin->handleGetProperties( |
|||
$propFind, |
|||
$sabreNode |
|||
); |
|||
|
|||
$result = $propFind->getResultForMultiStatus(); |
|||
|
|||
$this->assertEmpty($result[404]); |
|||
unset($result[404]); |
|||
$this->assertEquals($shareTypes, $result[200][self::SHARETYPES_PROPERTYNAME]->getShareTypes()); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider sharesGetPropertiesDataProvider |
|||
*/ |
|||
public function testPreloadThenGetProperties($shareTypes) { |
|||
$sabreNode1 = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\File') |
|||
->disableOriginalConstructor() |
|||
->getMock(); |
|||
$sabreNode1->expects($this->any()) |
|||
->method('getId') |
|||
->will($this->returnValue(111)); |
|||
$sabreNode1->expects($this->never()) |
|||
->method('getPath'); |
|||
$sabreNode2 = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\File') |
|||
->disableOriginalConstructor() |
|||
->getMock(); |
|||
$sabreNode2->expects($this->any()) |
|||
->method('getId') |
|||
->will($this->returnValue(222)); |
|||
$sabreNode2->expects($this->never()) |
|||
->method('getPath'); |
|||
|
|||
$sabreNode = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Directory') |
|||
->disableOriginalConstructor() |
|||
->getMock(); |
|||
$sabreNode->expects($this->any()) |
|||
->method('getId') |
|||
->will($this->returnValue(123)); |
|||
// never, because we use getDirectoryListing from the Node API instead
|
|||
$sabreNode->expects($this->never()) |
|||
->method('getChildren'); |
|||
$sabreNode->expects($this->any()) |
|||
->method('getPath') |
|||
->will($this->returnValue('/subdir')); |
|||
|
|||
// node API nodes
|
|||
$node = $this->getMock('\OCP\Files\Folder'); |
|||
$node->expects($this->any()) |
|||
->method('getId') |
|||
->will($this->returnValue(123)); |
|||
$node1 = $this->getMock('\OCP\Files\File'); |
|||
$node1->expects($this->any()) |
|||
->method('getId') |
|||
->will($this->returnValue(111)); |
|||
$node2 = $this->getMock('\OCP\Files\File'); |
|||
$node2->expects($this->any()) |
|||
->method('getId') |
|||
->will($this->returnValue(222)); |
|||
$node->expects($this->once()) |
|||
->method('getDirectoryListing') |
|||
->will($this->returnValue([$node1, $node2])); |
|||
|
|||
$this->userFolder->expects($this->once()) |
|||
->method('get') |
|||
->with('/subdir') |
|||
->will($this->returnValue($node)); |
|||
|
|||
$this->shareManager->expects($this->any()) |
|||
->method('getSharesBy') |
|||
->with( |
|||
$this->equalTo('user1'), |
|||
$this->anything(), |
|||
$this->anything(), |
|||
$this->equalTo(false), |
|||
$this->equalTo(1) |
|||
) |
|||
->will($this->returnCallback(function($userId, $requestedShareType, $node, $flag, $limit) use ($shareTypes){ |
|||
if ($node->getId() === 111 && in_array($requestedShareType, $shareTypes)) { |
|||
return ['dummyshare']; |
|||
} |
|||
|
|||
return []; |
|||
})); |
|||
|
|||
// simulate sabre recursive PROPFIND traversal
|
|||
$propFindRoot = new \Sabre\DAV\PropFind( |
|||
'/subdir', |
|||
[self::SHARETYPES_PROPERTYNAME], |
|||
1 |
|||
); |
|||
$propFind1 = new \Sabre\DAV\PropFind( |
|||
'/subdir/test.txt', |
|||
[self::SHARETYPES_PROPERTYNAME], |
|||
0 |
|||
); |
|||
$propFind2 = new \Sabre\DAV\PropFind( |
|||
'/subdir/test2.txt', |
|||
[self::SHARETYPES_PROPERTYNAME], |
|||
0 |
|||
); |
|||
|
|||
$this->plugin->handleGetProperties( |
|||
$propFindRoot, |
|||
$sabreNode |
|||
); |
|||
$this->plugin->handleGetProperties( |
|||
$propFind1, |
|||
$sabreNode1 |
|||
); |
|||
$this->plugin->handleGetProperties( |
|||
$propFind2, |
|||
$sabreNode2 |
|||
); |
|||
|
|||
$result = $propFind1->getResultForMultiStatus(); |
|||
|
|||
$this->assertEmpty($result[404]); |
|||
unset($result[404]); |
|||
$this->assertEquals($shareTypes, $result[200][self::SHARETYPES_PROPERTYNAME]->getShareTypes()); |
|||
} |
|||
|
|||
function sharesGetPropertiesDataProvider() { |
|||
return [ |
|||
[[]], |
|||
[[\OCP\Share::SHARE_TYPE_USER]], |
|||
[[\OCP\Share::SHARE_TYPE_GROUP]], |
|||
[[\OCP\Share::SHARE_TYPE_LINK]], |
|||
[[\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP]], |
|||
[[\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_LINK]], |
|||
[[\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_LINK]], |
|||
[[\OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_LINK]], |
|||
]; |
|||
} |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue