diff options
author | Julius Härtl <jus@bitgrid.net> | 2019-04-23 13:00:59 +0300 |
---|---|---|
committer | Julius Härtl <jus@bitgrid.net> | 2019-04-23 13:00:59 +0300 |
commit | 2660b7fee51fe87c375307891e4a8abb98c07a69 (patch) | |
tree | 0be402f081d029bdc6a82bd80e8df0b7add65ce6 /lib | |
parent | 1bdb0ff3eaa0f8a874bbf306ca5fe8a45f8e6ff4 (diff) |
Add first file handling
Signed-off-by: Julius Härtl <jus@bitgrid.net>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Controller/SessionController.php | 19 | ||||
-rw-r--r-- | lib/Db/Document.php | 13 | ||||
-rw-r--r-- | lib/Db/StepMapper.php | 8 | ||||
-rw-r--r-- | lib/DocumentSaveConflictException.php | 29 | ||||
-rw-r--r-- | lib/Service/DocumentService.php | 76 | ||||
-rw-r--r-- | lib/Service/SessionService.php | 1 |
6 files changed, 129 insertions, 17 deletions
diff --git a/lib/Controller/SessionController.php b/lib/Controller/SessionController.php index 7416bd5c3..2347d953c 100644 --- a/lib/Controller/SessionController.php +++ b/lib/Controller/SessionController.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace OCA\Text\Controller; +use OC\Files\Node\File; use OCA\Text\Service\DocumentService; use OCA\Text\Service\SessionService; use OCP\AppFramework\Controller; @@ -16,6 +17,7 @@ use OCP\ICacheFactory; use OCP\IRequest; use OCP\ITempManager; use OCP\Security\ISecureRandom; +use OCA\Text\DocumentSaveConflictException; class SessionController extends Controller { @@ -97,13 +99,26 @@ class SessionController extends Controller { * @NoCSRFRequired * @NoAdminRequired */ - public function sync($documentId, $version = 0): DataResponse { + public function sync($documentId, $sessionId, $token, $version = 0, $autosaveContent = null): DataResponse { + if (!$this->sessionService->isValidSession($documentId, $sessionId, $token)) { + return new DataResponse([], 500); + } if ($version === $this->cache->get('document-version-'.$documentId)) { return new DataResponse(['steps' => []]); } + try { + $document = $this->documentService->autosave($documentId, $version, $autosaveContent); + } catch (DocumentSaveConflictException $e) { + /** @var File $file */ + $file = $this->documentService->getFile($documentId); + return new DataResponse([ + 'outsideChange' => $file->getContent() + ], 409); + } return new DataResponse([ 'steps' => $this->documentService->getSteps($documentId, $version), - 'sessions' => $this->sessionService->getActiveSessions($documentId) + 'sessions' => $this->sessionService->getActiveSessions($documentId), + 'document' => $document ]); } diff --git a/lib/Db/Document.php b/lib/Db/Document.php index 3155eac73..5c1e30c06 100644 --- a/lib/Db/Document.php +++ b/lib/Db/Document.php @@ -26,18 +26,29 @@ namespace OCA\Text\Db; use OCP\AppFramework\Db\Entity; -class Document extends Entity { +class Document extends Entity implements \JsonSerializable { public $id; protected $currentVersion = 0; protected $lastSavedVersion = 0; protected $initialVersion = 0; + protected $lastSavedVersionTime = 0; public function __construct() { $this->addType('id', 'integer'); $this->addType('currentVersion', 'integer'); $this->addType('lastSavedVersion', 'integer'); + $this->addType('lastSavedVersionTime', 'integer'); $this->addType('initialVersion', 'integer'); } + public function jsonSerialize() { + return [ + 'id' => $this->id, + 'currentVersion' => $this->currentVersion, + 'lastSavedVersion' => $this->lastSavedVersion, + 'lastSavedVersionTime' => $this->lastSavedVersionTime, + ]; + } + } diff --git a/lib/Db/StepMapper.php b/lib/Db/StepMapper.php index d8b370592..847409750 100644 --- a/lib/Db/StepMapper.php +++ b/lib/Db/StepMapper.php @@ -45,4 +45,12 @@ class StepMapper extends QBMapper { return $this->findEntities($qb); } + + public function deleteAll($documentId) { + /* @var $qb IQueryBuilder */ + $qb = $this->db->getQueryBuilder(); + $qb->delete($this->getTableName()) + ->where($qb->expr()->eq('document_id', $qb->createNamedParameter($documentId))) + ->execute(); + } } diff --git a/lib/DocumentSaveConflictException.php b/lib/DocumentSaveConflictException.php new file mode 100644 index 000000000..927c90a6a --- /dev/null +++ b/lib/DocumentSaveConflictException.php @@ -0,0 +1,29 @@ +<?php +/** + * @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net> + * + * @author Julius Härtl <jus@bitgrid.net> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\Text; + + +class DocumentSaveConflictException extends \Exception { + +} diff --git a/lib/Service/DocumentService.php b/lib/Service/DocumentService.php index 77c67557a..e68bfebd5 100644 --- a/lib/Service/DocumentService.php +++ b/lib/Service/DocumentService.php @@ -29,15 +29,21 @@ use OCA\Text\Db\DocumentMapper; use OCA\Text\Db\SessionMapper; use OCA\Text\Db\Step; use OCA\Text\Db\StepMapper; +use OCA\Text\DocumentSaveConflictException; use OCP\AppFramework\Db\DoesNotExistException; use OCP\Files\IAppData; use OCP\Files\InvalidPathException; use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; +use OCP\Files\NotPermittedException; use OCP\ICacheFactory; class DocumentService { + /** + * Delay to wait for between autosave versions + */ + const AUTOSAVE_MINIMUM_DELAY = 10; private $sessionMapper; private $userId; @@ -80,9 +86,12 @@ class DocumentService { * @throws \OCP\Files\NotPermittedException */ public function getDocumentById($fileId) { + return $this->createDocument($this->getFile($fileId)); + } + + public function getFile($fileId) { /** @var File $file */ - $file = $this->rootFolder->getUserFolder($this->userId)->getById($fileId); - return $this->createDocument($file); + return $this->rootFolder->getUserFolder($this->userId)->getById($fileId)[0]; } /** @@ -93,24 +102,23 @@ class DocumentService { * @throws \OCP\Files\NotPermittedException */ protected function createDocument(File $file) { - /* remove this after debugging */ - try { - $documentBaseFile = $this->appData->getFolder('documents')->getFile($file->getFileInfo()->getId()); - } catch (NotFoundException $e) { - $documentBaseFile = $this->appData->getFolder('documents')->newFile($file->getFileInfo()->getId()); - } - $documentBaseFile->putContent($file->fopen('r')); - /** endremove */ - try { $document = $this->documentMapper->find($file->getFileInfo()->getId()); + + // TODO: do not hard reset if changed from outside since this will throw away possible steps + // TODO: Only do this when no sessions active, otherise we need to resolve the conflict differently + $lastMTime = $document->getLastSavedVersionTime(); + if ($file->getMTime() > $lastMTime && $lastMTime > 0) { + $this->resetDocument($document->getId()); + throw new NotFoundException(); + } + return $document; } catch (DoesNotExistException $e) { } catch (InvalidPathException $e) { } catch (NotFoundException $e) { } - // TODO: lock file - // TODO: unlock after saving + try { $documentBaseFile = $this->appData->getFolder('documents')->getFile($file->getFileInfo()->getId()); } catch (NotFoundException $e) { @@ -122,6 +130,7 @@ class DocumentService { $document->setId($file->getFileInfo()->getId()); $document->setCurrentVersion(0); $document->setLastSavedVersion(0); + $document->setLastSavedVersionTime($file->getFileInfo()->getMtime()); $document = $this->documentMapper->insert($document); $this->cache->set('document-version-'.$document->getId(), 0); return $document; @@ -155,7 +164,6 @@ class DocumentService { $document->setCurrentVersion($newVersion); $this->documentMapper->update($document); $this->cache->set('document-version-'.$document->getId(), $newVersion); - // TODO write version to cache for quicker checking // TODO write steps to cache for quicker reading return $steps; } @@ -164,4 +172,44 @@ class DocumentService { return $this->stepMapper->find($documentId, $lastVersion); } + public function autosave($documentId, $version, $autoaveDocument, $force = false, $manualSave = false) { + /** @var Document $document */ + $document = $this->documentMapper->find($documentId); + $lastMTime = $document->getLastSavedVersionTime(); + /** @var File $file */ + $file = $this->rootFolder->getUserFolder($this->userId)->getById($documentId)[0]; + if ($file->getMTime() > $lastMTime && $lastMTime > 0 && $force === false) { + throw new DocumentSaveConflictException('File changed in the meantime from outside'); + } + // TODO: check for etag rather than mtime + // Do not save if version already saved + if ($version === (string)$document->getLastSavedVersion()) { + return null; + } + // Only save once every AUTOSAVE_MINIMUM_DELAY seconds + if ($file->getMTime() === $lastMTime && $lastMTime > time()- self::AUTOSAVE_MINIMUM_DELAY && $manualSave === false) { + return null; + } + $file->putContent($autoaveDocument); + $document->setLastSavedVersion($version); + $document->setLastSavedVersionTime(time()); + $this->documentMapper->update($document); + return $document; + } + + public function resetDocument($documentId) { + $this->stepMapper->deleteAll($documentId); + try { + $document = $this->documentMapper->find($documentId); + $this->documentMapper->delete($document); + } catch (DoesNotExistException $e) { + } + + try { + $this->appData->getFolder('documents')->getFile($documentId)->delete(); + } catch (NotFoundException $e) { + } catch (NotPermittedException $e) { + } + } + } diff --git a/lib/Service/SessionService.php b/lib/Service/SessionService.php index 748baf2f5..94bc145ad 100644 --- a/lib/Service/SessionService.php +++ b/lib/Service/SessionService.php @@ -95,6 +95,7 @@ class SessionService { } catch (DoesNotExistException $e) { return false; } + // TODO: move to cache $session->setLastContact($this->timeFactory->getTime()); $this->sessionMapper->update($session); return true; |