From 30ad56813a16908e3862c353256f2a6d0f05fe3a Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 9 Mar 2015 16:20:18 +0100 Subject: [PATCH] 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 d009fbca3b..19d3b6cd9c 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 0000000000..6848c9e836 --- /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 9ac3a1f731..db4566eb61 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 5b5525e244..05b0352ca1 100644 --- a/apps/files_sharing/lib/helper.php +++ b/apps/files_sharing/lib/helper.php @@ -30,9 +30,7 @@ namespace OCA\Files_Sharing; 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 0000000000..0ba44ec7a9 --- /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 0000000000..fa5208b498 --- /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 0000000000..bf530d369b --- /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 0000000000..da71612fd4 --- /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 da00549541..fbf8d05c1b 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 @@ class SharedMount extends MountPoint implements MoveableMount { 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 8c2f03b141..bc01465cb4 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 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage { /** * get id of the mount point + * * @return string */ public function getId() { @@ -55,14 +60,16 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage { /** * 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 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage { /** * 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 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage { /** * Get the permissions granted for a shared file + * * @param string $target Shared target file path * @return int CRUDS permissions granted */ @@ -138,13 +147,14 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage { /** * 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 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage { /** * Delete the file if DELETE permission is granted + * * @param string $path * @return boolean */ @@ -426,37 +437,6 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage { 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 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage { /** * does the group share already has a user specific unique name + * * @return bool */ public function uniqueNameSet() { @@ -493,6 +474,7 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage { /** * get share ID + * * @return integer unique share ID */ public function getShareId() { @@ -501,6 +483,7 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage { /** * get the user who shared the file + * * @return string */ public function getSharedFrom() { @@ -516,6 +499,7 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage { /** * 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 322c031f2f..45cf662d14 100644 --- a/apps/files_sharing/lib/updater.php +++ b/apps/files_sharing/lib/updater.php @@ -49,48 +49,10 @@ class Shared_Updater { } } - /** - * 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 @@ class Shared_Updater { */ 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 1949f7808a..c42dccce88 100644 --- a/apps/files_sharing/tests/propagation.php +++ b/apps/files_sharing/tests/propagation.php @@ -242,4 +242,52 @@ class Propagation extends TestCase { $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 b9f8658a69..5cae54fef6 100644 --- a/apps/files_sharing/tests/testcase.php +++ b/apps/files_sharing/tests/testcase.php @@ -31,6 +31,7 @@ namespace OCA\Files_Sharing\Tests; 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 @@ abstract class TestCase extends \Test\TestCase { // 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 @@ abstract class TestCase extends \Test\TestCase { \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 63bba06e67..d18bdffc69 100644 --- a/lib/private/share/share.php +++ b/lib/private/share/share.php @@ -2518,6 +2518,7 @@ class Share extends \OC\Share\Constants { $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 @@ class Share extends \OC\Share\Constants { 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(); + } }