propagate etags for all user of a share
This commit is contained in:
parent
518d5aadf5
commit
30ad56813a
|
@ -25,31 +25,38 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
namespace OCA\Files_Sharing\Appinfo;
|
||||||
|
|
||||||
$l = \OC::$server->getL10N('files_sharing');
|
$l = \OC::$server->getL10N('files_sharing');
|
||||||
|
|
||||||
OC::$CLASSPATH['OC_Share_Backend_File'] = 'files_sharing/lib/share/file.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_Share_Backend_Folder'] = 'files_sharing/lib/share/folder.php';
|
||||||
OC::$CLASSPATH['OC\Files\Storage\Shared'] = 'files_sharing/lib/sharedstorage.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\SharedScanner'] = 'files_sharing/lib/scanner.php';
|
||||||
OC::$CLASSPATH['OC\Files\Cache\Shared_Cache'] = 'files_sharing/lib/cache.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_Permissions'] = 'files_sharing/lib/permissions.php';
|
||||||
OC::$CLASSPATH['OC\Files\Cache\Shared_Updater'] = 'files_sharing/lib/updater.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['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\Maintainer'] = 'files_sharing/lib/maintainer.php';
|
||||||
OC::$CLASSPATH['OCA\Files\Share\Proxy'] = 'files_sharing/lib/proxy.php';
|
\OC::$CLASSPATH['OCA\Files\Share\Proxy'] = 'files_sharing/lib/proxy.php';
|
||||||
|
|
||||||
// Exceptions
|
// 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');
|
\OCP\App::registerAdmin('files_sharing', 'settings-admin');
|
||||||
|
|
||||||
\OCA\Files_Sharing\Helper::registerHooks();
|
\OCA\Files_Sharing\Helper::registerHooks();
|
||||||
|
|
||||||
OCP\Share::registerBackend('file', 'OC_Share_Backend_File');
|
\OCP\Share::registerBackend('file', 'OC_Share_Backend_File');
|
||||||
OCP\Share::registerBackend('folder', 'OC_Share_Backend_Folder', 'file');
|
\OCP\Share::registerBackend('folder', 'OC_Share_Backend_Folder', 'file');
|
||||||
|
|
||||||
OCP\Util::addScript('files_sharing', 'share');
|
\OCP\Util::addScript('files_sharing', 'share');
|
||||||
OCP\Util::addScript('files_sharing', 'external');
|
\OCP\Util::addScript('files_sharing', 'external');
|
||||||
|
|
||||||
// FIXME: registering a job here will cause additional useless SQL queries
|
// FIXME: registering a job here will cause additional useless SQL queries
|
||||||
// when the route is not cron.php, needs a better way
|
// when the route is not cron.php, needs a better way
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015 Robin Appelman <icewind@owncloud.com>
|
||||||
|
* 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');
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,9 +25,6 @@
|
||||||
*/
|
*/
|
||||||
namespace OCA\Files_Sharing\AppInfo;
|
namespace OCA\Files_Sharing\AppInfo;
|
||||||
|
|
||||||
use OCA\Files_Sharing\Application;
|
|
||||||
use OCP\API;
|
|
||||||
|
|
||||||
$application = new Application();
|
$application = new Application();
|
||||||
$application->registerRoutes($this, [
|
$application->registerRoutes($this, [
|
||||||
'resources' => [
|
'resources' => [
|
||||||
|
|
|
@ -30,9 +30,7 @@ namespace OCA\Files_Sharing;
|
||||||
class Helper {
|
class Helper {
|
||||||
|
|
||||||
public static function registerHooks() {
|
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', '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', '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_rename', '\OC\Files\Cache\Shared_Updater', 'renameHook');
|
||||||
\OCP\Util::connectHook('OC_Filesystem', 'post_delete', '\OCA\Files_Sharing\Hooks', 'unshareChildren');
|
\OCP\Util::connectHook('OC_Filesystem', 'post_delete', '\OCA\Files_Sharing\Hooks', 'unshareChildren');
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015 Robin Appelman <icewind@owncloud.com>
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015 Robin Appelman <icewind@owncloud.com>
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015 Robin Appelman <icewind@owncloud.com>
|
||||||
|
* 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');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015 Robin Appelman <icewind@owncloud.com>
|
||||||
|
* 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());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,8 +35,14 @@ class SharedMount extends MountPoint implements MoveableMount {
|
||||||
*/
|
*/
|
||||||
protected $storage = null;
|
protected $storage = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \OC\Files\Cache\ChangePropagator
|
||||||
|
*/
|
||||||
|
protected $ownerPropagator;
|
||||||
|
|
||||||
public function __construct($storage, $mountpoint, $arguments = null, $loader = null) {
|
public function __construct($storage, $mountpoint, $arguments = null, $loader = null) {
|
||||||
// first update the mount point before creating the parent
|
// first update the mount point before creating the parent
|
||||||
|
$this->ownerPropagator = $arguments['propagator'];
|
||||||
$newMountPoint = $this->verifyMountPoint($arguments['share'], $arguments['user']);
|
$newMountPoint = $this->verifyMountPoint($arguments['share'], $arguments['user']);
|
||||||
$absMountPoint = '/' . $arguments['user'] . '/files' . $newMountPoint;
|
$absMountPoint = '/' . $arguments['user'] . '/files' . $newMountPoint;
|
||||||
parent::__construct($storage, $absMountPoint, $arguments, $loader);
|
parent::__construct($storage, $absMountPoint, $arguments, $loader);
|
||||||
|
@ -174,4 +180,16 @@ class SharedMount extends MountPoint implements MoveableMount {
|
||||||
|
|
||||||
return $result;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace OC\Files\Storage;
|
namespace OC\Files\Storage;
|
||||||
|
|
||||||
|
use OC\Files\Cache\ChangePropagator;
|
||||||
use OC\Files\Filesystem;
|
use OC\Files\Filesystem;
|
||||||
|
use OC\Files\View;
|
||||||
use OCA\Files_Sharing\ISharedStorage;
|
use OCA\Files_Sharing\ISharedStorage;
|
||||||
|
use OCA\Files_Sharing\Propagator;
|
||||||
use OCA\Files_Sharing\SharedMount;
|
use OCA\Files_Sharing\SharedMount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -47,6 +51,7 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get id of the mount point
|
* get id of the mount point
|
||||||
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getId() {
|
public function getId() {
|
||||||
|
@ -55,14 +60,16 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get file cache of the shared item source
|
* get file cache of the shared item source
|
||||||
|
*
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
public function getSourceId() {
|
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
|
* Get the source file path, permissions, and owner for a shared file
|
||||||
|
*
|
||||||
* @param string $target Shared target file path
|
* @param string $target Shared target file path
|
||||||
* @return Returns array with the keys path, permissions, and owner or false if not found
|
* @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
|
* Get the source file path for a shared file
|
||||||
|
*
|
||||||
* @param string $target Shared target file path
|
* @param string $target Shared target file path
|
||||||
* @return string|false source file path or false if not found
|
* @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
|
* Get the permissions granted for a shared file
|
||||||
|
*
|
||||||
* @param string $target Shared target file path
|
* @param string $target Shared target file path
|
||||||
* @return int CRUDS permissions granted
|
* @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
|
* Delete the directory if DELETE permission is granted
|
||||||
|
*
|
||||||
* @param string $path
|
* @param string $path
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function rmdir($path) {
|
public function rmdir($path) {
|
||||||
|
|
||||||
// never delete a share mount point
|
// never delete a share mount point
|
||||||
if(empty($path)) {
|
if (empty($path)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,6 +287,7 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete the file if DELETE permission is granted
|
* Delete the file if DELETE permission is granted
|
||||||
|
*
|
||||||
* @param string $path
|
* @param string $path
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
|
@ -426,37 +437,6 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage {
|
||||||
return false;
|
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
|
* 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
|
* does the group share already has a user specific unique name
|
||||||
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function uniqueNameSet() {
|
public function uniqueNameSet() {
|
||||||
|
@ -493,6 +474,7 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get share ID
|
* get share ID
|
||||||
|
*
|
||||||
* @return integer unique share ID
|
* @return integer unique share ID
|
||||||
*/
|
*/
|
||||||
public function getShareId() {
|
public function getShareId() {
|
||||||
|
@ -501,6 +483,7 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get the user who shared the file
|
* get the user who shared the file
|
||||||
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getSharedFrom() {
|
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 share type, can be "file" or "folder"
|
||||||
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getItemType() {
|
public function getItemType() {
|
||||||
|
|
|
@ -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
|
* @param array $params
|
||||||
*/
|
*/
|
||||||
static public function renameHook($params) {
|
static public function renameHook($params) {
|
||||||
self::correctFolders($params['newpath']);
|
|
||||||
self::correctFolders(pathinfo($params['oldpath'], PATHINFO_DIRNAME));
|
|
||||||
self::renameChildren($params['oldpath'], $params['newpath']);
|
self::renameChildren($params['oldpath'], $params['newpath']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +61,6 @@ class Shared_Updater {
|
||||||
*/
|
*/
|
||||||
static public function deleteHook($params) {
|
static public function deleteHook($params) {
|
||||||
$path = $params['path'];
|
$path = $params['path'];
|
||||||
self::correctFolders($path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -242,4 +242,52 @@ class Propagation extends TestCase {
|
||||||
$newRootInfo = $view1->getFileInfo('');
|
$newRootInfo = $view1->getFileInfo('');
|
||||||
$this->assertNotEquals($rootInfo->getEtag(), $newRootInfo->getEtag());
|
$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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ namespace OCA\Files_Sharing\Tests;
|
||||||
|
|
||||||
use OC\Files\Filesystem;
|
use OC\Files\Filesystem;
|
||||||
use OCA\Files\Share;
|
use OCA\Files\Share;
|
||||||
|
use OCA\Files_Sharing\Appinfo\Application;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class Test_Files_Sharing_Base
|
* Class Test_Files_Sharing_Base
|
||||||
|
@ -57,6 +58,10 @@ abstract class TestCase extends \Test\TestCase {
|
||||||
public static function setUpBeforeClass() {
|
public static function setUpBeforeClass() {
|
||||||
parent::setUpBeforeClass();
|
parent::setUpBeforeClass();
|
||||||
|
|
||||||
|
$application = new Application();
|
||||||
|
$application->registerMountProviders();
|
||||||
|
$application->setupPropagation();
|
||||||
|
|
||||||
// reset backend
|
// reset backend
|
||||||
\OC_User::clearBackends();
|
\OC_User::clearBackends();
|
||||||
\OC_Group::clearBackends();
|
\OC_Group::clearBackends();
|
||||||
|
@ -64,7 +69,6 @@ abstract class TestCase extends \Test\TestCase {
|
||||||
// clear share hooks
|
// clear share hooks
|
||||||
\OC_Hook::clear('OCP\\Share');
|
\OC_Hook::clear('OCP\\Share');
|
||||||
\OC::registerShareHooks();
|
\OC::registerShareHooks();
|
||||||
\OCP\Util::connectHook('OC_Filesystem', 'setup', '\OC\Files\Storage\Shared', 'setup');
|
|
||||||
|
|
||||||
// create users
|
// create users
|
||||||
$backend = new \OC_User_Dummy();
|
$backend = new \OC_User_Dummy();
|
||||||
|
@ -147,6 +151,7 @@ abstract class TestCase extends \Test\TestCase {
|
||||||
\OC::$server->getUserSession()->setUser(null);
|
\OC::$server->getUserSession()->setUser(null);
|
||||||
\OC\Files\Filesystem::tearDown();
|
\OC\Files\Filesystem::tearDown();
|
||||||
\OC::$server->getUserSession()->login($user, $password);
|
\OC::$server->getUserSession()->login($user, $password);
|
||||||
|
|
||||||
\OC_Util::setupFS($user);
|
\OC_Util::setupFS($user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2518,6 +2518,7 @@ class Share extends \OC\Share\Constants {
|
||||||
$enforcePassword = $config->getAppValue('core', 'shareapi_enforce_links_password', 'no');
|
$enforcePassword = $config->getAppValue('core', 'shareapi_enforce_links_password', 'no');
|
||||||
return ($enforcePassword === "yes") ? true : false;
|
return ($enforcePassword === "yes") ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all share entries, including non-unique group items
|
* Get all share entries, including non-unique group items
|
||||||
*
|
*
|
||||||
|
@ -2530,4 +2531,15 @@ class Share extends \OC\Share\Constants {
|
||||||
return $result->fetchAll();
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue