diff options
author | Lukas Reschke <lukas@statuscode.ch> | 2016-11-01 18:44:00 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-11-01 18:44:00 +0100 |
commit | ef467c3195731cb1e4fff504b39c06b017deb5ec (patch) | |
tree | 4c74374891448f9243cc0fa7ead2c6e7419ffbf0 | |
parent | 20f45e6fe4626e64df8383d8250acd59c3b290c5 (diff) | |
parent | 7e2159e9bb1769455f56099c13f864edfb624ac1 (diff) | |
download | nextcloud-server-ef467c3195731cb1e4fff504b39c06b017deb5ec.tar.gz nextcloud-server-ef467c3195731cb1e4fff504b39c06b017deb5ec.zip |
Merge pull request #339 from nextcloud/share-types-by-folder
Add getShareTypesInFolder to optimize folder listening
-rw-r--r-- | apps/dav/lib/Connector/Sabre/SharesPlugin.php | 35 | ||||
-rw-r--r-- | apps/dav/tests/unit/Connector/Sabre/SharesPluginTest.php | 19 | ||||
-rw-r--r-- | apps/federatedfilesharing/lib/FederatedShareProvider.php | 43 | ||||
-rw-r--r-- | apps/federatedfilesharing/tests/FederatedShareProviderTest.php | 45 | ||||
-rw-r--r-- | lib/private/Share20/DefaultShareProvider.php | 45 | ||||
-rw-r--r-- | lib/private/Share20/Manager.php | 18 | ||||
-rw-r--r-- | lib/private/Share20/ProviderFactory.php | 4 | ||||
-rw-r--r-- | lib/public/Share/IManager.php | 12 | ||||
-rw-r--r-- | lib/public/Share/IProviderFactory.php | 6 | ||||
-rw-r--r-- | lib/public/Share/IShareProvider.php | 12 | ||||
-rw-r--r-- | tests/lib/Share20/DefaultShareProviderTest.php | 91 | ||||
-rw-r--r-- | tests/lib/Share20/ManagerTest.php | 92 |
12 files changed, 412 insertions, 10 deletions
diff --git a/apps/dav/lib/Connector/Sabre/SharesPlugin.php b/apps/dav/lib/Connector/Sabre/SharesPlugin.php index cd58f050dda..56d76e66184 100644 --- a/apps/dav/lib/Connector/Sabre/SharesPlugin.php +++ b/apps/dav/lib/Connector/Sabre/SharesPlugin.php @@ -136,6 +136,33 @@ class SharesPlugin extends \Sabre\DAV\ServerPlugin { return $shareTypes; } + private function getSharesTypesInFolder(\OCP\Files\Folder $node) { + $shares = $this->shareManager->getSharesInFolder( + $this->userId, + $node, + false + ); + + $children = $node->getDirectoryListing(); + + $values = array_map(function (\OCP\Files\Node $node) use ($shares) { + /** @var IShare[] $shares */ + $shares = (isset($shares[$node->getId()])) ? $shares[$node->getId()] : []; + $types = array_map(function(IShare $share) { + return $share->getShareType(); + }, $shares); + $types = array_unique($types); + sort($types); + return $types; + }, $children); + + $keys = array_map(function (\OCP\Files\Node $node) { + return $node->getId(); + }, $children); + + return array_combine($keys, $values); + } + /** * Adds shares to propfind response * @@ -156,15 +183,15 @@ class SharesPlugin extends \Sabre\DAV\ServerPlugin { && !is_null($propFind->getStatus(self::SHARETYPES_PROPERTYNAME)) ) { $folderNode = $this->userFolder->get($sabreNode->getPath()); - $children = $folderNode->getDirectoryListing(); + $childShares = $this->getSharesTypesInFolder($folderNode); $this->cachedShareTypes[$folderNode->getId()] = $this->getShareTypes($folderNode); - foreach ($children as $childNode) { - $this->cachedShareTypes[$childNode->getId()] = $this->getShareTypes($childNode); + foreach ($childShares as $id => $shares) { + $this->cachedShareTypes[$id] = $shares; } } - $propFind->handle(self::SHARETYPES_PROPERTYNAME, function() use ($sabreNode) { + $propFind->handle(self::SHARETYPES_PROPERTYNAME, function () use ($sabreNode) { if (isset($this->cachedShareTypes[$sabreNode->getId()])) { $shareTypes = $this->cachedShareTypes[$sabreNode->getId()]; } else { diff --git a/apps/dav/tests/unit/Connector/Sabre/SharesPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/SharesPluginTest.php index 433ef4ba63a..2e17c7d0b38 100644 --- a/apps/dav/tests/unit/Connector/Sabre/SharesPluginTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/SharesPluginTest.php @@ -206,6 +206,14 @@ class SharesPluginTest extends \Test\TestCase { ->method('get') ->with('/subdir') ->will($this->returnValue($node)); + + $dummyShares = array_map(function($type) { + $share = $this->getMock('\OCP\Share\IShare'); + $share->expects($this->any()) + ->method('getShareType') + ->will($this->returnValue($type)); + return $share; + }, $shareTypes); $this->shareManager->expects($this->any()) ->method('getSharesBy') @@ -224,6 +232,17 @@ class SharesPluginTest extends \Test\TestCase { return []; })); + $this->shareManager->expects($this->any()) + ->method('getSharesInFolder') + ->with( + $this->equalTo('user1'), + $this->anything(), + $this->equalTo(false) + ) + ->will($this->returnCallback(function ($userId, $node, $flag) use ($shareTypes, $dummyShares) { + return [111 => $dummyShares]; + })); + // simulate sabre recursive PROPFIND traversal $propFindRoot = new \Sabre\DAV\PropFind( '/subdir', diff --git a/apps/federatedfilesharing/lib/FederatedShareProvider.php b/apps/federatedfilesharing/lib/FederatedShareProvider.php index c567b92c3e4..270bf86daf7 100644 --- a/apps/federatedfilesharing/lib/FederatedShareProvider.php +++ b/apps/federatedfilesharing/lib/FederatedShareProvider.php @@ -27,6 +27,7 @@ namespace OCA\FederatedFileSharing; use OC\Share20\Share; +use OCP\Files\Folder; use OCP\Files\IRootFolder; use OCP\IConfig; use OCP\IL10N; @@ -563,6 +564,48 @@ class FederatedShareProvider implements IShareProvider { return; } + + public function getSharesInFolder($userId, Folder $node, $reshares) { + $qb = $this->dbConnection->getQueryBuilder(); + $qb->select('*') + ->from('share', 's') + ->andWhere($qb->expr()->orX( + $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), + $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) + )) + ->andWhere( + $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE)) + ); + + /** + * Reshares for this user are shares where they are the owner. + */ + if ($reshares === false) { + $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))); + } else { + $qb->andWhere( + $qb->expr()->orX( + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) + ) + ); + } + + $qb->innerJoin('s', 'filecache' ,'f', 's.file_source = f.fileid'); + $qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId()))); + + $qb->orderBy('id'); + + $cursor = $qb->execute(); + $shares = []; + while ($data = $cursor->fetch()) { + $shares[$data['fileid']][] = $this->createShareObject($data); + } + $cursor->closeCursor(); + + return $shares; + } + /** * @inheritdoc */ diff --git a/apps/federatedfilesharing/tests/FederatedShareProviderTest.php b/apps/federatedfilesharing/tests/FederatedShareProviderTest.php index e4c234fd038..92f6ac5e996 100644 --- a/apps/federatedfilesharing/tests/FederatedShareProviderTest.php +++ b/apps/federatedfilesharing/tests/FederatedShareProviderTest.php @@ -702,4 +702,49 @@ class FederatedShareProviderTest extends \Test\TestCase { ['no', false] ]; } + + public function testGetSharesInFolder() { + $userManager = \OC::$server->getUserManager(); + $rootFolder = \OC::$server->getRootFolder(); + + $u1 = $userManager->createUser('testFed', 'test'); + $u2 = $userManager->createUser('testFed2', 'test'); + + $folder1 = $rootFolder->getUserFolder($u1->getUID())->newFolder('foo'); + $file1 = $folder1->newFile('bar1'); + $file2 = $folder1->newFile('bar2'); + + $this->tokenHandler->method('generateToken')->willReturn('token'); + $this->notifications + ->method('sendRemoteShare') + ->willReturn(true); + + $share1 = $this->shareManager->newShare(); + $share1->setSharedWith('user@server.com') + ->setSharedBy($u1->getUID()) + ->setShareOwner($u1->getUID()) + ->setPermissions(\OCP\Constants::PERMISSION_READ) + ->setNode($file1); + $this->provider->create($share1); + + $share2 = $this->shareManager->newShare(); + $share2->setSharedWith('user@server.com') + ->setSharedBy($u2->getUID()) + ->setShareOwner($u1->getUID()) + ->setPermissions(\OCP\Constants::PERMISSION_READ) + ->setNode($file2); + $this->provider->create($share2); + + $result = $this->provider->getSharesInFolder($u1->getUID(), $folder1, false); + $this->assertCount(1, $result); + $this->assertCount(1, $result[$file1->getId()]); + + $result = $this->provider->getSharesInFolder($u1->getUID(), $folder1, true); + $this->assertCount(2, $result); + $this->assertCount(1, $result[$file1->getId()]); + $this->assertCount(1, $result[$file2->getId()]); + + $u1->delete(); + $u2->delete(); + } } diff --git a/lib/private/Share20/DefaultShareProvider.php b/lib/private/Share20/DefaultShareProvider.php index 56b9d5b1ee8..bdb6ac466ec 100644 --- a/lib/private/Share20/DefaultShareProvider.php +++ b/lib/private/Share20/DefaultShareProvider.php @@ -24,6 +24,7 @@ namespace OC\Share20; use OCP\Files\File; +use OCP\Files\Folder; use OCP\Share\IShareProvider; use OC\Share20\Exception\InvalidShare; use OC\Share20\Exception\ProviderException; @@ -454,6 +455,50 @@ class DefaultShareProvider implements IShareProvider { return $share; } + public function getSharesInFolder($userId, Folder $node, $reshares) { + $qb = $this->dbConn->getQueryBuilder(); + $qb->select('*') + ->from('share', 's') + ->andWhere($qb->expr()->orX( + $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), + $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) + )); + + $qb->andWhere($qb->expr()->orX( + $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)), + $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)), + $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_LINK)) + )); + + /** + * Reshares for this user are shares where they are the owner. + */ + if ($reshares === false) { + $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))); + } else { + $qb->andWhere( + $qb->expr()->orX( + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) + ) + ); + } + + $qb->innerJoin('s', 'filecache' ,'f', 's.file_source = f.fileid'); + $qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId()))); + + $qb->orderBy('id'); + + $cursor = $qb->execute(); + $shares = []; + while ($data = $cursor->fetch()) { + $shares[$data['fileid']][] = $this->createShare($data); + } + $cursor->closeCursor(); + + return $shares; + } + /** * @inheritdoc */ diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php index 22cf5a3f65a..838650ada15 100644 --- a/lib/private/Share20/Manager.php +++ b/lib/private/Share20/Manager.php @@ -34,6 +34,7 @@ use OCP\Files\File; use OCP\Files\Folder; use OCP\Files\IRootFolder; use OCP\Files\Mount\IMountManager; +use OCP\Files\Node; use OCP\Files\NotFoundException; use OCP\IConfig; use OCP\IGroupManager; @@ -48,6 +49,7 @@ use OCP\Share\IManager; use OCP\Share\IProviderFactory; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\GenericEvent; +use OCP\Share\IShareProvider; /** * This class is the communication hub for all sharing related operations. @@ -881,6 +883,22 @@ class Manager implements IManager { $provider->move($share, $recipientId); } + public function getSharesInFolder($userId, Folder $node, $reshares = false) { + $providers = $this->factory->getAllProviders(); + + return array_reduce($providers, function($shares, IShareProvider $provider) use ($userId, $node, $reshares) { + $newShares = $provider->getSharesInFolder($userId, $node, $reshares); + foreach ($newShares as $fid => $data) { + if (!isset($shares[$fid])) { + $shares[$fid] = []; + } + + $shares[$fid] = array_merge($shares[$fid], $data); + } + return $shares; + }, []); + } + /** * @inheritdoc */ diff --git a/lib/private/Share20/ProviderFactory.php b/lib/private/Share20/ProviderFactory.php index e3de10a8aee..5cdc9a51a22 100644 --- a/lib/private/Share20/ProviderFactory.php +++ b/lib/private/Share20/ProviderFactory.php @@ -163,4 +163,8 @@ class ProviderFactory implements IProviderFactory { return $provider; } + + public function getAllProviders() { + return [$this->defaultShareProvider(), $this->federatedShareProvider()]; + } } diff --git a/lib/public/Share/IManager.php b/lib/public/Share/IManager.php index 37751911883..a74ab5fe796 100644 --- a/lib/public/Share/IManager.php +++ b/lib/public/Share/IManager.php @@ -22,6 +22,7 @@ namespace OCP\Share; +use OCP\Files\Folder; use OCP\Files\Node; use OCP\Share\Exceptions\ShareNotFound; @@ -88,6 +89,17 @@ interface IManager { public function moveShare(IShare $share, $recipientId); /** + * Get all shares shared by (initiated) by the provided user in a folder. + * + * @param string $userId + * @param Folder $node + * @param bool $reshares + * @return IShare[] + * @since 9.2.0 + */ + public function getSharesInFolder($userId, Folder $node, $reshares = false); + + /** * Get shares shared by (initiated) by the provided user. * * @param string $userId diff --git a/lib/public/Share/IProviderFactory.php b/lib/public/Share/IProviderFactory.php index 8d9ea5bfdd0..928298a7860 100644 --- a/lib/public/Share/IProviderFactory.php +++ b/lib/public/Share/IProviderFactory.php @@ -55,4 +55,10 @@ interface IProviderFactory { * @since 9.0.0 */ public function getProviderForType($shareType); + + /** + * @return IShareProvider[] + * @since 9.2.0 + */ + public function getAllProviders(); } diff --git a/lib/public/Share/IShareProvider.php b/lib/public/Share/IShareProvider.php index c4e116ac7fd..7d134583317 100644 --- a/lib/public/Share/IShareProvider.php +++ b/lib/public/Share/IShareProvider.php @@ -22,6 +22,7 @@ namespace OCP\Share; +use OCP\Files\Folder; use OCP\Share\Exceptions\ShareNotFound; use OCP\Files\Node; @@ -92,6 +93,17 @@ interface IShareProvider { public function move(\OCP\Share\IShare $share, $recipient); /** + * Get all shares by the given user in a folder + * + * @param string $userId + * @param Folder $node + * @param bool $reshares Also get the shares where $user is the owner instead of just the shares where $user is the initiator + * @return \OCP\Share\IShare[] + * @since 9.2.0 + */ + public function getSharesInFolder($userId, Folder $node, $reshares); + + /** * Get all shares by the given user * * @param string $userId diff --git a/tests/lib/Share20/DefaultShareProviderTest.php b/tests/lib/Share20/DefaultShareProviderTest.php index 778c1cb5722..5870da8d218 100644 --- a/tests/lib/Share20/DefaultShareProviderTest.php +++ b/tests/lib/Share20/DefaultShareProviderTest.php @@ -2298,4 +2298,95 @@ class DefaultShareProviderTest extends \Test\TestCase { $this->assertCount($toDelete ? 0 : 1, $data); } + + public function testGetSharesInFolder() { + $userManager = \OC::$server->getUserManager(); + $groupManager = \OC::$server->getGroupManager(); + $rootFolder = \OC::$server->getRootFolder(); + + $provider = new DefaultShareProvider( + $this->dbConn, + $userManager, + $groupManager, + $rootFolder + ); + + $u1 = $userManager->createUser('testShare1', 'test'); + $u2 = $userManager->createUser('testShare2', 'test'); + $u3 = $userManager->createUser('testShare3', 'test'); + + $g1 = $groupManager->createGroup('group1'); + + $u1Folder = $rootFolder->getUserFolder($u1->getUID()); + $folder1 = $u1Folder->newFolder('foo'); + $file1 = $folder1->newFile('bar'); + $folder2 = $folder1->newFolder('baz'); + + $shareManager = \OC::$server->getShareManager(); + $share1 = $shareManager->newShare(); + $share1->setNode($folder1) + ->setSharedBy($u1->getUID()) + ->setSharedWith($u2->getUID()) + ->setShareOwner($u1->getUID()) + ->setShareType(\OCP\Share::SHARE_TYPE_USER) + ->setPermissions(\OCP\Constants::PERMISSION_ALL); + $share1 = $this->provider->create($share1); + + $share2 = $shareManager->newShare(); + $share2->setNode($file1) + ->setSharedBy($u2->getUID()) + ->setSharedWith($u3->getUID()) + ->setShareOwner($u1->getUID()) + ->setShareType(\OCP\Share::SHARE_TYPE_USER) + ->setPermissions(\OCP\Constants::PERMISSION_READ); + $share2 = $this->provider->create($share2); + + $share3 = $shareManager->newShare(); + $share3->setNode($folder2) + ->setSharedBy($u2->getUID()) + ->setShareOwner($u1->getUID()) + ->setShareType(\OCP\Share::SHARE_TYPE_LINK) + ->setPermissions(\OCP\Constants::PERMISSION_READ); + $share3 = $this->provider->create($share3); + + $share4 = $shareManager->newShare(); + $share4->setNode($folder2) + ->setSharedBy($u1->getUID()) + ->setSharedWith($g1->getGID()) + ->setShareOwner($u1->getUID()) + ->setShareType(\OCP\Share::SHARE_TYPE_GROUP) + ->setPermissions(\OCP\Constants::PERMISSION_READ); + $share4 = $this->provider->create($share4); + + $result = $provider->getSharesInFolder($u1->getUID(), $folder1, false); + $this->assertCount(1, $result); + $shares = array_pop($result); + $this->assertCount(1, $shares); + $this->assertSame($folder2->getId(), $shares[0]->getNodeId()); + + $result = $provider->getSharesInFolder($u1->getUID(), $folder1, true); + $this->assertCount(2, $result); + + $file_shares = $result[$file1->getId()]; + $this->assertCount(1, $file_shares); + $this->assertSame($file1->getId(), $file_shares[0]->getNodeId()); + $this->assertSame(\OCP\Share::SHARE_TYPE_USER, $file_shares[0]->getShareType()); + + $folder_shares = $result[$folder2->getId()]; + $this->assertCount(2, $folder_shares); + $this->assertSame($folder2->getId(), $folder_shares[0]->getNodeId()); + $this->assertSame($folder2->getId(), $folder_shares[1]->getNodeId()); + $this->assertSame(\OCP\Share::SHARE_TYPE_LINK, $folder_shares[0]->getShareType()); + $this->assertSame(\OCP\Share::SHARE_TYPE_GROUP, $folder_shares[1]->getShareType()); + + $provider->delete($share1); + $provider->delete($share2); + $provider->delete($share3); + $provider->delete($share4); + + $u1->delete(); + $u2->delete(); + $u3->delete(); + $g1->delete(); + } } diff --git a/tests/lib/Share20/ManagerTest.php b/tests/lib/Share20/ManagerTest.php index 72ba4da6e5d..c0c7b48b35d 100644 --- a/tests/lib/Share20/ManagerTest.php +++ b/tests/lib/Share20/ManagerTest.php @@ -22,6 +22,7 @@ namespace Test\Share20; use OC\Files\Mount\MoveableMount; use OC\HintException; +use OC\Share20\DefaultShareProvider; use OCP\Files\File; use OCP\Files\Folder; use OCP\Files\IRootFolder; @@ -29,6 +30,7 @@ use OCP\Files\Mount\IMountPoint; use OCP\Files\Node; use OCP\Files\Storage; use OCP\IGroup; +use OCP\IServerContainer; use OCP\IUser; use OCP\IUserManager; use OCP\Share\Exceptions\ShareNotFound; @@ -118,13 +120,9 @@ class ManagerTest extends \Test\TestCase { $this->eventDispatcher ); - $this->defaultProvider = $this->getMockBuilder('\OC\Share20\DefaultShareProvider') - ->disableOriginalConstructor() - ->getMock(); + $this->defaultProvider = $this->createMock(DefaultShareProvider::class); $this->defaultProvider->method('identifier')->willReturn('default'); $this->factory->setProvider($this->defaultProvider); - - } /** @@ -2531,12 +2529,71 @@ class ManagerTest extends \Test\TestCase { $this->manager->moveShare($share, 'recipient'); } + + public function testGetSharesInFolder() { + $factory = new DummyFactory2($this->createMock(IServerContainer::class)); + + $manager = new Manager( + $this->logger, + $this->config, + $this->secureRandom, + $this->hasher, + $this->mountManager, + $this->groupManager, + $this->l, + $factory, + $this->userManager, + $this->rootFolder, + $this->eventDispatcher + ); + + $factory->setProvider($this->defaultProvider); + $extraProvider = $this->createMock(IShareProvider::class); + $factory->setSecondProvider($extraProvider); + + $share1 = $this->createMock(IShare::class); + $share2 = $this->createMock(IShare::class); + $share3 = $this->createMock(IShare::class); + $share4 = $this->createMock(IShare::class); + + $folder = $this->createMock(Folder::class); + + $this->defaultProvider->method('getSharesInFolder') + ->with( + $this->equalTo('user'), + $this->equalTo($folder), + $this->equalTo(false) + )->willReturn([ + 1 => [$share1], + 2 => [$share2], + ]); + + $extraProvider->method('getSharesInFolder') + ->with( + $this->equalTo('user'), + $this->equalTo($folder), + $this->equalTo(false) + )->willReturn([ + 2 => [$share3], + 3 => [$share4], + ]); + + $result = $manager->getSharesInFolder('user', $folder, false); + + $expects = [ + 1 => [$share1], + 2 => [$share2, $share3], + 3 => [$share4], + ]; + + $this->assertSame($expects, $result); + } } class DummyFactory implements IProviderFactory { /** @var IShareProvider */ - private $provider; + protected $provider; public function __construct(\OCP\IServerContainer $serverContainer) { @@ -2564,4 +2621,27 @@ class DummyFactory implements IProviderFactory { public function getProviderForType($shareType) { return $this->provider; } + + /** + * @return IShareProvider[] + */ + public function getAllProviders() { + return [$this->provider]; + } +} + +class DummyFactory2 extends DummyFactory { + /** @var IShareProvider */ + private $provider2; + + /** + * @param IShareProvider $provider + */ + public function setSecondProvider($provider) { + $this->provider2 = $provider; + } + + public function getAllProviders() { + return [$this->provider, $this->provider2]; + } } |