diff options
author | Julius Härtl <jus@bitgrid.net> | 2022-03-23 16:54:21 +0300 |
---|---|---|
committer | Julius Härtl <jus@bitgrid.net> | 2022-04-21 14:27:43 +0300 |
commit | 8c00c8eb25b622ac2f32b9c6e2928ca8fcae7feb (patch) | |
tree | 16185e3da6f509516e84a60314ef811cc6b2cef6 /lib | |
parent | bc020a0bbc6dadd096c5f1fef3760481f534b775 (diff) |
Implement collaborative locking
Signed-off-by: Julius Härtl <jus@bitgrid.net>
TMP: Switch to OCP branch for tests
Signed-off-by: Julius Härtl <jus@bitgrid.net>
Let locked files open in read only
Signed-off-by: Julius Härtl <jus@bitgrid.net>
Adapt LockScope to LockContext rename
Signed-off-by: Julius Härtl <jus@bitgrid.net>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Service/ApiService.php | 6 | ||||
-rw-r--r-- | lib/Service/DocumentService.php | 106 |
2 files changed, 72 insertions, 40 deletions
diff --git a/lib/Service/ApiService.php b/lib/Service/ApiService.php index 1ef829466..850c72c74 100644 --- a/lib/Service/ApiService.php +++ b/lib/Service/ApiService.php @@ -113,6 +113,12 @@ class ApiService { $this->logger->logException($e, ['level' => ILogger::INFO]); $content = null; } + + $isLocked = $this->documentService->lock($fileId); + if (!$isLocked) { + $readOnly = true; + } + return new DataResponse([ 'document' => $document, 'session' => $session, diff --git a/lib/Service/DocumentService.php b/lib/Service/DocumentService.php index 9ab2e606b..319412f1d 100644 --- a/lib/Service/DocumentService.php +++ b/lib/Service/DocumentService.php @@ -27,11 +27,18 @@ declare(strict_types=1); namespace OCA\Text\Service; use \InvalidArgumentException; +use OCA\Text\AppInfo\Application; use OCA\Text\Db\Session; use OCA\Text\Db\SessionMapper; use OCP\DirectEditing\IManager; +use OCP\Files\Lock\ILock; +use OCP\Files\Lock\ILockManager; +use OCP\Files\Lock\LockContext; +use OCP\Files\Lock\NoLockProviderException; +use OCP\Files\Lock\OwnerLockedException; use OCP\IRequest; use OCP\Lock\ILockingProvider; +use OCP\PreConditionNotMetException; use function json_encode; use OC\Files\Node\File; use OCA\Text\Db\Document; @@ -67,44 +74,18 @@ class DocumentService { */ public const AUTOSAVE_MINIMUM_DELAY = 10; - /** - * @var string|null - */ - private $userId; - /** - * @var DocumentMapper - */ - private $documentMapper; - /** - * @var SessionMapper - */ - private $sessionMapper; - /** - * @var ILogger - */ - private $logger; - /** - * @var ShareManager - */ - private $shareManager; - /** - * @var StepMapper - */ - private $stepMapper; - /** - * @var IRootFolder - */ - private $rootFolder; - /** - * @var ICache - */ - private $cache; - /** - * @var IAppData - */ - private $appData; - - public function __construct(DocumentMapper $documentMapper, StepMapper $stepMapper, SessionMapper $sessionMapper, IAppData $appData, $userId, IRootFolder $rootFolder, ICacheFactory $cacheFactory, ILogger $logger, ShareManager $shareManager, IRequest $request, IManager $directManager, ILockingProvider $lockingProvider) { + private ?string $userId; + private DocumentMapper $documentMapper; + private SessionMapper $sessionMapper; + private ILogger $logger; + private ShareManager $shareManager; + private StepMapper $stepMapper; + private IRootFolder $rootFolder; + private ICache $cache; + private IAppData $appData; + private ILockManager $lockManager; + + public function __construct(DocumentMapper $documentMapper, StepMapper $stepMapper, SessionMapper $sessionMapper, IAppData $appData, $userId, IRootFolder $rootFolder, ICacheFactory $cacheFactory, ILogger $logger, ShareManager $shareManager, IRequest $request, IManager $directManager, ILockingProvider $lockingProvider, ILockManager $lockManager) { $this->documentMapper = $documentMapper; $this->stepMapper = $stepMapper; $this->sessionMapper = $sessionMapper; @@ -115,7 +96,7 @@ class DocumentService { $this->logger = $logger; $this->shareManager = $shareManager; $this->lockingProvider = $lockingProvider; - + $this->lockManager = $lockManager; $token = $request->getParam('token'); if ($this->userId === null && $token !== null) { try { @@ -328,7 +309,13 @@ class DocumentService { } $this->cache->set('document-save-lock-' . $documentId, true, 10); try { - $file->putContent($autoaveDocument); + $this->lockManager->runInScope(new LockContext( + $file, + ILock::TYPE_APP, + Application::APP_NAME + ), function () use ($file, $autoaveDocument) { + $file->putContent($autoaveDocument); + }); } catch (LockedException $e) { // Ignore lock since it might occur when multiple people save at the same time return $document; @@ -348,6 +335,8 @@ class DocumentService { */ public function resetDocument($documentId, $force = false): void { try { + $this->unlock($documentId); + $document = $this->documentMapper->find($documentId); if ($force || !$this->hasUnsavedChanges($document)) { @@ -490,4 +479,41 @@ class DocumentService { return true; } + + public function lock(int $fileId): bool { + if (!$this->lockManager->isLockProviderAvailable()) { + return true; + } + + $file = $this->getFileById($fileId); + try { + $this->lockManager->lock(new LockContext( + $file, + ILock::TYPE_APP, + Application::APP_NAME + )); + } catch (NoLockProviderException $e) { + } catch (OwnerLockedException $e) { + return false; + } catch (PreConditionNotMetException $e) { + } + return true; + } + + public function unlock(int $fileId): void { + if (!$this->lockManager->isLockProviderAvailable()) { + return; + } + + $file = $this->getFileById($fileId); + try { + $this->lockManager->unlock(new LockContext( + $file, + ILock::TYPE_APP, + Application::APP_NAME + )); + } catch (NoLockProviderException $e) { + } catch (PreConditionNotMetException $e) { + } + } } |