diff --git a/apps/files_sharing/lib/propagator.php b/apps/files_sharing/lib/propagator.php new file mode 100644 index 000000000000..4963bf71a3e8 --- /dev/null +++ b/apps/files_sharing/lib/propagator.php @@ -0,0 +1,138 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OCA\Files_Sharing; + +use OC\Files\Cache\ChangePropagator; +use OC\Files\Filesystem; +use OC\Files\View; +use OC\Share\Share; + +class Propagator { + /** + * @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']); + } + $this->config->setUserValue($this->userId, 'files_sharing', 'last_propagate', $time); + if (count($dirtyShares)) { + $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); + } + + public static function writeHook($params) { + $path = $params['path']; + $fullPath = Filesystem::getView()->getAbsolutePath($path); + $mount = Filesystem::getView()->getMount($path); + if ($mount instanceof SharedMount) { + self::propagateForOwner($mount->getShare(), $mount->getInternalPath($fullPath), $mount->getOwnerPropagator()); + } + } + + /** + * @param array $share + * @param string $internalPath + * @param \OC\Files\Cache\ChangePropagator $propagator + */ + private static 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(); + } + } + + /** + * 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) { + $shares = Share::getAllSharesForOwner($owner); + $propagator->listen('\OC\Files', 'propagate', function ($path, $entry) use ($shares) { + foreach ($shares as $share) { + if ((int)$share['file_source'] === $entry['fileid']) { + // make sure we also get child entries from group shares +// $sharesForFile = Share:: + $this->markDirty($share, time()); + } + } + }); + } +} diff --git a/apps/files_sharing/lib/sharedmount.php b/apps/files_sharing/lib/sharedmount.php index d16dbf89ccf7..4d0584c974f9 100644 --- a/apps/files_sharing/lib/sharedmount.php +++ b/apps/files_sharing/lib/sharedmount.php @@ -20,8 +20,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); @@ -159,4 +165,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 ccfbebddb294..d60a72355625 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -22,8 +22,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; /** @@ -40,6 +44,7 @@ public function __construct($arguments) { /** * get id of the mount point + * * @return string */ public function getId() { @@ -48,14 +53,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 */ @@ -79,6 +86,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 */ @@ -102,6 +110,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 */ @@ -131,13 +140,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; } @@ -270,6 +280,7 @@ public function file_put_contents($path, $data) { /** * Delete the file if DELETE permission is granted + * * @param string $path * @return boolean */ @@ -400,25 +411,33 @@ public static function setup($options) { $shares = \OCP\Share::getItemsSharedWithUser('file', $options['user']); $manager = Filesystem::getMountManager(); $loader = Filesystem::getLoader(); + $view = Filesystem::getView(); + $propagator = new Propagator($options['user'], new ChangePropagator($view), \OC::$server->getConfig()); if (!\OCP\User::isLoggedIn() || \OCP\User::getUser() != $options['user'] || $shares ) { + $propagator->propagateDirtyMountPoints($shares); foreach ($shares as $share) { // don't mount shares where we have no permissions if ($share['permissions'] > 0) { + $ownerPropagator = new ChangePropagator(new View('/' . $share['uid_owner'] . '/files')); $mount = new SharedMount( - '\OC\Files\Storage\Shared', - $options['user_dir'] . '/' . $share['file_target'], - array( - 'share' => $share, - 'user' => $options['user'] - ), - $loader - ); + '\OC\Files\Storage\Shared', + $options['user_dir'] . '/' . $share['file_target'], + array( + 'propagator' => $ownerPropagator, + 'share' => $share, + 'user' => $options['user'] + ), + $loader + ); + $propagator->attachToPropagator($ownerPropagator, $share['uid_owner']); $manager->addMount($mount); } } } + $propagator->attachToPropagator($view->getUpdater()->getPropagator(), $options['user']); + \OC_Hook::connect('OC_Filesystem', 'write', '\OCA\Files_Sharing\Propagator', 'writeHook'); } /** @@ -440,6 +459,7 @@ public function getShareType() { /** * does the group share already has a user specific unique name + * * @return bool */ public function uniqueNameSet() { @@ -457,6 +477,7 @@ public function setUniqueName() { /** * get share ID + * * @return integer unique share ID */ public function getShareId() { @@ -465,6 +486,7 @@ public function getShareId() { /** * get the user who shared the file + * * @return string */ public function getSharedFrom() { @@ -480,6 +502,7 @@ public function getShare() { /** * return share type, can be "file" or "folder" + * * @return string */ public function getItemType() {