From 2dc11cf20e85e9f47c0a2e7027e1229b41c46e6d Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 6 Mar 2015 13:15:50 +0100 Subject: [PATCH 01/20] unit test for etag propagation across shared storage boundary --- apps/files_sharing/tests/propagation.php | 160 ++++++++++++++++++++++- 1 file changed, 158 insertions(+), 2 deletions(-) diff --git a/apps/files_sharing/tests/propagation.php b/apps/files_sharing/tests/propagation.php index 546f67556f2c..1949f7808a4d 100644 --- a/apps/files_sharing/tests/propagation.php +++ b/apps/files_sharing/tests/propagation.php @@ -19,12 +19,12 @@ * */ -namespace OCA\Files_Sharing\Tests; +namespace OCA\Files_sharing\Tests; +use OC\Files\Filesystem; use OC\Files\View; class Propagation extends TestCase { - public function testSizePropagationWhenOwnerChangesFile() { $this->loginHelper(self::TEST_FILES_SHARING_API_USER1); $recipientView = new View('/' . self::TEST_FILES_SHARING_API_USER1 . '/files'); @@ -86,4 +86,160 @@ public function testSizePropagationWhenRecipientChangesFile() { $newOwnerRootInfo = $ownerView->getFileInfo('', false); $this->assertEquals($ownerRootInfo->getSize() + 3, $newOwnerRootInfo->getSize()); } + + /** + * @return \OC\Files\View[] + */ + private function setupViews() { + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); + $view1 = new View('/' . self::TEST_FILES_SHARING_API_USER1 . '/files'); + + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER2); + $view2 = new View('/' . self::TEST_FILES_SHARING_API_USER2 . '/files'); + $view2->mkdir('/sharedfolder/subfolder'); + $view2->file_put_contents('/sharedfolder/subfolder/foo.txt', 'bar'); + return [$view1, $view2]; + } + + public function testEtagPropagationSingleUserShareRecipient() { + /** + * @var \OC\Files\View $view1 + * @var \OC\Files\View $view2 + */ + list($view1, $view2) = $this->setupViews(); + + $sharedFolderInfo = $view2->getFileInfo('/sharedfolder'); + \OCP\Share::shareItem('folder', $sharedFolderInfo->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER1, 31); + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); + $this->assertTrue($view1->file_exists('/sharedfolder/subfolder/foo.txt')); + + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER2); + $rootInfo = $view2->getFileInfo(''); + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); + + Filesystem::file_put_contents('/sharedfolder/subfolder/foo.txt', 'foobar'); + + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER2); + $newRootInfo = $view2->getFileInfo(''); + $this->assertNotEquals($rootInfo->getEtag(), $newRootInfo->getEtag()); + } + + public function testEtagPropagationSingleUserShare() { + /** + * @var \OC\Files\View $view1 + * @var \OC\Files\View $view2 + */ + list($view1, $view2) = $this->setupViews(); + + $sharedFolderInfo = $view2->getFileInfo('/sharedfolder'); + \OCP\Share::shareItem('folder', $sharedFolderInfo->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER1, 31); + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); + $this->assertTrue($view1->file_exists('/sharedfolder/subfolder/foo.txt')); + + $rootInfo = $view1->getFileInfo(''); + + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER2); + Filesystem::file_put_contents('/sharedfolder/subfolder/foo.txt', 'foobar'); + + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); + $newRootInfo = $view1->getFileInfo(''); + $this->assertNotEquals($rootInfo->getEtag(), $newRootInfo->getEtag()); + } + + public function testEtagPropagationGroupShare() { + /** + * @var \OC\Files\View $view1 + * @var \OC\Files\View $view2 + */ + list($view1, $view2) = $this->setupViews(); + + $sharedFolderInfo = $view2->getFileInfo('/sharedfolder'); + \OCP\Share::shareItem('folder', $sharedFolderInfo->getId(), \OCP\Share::SHARE_TYPE_GROUP, 'group', 31); + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); + $this->assertTrue($view1->file_exists('/sharedfolder/subfolder/foo.txt')); + + $rootInfo = $view1->getFileInfo(''); + + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER2); + Filesystem::file_put_contents('/sharedfolder/subfolder/foo.txt', 'foobar'); + + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); + $newRootInfo = $view1->getFileInfo(''); + $this->assertNotEquals($rootInfo->getEtag(), $newRootInfo->getEtag()); + } + + public function testEtagPropagationGroupShareOtherRecipient() { + /** + * @var \OC\Files\View $view1 + * @var \OC\Files\View $view2 + */ + list($view1, $view2) = $this->setupViews(); + + $sharedFolderInfo = $view2->getFileInfo('/sharedfolder'); + \OCP\Share::shareItem('folder', $sharedFolderInfo->getId(), \OCP\Share::SHARE_TYPE_GROUP, 'group', 31); + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER3); + $view3 = new View('/' . self::TEST_FILES_SHARING_API_USER3 . '/files'); + $this->assertTrue($view3->file_exists('/sharedfolder/subfolder/foo.txt')); + + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); + $rootInfo = $view1->getFileInfo(''); + + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER3); + Filesystem::file_put_contents('/sharedfolder/subfolder/foo.txt', 'foobar'); + + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); + $newRootInfo = $view1->getFileInfo(''); + $this->assertNotEquals($rootInfo->getEtag(), $newRootInfo->getEtag()); + } + + public function testEtagPropagationOtherShare() { + /** + * @var \OC\Files\View $view1 + * @var \OC\Files\View $view2 + */ + list($view1, $view2) = $this->setupViews(); + + $sharedFolderInfo = $view2->getFileInfo('/sharedfolder'); + \OCP\Share::shareItem('folder', $sharedFolderInfo->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER1, 31); + \OCP\Share::shareItem('folder', $sharedFolderInfo->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER3, 31); + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER3); + $view3 = new View('/' . self::TEST_FILES_SHARING_API_USER3 . '/files'); + $this->assertTrue($view3->file_exists('/sharedfolder/subfolder/foo.txt')); + + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); + $rootInfo = $view1->getFileInfo(''); + + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER3); + Filesystem::file_put_contents('/sharedfolder/subfolder/foo.txt', 'foobar'); + + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); + $newRootInfo = $view1->getFileInfo(''); + $this->assertNotEquals($rootInfo->getEtag(), $newRootInfo->getEtag()); + } + + public function testEtagPropagationOtherShareSubFolder() { + /** + * @var \OC\Files\View $view1 + * @var \OC\Files\View $view2 + */ + list($view1, $view2) = $this->setupViews(); + + $sharedFolderInfo = $view2->getFileInfo('/sharedfolder'); + \OCP\Share::shareItem('folder', $sharedFolderInfo->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER1, 31); + $sharedFolderInfo = $view2->getFileInfo('/sharedfolder/subfolder'); + \OCP\Share::shareItem('folder', $sharedFolderInfo->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER3, 31); + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER3); + $view3 = new View('/' . self::TEST_FILES_SHARING_API_USER3 . '/files'); + $this->assertTrue($view3->file_exists('/subfolder/foo.txt')); + + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); + $rootInfo = $view1->getFileInfo(''); + + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER3); + Filesystem::file_put_contents('/subfolder/foo.txt', 'foobar'); + + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); + $newRootInfo = $view1->getFileInfo(''); + $this->assertNotEquals($rootInfo->getEtag(), $newRootInfo->getEtag()); + } } From 849e5521de2afdf6efbcb70337079a50f012a6ce Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 9 Mar 2015 16:15:06 +0100 Subject: [PATCH 02/20] Make the change propagator an emitter --- lib/private/files/cache/changepropagator.php | 5 ++++- lib/private/files/cache/updater.php | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/private/files/cache/changepropagator.php b/lib/private/files/cache/changepropagator.php index 4a860631ae0f..9696a82257e1 100644 --- a/lib/private/files/cache/changepropagator.php +++ b/lib/private/files/cache/changepropagator.php @@ -22,12 +22,14 @@ namespace OC\Files\Cache; +use OC\Hooks\BasicEmitter; + /** * Propagates changes in etag and mtime up the filesystem tree * * @package OC\Files\Cache */ -class ChangePropagator { +class ChangePropagator extends BasicEmitter { /** * @var string[] */ @@ -75,6 +77,7 @@ public function propagateChanges($time = null) { $cache = $storage->getCache(); $entry = $cache->get($internalPath); $cache->update($entry['fileid'], array('mtime' => max($time, $entry['mtime']), 'etag' => $storage->getETag($internalPath))); + $this->emit('\OC\Files', 'propagate', [$parent, $entry]); } } } diff --git a/lib/private/files/cache/updater.php b/lib/private/files/cache/updater.php index 14aecf01220a..66345350168a 100644 --- a/lib/private/files/cache/updater.php +++ b/lib/private/files/cache/updater.php @@ -60,6 +60,10 @@ public function enable() { $this->enabled = true; } + public function getPropagator() { + return $this->propagator; + } + public function propagate($path, $time = null) { if (Scanner::isPartialFile($path)) { return; From 518d5aadf51318886481696e4308fcc39684b508 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 9 Mar 2015 16:15:29 +0100 Subject: [PATCH 03/20] Allow getting *all* share entries owned by a user --- apps/files_sharing/tests/permissions.php | 4 +++- lib/private/share/share.php | 12 ++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/apps/files_sharing/tests/permissions.php b/apps/files_sharing/tests/permissions.php index 92703f4a9050..91f0347163a5 100644 --- a/apps/files_sharing/tests/permissions.php +++ b/apps/files_sharing/tests/permissions.php @@ -103,7 +103,9 @@ protected function setUp() { } protected function tearDown() { - $this->sharedCache->clear(); + if ($this->sharedCache) { + $this->sharedCache->clear(); + } self::loginHelper(self::TEST_FILES_SHARING_API_USER1); diff --git a/lib/private/share/share.php b/lib/private/share/share.php index 227a3d5a411e..63bba06e67c9 100644 --- a/lib/private/share/share.php +++ b/lib/private/share/share.php @@ -2518,4 +2518,16 @@ public static function enforcePassword(IConfig $config) { $enforcePassword = $config->getAppValue('core', 'shareapi_enforce_links_password', 'no'); return ($enforcePassword === "yes") ? true : false; } + /** + * Get all share entries, including non-unique group items + * + * @param string $owner + * @return array + */ + public static function getAllSharesForOwner($owner) { + $query = 'SELECT * FROM `*PREFIX*share` WHERE `uid_owner` = ?'; + $result = \OC::$server->getDatabaseConnection()->executeQuery($query, [$owner]); + return $result->fetchAll(); + } + } From 30ad56813a16908e3862c353256f2a6d0f05fe3a Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 9 Mar 2015 16:20:18 +0100 Subject: [PATCH 04/20] propagate etags for all user of a share --- apps/files_sharing/appinfo/app.php | 37 +++--- apps/files_sharing/appinfo/application.php | 51 ++++++++ apps/files_sharing/appinfo/routes.php | 3 - apps/files_sharing/lib/helper.php | 2 - apps/files_sharing/lib/mountprovider.php | 70 +++++++++++ .../lib/propagation/changewatcher.php | 56 +++++++++ .../lib/propagation/propagationmanager.php | 113 ++++++++++++++++++ .../lib/propagation/recipientpropagator.php | 108 +++++++++++++++++ apps/files_sharing/lib/sharedmount.php | 18 +++ apps/files_sharing/lib/sharedstorage.php | 50 +++----- apps/files_sharing/lib/updater.php | 39 ------ apps/files_sharing/tests/propagation.php | 48 ++++++++ apps/files_sharing/tests/testcase.php | 7 +- lib/private/share/share.php | 12 ++ 14 files changed, 521 insertions(+), 93 deletions(-) create mode 100644 apps/files_sharing/appinfo/application.php create mode 100644 apps/files_sharing/lib/mountprovider.php create mode 100644 apps/files_sharing/lib/propagation/changewatcher.php create mode 100644 apps/files_sharing/lib/propagation/propagationmanager.php create mode 100644 apps/files_sharing/lib/propagation/recipientpropagator.php diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php index d009fbca3b9b..19d3b6cd9cc9 100644 --- a/apps/files_sharing/appinfo/app.php +++ b/apps/files_sharing/appinfo/app.php @@ -25,31 +25,38 @@ * along with this program. If not, see * */ + +namespace OCA\Files_Sharing\Appinfo; + $l = \OC::$server->getL10N('files_sharing'); -OC::$CLASSPATH['OC_Share_Backend_File'] = 'files_sharing/lib/share/file.php'; -OC::$CLASSPATH['OC_Share_Backend_Folder'] = 'files_sharing/lib/share/folder.php'; -OC::$CLASSPATH['OC\Files\Storage\Shared'] = 'files_sharing/lib/sharedstorage.php'; -OC::$CLASSPATH['OC\Files\Cache\SharedScanner'] = 'files_sharing/lib/scanner.php'; -OC::$CLASSPATH['OC\Files\Cache\Shared_Cache'] = 'files_sharing/lib/cache.php'; -OC::$CLASSPATH['OC\Files\Cache\Shared_Permissions'] = 'files_sharing/lib/permissions.php'; -OC::$CLASSPATH['OC\Files\Cache\Shared_Updater'] = 'files_sharing/lib/updater.php'; -OC::$CLASSPATH['OC\Files\Cache\Shared_Watcher'] = 'files_sharing/lib/watcher.php'; -OC::$CLASSPATH['OCA\Files\Share\Maintainer'] = 'files_sharing/lib/maintainer.php'; -OC::$CLASSPATH['OCA\Files\Share\Proxy'] = 'files_sharing/lib/proxy.php'; +\OC::$CLASSPATH['OC_Share_Backend_File'] = 'files_sharing/lib/share/file.php'; +\OC::$CLASSPATH['OC_Share_Backend_Folder'] = 'files_sharing/lib/share/folder.php'; +\OC::$CLASSPATH['OC\Files\Storage\Shared'] = 'files_sharing/lib/sharedstorage.php'; +\OC::$CLASSPATH['OC\Files\Cache\SharedScanner'] = 'files_sharing/lib/scanner.php'; +\OC::$CLASSPATH['OC\Files\Cache\Shared_Cache'] = 'files_sharing/lib/cache.php'; +\OC::$CLASSPATH['OC\Files\Cache\Shared_Permissions'] = 'files_sharing/lib/permissions.php'; +\OC::$CLASSPATH['OC\Files\Cache\Shared_Updater'] = 'files_sharing/lib/updater.php'; +\OC::$CLASSPATH['OC\Files\Cache\Shared_Watcher'] = 'files_sharing/lib/watcher.php'; +\OC::$CLASSPATH['OCA\Files\Share\Maintainer'] = 'files_sharing/lib/maintainer.php'; +\OC::$CLASSPATH['OCA\Files\Share\Proxy'] = 'files_sharing/lib/proxy.php'; // Exceptions -OC::$CLASSPATH['OCA\Files_Sharing\Exceptions\BrokenPath'] = 'files_sharing/lib/exceptions.php'; +\OC::$CLASSPATH['OCA\Files_Sharing\Exceptions\BrokenPath'] = 'files_sharing/lib/exceptions.php'; + +$application = new Application(); +$application->registerMountProviders(); +$application->setupPropagation(); \OCP\App::registerAdmin('files_sharing', 'settings-admin'); \OCA\Files_Sharing\Helper::registerHooks(); -OCP\Share::registerBackend('file', 'OC_Share_Backend_File'); -OCP\Share::registerBackend('folder', 'OC_Share_Backend_Folder', 'file'); +\OCP\Share::registerBackend('file', 'OC_Share_Backend_File'); +\OCP\Share::registerBackend('folder', 'OC_Share_Backend_Folder', 'file'); -OCP\Util::addScript('files_sharing', 'share'); -OCP\Util::addScript('files_sharing', 'external'); +\OCP\Util::addScript('files_sharing', 'share'); +\OCP\Util::addScript('files_sharing', 'external'); // FIXME: registering a job here will cause additional useless SQL queries // when the route is not cron.php, needs a better way diff --git a/apps/files_sharing/appinfo/application.php b/apps/files_sharing/appinfo/application.php new file mode 100644 index 000000000000..6848c9e8363a --- /dev/null +++ b/apps/files_sharing/appinfo/application.php @@ -0,0 +1,51 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OCA\Files_Sharing\Appinfo; + +use OCA\Files_Sharing\MountProvider; +use OCA\Files_Sharing\Propagation\PropagationManager; +use OCP\AppFramework\App; +use \OCP\IContainer; + +class Application extends App { + public function __construct(array $urlParams = array()) { + parent::__construct('files_sharing', $urlParams); + $container = $this->getContainer(); + + $container->registerService('MountProvider', function (IContainer $c) { + /** @var \OCP\IServerContainer $server */ + $server = $c->query('ServerContainer'); + return new MountProvider( + $server->getConfig(), + $c->query('PropagationManager') + ); + }); + + $container->registerService('PropagationManager', function (IContainer $c) { + /** @var \OCP\IServerContainer $server */ + $server = $c->query('ServerContainer'); + return new PropagationManager( + $server->getUserSession(), + $server->getConfig() + ); + }); + } + + public function registerMountProviders() { + /** @var \OCP\IServerContainer $server */ + $server = $this->getContainer()->query('ServerContainer'); + $mountProviderCollection = $server->getMountProviderCollection(); + $mountProviderCollection->registerProvider($this->getContainer()->query('MountProvider')); + } + + public function setupPropagation() { + $propagationManager = $this->getContainer()->query('PropagationManager'); + \OCP\Util::connectHook('OC_Filesystem', 'setup', $propagationManager, 'globalSetup'); + } +} diff --git a/apps/files_sharing/appinfo/routes.php b/apps/files_sharing/appinfo/routes.php index 9ac3a1f731ab..db4566eb6126 100644 --- a/apps/files_sharing/appinfo/routes.php +++ b/apps/files_sharing/appinfo/routes.php @@ -25,9 +25,6 @@ */ namespace OCA\Files_Sharing\AppInfo; -use OCA\Files_Sharing\Application; -use OCP\API; - $application = new Application(); $application->registerRoutes($this, [ 'resources' => [ diff --git a/apps/files_sharing/lib/helper.php b/apps/files_sharing/lib/helper.php index 5b5525e244fa..05b0352ca1ff 100644 --- a/apps/files_sharing/lib/helper.php +++ b/apps/files_sharing/lib/helper.php @@ -30,9 +30,7 @@ class Helper { public static function registerHooks() { - \OCP\Util::connectHook('OC_Filesystem', 'setup', '\OC\Files\Storage\Shared', 'setup'); \OCP\Util::connectHook('OC_Filesystem', 'setup', '\OCA\Files_Sharing\External\Manager', 'setup'); - \OCP\Util::connectHook('OC_Filesystem', 'post_write', '\OC\Files\Cache\Shared_Updater', 'writeHook'); \OCP\Util::connectHook('OC_Filesystem', 'delete', '\OC\Files\Cache\Shared_Updater', 'deleteHook'); \OCP\Util::connectHook('OC_Filesystem', 'post_rename', '\OC\Files\Cache\Shared_Updater', 'renameHook'); \OCP\Util::connectHook('OC_Filesystem', 'post_delete', '\OCA\Files_Sharing\Hooks', 'unshareChildren'); diff --git a/apps/files_sharing/lib/mountprovider.php b/apps/files_sharing/lib/mountprovider.php new file mode 100644 index 000000000000..0ba44ec7a95f --- /dev/null +++ b/apps/files_sharing/lib/mountprovider.php @@ -0,0 +1,70 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OCA\Files_Sharing; + +use OCA\Files_Sharing\Propagation\PropagationManager; +use OCP\Files\Config\IMountProvider; +use OCP\Files\Storage\IStorageFactory; +use OCP\IConfig; +use OCP\IUser; + +class MountProvider implements IMountProvider { + /** + * @var \OCP\IConfig + */ + protected $config; + + /** + * @var \OCA\Files_Sharing\Propagation\PropagationManager + */ + protected $propagationManager; + + /** + * @param \OCP\IConfig $config + * @param \OCA\Files_Sharing\Propagation\PropagationManager $propagationManager + */ + function __construct(IConfig $config, PropagationManager $propagationManager) { + $this->config = $config; + $this->propagationManager = $propagationManager; + } + + + /** + * Get all mountpoints applicable for the user + * + * @param \OCP\IUser $user + * @param \OCP\Files\Storage\IStorageFactory $storageFactory + * @return \OCP\Files\Mount\IMountPoint[] + */ + public function getMountsForUser(IUser $user, IStorageFactory $storageFactory) { + $shares = \OCP\Share::getItemsSharedWithUser('file', $user->getUID()); + $propagator = $this->propagationManager->getSharePropagator($user->getUID()); + $propagator->propagateDirtyMountPoints($shares); + $shares = array_filter($shares, function ($share) { + return $share['permissions'] > 0; + }); + return array_map(function ($share) use ($user, $storageFactory) { + // for updating etags for the share owner when we make changes to this share. + $ownerPropagator = $this->propagationManager->getChangePropagator($share['uid_owner']); + + // for updating our etags when changes are made to the share from the owners side (probably indirectly by us trough another share) + $this->propagationManager->listenToOwnerChanges($share['uid_owner'], $user->getUID()); + return new SharedMount( + '\OC\Files\Storage\Shared', + '/' . $user->getUID() . '/' . $share['file_target'], + array( + 'propagator' => $ownerPropagator, + 'share' => $share, + 'user' => $user->getUID() + ), + $storageFactory + ); + }, $shares); + } +} diff --git a/apps/files_sharing/lib/propagation/changewatcher.php b/apps/files_sharing/lib/propagation/changewatcher.php new file mode 100644 index 000000000000..fa5208b498bb --- /dev/null +++ b/apps/files_sharing/lib/propagation/changewatcher.php @@ -0,0 +1,56 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OCA\Files_Sharing\Propagation; + +use OC\Files\Cache\ChangePropagator; +use OC\Files\View; +use OCA\Files_Sharing\SharedMount; + +/** + * Watch for changes made in a shared mount and propagate the changes to the share owner + */ +class ChangeWatcher { + /** + * The user view for the logged in user + * + * @var \OC\Files\View + */ + private $baseView; + + function __construct(View $baseView) { + $this->baseView = $baseView; + } + + + public function writeHook($params) { + $path = $params['path']; + $fullPath = $this->baseView->getAbsolutePath($path); + $mount = $this->baseView->getMount($path); + if ($mount instanceof SharedMount) { + $this->propagateForOwner($mount->getShare(), $mount->getInternalPath($fullPath), $mount->getOwnerPropagator()); + } + } + + /** + * @param array $share + * @param string $internalPath + * @param \OC\Files\Cache\ChangePropagator $propagator + */ + private function propagateForOwner($share, $internalPath, ChangePropagator $propagator) { + // note that we have already set up the filesystem for the owner when mounting the share + $view = new View('/' . $share['uid_owner'] . '/files'); + + $shareRootPath = $view->getPath($share['item_source']); + if ($shareRootPath) { + $path = $shareRootPath . '/' . $internalPath; + $propagator->addChange($path); + $propagator->propagateChanges(); + } + } +} diff --git a/apps/files_sharing/lib/propagation/propagationmanager.php b/apps/files_sharing/lib/propagation/propagationmanager.php new file mode 100644 index 000000000000..bf530d369b23 --- /dev/null +++ b/apps/files_sharing/lib/propagation/propagationmanager.php @@ -0,0 +1,113 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OCA\Files_Sharing\Propagation; + +use OC\Files\Filesystem; +use OC\Files\View; +use OCP\IConfig; +use OCP\IUserSession; + + +/** + * Keep track of all change and share propagators by owner + */ +class PropagationManager { + /** + * @var \OCP\IUserSession + */ + private $userSession; + + /** + * @var \OCP\IConfig + */ + private $config; + + /** + * Change propagators for share owner + * + * @var \OC\Files\Cache\ChangePropagator[] + */ + private $changePropagators = []; + + /** + * Recipient propagators + * + * @var \OCA\Files_Sharing\Propagation\RecipientPropagator[] + */ + private $sharePropagators = []; + + private $globalSetupDone = false; + + function __construct(IUserSession $userSession, IConfig $config) { + $this->userSession = $userSession; + $this->config = $config; + } + + /** + * @param string $user + * @return \OC\Files\Cache\ChangePropagator + */ + public function getChangePropagator($user) { + $activeUser = $this->userSession->getUser(); + + // for the local user we want to propagator from the active view, not any cached one + if ($activeUser && $activeUser->getUID() === $user && Filesystem::getView() instanceof View) { + // it's important that we take the existing propagator here to make sure we can listen to external changes + $this->changePropagators[$user] = Filesystem::getView()->getUpdater()->getPropagator(); + } + if (isset($this->changePropagators[$user])) { + return $this->changePropagators[$user]; + } + $view = new View('/' . $user . '/files'); + $this->changePropagators[$user] = $view->getUpdater()->getPropagator(); + return $this->changePropagators[$user]; + } + + /** + * @param string $user + * @return \OCA\Files_Sharing\Propagation\RecipientPropagator + */ + public function getSharePropagator($user) { + if (isset($this->sharePropagators[$user])) { + return $this->sharePropagators[$user]; + } + $this->sharePropagators[$user] = new RecipientPropagator($user, $this->getChangePropagator($user), $this->config); + return $this->sharePropagators[$user]; + } + + /** + * Attach the propagator to the change propagator of a user to listen to changes made to files shared by the user + * + * @param string $shareOwner + * @param string $user + */ + public function listenToOwnerChanges($shareOwner, $user) { + $sharePropagator = $this->getSharePropagator($user); + $ownerPropagator = $this->getChangePropagator($shareOwner); + $sharePropagator->attachToPropagator($ownerPropagator, $shareOwner); + } + + /** + * To be called from setupFS trough a hook + * + * Sets up listening to changes made to shares owned by the current user + */ + public function globalSetup() { + $user = $this->userSession->getUser(); + if (!$user) { + return; + } + $watcher = new ChangeWatcher(Filesystem::getView()); + + // for marking shares owned by the active user as dirty when a file inside them changes + $this->listenToOwnerChanges($user->getUID(), $user->getUID()); + \OC_Hook::connect('OC_Filesystem', 'write', $watcher, 'writeHook'); + \OC_Hook::connect('OC_Filesystem', 'delete', $watcher, 'writeHook'); + } +} diff --git a/apps/files_sharing/lib/propagation/recipientpropagator.php b/apps/files_sharing/lib/propagation/recipientpropagator.php new file mode 100644 index 000000000000..da71612fd4d7 --- /dev/null +++ b/apps/files_sharing/lib/propagation/recipientpropagator.php @@ -0,0 +1,108 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OCA\Files_Sharing\Propagation; + +use OC\Files\Cache\ChangePropagator; +use OC\Share\Share; + +/** + * Propagate etags for share recipients + */ +class RecipientPropagator { + /** + * @var string + */ + protected $userId; + + /** + * @var \OC\Files\Cache\ChangePropagator + */ + protected $changePropagator; + + /** + * @var \OCP\IConfig + */ + protected $config; + + /** + * @param string $userId current user, must match the propagator's + * user + * @param \OC\Files\Cache\ChangePropagator $changePropagator change propagator + * initialized with a view for $user + * @param \OCP\IConfig $config + */ + public function __construct($userId, $changePropagator, $config) { + $this->userId = $userId; + $this->changePropagator = $changePropagator; + $this->config = $config; + } + + /** + * Propagate the etag changes for all shares marked as dirty and mark the shares as clean + * + * @param array $shares the shares for the users + * @param int $time + */ + public function propagateDirtyMountPoints(array $shares, $time = null) { + if ($time === null) { + $time = time(); + } + $dirtyShares = $this->getDirtyShares($shares); + foreach ($dirtyShares as $share) { + $this->changePropagator->addChange($share['file_target']); + } + if (count($dirtyShares)) { + $this->config->setUserValue($this->userId, 'files_sharing', 'last_propagate', $time); + $this->changePropagator->propagateChanges($time); + } + } + + /** + * Get all shares we need to update the etag for + * + * @param array $shares the shares for the users + * @return string[] + */ + protected function getDirtyShares($shares) { + $dirty = []; + $userTime = $this->config->getUserValue($this->userId, 'files_sharing', 'last_propagate', 0); + foreach ($shares as $share) { + $updateTime = $this->config->getAppValue('files_sharing', $share['id'], 0); + if ($updateTime >= $userTime) { + $dirty[] = $share; + } + } + return $dirty; + } + + /** + * @param array $share + * @param int $time + */ + public function markDirty($share, $time = null) { + if ($time === null) { + $time = time(); + } + $this->config->setAppValue('files_sharing', $share['id'], $time); + } + /** + * Listen on the propagator for updates made to shares owned by a user + * + * @param \OC\Files\Cache\ChangePropagator $propagator + * @param string $owner + */ + public function attachToPropagator(ChangePropagator $propagator, $owner) { + $propagator->listen('\OC\Files', 'propagate', function ($path, $entry) use ($owner) { + $shares = Share::getAllSharesForFileId($entry['fileid']); + foreach ($shares as $share) { + $this->markDirty($share, time()); + } + }); + } +} diff --git a/apps/files_sharing/lib/sharedmount.php b/apps/files_sharing/lib/sharedmount.php index da00549541b6..fbf8d05c1b23 100644 --- a/apps/files_sharing/lib/sharedmount.php +++ b/apps/files_sharing/lib/sharedmount.php @@ -35,8 +35,14 @@ class SharedMount extends MountPoint implements MoveableMount { */ protected $storage = null; + /** + * @var \OC\Files\Cache\ChangePropagator + */ + protected $ownerPropagator; + public function __construct($storage, $mountpoint, $arguments = null, $loader = null) { // first update the mount point before creating the parent + $this->ownerPropagator = $arguments['propagator']; $newMountPoint = $this->verifyMountPoint($arguments['share'], $arguments['user']); $absMountPoint = '/' . $arguments['user'] . '/files' . $newMountPoint; parent::__construct($storage, $absMountPoint, $arguments, $loader); @@ -174,4 +180,16 @@ public function removeMount() { return $result; } + + public function getShare() { + $this->getStorage(); //ensure it exists + return $this->storage->getShare(); + } + + /** + * @return \OC\Files\Cache\ChangePropagator + */ + public function getOwnerPropagator() { + return $this->ownerPropagator; + } } diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index 8c2f03b14197..bc01465cb4ab 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -28,8 +28,12 @@ */ namespace OC\Files\Storage; + +use OC\Files\Cache\ChangePropagator; use OC\Files\Filesystem; +use OC\Files\View; use OCA\Files_Sharing\ISharedStorage; +use OCA\Files_Sharing\Propagator; use OCA\Files_Sharing\SharedMount; /** @@ -47,6 +51,7 @@ public function __construct($arguments) { /** * get id of the mount point + * * @return string */ public function getId() { @@ -55,14 +60,16 @@ public function getId() { /** * get file cache of the shared item source + * * @return int */ public function getSourceId() { - return (int) $this->share['file_source']; + return (int)$this->share['file_source']; } /** * Get the source file path, permissions, and owner for a shared file + * * @param string $target Shared target file path * @return Returns array with the keys path, permissions, and owner or false if not found */ @@ -86,6 +93,7 @@ public function getFile($target) { /** * Get the source file path for a shared file + * * @param string $target Shared target file path * @return string|false source file path or false if not found */ @@ -109,6 +117,7 @@ public function getSourcePath($target) { /** * Get the permissions granted for a shared file + * * @param string $target Shared target file path * @return int CRUDS permissions granted */ @@ -138,13 +147,14 @@ public function mkdir($path) { /** * Delete the directory if DELETE permission is granted + * * @param string $path * @return boolean */ public function rmdir($path) { // never delete a share mount point - if(empty($path)) { + if (empty($path)) { return false; } @@ -277,6 +287,7 @@ public function file_put_contents($path, $data) { /** * Delete the file if DELETE permission is granted + * * @param string $path * @return boolean */ @@ -426,37 +437,6 @@ public function touch($path, $mtime = null) { return false; } - public static function setup($options) { - $user = $options['user']; - $shares = \OCP\Share::getItemsSharedWithUser('file', $user); - $manager = Filesystem::getMountManager(); - $loader = Filesystem::getLoader(); - if ( - !isset(self::$isInitialized[$user]) && ( - !\OCP\User::isLoggedIn() - || \OCP\User::getUser() != $options['user'] - || $shares - ) - ) { - foreach ($shares as $share) { - // don't mount shares where we have no permissions - if ($share['permissions'] > 0) { - $mount = new SharedMount( - '\OC\Files\Storage\Shared', - $options['user_dir'] . '/' . $share['file_target'], - array( - 'share' => $share, - 'user' => $user - ), - $loader - ); - $manager->addMount($mount); - } - } - } - self::$isInitialized[$user] = true; - } - /** * return mount point of share, relative to data/user/files * @@ -476,6 +456,7 @@ public function getShareType() { /** * does the group share already has a user specific unique name + * * @return bool */ public function uniqueNameSet() { @@ -493,6 +474,7 @@ public function setUniqueName() { /** * get share ID + * * @return integer unique share ID */ public function getShareId() { @@ -501,6 +483,7 @@ public function getShareId() { /** * get the user who shared the file + * * @return string */ public function getSharedFrom() { @@ -516,6 +499,7 @@ public function getShare() { /** * return share type, can be "file" or "folder" + * * @return string */ public function getItemType() { diff --git a/apps/files_sharing/lib/updater.php b/apps/files_sharing/lib/updater.php index 322c031f2f1a..45cf662d1484 100644 --- a/apps/files_sharing/lib/updater.php +++ b/apps/files_sharing/lib/updater.php @@ -49,48 +49,10 @@ static private function correctUsersFolder($user, $path) { } } - /** - * Correct the parent folders' ETags for all users shared the file at $target - * - * @param string $target - */ - static public function correctFolders($target) { - - // ignore part files - if (pathinfo($target, PATHINFO_EXTENSION) === 'part') { - return false; - } - - // Correct Shared folders of other users shared with - $shares = \OCA\Files_Sharing\Helper::getSharesFromItem($target); - - foreach ($shares as $share) { - if ((int)$share['share_type'] === \OCP\Share::SHARE_TYPE_USER) { - self::correctUsersFolder($share['share_with'], $share['file_target']); - } elseif ((int)$share['share_type'] === \OCP\Share::SHARE_TYPE_GROUP) { - $users = \OC_Group::usersInGroup($share['share_with']); - foreach ($users as $user) { - self::correctUsersFolder($user, $share['file_target']); - } - } else { //unique name for group share - self::correctUsersFolder($share['share_with'], $share['file_target']); - } - } - } - - /** - * @param array $params - */ - static public function writeHook($params) { - self::correctFolders($params['path']); - } - /** * @param array $params */ static public function renameHook($params) { - self::correctFolders($params['newpath']); - self::correctFolders(pathinfo($params['oldpath'], PATHINFO_DIRNAME)); self::renameChildren($params['oldpath'], $params['newpath']); } @@ -99,7 +61,6 @@ static public function renameHook($params) { */ static public function deleteHook($params) { $path = $params['path']; - self::correctFolders($path); } /** diff --git a/apps/files_sharing/tests/propagation.php b/apps/files_sharing/tests/propagation.php index 1949f7808a4d..c42dccce88fc 100644 --- a/apps/files_sharing/tests/propagation.php +++ b/apps/files_sharing/tests/propagation.php @@ -242,4 +242,52 @@ public function testEtagPropagationOtherShareSubFolder() { $newRootInfo = $view1->getFileInfo(''); $this->assertNotEquals($rootInfo->getEtag(), $newRootInfo->getEtag()); } + + public function testSizePropagationWhenOwnerChangesFile() { + /** + * @var \OC\Files\View $recipientView + * @var \OC\Files\View $ownerView + */ + list($recipientView, $ownerView) = $this->setupViews(); + $sharedFolderInfo = $ownerView->getFileInfo('/sharedfolder', false); + \OCP\Share::shareItem('folder', $sharedFolderInfo->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER1, 31); + $ownerRootInfo = $ownerView->getFileInfo('', false); + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); + $this->assertTrue($recipientView->file_exists('/sharedfolder/subfolder/foo.txt')); + $recipientRootInfo = $recipientView->getFileInfo('', false); + // when file changed as owner + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER2); + $ownerView->file_put_contents('/sharedfolder/subfolder/foo.txt', 'foobar'); + // size of recipient's root stays the same + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); + $newRecipientRootInfo = $recipientView->getFileInfo('', false); + $this->assertEquals($recipientRootInfo->getSize(), $newRecipientRootInfo->getSize()); + // size of owner's root increases + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER2); + $newOwnerRootInfo = $ownerView->getFileInfo('', false); + $this->assertEquals($ownerRootInfo->getSize() + 3, $newOwnerRootInfo->getSize()); + } + + public function testSizePropagationWhenRecipientChangesFile() { + /** + * @var \OC\Files\View $recipientView + * @var \OC\Files\View $ownerView + */ + list($recipientView, $ownerView) = $this->setupViews(); + $sharedFolderInfo = $ownerView->getFileInfo('/sharedfolder', false); + \OCP\Share::shareItem('folder', $sharedFolderInfo->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER1, 31); + $ownerRootInfo = $ownerView->getFileInfo('', false); + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); + $this->assertTrue($recipientView->file_exists('/sharedfolder/subfolder/foo.txt')); + $recipientRootInfo = $recipientView->getFileInfo('', false); + // when file changed as recipient + $recipientView->file_put_contents('/sharedfolder/subfolder/foo.txt', 'foobar'); + // size of recipient's root stays the same + $newRecipientRootInfo = $recipientView->getFileInfo('', false); + $this->assertEquals($recipientRootInfo->getSize(), $newRecipientRootInfo->getSize()); + // size of owner's root increases + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER2); + $newOwnerRootInfo = $ownerView->getFileInfo('', false); + $this->assertEquals($ownerRootInfo->getSize() + 3, $newOwnerRootInfo->getSize()); + } } diff --git a/apps/files_sharing/tests/testcase.php b/apps/files_sharing/tests/testcase.php index b9f8658a69ec..5cae54fef6cd 100644 --- a/apps/files_sharing/tests/testcase.php +++ b/apps/files_sharing/tests/testcase.php @@ -31,6 +31,7 @@ use OC\Files\Filesystem; use OCA\Files\Share; +use OCA\Files_Sharing\Appinfo\Application; /** * Class Test_Files_Sharing_Base @@ -57,6 +58,10 @@ abstract class TestCase extends \Test\TestCase { public static function setUpBeforeClass() { parent::setUpBeforeClass(); + $application = new Application(); + $application->registerMountProviders(); + $application->setupPropagation(); + // reset backend \OC_User::clearBackends(); \OC_Group::clearBackends(); @@ -64,7 +69,6 @@ public static function setUpBeforeClass() { // clear share hooks \OC_Hook::clear('OCP\\Share'); \OC::registerShareHooks(); - \OCP\Util::connectHook('OC_Filesystem', 'setup', '\OC\Files\Storage\Shared', 'setup'); // create users $backend = new \OC_User_Dummy(); @@ -147,6 +151,7 @@ protected static function loginHelper($user, $create = false, $password = false) \OC::$server->getUserSession()->setUser(null); \OC\Files\Filesystem::tearDown(); \OC::$server->getUserSession()->login($user, $password); + \OC_Util::setupFS($user); } diff --git a/lib/private/share/share.php b/lib/private/share/share.php index 63bba06e67c9..d18bdffc6976 100644 --- a/lib/private/share/share.php +++ b/lib/private/share/share.php @@ -2518,6 +2518,7 @@ public static function enforcePassword(IConfig $config) { $enforcePassword = $config->getAppValue('core', 'shareapi_enforce_links_password', 'no'); return ($enforcePassword === "yes") ? true : false; } + /** * Get all share entries, including non-unique group items * @@ -2530,4 +2531,15 @@ public static function getAllSharesForOwner($owner) { return $result->fetchAll(); } + /** + * Get all share entries, including non-unique group items for a file + * + * @param int $id + * @return array + */ + public static function getAllSharesForFileId($id) { + $query = 'SELECT * FROM `*PREFIX*share` WHERE `file_source` = ?'; + $result = \OC::$server->getDatabaseConnection()->executeQuery($query, [$id]); + return $result->fetchAll(); + } } From be55a9032380629756e35ea4cf13fd34a213db41 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 27 Mar 2015 13:44:40 +0100 Subject: [PATCH 05/20] dont use our now non existing hook --- apps/files_trashbin/tests/trashbin.php | 4 +++- apps/files_versions/tests/versions.php | 4 +++- tests/lib/files/etagtest.php | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/apps/files_trashbin/tests/trashbin.php b/apps/files_trashbin/tests/trashbin.php index bf6446389f1d..a2e1a9addf62 100644 --- a/apps/files_trashbin/tests/trashbin.php +++ b/apps/files_trashbin/tests/trashbin.php @@ -54,7 +54,9 @@ public static function setUpBeforeClass() { // clear share hooks \OC_Hook::clear('OCP\\Share'); \OC::registerShareHooks(); - \OCP\Util::connectHook('OC_Filesystem', 'setup', '\OC\Files\Storage\Shared', 'setup'); + $application = new \OCA\Files_Sharing\AppInfo\Application(); + $application->registerMountProviders(); + $application->setupPropagation(); //disable encryption \OC_App::disable('files_encryption'); diff --git a/apps/files_versions/tests/versions.php b/apps/files_versions/tests/versions.php index f8bfe38df4d9..ff088ffdad36 100644 --- a/apps/files_versions/tests/versions.php +++ b/apps/files_versions/tests/versions.php @@ -47,7 +47,9 @@ public static function setUpBeforeClass() { \OC_Hook::clear('OCP\\Share'); \OC::registerShareHooks(); \OCA\Files_Versions\Hooks::connectHooks(); - \OCP\Util::connectHook('OC_Filesystem', 'setup', '\OC\Files\Storage\Shared', 'setup'); + $application = new \OCA\Files_Sharing\AppInfo\Application(); + $application->registerMountProviders(); + $application->setupPropagation(); // create test user self::loginHelper(self::TEST_VERSIONS_USER2, true); diff --git a/tests/lib/files/etagtest.php b/tests/lib/files/etagtest.php index 055927652bc7..c3d364d68009 100644 --- a/tests/lib/files/etagtest.php +++ b/tests/lib/files/etagtest.php @@ -25,7 +25,9 @@ protected function setUp() { parent::setUp(); \OC_Hook::clear('OC_Filesystem', 'setup'); - \OCP\Util::connectHook('OC_Filesystem', 'setup', '\OC\Files\Storage\Shared', 'setup'); + $application = new \OCA\Files_Sharing\AppInfo\Application(); + $application->registerMountProviders(); + $application->setupPropagation(); \OCP\Share::registerBackend('file', 'OC_Share_Backend_File'); \OCP\Share::registerBackend('folder', 'OC_Share_Backend_Folder', 'file'); From 19486837b2bc135a8f083ff97d477eb5d6314d42 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 27 Mar 2015 14:57:56 +0100 Subject: [PATCH 06/20] adjust test --- apps/files_sharing/tests/cache.php | 4 +++- apps/files_sharing/tests/sharedstorage.php | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/files_sharing/tests/cache.php b/apps/files_sharing/tests/cache.php index 882c8afadc33..7e7e5ee26d5c 100644 --- a/apps/files_sharing/tests/cache.php +++ b/apps/files_sharing/tests/cache.php @@ -113,7 +113,9 @@ protected function setUp() { } protected function tearDown() { - $this->sharedCache->clear(); + if($this->sharedCache) { + $this->sharedCache->clear(); + } self::loginHelper(self::TEST_FILES_SHARING_API_USER1); diff --git a/apps/files_sharing/tests/sharedstorage.php b/apps/files_sharing/tests/sharedstorage.php index a1469a74687a..7c28d0431e11 100644 --- a/apps/files_sharing/tests/sharedstorage.php +++ b/apps/files_sharing/tests/sharedstorage.php @@ -368,7 +368,10 @@ function testMountSharesOtherUser() { self::loginHelper(self::TEST_FILES_SHARING_API_USER2); $this->assertTrue($rootView->file_exists('/' . self::TEST_FILES_SHARING_API_USER2 . '/files/' . $this->folder)); - OC_Hook::emit('OC_Filesystem', 'setup', array('user' => self::TEST_FILES_SHARING_API_USER3, 'user_dir' => \OC_User::getHome(self::TEST_FILES_SHARING_API_USER3))); + + $mountConfigManager = \OC::$server->getMountProviderCollection(); + $mounts = $mountConfigManager->getMountsForUser(\OC::$server->getUserManager()->get(self::TEST_FILES_SHARING_API_USER3)); + array_walk($mounts, array(\OC\Files\Filesystem::getMountManager(), 'addMount')); $this->assertTrue($rootView->file_exists('/' . self::TEST_FILES_SHARING_API_USER3 . '/files/' . $this->filename)); From b5c62cdad640ca9d0c2c184a61f9f0d683f22509 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 8 Apr 2015 13:53:22 +0200 Subject: [PATCH 07/20] remove duplicates from rebase --- apps/files_sharing/tests/propagation.php | 48 ------------------------ 1 file changed, 48 deletions(-) diff --git a/apps/files_sharing/tests/propagation.php b/apps/files_sharing/tests/propagation.php index c42dccce88fc..1949f7808a4d 100644 --- a/apps/files_sharing/tests/propagation.php +++ b/apps/files_sharing/tests/propagation.php @@ -242,52 +242,4 @@ public function testEtagPropagationOtherShareSubFolder() { $newRootInfo = $view1->getFileInfo(''); $this->assertNotEquals($rootInfo->getEtag(), $newRootInfo->getEtag()); } - - public function testSizePropagationWhenOwnerChangesFile() { - /** - * @var \OC\Files\View $recipientView - * @var \OC\Files\View $ownerView - */ - list($recipientView, $ownerView) = $this->setupViews(); - $sharedFolderInfo = $ownerView->getFileInfo('/sharedfolder', false); - \OCP\Share::shareItem('folder', $sharedFolderInfo->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER1, 31); - $ownerRootInfo = $ownerView->getFileInfo('', false); - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); - $this->assertTrue($recipientView->file_exists('/sharedfolder/subfolder/foo.txt')); - $recipientRootInfo = $recipientView->getFileInfo('', false); - // when file changed as owner - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER2); - $ownerView->file_put_contents('/sharedfolder/subfolder/foo.txt', 'foobar'); - // size of recipient's root stays the same - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); - $newRecipientRootInfo = $recipientView->getFileInfo('', false); - $this->assertEquals($recipientRootInfo->getSize(), $newRecipientRootInfo->getSize()); - // size of owner's root increases - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER2); - $newOwnerRootInfo = $ownerView->getFileInfo('', false); - $this->assertEquals($ownerRootInfo->getSize() + 3, $newOwnerRootInfo->getSize()); - } - - public function testSizePropagationWhenRecipientChangesFile() { - /** - * @var \OC\Files\View $recipientView - * @var \OC\Files\View $ownerView - */ - list($recipientView, $ownerView) = $this->setupViews(); - $sharedFolderInfo = $ownerView->getFileInfo('/sharedfolder', false); - \OCP\Share::shareItem('folder', $sharedFolderInfo->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER1, 31); - $ownerRootInfo = $ownerView->getFileInfo('', false); - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); - $this->assertTrue($recipientView->file_exists('/sharedfolder/subfolder/foo.txt')); - $recipientRootInfo = $recipientView->getFileInfo('', false); - // when file changed as recipient - $recipientView->file_put_contents('/sharedfolder/subfolder/foo.txt', 'foobar'); - // size of recipient's root stays the same - $newRecipientRootInfo = $recipientView->getFileInfo('', false); - $this->assertEquals($recipientRootInfo->getSize(), $newRecipientRootInfo->getSize()); - // size of owner's root increases - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER2); - $newOwnerRootInfo = $ownerView->getFileInfo('', false); - $this->assertEquals($ownerRootInfo->getSize() + 3, $newOwnerRootInfo->getSize()); - } } From d5434c9c4e005ff0407d6adddbc2a81dbedd7783 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 15 Apr 2015 15:31:30 +0200 Subject: [PATCH 08/20] more propagation tests --- apps/files_sharing/tests/etagpropagation.php | 338 +++++++++++++++++++ apps/files_sharing/tests/propagation.php | 245 -------------- apps/files_sharing/tests/sizepropagation.php | 89 +++++ apps/files_sharing/tests/testcase.php | 2 + 4 files changed, 429 insertions(+), 245 deletions(-) create mode 100644 apps/files_sharing/tests/etagpropagation.php delete mode 100644 apps/files_sharing/tests/propagation.php create mode 100644 apps/files_sharing/tests/sizepropagation.php diff --git a/apps/files_sharing/tests/etagpropagation.php b/apps/files_sharing/tests/etagpropagation.php new file mode 100644 index 000000000000..b515362c02dd --- /dev/null +++ b/apps/files_sharing/tests/etagpropagation.php @@ -0,0 +1,338 @@ + + * + * @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 + * + */ + +namespace OCA\Files_sharing\Tests; + +use OC\Files\Filesystem; +use OC\Files\View; + +class EtagPropagation extends TestCase { + /** + * @var \OC\Files\View + */ + private $rootView; + protected $fileIds = []; // [$user=>[$path=>$id]] + protected $fileEtags = []; // [$id=>$etag] + + protected function setUp() { + parent::setUp(); + $this->setUpShares(); + } + + /** + * "user1" is the admin who shares a folder "sub1/sub2/folder" with "user2" and "user3" + * "user2" receives the folder and puts it in "sub1/sub2/folder" + * "user3" receives the folder and puts it in "sub1/sub2/folder" + * "user2" reshares the subdir "sub1/sub2/folder/inside" with "user4" + * "user4" puts the received "inside" folder into "sub1/sub2/inside" (this is to check if it propagates across multiple subfolders) + */ + private function setUpShares() { + $this->fileIds[self::TEST_FILES_SHARING_API_USER1] = []; + $this->fileIds[self::TEST_FILES_SHARING_API_USER2] = []; + $this->fileIds[self::TEST_FILES_SHARING_API_USER3] = []; + $this->fileIds[self::TEST_FILES_SHARING_API_USER4] = []; + + $this->rootView = new View(''); + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); + $view1 = new View('/' . self::TEST_FILES_SHARING_API_USER1 . '/files'); + $view1->mkdir('/sub1/sub2/folder/inside'); + $view1->mkdir('/directReshare'); + $view1->mkdir('/sub1/sub2/folder/other'); + $view1->mkdir('/sub1/sub2/folder/other'); + $view1->file_put_contents('/sub1/sub2/folder/file.txt', 'foobar'); + $view1->file_put_contents('/sub1/sub2/folder/inside/file.txt', 'foobar'); + $folderInfo = $view1->getFileInfo('/sub1/sub2/folder'); + \OCP\Share::shareItem('folder', $folderInfo->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER2, 31); + \OCP\Share::shareItem('folder', $folderInfo->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER3, 31); + $folderInfo = $view1->getFileInfo('/directReshare'); + \OCP\Share::shareItem('folder', $folderInfo->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER2, 31); + $this->fileIds[self::TEST_FILES_SHARING_API_USER1][''] = $view1->getFileInfo('')->getId(); + $this->fileIds[self::TEST_FILES_SHARING_API_USER1]['sub1'] = $view1->getFileInfo('sub1')->getId(); + $this->fileIds[self::TEST_FILES_SHARING_API_USER1]['sub1/sub2'] = $view1->getFileInfo('sub1/sub2')->getId(); + + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER2); + $view2 = new View('/' . self::TEST_FILES_SHARING_API_USER2 . '/files'); + $view2->mkdir('/sub1/sub2'); + $view2->rename('/folder', '/sub1/sub2/folder'); + $insideInfo = $view2->getFileInfo('/sub1/sub2/folder/inside'); + \OCP\Share::shareItem('folder', $insideInfo->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER4, 31); + $folderInfo = $view2->getFileInfo('/directReshare'); + \OCP\Share::shareItem('folder', $folderInfo->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER4, 31); + $this->fileIds[self::TEST_FILES_SHARING_API_USER2][''] = $view2->getFileInfo('')->getId(); + $this->fileIds[self::TEST_FILES_SHARING_API_USER2]['sub1'] = $view2->getFileInfo('sub1')->getId(); + $this->fileIds[self::TEST_FILES_SHARING_API_USER2]['sub1/sub2'] = $view2->getFileInfo('sub1/sub2')->getId(); + + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER3); + $view3 = new View('/' . self::TEST_FILES_SHARING_API_USER3 . '/files'); + $view3->mkdir('/sub1/sub2'); + $view3->rename('/folder', '/sub1/sub2/folder'); + $this->fileIds[self::TEST_FILES_SHARING_API_USER3][''] = $view3->getFileInfo('')->getId(); + $this->fileIds[self::TEST_FILES_SHARING_API_USER3]['sub1'] = $view3->getFileInfo('sub1')->getId(); + $this->fileIds[self::TEST_FILES_SHARING_API_USER3]['sub1/sub2'] = $view3->getFileInfo('sub1/sub2')->getId(); + + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER4); + $view4 = new View('/' . self::TEST_FILES_SHARING_API_USER4 . '/files'); + $view4->mkdir('/sub1/sub2'); + $view4->rename('/inside', '/sub1/sub2/inside'); + $this->fileIds[self::TEST_FILES_SHARING_API_USER4][''] = $view4->getFileInfo('')->getId(); + $this->fileIds[self::TEST_FILES_SHARING_API_USER4]['sub1'] = $view4->getFileInfo('sub1')->getId(); + $this->fileIds[self::TEST_FILES_SHARING_API_USER4]['sub1/sub2'] = $view4->getFileInfo('sub1/sub2')->getId(); + + foreach ($this->fileIds as $user => $ids) { + $this->loginAsUser($user); + foreach ($ids as $id) { + $path = $this->rootView->getPath($id); + $this->fileEtags[$id] = $this->rootView->getFileInfo($path)->getEtag(); + } + } + } + + /** + * @param string[] $users + * @param string $subPath + */ + private function assertEtagsChanged($users, $subPath = '') { + $oldUser = \OC::$server->getUserSession()->getUser(); + foreach ($users as $user) { + $this->loginAsUser($user); + $id = $this->fileIds[$user][$subPath]; + $path = $this->rootView->getPath($id); + $etag = $this->rootView->getFileInfo($path)->getEtag(); + $this->assertNotEquals($this->fileEtags[$id], $etag, 'Failed asserting that the etag for "' . $subPath . '" of user ' . $user . ' has changed'); + $this->fileEtags[$id] = $etag; + } + $this->loginAsUser($oldUser->getUID()); + } + + /** + * @param string[] $users + * @param string $subPath + */ + private function assertEtagsNotChanged($users, $subPath = '') { + $oldUser = \OC::$server->getUserSession()->getUser(); + foreach ($users as $user) { + $this->loginAsUser($user); + $id = $this->fileIds[$user][$subPath]; + $path = $this->rootView->getPath($id); + $etag = $this->rootView->getFileInfo($path)->getEtag(); + $this->assertEquals($this->fileEtags[$id], $etag, 'Failed asserting that the etag for "' . $subPath . '" of user ' . $user . ' has not changed'); + $this->fileEtags[$id] = $etag; + } + $this->loginAsUser($oldUser->getUID()); + } + + /** + * Assert that the etags for the root, /sub1 and /sub1/sub2 have changed + * + * @param string[] $users + */ + private function assertEtagsForFoldersChanged($users) { + $this->assertEtagsChanged($users); + + $this->assertEtagsChanged($users, 'sub1'); + $this->assertEtagsChanged($users, 'sub1/sub2'); + } + + private function assertAllUnchaged() { + $users = [self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER2, + self::TEST_FILES_SHARING_API_USER3, self::TEST_FILES_SHARING_API_USER4]; + $this->assertEtagsNotChanged($users); + } + + public function testOwnerWritesToShare() { + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); + Filesystem::file_put_contents('/sub1/sub2/folder/asd.txt', 'bar'); + $this->assertEtagsNotChanged([self::TEST_FILES_SHARING_API_USER4]); + $this->assertEtagsForFoldersChanged([self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER2, + self::TEST_FILES_SHARING_API_USER3]); + + $this->assertAllUnchaged(); + } + + public function testOwnerWritesToShareWithReshare() { + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); + Filesystem::file_put_contents('/sub1/sub2/folder/inside/bar.txt', 'bar'); + $this->assertEtagsForFoldersChanged([self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER2, + self::TEST_FILES_SHARING_API_USER3, self::TEST_FILES_SHARING_API_USER4]); + + $this->assertAllUnchaged(); + } + + public function testOwnerRenameInShare() { + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); + $this->assertEtagsNotChanged([self::TEST_FILES_SHARING_API_USER4]); + Filesystem::rename('/sub1/sub2/folder/file.txt', '/sub1/sub2/folder/renamed.txt'); + $this->assertEtagsForFoldersChanged([self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER2, + self::TEST_FILES_SHARING_API_USER3]); + + $this->assertAllUnchaged(); + } + + public function testOwnerRenameInReShare() { + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); + Filesystem::rename('/sub1/sub2/folder/inside/file.txt', '/sub1/sub2/folder/inside/renamed.txt'); + $this->assertEtagsForFoldersChanged([self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER2, + self::TEST_FILES_SHARING_API_USER3, self::TEST_FILES_SHARING_API_USER4]); + + $this->assertAllUnchaged(); + } + + public function testOwnerRenameIntoReShare() { + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); + Filesystem::rename('/sub1/sub2/folder/file.txt', '/sub1/sub2/folder/inside/renamed.txt'); + $this->assertEtagsForFoldersChanged([self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER2, + self::TEST_FILES_SHARING_API_USER3, self::TEST_FILES_SHARING_API_USER4]); + + $this->assertAllUnchaged(); + } + + public function testOwnerRenameOutOfReShare() { + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); + Filesystem::rename('/sub1/sub2/folder/inside/file.txt', '/sub1/sub2/folder/renamed.txt'); + $this->assertEtagsForFoldersChanged([self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER2, + self::TEST_FILES_SHARING_API_USER3, self::TEST_FILES_SHARING_API_USER4]); + + $this->assertAllUnchaged(); + } + + public function testOwnerDeleteInShare() { + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); + Filesystem::unlink('/sub1/sub2/folder/file.txt'); + $this->assertEtagsNotChanged([self::TEST_FILES_SHARING_API_USER4]); + $this->assertEtagsForFoldersChanged([self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER2, + self::TEST_FILES_SHARING_API_USER3]); + + $this->assertAllUnchaged(); + } + + public function testOwnerDeleteInReShare() { + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); + Filesystem::unlink('/sub1/sub2/folder/inside/file.txt'); + $this->assertEtagsForFoldersChanged([self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER2, + self::TEST_FILES_SHARING_API_USER3, self::TEST_FILES_SHARING_API_USER4]); + + $this->assertAllUnchaged(); + } + + public function testRecipientWritesToShare() { + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER2); + Filesystem::file_put_contents('/sub1/sub2/folder/asd.txt', 'bar'); + $this->assertEtagsNotChanged([self::TEST_FILES_SHARING_API_USER4]); + $this->assertEtagsForFoldersChanged([self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER2, + self::TEST_FILES_SHARING_API_USER3]); + + $this->assertAllUnchaged(); + } + + public function testRecipientWritesToReshare() { + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER2); + Filesystem::file_put_contents('/sub1/sub2/folder/inside/asd.txt', 'bar'); + $this->assertEtagsForFoldersChanged([self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER2, + self::TEST_FILES_SHARING_API_USER3, self::TEST_FILES_SHARING_API_USER4]); + + $this->assertAllUnchaged(); + } + + public function testRecipientWritesToOtherRecipientsReshare() { + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER3); + Filesystem::file_put_contents('/sub1/sub2/folder/inside/asd.txt', 'bar'); + $this->assertEtagsForFoldersChanged([self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER2, + self::TEST_FILES_SHARING_API_USER3, self::TEST_FILES_SHARING_API_USER4]); + + $this->assertAllUnchaged(); + } + + public function testRecipientRenameInShare() { + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER2); + Filesystem::rename('/sub1/sub2/folder/file.txt', '/sub1/sub2/folder/renamed.txt'); + $this->assertEtagsNotChanged([self::TEST_FILES_SHARING_API_USER4]); + $this->assertEtagsForFoldersChanged([self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER2, + self::TEST_FILES_SHARING_API_USER3]); + + $this->assertAllUnchaged(); + } + + public function testRecipientRenameInReShare() { + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER2); + Filesystem::rename('/sub1/sub2/folder/inside/file.txt', '/sub1/sub2/folder/inside/renamed.txt'); + $this->assertEtagsForFoldersChanged([self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER2, + self::TEST_FILES_SHARING_API_USER3, self::TEST_FILES_SHARING_API_USER4]); + + $this->assertAllUnchaged(); + } + + public function testRecipientRenameResharedFolder() { + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER2); + Filesystem::rename('/directReshare', '/sub1/directReshare'); + $this->assertEtagsNotChanged([self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER3, self::TEST_FILES_SHARING_API_USER4]); + $this->assertEtagsChanged([self::TEST_FILES_SHARING_API_USER2]); + + $this->assertEtagsChanged([self::TEST_FILES_SHARING_API_USER2], 'sub1'); + + $this->assertAllUnchaged(); + } + + public function testRecipientDeleteInShare() { + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER2); + Filesystem::unlink('/sub1/sub2/folder/file.txt'); + $this->assertEtagsNotChanged([self::TEST_FILES_SHARING_API_USER4]); + $this->assertEtagsForFoldersChanged([self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER2, + self::TEST_FILES_SHARING_API_USER3]); + + $this->assertAllUnchaged(); + } + + public function testRecipientDeleteInReShare() { + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER2); + Filesystem::unlink('/sub1/sub2/folder/inside/file.txt'); + $this->assertEtagsForFoldersChanged([self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER2, + self::TEST_FILES_SHARING_API_USER3, self::TEST_FILES_SHARING_API_USER4]); + + $this->assertAllUnchaged(); + } + + public function testReshareRecipientWritesToReshare() { + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER4); + Filesystem::file_put_contents('/sub1/sub2/inside/asd.txt', 'bar'); + $this->assertEtagsForFoldersChanged([self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER2, + self::TEST_FILES_SHARING_API_USER3, self::TEST_FILES_SHARING_API_USER4]); + + $this->assertAllUnchaged(); + } + + public function testReshareRecipientRenameInReShare() { + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER4); + Filesystem::rename('/sub1/sub2/inside/file.txt', '/sub1/sub2/inside/renamed.txt'); + $this->assertEtagsForFoldersChanged([self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER2, + self::TEST_FILES_SHARING_API_USER3, self::TEST_FILES_SHARING_API_USER4]); + + $this->assertAllUnchaged(); + } + + public function testReshareRecipientDeleteInReShare() { + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER4); + Filesystem::unlink('/sub1/sub2/inside/file.txt'); + $this->assertEtagsForFoldersChanged([self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER2, + self::TEST_FILES_SHARING_API_USER3, self::TEST_FILES_SHARING_API_USER4]); + + $this->assertAllUnchaged(); + } +} diff --git a/apps/files_sharing/tests/propagation.php b/apps/files_sharing/tests/propagation.php deleted file mode 100644 index 1949f7808a4d..000000000000 --- a/apps/files_sharing/tests/propagation.php +++ /dev/null @@ -1,245 +0,0 @@ - - * - * @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 - * - */ - -namespace OCA\Files_sharing\Tests; - -use OC\Files\Filesystem; -use OC\Files\View; - -class Propagation extends TestCase { - public function testSizePropagationWhenOwnerChangesFile() { - $this->loginHelper(self::TEST_FILES_SHARING_API_USER1); - $recipientView = new View('/' . self::TEST_FILES_SHARING_API_USER1 . '/files'); - - $this->loginHelper(self::TEST_FILES_SHARING_API_USER2); - $ownerView = new View('/' . self::TEST_FILES_SHARING_API_USER2 . '/files'); - $ownerView->mkdir('/sharedfolder/subfolder'); - $ownerView->file_put_contents('/sharedfolder/subfolder/foo.txt', 'bar'); - - $sharedFolderInfo = $ownerView->getFileInfo('/sharedfolder', false); - \OCP\Share::shareItem('folder', $sharedFolderInfo->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER1, 31); - $ownerRootInfo = $ownerView->getFileInfo('', false); - - $this->loginHelper(self::TEST_FILES_SHARING_API_USER1); - $this->assertTrue($recipientView->file_exists('/sharedfolder/subfolder/foo.txt')); - $recipientRootInfo = $recipientView->getFileInfo('', false); - - // when file changed as owner - $this->loginHelper(self::TEST_FILES_SHARING_API_USER2); - $ownerView->file_put_contents('/sharedfolder/subfolder/foo.txt', 'foobar'); - - // size of recipient's root stays the same - $this->loginHelper(self::TEST_FILES_SHARING_API_USER1); - $newRecipientRootInfo = $recipientView->getFileInfo('', false); - $this->assertEquals($recipientRootInfo->getSize(), $newRecipientRootInfo->getSize()); - - // size of owner's root increases - $this->loginHelper(self::TEST_FILES_SHARING_API_USER2); - $newOwnerRootInfo = $ownerView->getFileInfo('', false); - $this->assertEquals($ownerRootInfo->getSize() + 3, $newOwnerRootInfo->getSize()); - } - - public function testSizePropagationWhenRecipientChangesFile() { - $this->loginHelper(self::TEST_FILES_SHARING_API_USER1); - $recipientView = new View('/' . self::TEST_FILES_SHARING_API_USER1 . '/files'); - - $this->loginHelper(self::TEST_FILES_SHARING_API_USER2); - $ownerView = new View('/' . self::TEST_FILES_SHARING_API_USER2 . '/files'); - $ownerView->mkdir('/sharedfolder/subfolder'); - $ownerView->file_put_contents('/sharedfolder/subfolder/foo.txt', 'bar'); - - $sharedFolderInfo = $ownerView->getFileInfo('/sharedfolder', false); - \OCP\Share::shareItem('folder', $sharedFolderInfo->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER1, 31); - $ownerRootInfo = $ownerView->getFileInfo('', false); - - $this->loginHelper(self::TEST_FILES_SHARING_API_USER1); - $this->assertTrue($recipientView->file_exists('/sharedfolder/subfolder/foo.txt')); - $recipientRootInfo = $recipientView->getFileInfo('', false); - - // when file changed as recipient - $recipientView->file_put_contents('/sharedfolder/subfolder/foo.txt', 'foobar'); - - // size of recipient's root stays the same - $newRecipientRootInfo = $recipientView->getFileInfo('', false); - $this->assertEquals($recipientRootInfo->getSize(), $newRecipientRootInfo->getSize()); - - // size of owner's root increases - $this->loginHelper(self::TEST_FILES_SHARING_API_USER2); - $newOwnerRootInfo = $ownerView->getFileInfo('', false); - $this->assertEquals($ownerRootInfo->getSize() + 3, $newOwnerRootInfo->getSize()); - } - - /** - * @return \OC\Files\View[] - */ - private function setupViews() { - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); - $view1 = new View('/' . self::TEST_FILES_SHARING_API_USER1 . '/files'); - - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER2); - $view2 = new View('/' . self::TEST_FILES_SHARING_API_USER2 . '/files'); - $view2->mkdir('/sharedfolder/subfolder'); - $view2->file_put_contents('/sharedfolder/subfolder/foo.txt', 'bar'); - return [$view1, $view2]; - } - - public function testEtagPropagationSingleUserShareRecipient() { - /** - * @var \OC\Files\View $view1 - * @var \OC\Files\View $view2 - */ - list($view1, $view2) = $this->setupViews(); - - $sharedFolderInfo = $view2->getFileInfo('/sharedfolder'); - \OCP\Share::shareItem('folder', $sharedFolderInfo->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER1, 31); - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); - $this->assertTrue($view1->file_exists('/sharedfolder/subfolder/foo.txt')); - - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER2); - $rootInfo = $view2->getFileInfo(''); - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); - - Filesystem::file_put_contents('/sharedfolder/subfolder/foo.txt', 'foobar'); - - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER2); - $newRootInfo = $view2->getFileInfo(''); - $this->assertNotEquals($rootInfo->getEtag(), $newRootInfo->getEtag()); - } - - public function testEtagPropagationSingleUserShare() { - /** - * @var \OC\Files\View $view1 - * @var \OC\Files\View $view2 - */ - list($view1, $view2) = $this->setupViews(); - - $sharedFolderInfo = $view2->getFileInfo('/sharedfolder'); - \OCP\Share::shareItem('folder', $sharedFolderInfo->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER1, 31); - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); - $this->assertTrue($view1->file_exists('/sharedfolder/subfolder/foo.txt')); - - $rootInfo = $view1->getFileInfo(''); - - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER2); - Filesystem::file_put_contents('/sharedfolder/subfolder/foo.txt', 'foobar'); - - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); - $newRootInfo = $view1->getFileInfo(''); - $this->assertNotEquals($rootInfo->getEtag(), $newRootInfo->getEtag()); - } - - public function testEtagPropagationGroupShare() { - /** - * @var \OC\Files\View $view1 - * @var \OC\Files\View $view2 - */ - list($view1, $view2) = $this->setupViews(); - - $sharedFolderInfo = $view2->getFileInfo('/sharedfolder'); - \OCP\Share::shareItem('folder', $sharedFolderInfo->getId(), \OCP\Share::SHARE_TYPE_GROUP, 'group', 31); - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); - $this->assertTrue($view1->file_exists('/sharedfolder/subfolder/foo.txt')); - - $rootInfo = $view1->getFileInfo(''); - - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER2); - Filesystem::file_put_contents('/sharedfolder/subfolder/foo.txt', 'foobar'); - - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); - $newRootInfo = $view1->getFileInfo(''); - $this->assertNotEquals($rootInfo->getEtag(), $newRootInfo->getEtag()); - } - - public function testEtagPropagationGroupShareOtherRecipient() { - /** - * @var \OC\Files\View $view1 - * @var \OC\Files\View $view2 - */ - list($view1, $view2) = $this->setupViews(); - - $sharedFolderInfo = $view2->getFileInfo('/sharedfolder'); - \OCP\Share::shareItem('folder', $sharedFolderInfo->getId(), \OCP\Share::SHARE_TYPE_GROUP, 'group', 31); - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER3); - $view3 = new View('/' . self::TEST_FILES_SHARING_API_USER3 . '/files'); - $this->assertTrue($view3->file_exists('/sharedfolder/subfolder/foo.txt')); - - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); - $rootInfo = $view1->getFileInfo(''); - - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER3); - Filesystem::file_put_contents('/sharedfolder/subfolder/foo.txt', 'foobar'); - - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); - $newRootInfo = $view1->getFileInfo(''); - $this->assertNotEquals($rootInfo->getEtag(), $newRootInfo->getEtag()); - } - - public function testEtagPropagationOtherShare() { - /** - * @var \OC\Files\View $view1 - * @var \OC\Files\View $view2 - */ - list($view1, $view2) = $this->setupViews(); - - $sharedFolderInfo = $view2->getFileInfo('/sharedfolder'); - \OCP\Share::shareItem('folder', $sharedFolderInfo->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER1, 31); - \OCP\Share::shareItem('folder', $sharedFolderInfo->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER3, 31); - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER3); - $view3 = new View('/' . self::TEST_FILES_SHARING_API_USER3 . '/files'); - $this->assertTrue($view3->file_exists('/sharedfolder/subfolder/foo.txt')); - - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); - $rootInfo = $view1->getFileInfo(''); - - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER3); - Filesystem::file_put_contents('/sharedfolder/subfolder/foo.txt', 'foobar'); - - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); - $newRootInfo = $view1->getFileInfo(''); - $this->assertNotEquals($rootInfo->getEtag(), $newRootInfo->getEtag()); - } - - public function testEtagPropagationOtherShareSubFolder() { - /** - * @var \OC\Files\View $view1 - * @var \OC\Files\View $view2 - */ - list($view1, $view2) = $this->setupViews(); - - $sharedFolderInfo = $view2->getFileInfo('/sharedfolder'); - \OCP\Share::shareItem('folder', $sharedFolderInfo->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER1, 31); - $sharedFolderInfo = $view2->getFileInfo('/sharedfolder/subfolder'); - \OCP\Share::shareItem('folder', $sharedFolderInfo->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER3, 31); - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER3); - $view3 = new View('/' . self::TEST_FILES_SHARING_API_USER3 . '/files'); - $this->assertTrue($view3->file_exists('/subfolder/foo.txt')); - - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); - $rootInfo = $view1->getFileInfo(''); - - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER3); - Filesystem::file_put_contents('/subfolder/foo.txt', 'foobar'); - - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); - $newRootInfo = $view1->getFileInfo(''); - $this->assertNotEquals($rootInfo->getEtag(), $newRootInfo->getEtag()); - } -} diff --git a/apps/files_sharing/tests/sizepropagation.php b/apps/files_sharing/tests/sizepropagation.php new file mode 100644 index 000000000000..4ab3475ccfcd --- /dev/null +++ b/apps/files_sharing/tests/sizepropagation.php @@ -0,0 +1,89 @@ + + * + * @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 + * + */ + +namespace OCA\Files_sharing\Tests; + +use OC\Files\View; + +class SizePropagation extends TestCase { + + public function testSizePropagationWhenOwnerChangesFile() { + $this->loginHelper(self::TEST_FILES_SHARING_API_USER1); + $recipientView = new View('/' . self::TEST_FILES_SHARING_API_USER1 . '/files'); + + $this->loginHelper(self::TEST_FILES_SHARING_API_USER2); + $ownerView = new View('/' . self::TEST_FILES_SHARING_API_USER2 . '/files'); + $ownerView->mkdir('/sharedfolder/subfolder'); + $ownerView->file_put_contents('/sharedfolder/subfolder/foo.txt', 'bar'); + + $sharedFolderInfo = $ownerView->getFileInfo('/sharedfolder', false); + \OCP\Share::shareItem('folder', $sharedFolderInfo->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER1, 31); + $ownerRootInfo = $ownerView->getFileInfo('', false); + + $this->loginHelper(self::TEST_FILES_SHARING_API_USER1); + $this->assertTrue($recipientView->file_exists('/sharedfolder/subfolder/foo.txt')); + $recipientRootInfo = $recipientView->getFileInfo('', false); + + // when file changed as owner + $this->loginHelper(self::TEST_FILES_SHARING_API_USER2); + $ownerView->file_put_contents('/sharedfolder/subfolder/foo.txt', 'foobar'); + + // size of recipient's root stays the same + $this->loginHelper(self::TEST_FILES_SHARING_API_USER1); + $newRecipientRootInfo = $recipientView->getFileInfo('', false); + $this->assertEquals($recipientRootInfo->getSize(), $newRecipientRootInfo->getSize()); + + // size of owner's root increases + $this->loginHelper(self::TEST_FILES_SHARING_API_USER2); + $newOwnerRootInfo = $ownerView->getFileInfo('', false); + $this->assertEquals($ownerRootInfo->getSize() + 3, $newOwnerRootInfo->getSize()); + } + + public function testSizePropagationWhenRecipientChangesFile() { + $this->loginHelper(self::TEST_FILES_SHARING_API_USER1); + $recipientView = new View('/' . self::TEST_FILES_SHARING_API_USER1 . '/files'); + + $this->loginHelper(self::TEST_FILES_SHARING_API_USER2); + $ownerView = new View('/' . self::TEST_FILES_SHARING_API_USER2 . '/files'); + $ownerView->mkdir('/sharedfolder/subfolder'); + $ownerView->file_put_contents('/sharedfolder/subfolder/foo.txt', 'bar'); + + $sharedFolderInfo = $ownerView->getFileInfo('/sharedfolder', false); + \OCP\Share::shareItem('folder', $sharedFolderInfo->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER1, 31); + $ownerRootInfo = $ownerView->getFileInfo('', false); + + $this->loginHelper(self::TEST_FILES_SHARING_API_USER1); + $this->assertTrue($recipientView->file_exists('/sharedfolder/subfolder/foo.txt')); + $recipientRootInfo = $recipientView->getFileInfo('', false); + + // when file changed as recipient + $recipientView->file_put_contents('/sharedfolder/subfolder/foo.txt', 'foobar'); + + // size of recipient's root stays the same + $newRecipientRootInfo = $recipientView->getFileInfo('', false); + $this->assertEquals($recipientRootInfo->getSize(), $newRecipientRootInfo->getSize()); + + // size of owner's root increases + $this->loginHelper(self::TEST_FILES_SHARING_API_USER2); + $newOwnerRootInfo = $ownerView->getFileInfo('', false); + $this->assertEquals($ownerRootInfo->getSize() + 3, $newOwnerRootInfo->getSize()); + } +} diff --git a/apps/files_sharing/tests/testcase.php b/apps/files_sharing/tests/testcase.php index 5cae54fef6cd..b9e9f0772019 100644 --- a/apps/files_sharing/tests/testcase.php +++ b/apps/files_sharing/tests/testcase.php @@ -43,6 +43,7 @@ abstract class TestCase extends \Test\TestCase { const TEST_FILES_SHARING_API_USER1 = "test-share-user1"; const TEST_FILES_SHARING_API_USER2 = "test-share-user2"; const TEST_FILES_SHARING_API_USER3 = "test-share-user3"; + const TEST_FILES_SHARING_API_USER4 = "test-share-user4"; const TEST_FILES_SHARING_API_GROUP1 = "test-share-group1"; @@ -76,6 +77,7 @@ public static function setUpBeforeClass() { $backend->createUser(self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER1); $backend->createUser(self::TEST_FILES_SHARING_API_USER2, self::TEST_FILES_SHARING_API_USER2); $backend->createUser(self::TEST_FILES_SHARING_API_USER3, self::TEST_FILES_SHARING_API_USER3); + $backend->createUser(self::TEST_FILES_SHARING_API_USER4, self::TEST_FILES_SHARING_API_USER4); // create group $groupBackend = new \OC_Group_Dummy(); From 8cb40f6043c9eb7d2285954a404bd52162851beb Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 15 Apr 2015 18:18:28 +0200 Subject: [PATCH 09/20] Make sure the owner mountpoints are setup --- apps/files_sharing/lib/mountprovider.php | 2 ++ apps/files_sharing/lib/sharedmount.php | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/files_sharing/lib/mountprovider.php b/apps/files_sharing/lib/mountprovider.php index 0ba44ec7a95f..9f18ef737b38 100644 --- a/apps/files_sharing/lib/mountprovider.php +++ b/apps/files_sharing/lib/mountprovider.php @@ -8,6 +8,7 @@ namespace OCA\Files_Sharing; +use OC\Files\Filesystem; use OCA\Files_Sharing\Propagation\PropagationManager; use OCP\Files\Config\IMountProvider; use OCP\Files\Storage\IStorageFactory; @@ -50,6 +51,7 @@ public function getMountsForUser(IUser $user, IStorageFactory $storageFactory) { return $share['permissions'] > 0; }); return array_map(function ($share) use ($user, $storageFactory) { + Filesystem::initMountPoints($share['uid_owner']); // for updating etags for the share owner when we make changes to this share. $ownerPropagator = $this->propagationManager->getChangePropagator($share['uid_owner']); diff --git a/apps/files_sharing/lib/sharedmount.php b/apps/files_sharing/lib/sharedmount.php index fbf8d05c1b23..8c473e1f1c23 100644 --- a/apps/files_sharing/lib/sharedmount.php +++ b/apps/files_sharing/lib/sharedmount.php @@ -25,6 +25,7 @@ use OC\Files\Mount\MountPoint; use OC\Files\Mount\MoveableMount; +use OC\Files\View; /** * Shared mount points can be moved by the user @@ -55,8 +56,9 @@ private function verifyMountPoint(&$share, $user) { $mountPoint = basename($share['file_target']); $parent = dirname($share['file_target']); + $view = new View('/' . $user . '/files'); - if (!\OC\Files\Filesystem::is_dir($parent)) { + if (!$view->is_dir($parent)) { $parent = Helper::getShareFolder(); } From 77fbb4125b95d6fb9c8572bffa007bcf77e16dfa Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 16 Apr 2015 15:13:00 +0200 Subject: [PATCH 10/20] propagate changes up reshares --- .../lib/propagation/recipientpropagator.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/apps/files_sharing/lib/propagation/recipientpropagator.php b/apps/files_sharing/lib/propagation/recipientpropagator.php index da71612fd4d7..e1b3523ca687 100644 --- a/apps/files_sharing/lib/propagation/recipientpropagator.php +++ b/apps/files_sharing/lib/propagation/recipientpropagator.php @@ -9,6 +9,7 @@ namespace OCA\Files_Sharing\Propagation; use OC\Files\Cache\ChangePropagator; +use OC\Files\View; use OC\Share\Share; /** @@ -91,6 +92,7 @@ public function markDirty($share, $time = null) { } $this->config->setAppValue('files_sharing', $share['id'], $time); } + /** * Listen on the propagator for updates made to shares owned by a user * @@ -101,7 +103,15 @@ public function attachToPropagator(ChangePropagator $propagator, $owner) { $propagator->listen('\OC\Files', 'propagate', function ($path, $entry) use ($owner) { $shares = Share::getAllSharesForFileId($entry['fileid']); foreach ($shares as $share) { + // propagate down the share tree $this->markDirty($share, time()); + + // propagate up the share tree + $user = $share['uid_owner']; + $view = new View('/' . $user . '/files'); + $path = $view->getPath($share['file_source']); + $watcher = new ChangeWatcher($view); + $watcher->writeHook(['path' => $path]); } }); } From 526ea3fcba56502b9a3a5204221a0aa7e4fa538a Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 17 Apr 2015 13:31:22 +0200 Subject: [PATCH 11/20] add rename hook for propagation --- .../lib/propagation/changewatcher.php | 15 +++++++++++++++ .../lib/propagation/propagationmanager.php | 1 + 2 files changed, 16 insertions(+) diff --git a/apps/files_sharing/lib/propagation/changewatcher.php b/apps/files_sharing/lib/propagation/changewatcher.php index fa5208b498bb..e5ca62c6d8cb 100644 --- a/apps/files_sharing/lib/propagation/changewatcher.php +++ b/apps/files_sharing/lib/propagation/changewatcher.php @@ -37,6 +37,21 @@ public function writeHook($params) { } } + public function renameHook($params) { + $path1 = $params['oldpath']; + $path2 = $params['newpath']; + $fullPath1 = $this->baseView->getAbsolutePath($path1); + $fullPath2 = $this->baseView->getAbsolutePath($path2); + $mount1 = $this->baseView->getMount($path1); + $mount2 = $this->baseView->getMount($path2); + if ($mount1 instanceof SharedMount) { + $this->propagateForOwner($mount1->getShare(), $mount1->getInternalPath($fullPath1), $mount1->getOwnerPropagator()); + } + if ($mount1 !== $mount2 and $mount2 instanceof SharedMount) { + $this->propagateForOwner($mount2->getShare(), $mount2->getInternalPath($fullPath2), $mount2->getOwnerPropagator()); + } + } + /** * @param array $share * @param string $internalPath diff --git a/apps/files_sharing/lib/propagation/propagationmanager.php b/apps/files_sharing/lib/propagation/propagationmanager.php index bf530d369b23..14763a4df7a9 100644 --- a/apps/files_sharing/lib/propagation/propagationmanager.php +++ b/apps/files_sharing/lib/propagation/propagationmanager.php @@ -109,5 +109,6 @@ public function globalSetup() { $this->listenToOwnerChanges($user->getUID(), $user->getUID()); \OC_Hook::connect('OC_Filesystem', 'write', $watcher, 'writeHook'); \OC_Hook::connect('OC_Filesystem', 'delete', $watcher, 'writeHook'); + \OC_Hook::connect('OC_Filesystem', 'rename', $watcher, 'renameHook'); } } From f488624e5ff3a8f580029e98373071d6fe91e2eb Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 20 Apr 2015 13:32:09 +0200 Subject: [PATCH 12/20] minor fixes --- apps/files_sharing/lib/mountprovider.php | 4 ++-- apps/files_sharing/lib/propagation/changewatcher.php | 7 +++++-- apps/files_sharing/lib/propagation/propagationmanager.php | 6 ++---- apps/files_sharing/lib/sharedmount.php | 3 +-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/files_sharing/lib/mountprovider.php b/apps/files_sharing/lib/mountprovider.php index 9f18ef737b38..94fb473883d6 100644 --- a/apps/files_sharing/lib/mountprovider.php +++ b/apps/files_sharing/lib/mountprovider.php @@ -30,14 +30,14 @@ class MountProvider implements IMountProvider { * @param \OCP\IConfig $config * @param \OCA\Files_Sharing\Propagation\PropagationManager $propagationManager */ - function __construct(IConfig $config, PropagationManager $propagationManager) { + public function __construct(IConfig $config, PropagationManager $propagationManager) { $this->config = $config; $this->propagationManager = $propagationManager; } /** - * Get all mountpoints applicable for the user + * Get all mountpoints applicable for the user and check for shares where we need to update the etags * * @param \OCP\IUser $user * @param \OCP\Files\Storage\IStorageFactory $storageFactory diff --git a/apps/files_sharing/lib/propagation/changewatcher.php b/apps/files_sharing/lib/propagation/changewatcher.php index e5ca62c6d8cb..33048081f9ca 100644 --- a/apps/files_sharing/lib/propagation/changewatcher.php +++ b/apps/files_sharing/lib/propagation/changewatcher.php @@ -23,7 +23,10 @@ class ChangeWatcher { */ private $baseView; - function __construct(View $baseView) { + /** + * @param \OC\Files\View $baseView the view for the logged in user + */ + public function __construct(View $baseView) { $this->baseView = $baseView; } @@ -62,7 +65,7 @@ private function propagateForOwner($share, $internalPath, ChangePropagator $prop $view = new View('/' . $share['uid_owner'] . '/files'); $shareRootPath = $view->getPath($share['item_source']); - if ($shareRootPath) { + if (!is_null($shareRootPath)) { $path = $shareRootPath . '/' . $internalPath; $propagator->addChange($path); $propagator->propagateChanges(); diff --git a/apps/files_sharing/lib/propagation/propagationmanager.php b/apps/files_sharing/lib/propagation/propagationmanager.php index 14763a4df7a9..794a7ae04f58 100644 --- a/apps/files_sharing/lib/propagation/propagationmanager.php +++ b/apps/files_sharing/lib/propagation/propagationmanager.php @@ -42,9 +42,7 @@ class PropagationManager { */ private $sharePropagators = []; - private $globalSetupDone = false; - - function __construct(IUserSession $userSession, IConfig $config) { + public function __construct(IUserSession $userSession, IConfig $config) { $this->userSession = $userSession; $this->config = $config; } @@ -82,7 +80,7 @@ public function getSharePropagator($user) { } /** - * Attach the propagator to the change propagator of a user to listen to changes made to files shared by the user + * Attach the recipient propagator for $user to the change propagator of a share owner to mark shares as dirty when the owner makes a change to a share * * @param string $shareOwner * @param string $user diff --git a/apps/files_sharing/lib/sharedmount.php b/apps/files_sharing/lib/sharedmount.php index 8c473e1f1c23..15aea0aa52a3 100644 --- a/apps/files_sharing/lib/sharedmount.php +++ b/apps/files_sharing/lib/sharedmount.php @@ -184,8 +184,7 @@ public function removeMount() { } public function getShare() { - $this->getStorage(); //ensure it exists - return $this->storage->getShare(); + return $this->getStorage()->getShare(); } /** From 45784f213f5e7cf9a38feabd52c4ffadee9ae4f9 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 20 Apr 2015 16:46:35 +0200 Subject: [PATCH 13/20] fix propagation when renaming a directly reshared folder --- apps/files_sharing/lib/propagation/changewatcher.php | 6 ++++-- lib/private/files/view.php | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/files_sharing/lib/propagation/changewatcher.php b/apps/files_sharing/lib/propagation/changewatcher.php index 33048081f9ca..483f436e2892 100644 --- a/apps/files_sharing/lib/propagation/changewatcher.php +++ b/apps/files_sharing/lib/propagation/changewatcher.php @@ -9,6 +9,7 @@ namespace OCA\Files_Sharing\Propagation; use OC\Files\Cache\ChangePropagator; +use OC\Files\Filesystem; use OC\Files\View; use OCA\Files_Sharing\SharedMount; @@ -47,10 +48,10 @@ public function renameHook($params) { $fullPath2 = $this->baseView->getAbsolutePath($path2); $mount1 = $this->baseView->getMount($path1); $mount2 = $this->baseView->getMount($path2); - if ($mount1 instanceof SharedMount) { + if ($mount1 instanceof SharedMount and $mount1->getInternalPath($fullPath1) !== '') { $this->propagateForOwner($mount1->getShare(), $mount1->getInternalPath($fullPath1), $mount1->getOwnerPropagator()); } - if ($mount1 !== $mount2 and $mount2 instanceof SharedMount) { + if ($mount1 !== $mount2 and $mount2 instanceof SharedMount and $mount2->getInternalPath($fullPath2) !== '') { $this->propagateForOwner($mount2->getShare(), $mount2->getInternalPath($fullPath2), $mount2->getOwnerPropagator()); } } @@ -67,6 +68,7 @@ private function propagateForOwner($share, $internalPath, ChangePropagator $prop $shareRootPath = $view->getPath($share['item_source']); if (!is_null($shareRootPath)) { $path = $shareRootPath . '/' . $internalPath; + $path = Filesystem::normalizePath($path); $propagator->addChange($path); $propagator->propagateChanges(); } diff --git a/lib/private/files/view.php b/lib/private/files/view.php index 162bf568de74..63af2b616cff 100644 --- a/lib/private/files/view.php +++ b/lib/private/files/view.php @@ -658,6 +658,10 @@ public function rename($path1, $path2) { } elseif ($result) { if ($internalPath1 !== '') { // dont do a cache update for moved mounts $this->updater->rename($path1, $path2); + } else { // only do etag propagation + $this->getUpdater()->getPropagator()->addChange($path1); + $this->getUpdater()->getPropagator()->addChange($path2); + $this->getUpdater()->getPropagator()->propagateChanges(); } if ($this->shouldEmitHooks($path1) and $this->shouldEmitHooks($path2)) { \OC_Hook::emit( From dd9601ae8fd0f50eb87d2ce3eed6c5f845573987 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 20 Apr 2015 16:59:20 +0200 Subject: [PATCH 14/20] fix merge --- apps/files_sharing/appinfo/application.php | 73 +++++++++++ apps/files_sharing/appinfo/routes.php | 2 + apps/files_sharing/application.php | 115 ------------------ .../tests/controller/sharecontroller.php | 2 +- 4 files changed, 76 insertions(+), 116 deletions(-) delete mode 100644 apps/files_sharing/application.php diff --git a/apps/files_sharing/appinfo/application.php b/apps/files_sharing/appinfo/application.php index 6848c9e8363a..0eb209450709 100644 --- a/apps/files_sharing/appinfo/application.php +++ b/apps/files_sharing/appinfo/application.php @@ -8,15 +8,88 @@ namespace OCA\Files_Sharing\Appinfo; +use OCA\Files_Sharing\Helper; use OCA\Files_Sharing\MountProvider; use OCA\Files_Sharing\Propagation\PropagationManager; use OCP\AppFramework\App; +use OC\AppFramework\Utility\SimpleContainer; +use OCA\Files_Sharing\Controllers\ExternalSharesController; +use OCA\Files_Sharing\Controllers\ShareController; +use OCA\Files_Sharing\Middleware\SharingCheckMiddleware; use \OCP\IContainer; class Application extends App { public function __construct(array $urlParams = array()) { parent::__construct('files_sharing', $urlParams); + $container = $this->getContainer(); + $server = $container->getServer(); + + /** + * Controllers + */ + $container->registerService('ShareController', function (SimpleContainer $c) use ($server) { + return new ShareController( + $c->query('AppName'), + $c->query('Request'), + $c->query('UserSession'), + $server->getAppConfig(), + $server->getConfig(), + $c->query('URLGenerator'), + $c->query('UserManager'), + $server->getLogger(), + $server->getActivityManager() + ); + }); + $container->registerService('ExternalSharesController', function (SimpleContainer $c) { + return new ExternalSharesController( + $c->query('AppName'), + $c->query('Request'), + $c->query('IsIncomingShareEnabled'), + $c->query('ExternalManager') + ); + }); + + /** + * Core class wrappers + */ + $container->registerService('UserSession', function (SimpleContainer $c) use ($server) { + return $server->getUserSession(); + }); + $container->registerService('URLGenerator', function (SimpleContainer $c) use ($server) { + return $server->getUrlGenerator(); + }); + $container->registerService('UserManager', function (SimpleContainer $c) use ($server) { + return $server->getUserManager(); + }); + $container->registerService('IsIncomingShareEnabled', function (SimpleContainer $c) { + return Helper::isIncomingServer2serverShareEnabled(); + }); + $container->registerService('ExternalManager', function (SimpleContainer $c) use ($server) { + $user = $server->getUserSession()->getUser(); + $uid = $user ? $user->getUID() : null; + return new \OCA\Files_Sharing\External\Manager( + $server->getDatabaseConnection(), + \OC\Files\Filesystem::getMountManager(), + \OC\Files\Filesystem::getLoader(), + $server->getHTTPHelper(), + $uid + ); + }); + + /** + * Middleware + */ + $container->registerService('SharingCheckMiddleware', function (SimpleContainer $c) use ($server) { + return new SharingCheckMiddleware( + $c->query('AppName'), + $server->getConfig(), + $server->getAppManager() + ); + }); + + // Execute middlewares + $container->registerMiddleware('SharingCheckMiddleware'); $container->registerService('MountProvider', function (IContainer $c) { /** @var \OCP\IServerContainer $server */ diff --git a/apps/files_sharing/appinfo/routes.php b/apps/files_sharing/appinfo/routes.php index db4566eb6126..78fa138f5b5e 100644 --- a/apps/files_sharing/appinfo/routes.php +++ b/apps/files_sharing/appinfo/routes.php @@ -25,6 +25,8 @@ */ namespace OCA\Files_Sharing\AppInfo; +use OCP\API; + $application = new Application(); $application->registerRoutes($this, [ 'resources' => [ diff --git a/apps/files_sharing/application.php b/apps/files_sharing/application.php deleted file mode 100644 index 62c07ffe4041..000000000000 --- a/apps/files_sharing/application.php +++ /dev/null @@ -1,115 +0,0 @@ - - * @author Lukas Reschke - * @author Morris Jobke - * @author Thomas Müller - * - * @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 - * - */ - -namespace OCA\Files_Sharing; - -use OC\AppFramework\Utility\SimpleContainer; -use OCA\Files_Sharing\Controllers\ExternalSharesController; -use OCA\Files_Sharing\Controllers\ShareController; -use OCA\Files_Sharing\Middleware\SharingCheckMiddleware; -use \OCP\AppFramework\App; - -/** - * @package OCA\Files_Sharing - */ -class Application extends App { - - - /** - * @param array $urlParams - */ - public function __construct(array $urlParams=array()){ - parent::__construct('files_sharing', $urlParams); - - $container = $this->getContainer(); - $server = $container->getServer(); - - /** - * Controllers - */ - $container->registerService('ShareController', function(SimpleContainer $c) use ($server) { - return new ShareController( - $c->query('AppName'), - $c->query('Request'), - $c->query('UserSession'), - $server->getAppConfig(), - $server->getConfig(), - $c->query('URLGenerator'), - $c->query('UserManager'), - $server->getLogger(), - $server->getActivityManager() - ); - }); - $container->registerService('ExternalSharesController', function(SimpleContainer $c) { - return new ExternalSharesController( - $c->query('AppName'), - $c->query('Request'), - $c->query('IsIncomingShareEnabled'), - $c->query('ExternalManager') - ); - }); - - /** - * Core class wrappers - */ - $container->registerService('UserSession', function(SimpleContainer $c) use ($server) { - return $server->getUserSession(); - }); - $container->registerService('URLGenerator', function(SimpleContainer $c) use ($server){ - return $server->getUrlGenerator(); - }); - $container->registerService('UserManager', function(SimpleContainer $c) use ($server){ - return $server->getUserManager(); - }); - $container->registerService('IsIncomingShareEnabled', function(SimpleContainer $c) { - return Helper::isIncomingServer2serverShareEnabled(); - }); - $container->registerService('ExternalManager', function(SimpleContainer $c) use ($server){ - $user = $server->getUserSession()->getUser(); - $uid = $user ? $user->getUID() : null; - return new \OCA\Files_Sharing\External\Manager( - $server->getDatabaseConnection(), - \OC\Files\Filesystem::getMountManager(), - \OC\Files\Filesystem::getLoader(), - $server->getHTTPHelper(), - $uid - ); - }); - - /** - * Middleware - */ - $container->registerService('SharingCheckMiddleware', function(SimpleContainer $c) use ($server){ - return new SharingCheckMiddleware( - $c->query('AppName'), - $server->getConfig(), - $server->getAppManager() - ); - }); - - // Execute middlewares - $container->registerMiddleware('SharingCheckMiddleware'); - } - -} diff --git a/apps/files_sharing/tests/controller/sharecontroller.php b/apps/files_sharing/tests/controller/sharecontroller.php index c3bd41b04027..64ee5b8ce511 100644 --- a/apps/files_sharing/tests/controller/sharecontroller.php +++ b/apps/files_sharing/tests/controller/sharecontroller.php @@ -26,7 +26,7 @@ namespace OCA\Files_Sharing\Controllers; use OC\Files\Filesystem; -use OCA\Files_Sharing\Application; +use OCA\Files_Sharing\AppInfo\Application; use OCP\AppFramework\Http\NotFoundResponse; use OCP\AppFramework\IAppContainer; use OCP\Files; From 2e897f05b12689d783c68bcfc6206e2adfde75b1 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 21 Apr 2015 13:50:33 +0200 Subject: [PATCH 15/20] triger propagation for webdav uploads use post hooks for share etag propagator --- apps/files_sharing/lib/propagation/propagationmanager.php | 6 +++--- lib/private/connector/sabre/file.php | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/files_sharing/lib/propagation/propagationmanager.php b/apps/files_sharing/lib/propagation/propagationmanager.php index 794a7ae04f58..fa073be7f60b 100644 --- a/apps/files_sharing/lib/propagation/propagationmanager.php +++ b/apps/files_sharing/lib/propagation/propagationmanager.php @@ -105,8 +105,8 @@ public function globalSetup() { // for marking shares owned by the active user as dirty when a file inside them changes $this->listenToOwnerChanges($user->getUID(), $user->getUID()); - \OC_Hook::connect('OC_Filesystem', 'write', $watcher, 'writeHook'); - \OC_Hook::connect('OC_Filesystem', 'delete', $watcher, 'writeHook'); - \OC_Hook::connect('OC_Filesystem', 'rename', $watcher, 'renameHook'); + \OC_Hook::connect('OC_Filesystem', 'post_write', $watcher, 'writeHook'); + \OC_Hook::connect('OC_Filesystem', 'post_delete', $watcher, 'writeHook'); + \OC_Hook::connect('OC_Filesystem', 'post_rename', $watcher, 'renameHook'); } } diff --git a/lib/private/connector/sabre/file.php b/lib/private/connector/sabre/file.php index 100aba13668d..8ff5577629df 100644 --- a/lib/private/connector/sabre/file.php +++ b/lib/private/connector/sabre/file.php @@ -181,6 +181,7 @@ public function put($data) { $view = \OC\Files\Filesystem::getView(); if ($view) { $hookPath = $view->getRelativePath($this->fileView->getAbsolutePath($this->path)); + $this->fileView->getUpdater()->propagate($hookPath); if (!$exists) { \OC_Hook::emit(\OC\Files\Filesystem::CLASSNAME, \OC\Files\Filesystem::signal_post_create, array( \OC\Files\Filesystem::signal_param_path => $hookPath From 6bf05796224ce83ec74b08f9bd428619ca3c9f7e Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 21 Apr 2015 16:13:25 +0200 Subject: [PATCH 16/20] fix test --- tests/lib/connector/sabre/file.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/lib/connector/sabre/file.php b/tests/lib/connector/sabre/file.php index 3fe5c2751f1b..ee9c20fd9cb6 100644 --- a/tests/lib/connector/sabre/file.php +++ b/tests/lib/connector/sabre/file.php @@ -52,7 +52,6 @@ public function testPutSingleFileShare() { $view = $this->getMock('\OC\Files\View', array('file_put_contents', 'getRelativePath', 'resolvePath'), array()); $view->expects($this->any()) ->method('resolvePath') - ->with('') ->will($this->returnValue(array($storage, ''))); $view->expects($this->any()) ->method('getRelativePath') From 83b8ac0d7b90840ac3d22e95a442d3f5db413ea6 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 23 Apr 2015 18:24:46 +0200 Subject: [PATCH 17/20] cleanup hooks --- apps/files_sharing/tests/etagpropagation.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/files_sharing/tests/etagpropagation.php b/apps/files_sharing/tests/etagpropagation.php index b515362c02dd..041ed34ce110 100644 --- a/apps/files_sharing/tests/etagpropagation.php +++ b/apps/files_sharing/tests/etagpropagation.php @@ -37,6 +37,13 @@ protected function setUp() { $this->setUpShares(); } + protected function tearDown() { + \OC_Hook::clear('OC_Filesystem', 'post_write'); + \OC_Hook::clear('OC_Filesystem', 'post_delete'); + \OC_Hook::clear('OC_Filesystem', 'post_rename'); + parent::tearDown(); + } + /** * "user1" is the admin who shares a folder "sub1/sub2/folder" with "user2" and "user3" * "user2" receives the folder and puts it in "sub1/sub2/folder" From 32067f9e8043ee5ac8e6735743a94c146bba6c4c Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 23 Apr 2015 18:34:06 +0200 Subject: [PATCH 18/20] use microtime to prevent race conditions --- apps/files_sharing/lib/propagation/recipientpropagator.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/files_sharing/lib/propagation/recipientpropagator.php b/apps/files_sharing/lib/propagation/recipientpropagator.php index e1b3523ca687..9c9fe2bedbff 100644 --- a/apps/files_sharing/lib/propagation/recipientpropagator.php +++ b/apps/files_sharing/lib/propagation/recipientpropagator.php @@ -52,7 +52,7 @@ public function __construct($userId, $changePropagator, $config) { */ public function propagateDirtyMountPoints(array $shares, $time = null) { if ($time === null) { - $time = time(); + $time = microtime(true); } $dirtyShares = $this->getDirtyShares($shares); foreach ($dirtyShares as $share) { @@ -88,7 +88,7 @@ protected function getDirtyShares($shares) { */ public function markDirty($share, $time = null) { if ($time === null) { - $time = time(); + $time = microtime(true); } $this->config->setAppValue('files_sharing', $share['id'], $time); } @@ -104,7 +104,7 @@ public function attachToPropagator(ChangePropagator $propagator, $owner) { $shares = Share::getAllSharesForFileId($entry['fileid']); foreach ($shares as $share) { // propagate down the share tree - $this->markDirty($share, time()); + $this->markDirty($share, microtime(true)); // propagate up the share tree $user = $share['uid_owner']; From 0789a6433e5b25b5edc3434b2650a067367b9130 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 24 Apr 2015 18:10:32 +0200 Subject: [PATCH 19/20] safer teardown --- apps/files_sharing/tests/watcher.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/files_sharing/tests/watcher.php b/apps/files_sharing/tests/watcher.php index a2e440069a41..4af5de6aaae7 100644 --- a/apps/files_sharing/tests/watcher.php +++ b/apps/files_sharing/tests/watcher.php @@ -76,7 +76,9 @@ protected function setUp() { } protected function tearDown() { - $this->sharedCache->clear(); + if ($this->sharedCache) { + $this->sharedCache->clear(); + } self::loginHelper(self::TEST_FILES_SHARING_API_USER1); From 5304afbecb37b841312e35594ef8cba403a4cd8c Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 27 Apr 2015 15:18:13 +0200 Subject: [PATCH 20/20] dont pass floats as timestamp to the changepropagator --- apps/files_sharing/lib/propagation/recipientpropagator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/files_sharing/lib/propagation/recipientpropagator.php b/apps/files_sharing/lib/propagation/recipientpropagator.php index 9c9fe2bedbff..5b7651f2ce79 100644 --- a/apps/files_sharing/lib/propagation/recipientpropagator.php +++ b/apps/files_sharing/lib/propagation/recipientpropagator.php @@ -60,7 +60,7 @@ public function propagateDirtyMountPoints(array $shares, $time = null) { } if (count($dirtyShares)) { $this->config->setUserValue($this->userId, 'files_sharing', 'last_propagate', $time); - $this->changePropagator->propagateChanges($time); + $this->changePropagator->propagateChanges(floor($time)); } }