mirror of
https://github.com/nextcloud/server.git
synced 2026-03-21 01:52:08 -04:00
Merge pull request #38044 from nextcloud/backport/37787/stable25
This commit is contained in:
commit
f47dcbc36e
2 changed files with 66 additions and 15 deletions
|
|
@ -436,24 +436,26 @@ class Storage {
|
|||
$view->lockFile($path1, ILockingProvider::LOCK_EXCLUSIVE);
|
||||
$view->lockFile($path2, ILockingProvider::LOCK_EXCLUSIVE);
|
||||
|
||||
// TODO add a proper way of overwriting a file while maintaining file ids
|
||||
if ($storage1->instanceOfStorage('\OC\Files\ObjectStore\ObjectStoreStorage') || $storage2->instanceOfStorage('\OC\Files\ObjectStore\ObjectStoreStorage')) {
|
||||
$source = $storage1->fopen($internalPath1, 'r');
|
||||
$target = $storage2->fopen($internalPath2, 'w');
|
||||
[, $result] = \OC_Helper::streamCopy($source, $target);
|
||||
fclose($source);
|
||||
fclose($target);
|
||||
try {
|
||||
// TODO add a proper way of overwriting a file while maintaining file ids
|
||||
if ($storage1->instanceOfStorage('\OC\Files\ObjectStore\ObjectStoreStorage') || $storage2->instanceOfStorage('\OC\Files\ObjectStore\ObjectStoreStorage')) {
|
||||
$source = $storage1->fopen($internalPath1, 'r');
|
||||
$target = $storage2->fopen($internalPath2, 'w');
|
||||
[, $result] = \OC_Helper::streamCopy($source, $target);
|
||||
fclose($source);
|
||||
fclose($target);
|
||||
|
||||
if ($result !== false) {
|
||||
$storage1->unlink($internalPath1);
|
||||
if ($result !== false) {
|
||||
$storage1->unlink($internalPath1);
|
||||
}
|
||||
} else {
|
||||
$result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2);
|
||||
}
|
||||
} else {
|
||||
$result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2);
|
||||
} finally {
|
||||
$view->unlockFile($path1, ILockingProvider::LOCK_EXCLUSIVE);
|
||||
$view->unlockFile($path2, ILockingProvider::LOCK_EXCLUSIVE);
|
||||
}
|
||||
|
||||
$view->unlockFile($path1, ILockingProvider::LOCK_EXCLUSIVE);
|
||||
$view->unlockFile($path2, ILockingProvider::LOCK_EXCLUSIVE);
|
||||
|
||||
return ($result !== false);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,8 +27,13 @@ namespace OCA\Files_Versions\Versions;
|
|||
|
||||
use OCP\Files\File;
|
||||
use OCP\Files\FileInfo;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\Files\Lock\ILock;
|
||||
use OCP\Files\Lock\ILockManager;
|
||||
use OCP\Files\Lock\LockContext;
|
||||
use OCP\Files\Storage\IStorage;
|
||||
use OCP\IUser;
|
||||
use OCP\Lock\ManuallyLockedException;
|
||||
|
||||
class VersionManager implements IVersionManager {
|
||||
/** @var (IVersionBackend[])[] */
|
||||
|
|
@ -94,7 +99,7 @@ class VersionManager implements IVersionManager {
|
|||
|
||||
public function rollback(IVersion $version) {
|
||||
$backend = $version->getBackend();
|
||||
return $backend->rollback($version);
|
||||
return self::handleAppLocks(fn(): ?bool => $backend->rollback($version));
|
||||
}
|
||||
|
||||
public function read(IVersion $version) {
|
||||
|
|
@ -110,4 +115,48 @@ class VersionManager implements IVersionManager {
|
|||
public function useBackendForStorage(IStorage $storage): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Catch ManuallyLockedException and retry in app context if possible.
|
||||
*
|
||||
* Allow users to go back to old versions via the versions tab in the sidebar
|
||||
* even when the file is opened in the viewer next to it.
|
||||
*
|
||||
* Context: If a file is currently opened for editing
|
||||
* the files_lock app will throw ManuallyLockedExceptions.
|
||||
* This prevented the user from rolling an opened file back to a previous version.
|
||||
*
|
||||
* Text and Richdocuments can handle changes of open files.
|
||||
* So we execute the rollback under their lock context
|
||||
* to let them handle the conflict.
|
||||
*
|
||||
* @param callable $callback function to run with app locks handled
|
||||
* @return bool|null
|
||||
* @throws ManuallyLockedException
|
||||
*
|
||||
*/
|
||||
private static function handleAppLocks(callable $callback): ?bool {
|
||||
try {
|
||||
return $callback();
|
||||
} catch (ManuallyLockedException $e) {
|
||||
$owner = (string) $e->getOwner();
|
||||
$appsThatHandleUpdates = array("text", "richdocuments");
|
||||
if (!in_array($owner, $appsThatHandleUpdates)) {
|
||||
throw $e;
|
||||
}
|
||||
// The LockWrapper in the files_lock app only compares the lock type and owner
|
||||
// when checking the lock against the current scope.
|
||||
// So we do not need to get the actual node here
|
||||
// and use the root node instead.
|
||||
$root = \OC::$server->get(IRootFolder::class);
|
||||
$lockContext = new LockContext($root, ILock::TYPE_APP, $owner);
|
||||
$lockManager = \OC::$server->get(ILockManager::class);
|
||||
$result = null;
|
||||
$lockManager->runInScope($lockContext, function() use ($callback, &$result) {
|
||||
$result = $callback();
|
||||
});
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue