Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/text.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulius Härtl <jus@bitgrid.net>2019-06-12 15:32:20 +0300
committerJulius Härtl <jus@bitgrid.net>2019-06-12 15:32:42 +0300
commit6824232ab2269a7b815c66f2b7cc57612f15f201 (patch)
tree61ab9e1f0a4221502187895c7c770bc97e132531
parenta0e5b0ddab7ebbaa4f09b379a7c98ad298b9b90d (diff)
Cleanup sessions/steps when no session are active anymore
Signed-off-by: Julius Härtl <jus@bitgrid.net>
-rw-r--r--lib/Controller/PublicSessionController.php4
-rw-r--r--lib/Controller/SessionController.php4
-rw-r--r--lib/Db/DocumentMapper.php8
-rw-r--r--lib/Db/SessionMapper.php11
-rw-r--r--lib/Db/StepMapper.php9
-rw-r--r--lib/DocumentHasUnsavedChangesException.php29
-rw-r--r--lib/Service/ApiService.php27
-rw-r--r--lib/Service/DocumentService.php85
-rw-r--r--lib/Service/SessionService.php1
-rw-r--r--src/components/EditorWrapper.vue6
-rw-r--r--src/files.js2
-rw-r--r--src/main.js22
-rw-r--r--src/services/SyncService.js6
13 files changed, 132 insertions, 82 deletions
diff --git a/lib/Controller/PublicSessionController.php b/lib/Controller/PublicSessionController.php
index 68eb2c937..9abb5170d 100644
--- a/lib/Controller/PublicSessionController.php
+++ b/lib/Controller/PublicSessionController.php
@@ -74,8 +74,8 @@ class PublicSessionController extends PublicShareController {
* @NoAdminRequired
* @PublicPage
*/
- public function create(string $token, string $file = null, $guestName = null): DataResponse {
- return $this->apiService->create(null, $file, $token, $guestName);
+ public function create(string $token, string $file = null, $guestName = null, $forceRecreate = false): DataResponse {
+ return $this->apiService->create(null, $file, $token, $guestName, $forceRecreate);
}
/**
diff --git a/lib/Controller/SessionController.php b/lib/Controller/SessionController.php
index aff55f0bc..380353a6f 100644
--- a/lib/Controller/SessionController.php
+++ b/lib/Controller/SessionController.php
@@ -25,8 +25,8 @@ class SessionController extends Controller {
/**
* @NoAdminRequired
*/
- public function create(int $fileId = null, string $file = null): DataResponse {
- return $this->apiService->create($fileId, $file);
+ public function create(int $fileId = null, string $file = null, $forceRecreate = false): DataResponse {
+ return $this->apiService->create($fileId, $file, null, null, $forceRecreate);
}
/**
diff --git a/lib/Db/DocumentMapper.php b/lib/Db/DocumentMapper.php
index 9f4113a9d..a4f097b1b 100644
--- a/lib/Db/DocumentMapper.php
+++ b/lib/Db/DocumentMapper.php
@@ -35,7 +35,12 @@ class DocumentMapper extends QBMapper {
parent::__construct($db, 'text_documents', Document::class);
}
- public function find($documentId) {
+ /**
+ * @param $documentId
+ * @return Document
+ * @throws DoesNotExistException
+ */
+ public function find($documentId): Document {
/* @var $qb IQueryBuilder */
$qb = $this->db->getQueryBuilder();
$result = $qb->select('*')
@@ -50,4 +55,5 @@ class DocumentMapper extends QBMapper {
}
return Document::fromRow($data);
}
+
}
diff --git a/lib/Db/SessionMapper.php b/lib/Db/SessionMapper.php
index 36e355d00..87e305cff 100644
--- a/lib/Db/SessionMapper.php
+++ b/lib/Db/SessionMapper.php
@@ -73,6 +73,17 @@ class SessionMapper extends QBMapper {
return $this->findEntities($qb);
}
+ public function findAllInactive() {
+ /* @var $qb IQueryBuilder */
+ $qb = $this->db->getQueryBuilder();
+ $qb->select('id','color','document_id', 'last_contact','user_id','guest_name')
+ ->from($this->getTableName())
+ ->where($qb->expr()->gt('last_contact', $qb->createNamedParameter(time()-SessionService::SESSION_VALID_TIME)))
+ ->execute();
+
+ return $this->findEntities($qb);
+ }
+
public function deleteInactive($documentId) {
/* @var $qb IQueryBuilder */
$qb = $this->db->getQueryBuilder();
diff --git a/lib/Db/StepMapper.php b/lib/Db/StepMapper.php
index b39c1d79b..1f0ec698e 100644
--- a/lib/Db/StepMapper.php
+++ b/lib/Db/StepMapper.php
@@ -59,4 +59,13 @@ class StepMapper extends QBMapper {
->where($qb->expr()->eq('document_id', $qb->createNamedParameter($documentId)))
->execute();
}
+
+ public function deleteBeforeVersion($documentId, $version) {
+ /* @var $qb IQueryBuilder */
+ $qb = $this->db->getQueryBuilder();
+ $qb->delete($this->getTableName())
+ ->where($qb->expr()->eq('document_id', $qb->createNamedParameter($documentId)))
+ ->andWhere($qb->expr()->lte('version', $qb->createNamedParameter($version)))
+ ->execute();
+ }
}
diff --git a/lib/DocumentHasUnsavedChangesException.php b/lib/DocumentHasUnsavedChangesException.php
new file mode 100644
index 000000000..1fe827817
--- /dev/null
+++ b/lib/DocumentHasUnsavedChangesException.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 DocumentHasUnsavedChangesException extends \Exception {
+
+}
diff --git a/lib/Service/ApiService.php b/lib/Service/ApiService.php
index 3fd934a34..1d0c517ba 100644
--- a/lib/Service/ApiService.php
+++ b/lib/Service/ApiService.php
@@ -28,6 +28,7 @@ namespace OCA\Text\Service;
use Exception;
use OC\Files\Node\File;
+use OCA\Text\DocumentHasUnsavedChangesException;
use OCA\Text\DocumentSaveConflictException;
use OCA\Text\VersionMismatchException;
use OCP\AppFramework\Http\DataResponse;
@@ -50,26 +51,36 @@ class ApiService {
$this->documentService = $documentService;
}
- public function create($fileId = null, $filePath = null, $token = null, $guestName = null): DataResponse {
+ public function create($fileId = null, $filePath = null, $token = null, $guestName = null, $forceRecreate = false): DataResponse {
try {
$readOnly = true;
/** @var File $file */
$file = null;
if ($token) {
- list($document, $file) = $this->documentService->createDocumentByShareToken($token, $filePath);
+ $file = $this->documentService->getFileByShareToken($token, $filePath);
try {
$this->documentService->checkSharePermissions($token, Constants::PERMISSION_UPDATE);
$readOnly = false;
} catch (NotFoundException $e) {}
} else if ($fileId) {
- list($document, $file) = $this->documentService->createDocumentByFileId($fileId);
+ $file = $this->documentService->getFileById($fileId);
$readOnly = !$file->isUpdateable();
} else if ($filePath) {
- list($document, $file) = $this->documentService->createDocumentByPath($filePath);
+ $file = $this->documentService->getFileByPath($filePath);
$readOnly = !$file->isUpdateable();
} else {
return new DataResponse('No valid file argument provided', 500);
}
+
+ $this->sessionService->removeInactiveSessions($file->getId());
+ $activeSessions = $this->sessionService->getActiveSessions($file->getId());
+ if (count($activeSessions) === 0) {
+ try {
+ $this->documentService->resetDocument($file->getId(), $forceRecreate);
+ } catch (DocumentHasUnsavedChangesException $e) {}
+ }
+
+ $document = $this->documentService->createDocument($file);
} catch (Exception $e) {
return new DataResponse($e->getMessage(), 500);
}
@@ -97,9 +108,13 @@ class ApiService {
public function close($documentId, $sessionId, $sessionToken): DataResponse {
$this->sessionService->closeSession($documentId, $sessionId, $sessionToken);
- //if ($this->documentService->)
- //$this->sessionService->cleanupSessions();
$this->sessionService->removeInactiveSessions($documentId);
+ $activeSessions = $this->sessionService->getActiveSessions($documentId);
+ if (count($activeSessions) === 0) {
+ try {
+ $this->documentService->resetDocument($documentId);
+ } catch (DocumentHasUnsavedChangesException $e) {}
+ }
return new DataResponse([]);
}
diff --git a/lib/Service/DocumentService.php b/lib/Service/DocumentService.php
index a5b1f7e28..33062c327 100644
--- a/lib/Service/DocumentService.php
+++ b/lib/Service/DocumentService.php
@@ -32,6 +32,7 @@ use OCA\Text\Db\Document;
use OCA\Text\Db\DocumentMapper;
use OCA\Text\Db\Step;
use OCA\Text\Db\StepMapper;
+use OCA\Text\DocumentHasUnsavedChangesException;
use OCA\Text\DocumentSaveConflictException;
use OCA\Text\VersionMismatchException;
use OCP\AppFramework\Db\DoesNotExistException;
@@ -109,53 +110,13 @@ class DocumentService {
}
/**
- * @param $path
- * @return array
- * @throws NotFoundException
- * @throws InvalidPathException
- * @throws NotPermittedException
- */
- public function createDocumentByPath($path) {
- /** @var File $file */
- $file = $this->rootFolder->getUserFolder($this->userId)->get($path);
- return [$this->createDocument($file), $file];
- }
-
- /**
- * @param $fileId
- * @return array
- * @throws NotFoundException
- * @throws InvalidPathException
- * @throws NotPermittedException
- */
- public function createDocumentByFileId($fileId) {
- $file = $this->getFileById($fileId);
- return [$this->createDocument($file), $file];
- }
-
- /**
- * @param $shareToken
- * @param null $filePath
- * @return array
- * @throws InvalidPathException
- * @throws NotFoundException
- * @throws NotPermittedException
- */
- public function createDocumentByShareToken($shareToken, $filePath = null) {
- $file = $this->getFileByShareToken($shareToken, $filePath);
- return [$this->createDocument($file), $file];
- }
-
-
-
- /**
* @param File $file
* @return Entity
* @throws NotFoundException
* @throws InvalidPathException
* @throws NotPermittedException
*/
- protected function createDocument(File $file): Document {
+ public function createDocument(File $file): Document {
try {
$document = $this->documentMapper->find($file->getFileInfo()->getId());
@@ -166,14 +127,6 @@ class DocumentService {
return $document;
}
- // TODO: Only do this when no sessions active, otherise we need to resolve the conflict differently
- // TODO: Add parameter so that we can force this, else just opening the document will cause a rebuild
- $lastMTime = $document->getLastSavedVersionTime();
- if ($file->getMTime() > $lastMTime && $lastMTime > 0) {
- $this->resetDocument($document->getId());
- throw new NotFoundException();
- }
-
return $document;
} catch (DoesNotExistException $e) {
} catch (InvalidPathException $e) {
@@ -304,18 +257,30 @@ class DocumentService {
return $document;
}
- public function resetDocument($documentId): void {
- $this->stepMapper->deleteAll($documentId);
+ /**
+ * @param $documentId
+ * @param bool $force
+ * @throws DocumentHasUnsavedChangesException
+ */
+ public function resetDocument($documentId, $force = false): void {
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) {
+ if ($force || !$this->hasUnsavedChanges($document)) {
+ $this->stepMapper->deleteAll($documentId);
+ $this->documentMapper->delete($document);
+
+ try {
+ $this->appData->getFolder('documents')->getFile($documentId)->delete();
+ } catch (NotFoundException $e) {
+ } catch (NotPermittedException $e) {
+ }
+ }
+
+ if ($this->hasUnsavedChanges($document)) {
+ throw new DocumentHasUnsavedChangesException('Did not reset document, as it has unsaved changes');
+ }
+ } catch (DoesNotExistException $e) {
}
}
@@ -323,6 +288,10 @@ class DocumentService {
return $this->rootFolder->getUserFolder($this->userId)->getById($fileId)[0];
}
+ public function getFileByPath($path): Node {
+ return $this->rootFolder->getUserFolder($this->userId)->get($path);
+ }
+
/**
* @param $shareToken
* @param null|string $path
diff --git a/lib/Service/SessionService.php b/lib/Service/SessionService.php
index 9de0ea0a9..fe796955c 100644
--- a/lib/Service/SessionService.php
+++ b/lib/Service/SessionService.php
@@ -89,6 +89,7 @@ class SessionService {
public function removeInactiveSessions($documentId) {
return $this->sessionMapper->deleteInactive($documentId);
}
+
public function isValidSession($documentId, $sessionId, $token) {
try {
$session = $this->sessionMapper->find($documentId, $sessionId, $token);
diff --git a/src/components/EditorWrapper.vue b/src/components/EditorWrapper.vue
index b45c2e2b2..5de594bd4 100644
--- a/src/components/EditorWrapper.vue
+++ b/src/components/EditorWrapper.vue
@@ -183,6 +183,7 @@ export default {
lastSavedString: '',
syncError: null,
readOnly: true,
+ forceRecreate: false,
guestName: '',
guestNameConfirmed: false,
@@ -283,6 +284,7 @@ export default {
this.syncService = new SyncService({
shareToken: this.shareToken,
guestName: this.guestName,
+ forceRecreate: this.forceRecreate,
serialize: (document) => {
const markdown = (createMarkdownSerializer(this.tiptap.nodes, this.tiptap.marks)).serialize(document)
console.debug('serialized document', { markdown })
@@ -316,7 +318,7 @@ export default {
new Collaboration({
// the initial version we start with
// version is an integer which is incremented with every change
- version: this.syncService.steps.length,
+ version: this.document.initialVersion,
clientID: this.currentSession.id,
// debounce changes so we can save some bandwidth
debounce: EDITOR_PUSH_DEBOUNCE,
@@ -370,6 +372,7 @@ export default {
}
})
this.syncService.open({ fileId: this.fileId, filePath: this.filePath })
+ this.forceRecreate = false
},
resolveUseThisVersion() {
@@ -378,6 +381,7 @@ export default {
},
resolveUseServerVersion() {
+ this.forceRecreate = true
this.syncService.close()
this.syncService = null
this.tiptap.destroy()
diff --git a/src/files.js b/src/files.js
index 8ce91f0be..5bb990507 100644
--- a/src/files.js
+++ b/src/files.js
@@ -80,5 +80,5 @@ documentReady(() => {
})
OCA.Text = {
-
+ Editor
}
diff --git a/src/main.js b/src/main.js
index f4639a102..404cd20a4 100644
--- a/src/main.js
+++ b/src/main.js
@@ -1,5 +1,4 @@
import Vue from 'vue'
-
import Editor from './components/EditorWrapper'
__webpack_nonce__ = btoa(OC.requestToken) // eslint-disable-line
@@ -8,12 +7,17 @@ __webpack_public_path__ = OC.linkTo('text', 'js/') // eslint-disable-line
Vue.prototype.t = t
Vue.prototype.OCA = OCA
-new Vue({
- render: h => h(Editor, {
- props: {
- relativePath: '/welcome.md',
- active: true
- }
- })
+if (document.getElementById('maineditor')) {
+ new Vue({
+ render: h => h(Editor, {
+ props: {
+ relativePath: '/welcome.md',
+ active: true
+ }
+ })
+ }).$mount('#maineditor')
+}
-}).$mount('#maineditor')
+OCA.Text = {
+ Editor
+}
diff --git a/src/services/SyncService.js b/src/services/SyncService.js
index f63113f74..976c7b5b6 100644
--- a/src/services/SyncService.js
+++ b/src/services/SyncService.js
@@ -27,6 +27,7 @@ import { getVersion, sendableSteps } from 'prosemirror-collab'
const defaultOptions = {
shareToken: null,
+ forceRecreate: false,
serialize: (document) => document
}
@@ -90,7 +91,7 @@ class SyncService {
this.emit('loaded', {
document: this.document,
session: this.session,
- documentSource: data
+ documentSource: '' + data
})
})
}).catch((error) => {
@@ -109,7 +110,8 @@ class SyncService {
fileId: fileId,
file: filePath,
token: this.options.shareToken,
- guestName: this.options.guestName
+ guestName: this.options.guestName,
+ forceRecreate: this.options.forceRecreate
}
}).then((response) => {
this.document = response.data.document