propagate etags for all user of a share
parent
518d5aadf5
commit
30ad56813a
@ -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');
|
||||
}
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue