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

github.com/ONLYOFFICE/onlyoffice-nextcloud.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Linnik <sergey.linnik@onlyoffice.com>2020-09-17 10:58:02 +0300
committerGitHub <noreply@github.com>2020-09-17 10:58:02 +0300
commita4dcd81d4e5da4a7db95feff32f043b4f096ae76 (patch)
treecea95abfafef859bfaa2b042694270d02c4f4414
parentdc5082828f943fb0b6cb274447680dbc07451eec (diff)
parent44d8e51712777ffd15a064650d0bc8c6b5bbbbcd (diff)
Release/6.0.0v6.0.0
Release/6.0.0
-rw-r--r--CHANGELOG.md9
-rw-r--r--appinfo/database.xml54
-rw-r--r--appinfo/info.xml2
-rw-r--r--controller/callbackcontroller.php60
-rw-r--r--controller/editorcontroller.php36
-rw-r--r--controller/federationcontroller.php11
-rw-r--r--controller/settingscontroller.php4
-rw-r--r--css/main.css3
-rw-r--r--js/editor.js4
-rw-r--r--js/main.js10
-rw-r--r--js/settings.js2
-rw-r--r--l10n/bg.js3
-rw-r--r--l10n/bg.json3
-rw-r--r--l10n/de.js3
-rw-r--r--l10n/de.json3
-rw-r--r--l10n/de_DE.js3
-rw-r--r--l10n/de_DE.json3
-rw-r--r--l10n/es.js3
-rw-r--r--l10n/es.json3
-rw-r--r--l10n/fr.js3
-rw-r--r--l10n/fr.json3
-rw-r--r--l10n/ja.js3
-rw-r--r--l10n/ja.json3
-rw-r--r--l10n/ru.js3
-rw-r--r--l10n/ru.json3
-rw-r--r--l10n/zh_CN.js90
-rw-r--r--l10n/zh_CN.json88
-rw-r--r--lib/appconfig.php34
-rw-r--r--lib/fileutility.php30
-rw-r--r--lib/hooks.php39
-rw-r--r--lib/keymanager.php150
-rw-r--r--templates/settings.php10
32 files changed, 604 insertions, 74 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c06efc8..7f71ed7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,14 @@
# Change Log
+## 6.0.0
+## Added
+- saving intermediate versions when editing
+- Chinese translation
+
+## Changed
+- fix image insertion
+- fix styles for inline editor
+
## 5.0.0
## Added
- support for OpenDocument Templates
diff --git a/appinfo/database.xml b/appinfo/database.xml
new file mode 100644
index 0000000..a87a90e
--- /dev/null
+++ b/appinfo/database.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<database>
+ <name>*dbname*</name>
+ <create>true</create>
+ <overwrite>false</overwrite>
+ <charset>utf8</charset>
+ <table>
+ <name>*dbprefix*onlyoffice_filekey</name>
+ <declaration>
+ <field>
+ <name>id</name>
+ <type>integer</type>
+ <default>0</default>
+ <notnull>true</notnull>
+ <autoincrement>1</autoincrement>
+ </field>
+ <field>
+ <name>file_id</name>
+ <type>bigint</type>
+ <default>-1</default>
+ <comments>internal file identifier</comments>
+ </field>
+ <field>
+ <name>key</name>
+ <type>text</type>
+ <notnull>true</notnull>
+ <length>128</length>
+ <comments>unique key for the docoment server</comments>
+ </field>
+ <field>
+ <name>lock</name>
+ <type>integer</type>
+ <default>0</default>
+ <notnull>true</notnull>
+ <comments>do not delete on file change</comments>
+ </field>
+ <field>
+ <name>fs</name>
+ <type>integer</type>
+ <default>0</default>
+ <notnull>true</notnull>
+ <comments>last forcesave done</comments>
+ </field>
+ <index>
+ <name>file_id_index</name>
+ <unique>true</unique>
+ <field>
+ <name>file_id</name>
+ <sorting>ascending</sorting>
+ </field>
+ </index>
+ </declaration>
+ </table>
+</database>
diff --git a/appinfo/info.xml b/appinfo/info.xml
index 8f3f885..5adfbff 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -6,7 +6,7 @@
<description>ONLYOFFICE connector enables you to edit Office documents within ONLYOFFICE from the familiar web interface. This will create a new Open in ONLYOFFICE action within the document library for Office documents. This allows multiple users to collaborate in real time and to save back those changes to your file storage.</description>
<licence>apache</licence>
<author mail="dev@onlyoffice.com" homepage="https://www.onlyoffice.com/">Ascensio System SIA</author>
- <version>5.0.0</version>
+ <version>6.0.0</version>
<namespace>Onlyoffice</namespace>
<types>
<filesystem/>
diff --git a/controller/callbackcontroller.php b/controller/callbackcontroller.php
index 8742ce6..1413d9f 100644
--- a/controller/callbackcontroller.php
+++ b/controller/callbackcontroller.php
@@ -37,10 +37,12 @@ use OCP\Lock\LockedException;
use OCP\Share\Exceptions\ShareNotFound;
use OCP\Share\IManager;
+use OCA\Files_Sharing\External\Storage as SharingExternalStorage;
use OCA\Files_Versions\Versions\IVersionManager;
use OCA\Onlyoffice\AppConfig;
use OCA\Onlyoffice\Crypt;
+use OCA\Onlyoffice\KeyManager;
use OCA\Onlyoffice\DocumentService;
use OCA\Onlyoffice\FileVersions;
@@ -111,21 +113,18 @@ class CallbackController extends Controller {
* File version manager
*
* @var IVersionManager
- */
+ */
private $versionManager;
/**
* Status of the document
- *
- * @var Array
*/
- private $_trackerStatus = [
- 0 => "NotFound",
- 1 => "Editing",
- 2 => "MustSave",
- 3 => "Corrupted",
- 4 => "Closed"
- ];
+ private const TrackerStatus_Editing = 1;
+ private const TrackerStatus_MustSave = 2;
+ private const TrackerStatus_Corrupted = 3;
+ private const TrackerStatus_Closed = 4;
+ private const TrackerStatus_ForceSave = 6;
+ private const TrackerStatus_CorruptedForceSave = 7;
/**
* @param string $AppName - application name
@@ -416,14 +415,14 @@ class CallbackController extends Controller {
$url = isset($payload->url) ? $payload->url : null;
}
- $trackerStatus = $this->_trackerStatus[$status];
-
$result = 1;
- switch ($trackerStatus) {
- case "MustSave":
- case "Corrupted":
+ switch ($status) {
+ case self::TrackerStatus_MustSave:
+ case self::TrackerStatus_Corrupted:
+ case self::TrackerStatus_ForceSave:
+ case self::TrackerStatus_CorruptedForceSave:
if (empty($url)) {
- $this->logger->error("Track without url: $fileId status $trackerStatus", ["app" => $this->appName]);
+ $this->logger->error("Track without url: $fileId status $status", ["app" => $this->appName]);
return new JSONResponse(["message" => "Url not found"], Http::STATUS_BAD_REQUEST);
}
@@ -449,7 +448,7 @@ class CallbackController extends Controller {
// author of the callback link
$userId = $hashData->userId;
\OC_User::setUserId($userId);
- $this->logger->debug("Track for $userId: $fileId status $trackerStatus", ["app" => $this->appName]);
+ $this->logger->debug("Track for $userId: $fileId status $status", ["app" => $this->appName]);
$user = $this->userManager->get($userId);
if (!empty($user)) {
@@ -492,12 +491,31 @@ class CallbackController extends Controller {
$newData = $documentService->Request($url);
+ $prevIsForcesave = KeyManager::wasForcesave($fileId);
+
+ $isForcesave = $status === self::TrackerStatus_ForceSave || $status === self::TrackerStatus_CorruptedForceSave;
+
+ if ($isForcesave
+ && $file->getStorage()->instanceOfStorage(SharingExternalStorage::class)) {
+ $this->logger->info("Track: $fileId status $status not allowed for external file", ["app" => $this->appName]);
+ break;
+ }
+
+ //lock the key when forcesave and unlock if last forcesave is broken
+ KeyManager::lock($fileId, $isForcesave);
+
$this->logger->debug("Track put content " . $file->getPath(), ["app" => $this->appName]);
$this->retryOperation(function () use ($file, $newData) {
return $file->putContent($newData);
});
- if ($this->versionManager !== null) {
+ //unlock key for future federated save
+ KeyManager::lock($fileId, false);
+ KeyManager::setForcesave($fileId, $isForcesave);
+
+ if (!$isForcesave
+ && !$prevIsForcesave
+ && $this->versionManager !== null) {
$changes = null;
if (!empty($changesurl)) {
$changesurl = $this->config->ReplaceDocumentServerUrlToInternal($changesurl);
@@ -508,12 +526,12 @@ class CallbackController extends Controller {
$result = 0;
} catch (\Exception $e) {
- $this->logger->logException($e, ["message" => "Track: $fileId status $trackerStatus error", "app" => $this->appName]);
+ $this->logger->logException($e, ["message" => "Track: $fileId status $status error", "app" => $this->appName]);
}
break;
- case "Editing":
- case "Closed":
+ case self::TrackerStatus_Editing:
+ case self::TrackerStatus_Closed:
$result = 0;
break;
}
diff --git a/controller/editorcontroller.php b/controller/editorcontroller.php
index 1de77d0..ea82b4e 100644
--- a/controller/editorcontroller.php
+++ b/controller/editorcontroller.php
@@ -39,6 +39,7 @@ use OCP\IUserSession;
use OCP\Share\IManager;
use OCA\Files\Helper;
+use OCA\Files_Sharing\External\Storage as SharingExternalStorage;
use OCA\Files_Versions\Versions\IVersionManager;
use OCA\Onlyoffice\AppConfig;
@@ -962,6 +963,11 @@ class EditorController extends Controller {
$params = $this->setCustomization($params);
+ if ($file->getStorage()->instanceOfStorage(SharingExternalStorage::class)) {
+ //otherwise forcesave will delete the key
+ $params["editorConfig"]["customization"]["forcesave"] = false;
+ }
+
$params = $this->setWatermark($params, !empty($shareToken), $userId, $file);
if ($this->config->UseDemo()) {
@@ -1104,35 +1110,40 @@ class EditorController extends Controller {
$params["editorConfig"]["customization"]["feedback"] = true;
}
+ //default is false
+ if ($this->config->GetCustomizationForcesave() === true) {
+ $params["editorConfig"]["customization"]["forcesave"] = true;
+ }
+
//default is true
if ($this->config->GetCustomizationHelp() === false) {
$params["editorConfig"]["customization"]["help"] = false;
}
- //default is false
- if ($this->config->GetCustomizationToolbarNoTabs() === true) {
- $params["editorConfig"]["customization"]["toolbarNoTabs"] = true;
- }
-
//default is original
$reviewDisplay = $this->config->GetCustomizationReviewDisplay();
if ($reviewDisplay !== "original") {
$params["editorConfig"]["customization"]["reviewDisplay"] = $reviewDisplay;
}
+ //default is false
+ if ($this->config->GetCustomizationToolbarNoTabs() === true) {
+ $params["editorConfig"]["customization"]["toolbarNoTabs"] = true;
+ }
+
/* from system config */
+ $autosave = $this->config->GetSystemValue($this->config->_customization_autosave);
+ if (isset($autosave)) {
+ $params["editorConfig"]["customization"]["autosave"] = $autosave;
+ }
+
$customer = $this->config->GetSystemValue($this->config->_customization_customer);
if (isset($customer)) {
$params["editorConfig"]["customization"]["customer"] = $customer;
}
- $feedback = $this->config->GetSystemValue($this->config->_customization_feedback);
- if (isset($feedback)) {
- $params["editorConfig"]["customization"]["feedback"] = $feedback;
- }
-
$loaderLogo = $this->config->GetSystemValue($this->config->_customization_loaderLogo);
if (isset($loaderLogo)) {
$params["editorConfig"]["customization"]["loaderLogo"] = $loaderLogo;
@@ -1153,11 +1164,6 @@ class EditorController extends Controller {
$params["editorConfig"]["customization"]["zoom"] = $zoom;
}
- $autosave = $this->config->GetSystemValue($this->config->_customization_autosave);
- if (isset($autosave)) {
- $params["editorConfig"]["customization"]["autosave"] = $autosave;
- }
-
return $params;
}
diff --git a/controller/federationcontroller.php b/controller/federationcontroller.php
index b3eea62..5e98f78 100644
--- a/controller/federationcontroller.php
+++ b/controller/federationcontroller.php
@@ -28,6 +28,7 @@ use OCP\ISession;
use OCP\Share\IManager;
use OCA\Onlyoffice\AppConfig;
+use OCA\Onlyoffice\DocumentService;
use OCA\Onlyoffice\FileUtility;
/**
@@ -43,13 +44,6 @@ class FederationController extends OCSController {
private $logger;
/**
- * Share manager
- *
- * @var IManager
- */
- private $shareManager;
-
- /**
* Application configuration
*
* @var AppConfig
@@ -81,7 +75,6 @@ class FederationController extends OCSController {
parent::__construct($AppName, $request);
$this->logger = $logger;
- $this->shareManager = $shareManager;
$this->config = new AppConfig($this->appName);
$this->fileUtility = new FileUtility($AppName, $trans, $logger, $this->config, $shareManager, $session);
@@ -109,6 +102,8 @@ class FederationController extends OCSController {
$key = $this->fileUtility->getKey($file, true);
+ $key = DocumentService::GenerateRevisionId($key);
+
$this->logger->debug("Federated request get for " . $file->getId() . " key $key", ["app" => $this->appName]);
return new DataResponse(["key" => $key]);
diff --git a/controller/settingscontroller.php b/controller/settingscontroller.php
index 0fc88e1..654248b 100644
--- a/controller/settingscontroller.php
+++ b/controller/settingscontroller.php
@@ -117,6 +117,7 @@ class SettingsController extends Controller {
"chat" => $this->config->GetCustomizationChat(),
"compactHeader" => $this->config->GetCustomizationCompactHeader(),
"feedback" => $this->config->GetCustomizationFeedback(),
+ "forcesave" => $this->config->GetCustomizationForcesave(),
"help" => $this->config->GetCustomizationHelp(),
"toolbarNoTabs" => $this->config->GetCustomizationToolbarNoTabs(),
"successful" => $this->config->SettingsAreSuccessful(),
@@ -188,6 +189,7 @@ class SettingsController extends Controller {
* @param bool $chat - display chat
* @param bool $compactHeader - display compact header
* @param bool $feedback - display feedback
+ * @param bool $forcesave - forcesave
* @param bool $help - display help
* @param bool $toolbarNoTabs - display toolbar tab
* @param string $reviewDisplay - review viewing mode
@@ -201,6 +203,7 @@ class SettingsController extends Controller {
$chat,
$compactHeader,
$feedback,
+ $forcesave,
$help,
$toolbarNoTabs,
$reviewDisplay
@@ -213,6 +216,7 @@ class SettingsController extends Controller {
$this->config->SetCustomizationChat($chat);
$this->config->SetCustomizationCompactHeader($compactHeader);
$this->config->SetCustomizationFeedback($feedback);
+ $this->config->SetCustomizationForcesave($forcesave);
$this->config->SetCustomizationHelp($help);
$this->config->SetCustomizationToolbarNoTabs($toolbarNoTabs);
$this->config->SetCustomizationReviewDisplay($reviewDisplay);
diff --git a/css/main.css b/css/main.css
index 4dad33f..954b9fb 100644
--- a/css/main.css
+++ b/css/main.css
@@ -40,6 +40,9 @@ body.onlyoffice-inline .searchbox,
body.onlyoffice-inline #app-content #controls {
display: none;
}
+body.onlyoffice-inline #app-navigation-toggle {
+ display: none !important;
+}
body.onlyoffice-inline #content #app-content {
margin-left: 0;
}
diff --git a/js/editor.js b/js/editor.js
index 50325aa..6b84955 100644
--- a/js/editor.js
+++ b/js/editor.js
@@ -292,7 +292,9 @@
"image/png", "image/x-png", "application/png", "application/x-png"
];
- OCA.Onlyoffice.insertImageType = event.data.c;
+ if (event.data) {
+ OCA.Onlyoffice.insertImageType = event.data.c;
+ }
if (OCA.Onlyoffice.inframe) {
window.parent.postMessage({
diff --git a/js/main.js b/js/main.js
index 8c33d79..27bb2f5 100644
--- a/js/main.js
+++ b/js/main.js
@@ -104,13 +104,13 @@
if (OCA.Files.Sidebar) {
OCA.Files.Sidebar.close();
- return;
} else {
//todo: remove. only for v17
OC.Apps.hideAppSidebar();
}
- $("html, body").scrollTop(0);
+ var scrollTop = $(window).scrollTop();
+ $(OCA.Onlyoffice.frameSelector).css("top", scrollTop);
OCA.Onlyoffice.folderUrl = location.href;
window.history.pushState(null, null, url);
@@ -118,11 +118,15 @@
};
OCA.Onlyoffice.ShowHeaderButton = function () {
+ if ($("#onlyofficeHeader").length) {
+ return;
+ }
+
var wrapper = $("<div id='onlyofficeHeader' />")
var btnClose = $("<a class='icon icon-close-white'></a>");
btnClose.on("click", function() {
- OCA.Onlyoffice.CloseEditor();
+ OCA.Onlyoffice.onRequestClose();
});
wrapper.prepend(btnClose);
diff --git a/js/settings.js b/js/settings.js
index 1ac8d61..1c1a7ed 100644
--- a/js/settings.js
+++ b/js/settings.js
@@ -197,6 +197,7 @@
var chat = $("#onlyofficeChat").is(":checked");
var compactHeader = $("#onlyofficeCompactHeader").is(":checked");
var feedback = $("#onlyofficeFeedback").is(":checked");
+ var forcesave = $("#onlyofficeForcesave").is(":checked");
var help = $("#onlyofficeHelp").is(":checked");
var toolbarNoTabs = !$("#onlyofficeToolbarNoTabs").is(":checked");
var reviewDisplay = $("input[type='radio'][name='reviewDisplay']:checked").attr("id").replace("onlyofficeReviewDisplay_", "");
@@ -212,6 +213,7 @@
chat: chat,
compactHeader: compactHeader,
feedback: feedback,
+ forcesave: forcesave,
help: help,
toolbarNoTabs: toolbarNoTabs,
reviewDisplay: reviewDisplay
diff --git a/l10n/bg.js b/l10n/bg.js
index 6583a7a..650a525 100644
--- a/l10n/bg.js
+++ b/l10n/bg.js
@@ -50,6 +50,7 @@ OC.L10N.register(
"Insert image" : "Вмъкване на изображение",
"Select recipients" : "Изберете получатели",
"Select tag" : "Изберете етикет",
- "version": "версия"
+ "version": "версия",
+ "Keep intermediate versions when editing": "Съхранявайте междинни версии при редактиране"
},
"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);");
diff --git a/l10n/bg.json b/l10n/bg.json
index 1a728d4..286b587 100644
--- a/l10n/bg.json
+++ b/l10n/bg.json
@@ -48,6 +48,7 @@
"Insert image" : "Вмъкване на изображение",
"Select recipients" : "Изберете получатели",
"Select tag" : "Изберете етикет",
- "version": "версия"
+ "version": "версия",
+ "Keep intermediate versions when editing": "Съхранявайте междинни версии при редактиране"
},"pluralForm" :"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"
} \ No newline at end of file
diff --git a/l10n/de.js b/l10n/de.js
index fa9afa7..c3b6913 100644
--- a/l10n/de.js
+++ b/l10n/de.js
@@ -83,6 +83,7 @@ OC.L10N.register(
"Final": "Endgültig",
"Original": "Original",
"version": "version",
- "Disable certificate verification (insecure)": "Zertifikatsüberprüfung deaktivieren (unsicher)"
+ "Disable certificate verification (insecure)": "Zertifikatsüberprüfung deaktivieren (unsicher)",
+ "Keep intermediate versions when editing": "Zwischenversionen bei der Bearbeitung aufbewahren"
},
"nplurals=2; plural=(n != 1);");
diff --git a/l10n/de.json b/l10n/de.json
index 791b3e0..dfcc457 100644
--- a/l10n/de.json
+++ b/l10n/de.json
@@ -81,6 +81,7 @@
"Final": "Endgültig",
"Original": "Original",
"version": "version",
- "Disable certificate verification (insecure)": "Zertifikatsüberprüfung deaktivieren (unsicher)"
+ "Disable certificate verification (insecure)": "Zertifikatsüberprüfung deaktivieren (unsicher)",
+ "Keep intermediate versions when editing": "Zwischenversionen bei der Bearbeitung aufbewahren"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
} \ No newline at end of file
diff --git a/l10n/de_DE.js b/l10n/de_DE.js
index 0162211..4b1d350 100644
--- a/l10n/de_DE.js
+++ b/l10n/de_DE.js
@@ -83,6 +83,7 @@ OC.L10N.register(
"Final": "Endgültig",
"Original": "Original",
"version": "version",
- "Disable certificate verification (insecure)": "Zertifikatsüberprüfung deaktivieren (unsicher)"
+ "Disable certificate verification (insecure)": "Zertifikatsüberprüfung deaktivieren (unsicher)",
+ "Keep intermediate versions when editing": "Zwischenversionen bei der Bearbeitung aufbewahren"
},
"nplurals=2; plural=(n != 1);");
diff --git a/l10n/de_DE.json b/l10n/de_DE.json
index 39a832b..01f8545 100644
--- a/l10n/de_DE.json
+++ b/l10n/de_DE.json
@@ -81,6 +81,7 @@
"Final": "Endgültig",
"Original": "Original",
"version": "version",
- "Disable certificate verification (insecure)": "Zertifikatsüberprüfung deaktivieren (unsicher)"
+ "Disable certificate verification (insecure)": "Zertifikatsüberprüfung deaktivieren (unsicher)",
+ "Keep intermediate versions when editing": "Zwischenversionen bei der Bearbeitung aufbewahren"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
} \ No newline at end of file
diff --git a/l10n/es.js b/l10n/es.js
index 77e47d6..3c7dc14 100644
--- a/l10n/es.js
+++ b/l10n/es.js
@@ -66,6 +66,7 @@ OC.L10N.register(
"Final": "Final",
"Original": "Original",
"version": "versión",
- "Disable certificate verification (insecure)": "Desactivar la verificación de certificados (inseguro)"
+ "Disable certificate verification (insecure)": "Desactivar la verificación de certificados (inseguro)",
+ "Keep intermediate versions when editing": "Mantener las versiones intermedias durante la edición"
},
"nplurals=2; plural=(n != 1);");
diff --git a/l10n/es.json b/l10n/es.json
index 1fc44bd..307df30 100644
--- a/l10n/es.json
+++ b/l10n/es.json
@@ -64,6 +64,7 @@
"Final": "Final",
"Original": "Original",
"version": "versión",
- "Disable certificate verification (insecure)": "Desactivar la verificación de certificados (inseguro)"
+ "Disable certificate verification (insecure)": "Desactivar la verificación de certificados (inseguro)",
+ "Keep intermediate versions when editing": "Mantener las versiones intermedias durante la edición"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
} \ No newline at end of file
diff --git a/l10n/fr.js b/l10n/fr.js
index a40289b..d3bdd1d 100644
--- a/l10n/fr.js
+++ b/l10n/fr.js
@@ -64,6 +64,7 @@ OC.L10N.register(
"Final": "Finale",
"Original": "Original",
"version": "version",
- "Disable certificate verification (insecure)": "Désactiver la vérification du certificat (non sur)"
+ "Disable certificate verification (insecure)": "Désactiver la vérification du certificat (non sur)",
+ "Keep intermediate versions when editing": "Sauvegarder les versions intermédiaires lors de l'édition"
},
"nplurals=2; plural=(n != 1);");
diff --git a/l10n/fr.json b/l10n/fr.json
index 0d417f0..a4afe9c 100644
--- a/l10n/fr.json
+++ b/l10n/fr.json
@@ -62,6 +62,7 @@
"Final": "Finale",
"Original": "Original",
"version": "version",
- "Disable certificate verification (insecure)": "Désactiver la vérification du certificat (non sur)"
+ "Disable certificate verification (insecure)": "Désactiver la vérification du certificat (non sur)",
+ "Keep intermediate versions when editing": "Sauvegarder les versions intermédiaires lors de l'édition"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}
diff --git a/l10n/ja.js b/l10n/ja.js
index c66d302..44d2157 100644
--- a/l10n/ja.js
+++ b/l10n/ja.js
@@ -82,6 +82,7 @@ OC.L10N.register(
"Markup": "マークアップ",
"Final": "最終版",
"Original": "オリジナル",
- "version": "バージョン"
+ "version": "バージョン",
+ "Keep intermediate versions when editing": "編集時に、中間バージョンを保持する"
},
"nplurals=2; plural=(n != 1);");
diff --git a/l10n/ja.json b/l10n/ja.json
index 889ccba..6878dff 100644
--- a/l10n/ja.json
+++ b/l10n/ja.json
@@ -80,6 +80,7 @@
"Markup": "マークアップ",
"Final": "最終版",
"Original": "オリジナル",
- "version": "バージョン"
+ "version": "バージョン",
+ "Keep intermediate versions when editing": "編集時に、中間バージョンを保持する"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
} \ No newline at end of file
diff --git a/l10n/ru.js b/l10n/ru.js
index 065322c..810bb60 100644
--- a/l10n/ru.js
+++ b/l10n/ru.js
@@ -84,6 +84,7 @@ OC.L10N.register(
"Final": "Изменённый документ",
"Original": "Исходный документ",
"version": "версия",
- "Disable certificate verification (insecure)": "Отключить проверку сертификата (небезопасно)"
+ "Disable certificate verification (insecure)": "Отключить проверку сертификата (небезопасно)",
+ "Keep intermediate versions when editing": "Хранить промежуточные версии при редактировании"
},
"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);");
diff --git a/l10n/ru.json b/l10n/ru.json
index e8dbd83..7bf74b4 100644
--- a/l10n/ru.json
+++ b/l10n/ru.json
@@ -82,6 +82,7 @@
"Final": "Изменённый документ",
"Original": "Исходный документ",
"version": "версия",
- "Disable certificate verification (insecure)": "Отключить проверку сертификата (небезопасно)"
+ "Disable certificate verification (insecure)": "Отключить проверку сертификата (небезопасно)",
+ "Keep intermediate versions when editing": "Хранить промежуточные версии при редактировании"
},"pluralForm" :"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"
} \ No newline at end of file
diff --git a/l10n/zh_CN.js b/l10n/zh_CN.js
new file mode 100644
index 0000000..950d6d4
--- /dev/null
+++ b/l10n/zh_CN.js
@@ -0,0 +1,90 @@
+OC.L10N.register(
+ "onlyoffice",
+ {
+ "Access denied" : "禁止访问",
+ "Invalid request" : "非法请求",
+ "Files not found" : "文件未找到",
+ "File not found" : "文件未找到",
+ "Not permitted" : "没有权限",
+ "Download failed" : "下载失败",
+ "The required folder was not found" : "必须的文件夹未找到",
+ "You don't have enough permission to create" : "没有足够权限创建文件",
+ "Template not found" : "模板未找到",
+ "Can't create file" : "无法创建文件",
+ "Format is not supported" : "文件格式不支持",
+ "Conversion is not required" : "无需文件转换",
+ "Failed to download converted file" : "转换后的文件下载失败",
+ "ONLYOFFICE app is not configured. Please contact admin" : "ONLYOFFICE未配置,请联系管理员。",
+ "FileId is empty" : "文件ID为空",
+ "You do not have enough permissions to view the file" : "您没有足够权限浏览该文件",
+ "Error occurred in the document service" : "文档服务内部发生异常",
+ "Not supported version" : "不支持的版本",
+ "ONLYOFFICE cannot be reached. Please contact admin" : "ONLYOFFICE服务器无法连接,请联系管理员。",
+ "Loading, please wait." : "载入中,请稍后...",
+ "File created" : "文件已创建",
+ "Open in ONLYOFFICE" : "用 ONLYOFFICE 打开",
+ "Convert with ONLYOFFICE" : "用 ONLYOFFICE 转换",
+ "Document" : "文档",
+ "Spreadsheet" : "电子表格",
+ "Presentation" : "演示文稿",
+ "Error when trying to connect" : "连接是发生异常",
+ "Settings have been successfully updated" : "设置已保存",
+ "Server can't read xml" : "服务器无法读取XML",
+ "Bad Response. Errors: " : "错误的返回: ",
+ "Documentation" : "文档集",
+ "ONLYOFFICE Document Service Location specifies the address of the server with the document services installed. Please change the '<documentserver>' for the server address in the below line.": "ONLYOFFICE 文档服务地址需要一个确切的文档服务器的地址,请在下方的'<documentserver>' 中填入。",
+ "Document Editing Service address" : "文档编辑服务地址",
+ "Advanced server settings" : "更多设置",
+ "Document Editing Service address for internal requests from the server": "用于服务器内部访问的文档编辑服务器的地址",
+ "Server address for internal requests from the Document Editing Service": "用于文档编辑服务内部请求的服务器的地址",
+ "Secret key (leave blank to disable)" : "秘钥(留空为关闭)",
+ "Open file in the same tab" : "在相同的切签中打开",
+ "The default application for opening the format": "默认关联的文件格式",
+ "Open the file for editing (due to format restrictions, the data might be lost when saving to the formats from the list below)" : "默认的文件编辑器 (由于文件格式限制,保存为下列格式时,数据可能会缺失)",
+ "View details" : "查看详情",
+ "Save" : "保存",
+ "Mixed Active Content is not allowed. HTTPS address for Document Server is required." : "不允许混合活动内容,请使用HTTPS连接文件服务器。",
+ "Restrict access to editors to following groups" : "仅授权的用户组可以使用该服务",
+ "Server settings" : "服务器设置",
+ "Common settings" : "常用设置",
+ "Editor customization settings" : "编辑器自定义设置",
+ "The customization section allows personalizing the editor interface": "自定义设置区允许对编辑器界面进行个性化设置",
+ "Display Chat menu button" : "显示交流菜单按钮",
+ "Display the header more compact" : "更紧致的显示菜单栏",
+ "Display Feedback & Support menu button" : "显示反馈&支持菜单按钮",
+ "Display Help menu button" : "显示帮助菜单按钮",
+ "Display toolbar tabs" : "显示工具栏切签",
+ "Save as" : "另存为",
+ "File saved" : "文件已保存",
+ "Insert image" : "插入图片",
+ "Select recipients" : "选择接收者",
+ "Connect to demo ONLYOFFICE Document Server" : "连接到 ONLYOFFICE 演示服务器",
+ "This is a public test server, please do not use it for private sensitive data. The server will be available during a 30-day period.": "这是公开的测试服务器,请勿用于隐私数据。服务器试用期限为30天。",
+ "The 30-day test period is over, you can no longer connect to demo ONLYOFFICE Document Server." : "30天试用期已结束,无法连接演示服务器。",
+ "You are using public demo ONLYOFFICE Document Server. Please do not store private sensitive data." : "您正在使用公开的演示服务器,请勿存储隐私数据。",
+ "Secure view settings" : "安全视图设置",
+ "Secure view enables you to secure documents by embedding a watermark" : "启用安全视图可通过水印来保障文档安全",
+ "Enable watermarking" : "启用水印",
+ "Watermark text" : "水印文本",
+ "DO NOT SHARE THIS" : "请勿散播此文件",
+ "Show watermark on tagged files" : "在标记的文件上展示水印",
+ "Show watermark for users of groups" : "在组内用户中展示水印",
+ "Supported placeholders" : "支持的占位符",
+ "Show watermark for all shares" : "在所有的共享中展示水印",
+ "Show watermark for read only shares" : "在只读共享中展示水印",
+ "Link shares" : "分享链接",
+ "Show watermark for all link shares" : "在分享链接中展示水印",
+ "Show watermark for download hidden shares" : "在隐藏的下载分享链接中展示水印",
+ "Show watermark for read only link shares" : "在只读分享链接中展示水印",
+ "Show watermark on link shares with specific system tags" : "在有特定系统标签的分享链接中展示水印",
+ "Select tag" : "选择标签",
+ "Select file to compare" : "选择文件比较",
+ "Review mode for viewing": "审阅模式浏览",
+ "Markup": "修订",
+ "Final": "最终版",
+ "Original": "原始版本",
+ "version": "版本",
+ "Disable certificate verification (insecure)": "关闭证书校验(不安全)",
+ "Keep intermediate versions when editing": "编辑时保留中间版本"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/l10n/zh_CN.json b/l10n/zh_CN.json
new file mode 100644
index 0000000..5b06da2
--- /dev/null
+++ b/l10n/zh_CN.json
@@ -0,0 +1,88 @@
+{ "translations": {
+ "Access denied" : "禁止访问",
+ "Invalid request" : "非法请求",
+ "Files not found" : "文件未找到",
+ "File not found" : "文件未找到",
+ "Not permitted" : "没有权限",
+ "Download failed" : "下载失败",
+ "The required folder was not found" : "必须的文件夹未找到",
+ "You don't have enough permission to create" : "没有足够权限创建文件",
+ "Template not found" : "模板未找到",
+ "Can't create file" : "无法创建文件",
+ "Format is not supported" : "文件格式不支持",
+ "Conversion is not required" : "无需文件转换",
+ "Failed to download converted file" : "转换后的文件下载失败",
+ "ONLYOFFICE app is not configured. Please contact admin" : "ONLYOFFICE未配置,请联系管理员。",
+ "FileId is empty" : "文件ID为空",
+ "You do not have enough permissions to view the file" : "您没有足够权限浏览该文件",
+ "Error occurred in the document service" : "文档服务内部发生异常",
+ "Not supported version" : "不支持的版本",
+ "ONLYOFFICE cannot be reached. Please contact admin" : "ONLYOFFICE服务器无法连接,请联系管理员。",
+ "Loading, please wait." : "载入中,请稍后...",
+ "File created" : "文件已创建",
+ "Open in ONLYOFFICE" : "用 ONLYOFFICE 打开",
+ "Convert with ONLYOFFICE" : "用 ONLYOFFICE 转换",
+ "Document" : "文档",
+ "Spreadsheet" : "电子表格",
+ "Presentation" : "演示文稿",
+ "Error when trying to connect" : "连接是发生异常",
+ "Settings have been successfully updated" : "设置已保存",
+ "Server can't read xml" : "服务器无法读取XML",
+ "Bad Response. Errors: " : "错误的返回: ",
+ "Documentation" : "文档集",
+ "ONLYOFFICE Document Service Location specifies the address of the server with the document services installed. Please change the '<documentserver>' for the server address in the below line.": "ONLYOFFICE 文档服务地址需要一个确切的文档服务器的地址,请在下方的'<documentserver>' 中填入。",
+ "Document Editing Service address" : "文档编辑服务地址",
+ "Advanced server settings" : "更多设置",
+ "Document Editing Service address for internal requests from the server": "用于服务器内部访问的文档编辑服务器的地址",
+ "Server address for internal requests from the Document Editing Service": "用于文档编辑服务内部请求的服务器的地址",
+ "Secret key (leave blank to disable)" : "秘钥(留空为关闭)",
+ "Open file in the same tab" : "在相同的切签中打开",
+ "The default application for opening the format": "默认关联的文件格式",
+ "Open the file for editing (due to format restrictions, the data might be lost when saving to the formats from the list below)" : "默认的文件编辑器 (由于文件格式限制,保存为下列格式时,数据可能会缺失)",
+ "View details" : "查看详情",
+ "Save" : "保存",
+ "Mixed Active Content is not allowed. HTTPS address for Document Server is required." : "不允许混合活动内容,请使用HTTPS连接文件服务器。",
+ "Restrict access to editors to following groups" : "仅授权的用户组可以使用该服务",
+ "Server settings" : "服务器设置",
+ "Common settings" : "常用设置",
+ "Editor customization settings" : "编辑器自定义设置",
+ "The customization section allows personalizing the editor interface": "自定义设置区允许对编辑器界面进行个性化设置",
+ "Display Chat menu button" : "显示交流菜单按钮",
+ "Display the header more compact" : "更紧致的显示菜单栏",
+ "Display Feedback & Support menu button" : "显示反馈&支持菜单按钮",
+ "Display Help menu button" : "显示帮助菜单按钮",
+ "Display toolbar tabs" : "显示工具栏切签",
+ "Save as" : "另存为",
+ "File saved" : "文件已保存",
+ "Insert image" : "插入图片",
+ "Select recipients" : "选择接收者",
+ "Connect to demo ONLYOFFICE Document Server" : "连接到 ONLYOFFICE 演示服务器",
+ "This is a public test server, please do not use it for private sensitive data. The server will be available during a 30-day period.": "这是公开的测试服务器,请勿用于隐私数据。服务器试用期限为30天。",
+ "The 30-day test period is over, you can no longer connect to demo ONLYOFFICE Document Server." : "30天试用期已结束,无法连接演示服务器。",
+ "You are using public demo ONLYOFFICE Document Server. Please do not store private sensitive data." : "您正在使用公开的演示服务器,请勿存储隐私数据。",
+ "Secure view settings" : "安全视图设置",
+ "Secure view enables you to secure documents by embedding a watermark" : "启用安全视图可通过水印来保障文档安全",
+ "Enable watermarking" : "启用水印",
+ "Watermark text" : "水印文本",
+ "DO NOT SHARE THIS" : "请勿散播此文件",
+ "Show watermark on tagged files" : "在标记的文件上展示水印",
+ "Show watermark for users of groups" : "在组内用户中展示水印",
+ "Supported placeholders" : "支持的占位符",
+ "Show watermark for all shares" : "在所有的共享中展示水印",
+ "Show watermark for read only shares" : "在只读共享中展示水印",
+ "Link shares" : "分享链接",
+ "Show watermark for all link shares" : "在分享链接中展示水印",
+ "Show watermark for download hidden shares" : "在隐藏的下载分享链接中展示水印",
+ "Show watermark for read only link shares" : "在只读分享链接中展示水印",
+ "Show watermark on link shares with specific system tags" : "在有特定系统标签的分享链接中展示水印",
+ "Select tag" : "选择标签",
+ "Select file to compare" : "选择文件比较",
+ "Review mode for viewing": "审阅模式浏览",
+ "Markup": "修订",
+ "Final": "最终版",
+ "Original": "原始版本",
+ "version": "版本",
+ "Disable certificate verification (insecure)": "关闭证书校验(不安全)",
+ "Keep intermediate versions when editing": "编辑时保留中间版本"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/lib/appconfig.php b/lib/appconfig.php
index 98a8969..36479ac 100644
--- a/lib/appconfig.php
+++ b/lib/appconfig.php
@@ -131,6 +131,13 @@ class AppConfig {
private $_customizationFeedback = "customizationFeedback";
/**
+ * The config key for the forcesave setting
+ *
+ * @var string
+ */
+ private $_customizationForcesave = "customizationForcesave";
+
+ /**
* The config key for the help display setting
*
* @var string
@@ -208,13 +215,6 @@ class AppConfig {
public $_customization_customer = "customization_customer";
/**
- * The config key for the feedback
- *
- * @var string
- */
- public $_customization_feedback = "customization_feedback";
-
- /**
* The config key for the loaderLogo
*
* @var string
@@ -666,6 +666,26 @@ class AppConfig {
}
/**
+ * Save forcesave setting
+ *
+ * @param bool $value - forcesave
+ */
+ public function SetCustomizationForcesave($value) {
+ $this->logger->info("Set forcesave: " . json_encode($value), ["app" => $this->appName]);
+
+ $this->config->setAppValue($this->appName, $this->_customizationForcesave, json_encode($value));
+ }
+
+ /**
+ * Get forcesave setting
+ *
+ * @return bool
+ */
+ public function GetCustomizationForcesave() {
+ return $this->config->getAppValue($this->appName, $this->_customizationForcesave, "false") === "true";
+ }
+
+ /**
* Save help display setting
*
* @param bool $value - display help
diff --git a/lib/fileutility.php b/lib/fileutility.php
index 5dc4690..c166774 100644
--- a/lib/fileutility.php
+++ b/lib/fileutility.php
@@ -30,6 +30,7 @@ use OCP\Share\IManager;
use OCA\Files_Sharing\External\Storage as SharingExternalStorage;
use OCA\Onlyoffice\AppConfig;
+use OCA\Onlyoffice\KeyManager;
/**
* File utility
@@ -217,6 +218,8 @@ class FileUtility {
* @return string
*/
public function getKey($file, $origin = false) {
+ $fileId = $file->getId();
+
if ($origin
&& $file->getStorage()->instanceOfStorage(SharingExternalStorage::class)) {
@@ -227,18 +230,39 @@ class FileUtility {
return $key;
}
} catch (\Exception $e) {
- $this->logger->logException($e, ["message" => "Failed to request federated key " . $file->getId(), "app" => $this->appName]);
+ $this->logger->logException($e, ["message" => "Failed to request federated key $fileId", "app" => $this->appName]);
}
}
- $instanceId = $this->config->GetSystemValue("instanceid", true);
+ $key = KeyManager::get($fileId);
- $key = $instanceId . "_" . $file->getEtag();
+ if (empty($key) ) {
+ $instanceId = $this->config->GetSystemValue("instanceid", true);
+
+ $key = $instanceId . "_" . $this->GUID();
+
+ KeyManager::set($fileId, $key);
+ }
return $key;
}
/**
+ * Generate unique identifier
+ *
+ * @return string
+ */
+ private function GUID()
+ {
+ if (function_exists("com_create_guid") === true)
+ {
+ return trim(com_create_guid(), "{}");
+ }
+
+ return sprintf('%04X%04X-%04X-%04X-%04X-%04X%04X%04X', mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(16384, 20479), mt_rand(32768, 49151), mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535));
+ }
+
+ /**
* Generate unique document identifier in federated share
*
* @param File $file - file
diff --git a/lib/hooks.php b/lib/hooks.php
index 013c79c..e0d96b8 100644
--- a/lib/hooks.php
+++ b/lib/hooks.php
@@ -24,6 +24,7 @@ use OC\Files\Filesystem;
use OCP\Util;
use OCA\Onlyoffice\FileVersions;
+use OCA\Onlyoffice\KeyManager;
/**
* The class to handle the filesystem hooks
@@ -43,6 +44,9 @@ class Hooks {
// Listen user deletion
Util::connectHook("OC_User", "pre_deleteUser", Hooks::class, "userDelete");
+ // Listen file change
+ Util::connectHook("OC_Filesystem", "write", Hooks::class, "fileUpdate");
+
// Listen file deletion
Util::connectHook("OC_Filesystem", "delete", Hooks::class, "fileDelete");
@@ -65,6 +69,27 @@ class Hooks {
}
/**
+ * Listen of file change
+ *
+ * @param array $params - hook params
+ */
+ public static function fileUpdate($params) {
+ $filePath = $params[Filesystem::signal_param_path];
+ if (empty($filePath)) {
+ return;
+ }
+
+ $fileInfo = Filesystem::getFileInfo($filePath);
+ if ($fileInfo === false) {
+ return;
+ }
+
+ $fileId = $fileInfo->getId();
+
+ KeyManager::delete($fileId);
+ }
+
+ /**
* Erase versions of deleted file
*
* @param array $params - hook params
@@ -79,8 +104,14 @@ class Hooks {
$ownerId = Filesystem::getOwner($filePath);
$fileInfo = Filesystem::getFileInfo($filePath);
+ if ($fileInfo === false) {
+ return;
+ }
+
$fileId = $fileInfo->getId();
+ KeyManager::delete($fileId, true);
+
FileVersions::deleteAllVersions($ownerId, $fileId);
} catch (\Exception $e) {
\OC::$server->getLogger()->logException($e, ["message" => "Hook: fileDelete " . json_encode($params), "app" => self::$appName]);
@@ -107,6 +138,10 @@ class Hooks {
$ownerId = Filesystem::getOwner($filePath);
$fileInfo = Filesystem::getFileInfo($filePath);
+ if ($fileInfo === false) {
+ return;
+ }
+
$fileId = $fileInfo->getId();
FileVersions::deleteVersion($ownerId, $fileId, $versionId);
@@ -132,6 +167,10 @@ class Hooks {
$ownerId = Filesystem::getOwner($filePath);
$fileInfo = Filesystem::getFileInfo($filePath);
+ if ($fileInfo === false) {
+ return;
+ }
+
$fileId = $fileInfo->getId();
FileVersions::deleteVersion($ownerId, $fileId, $versionId);
diff --git a/lib/keymanager.php b/lib/keymanager.php
new file mode 100644
index 0000000..3571696
--- /dev/null
+++ b/lib/keymanager.php
@@ -0,0 +1,150 @@
+<?php
+/**
+ *
+ * (c) Copyright Ascensio System SIA 2020
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+namespace OCA\Onlyoffice;
+
+
+/**
+ * Key manager
+ *
+ * @package OCA\Onlyoffice
+ */
+class KeyManager {
+
+ /**
+ * Table name
+ */
+ private const TableName_Key = "onlyoffice_filekey";
+
+ /**
+ * Get document identifier
+ *
+ * @param integer $fileId - file identifier
+ *
+ * @return string
+ */
+ public static function get($fileId) {
+ $connection = \OC::$server->getDatabaseConnection();
+ $select = $connection->prepare("
+ SELECT `key`
+ FROM `*PREFIX*" . self::TableName_Key . "`
+ WHERE `file_id` = ?
+ ");
+ $result = $select->execute([$fileId]);
+
+ $keys = $result ? $select->fetch() : [];
+ $key = is_array($keys) && isset($keys["key"]) ? $keys["key"] : "";
+
+ return $key;
+ }
+
+ /**
+ * Store document identifier
+ *
+ * @param integer $fileId - file identifier
+ * @param integer $key - file key
+ *
+ * @return bool
+ */
+ public static function set($fileId, $key) {
+ $connection = \OC::$server->getDatabaseConnection();
+ $insert = $connection->prepare("
+ INSERT INTO `*PREFIX*" . self::TableName_Key . "`
+ (`file_id`, `key`)
+ VALUES (?, ?)
+ ");
+ return (bool)$insert->execute([$fileId, $key]);
+ }
+
+ /**
+ * Delete document identifier
+ *
+ * @param integer $fileId - file identifier
+ * @param bool $unlock - delete even with lock label
+ *
+ * @return bool
+ */
+ public static function delete($fileId, $unlock = false) {
+ $connection = \OC::$server->getDatabaseConnection();
+ $delete = $connection->prepare("
+ DELETE FROM `*PREFIX*" . self::TableName_Key . "`
+ WHERE `file_id` = ?
+ " . ($unlock === false ? "AND `lock` != 1" : "")
+ );
+ return (bool)$delete->execute([$fileId]);
+ }
+
+ /**
+ * Change lock status
+ *
+ * @param integer $fileId - file identifier
+ * @param integer $lock - status
+ *
+ * @return bool
+ */
+ public static function lock($fileId, $lock = true) {
+ $connection = \OC::$server->getDatabaseConnection();
+ $update = $connection->prepare("
+ UPDATE `*PREFIX*" . self::TableName_Key . "`
+ SET `lock` = ?
+ WHERE `file_id` = ?
+ ");
+ return (bool)$update->execute([$lock === true ? 1 : 0, $fileId]);
+ }
+
+ /**
+ * Change forcesave status
+ *
+ * @param integer $fileId - file identifier
+ * @param integer $fs - status
+ *
+ * @return bool
+ */
+ public static function setForcesave($fileId, $fs = true) {
+ $connection = \OC::$server->getDatabaseConnection();
+ $update = $connection->prepare("
+ UPDATE `*PREFIX*" . self::TableName_Key . "`
+ SET `fs` = ?
+ WHERE `file_id` = ?
+ ");
+ return (bool)$update->execute([$fs === true ? 1 : 0, $fileId]);
+ }
+
+ /**
+ * Get forcesave status
+ *
+ * @param integer $fileId - file identifier
+ *
+ * @return bool
+ */
+ public static function wasForcesave($fileId) {
+ $connection = \OC::$server->getDatabaseConnection();
+ $select = $connection->prepare("
+ SELECT `fs`
+ FROM `*PREFIX*" . self::TableName_Key . "`
+ WHERE `file_id` = ?
+ ");
+ $result = $select->execute([$fileId]);
+
+ $rows = $result ? $select->fetch() : [];
+ $fs = is_array($rows) && isset($rows["fs"]) ? $rows["fs"] : "";
+
+ return $fs === "1";
+ }
+}
diff --git a/templates/settings.php b/templates/settings.php
index 2b4ab7f..ea0e913 100644
--- a/templates/settings.php
+++ b/templates/settings.php
@@ -144,7 +144,15 @@
<a target="_blank" class="icon-info svg" title="" href="https://api.onlyoffice.com/editors/config/editor/customization" data-original-title="<?php p($l->t("View details")) ?>"></a>
</h3>
- <p><?php p($l->t("The customization section allows personalizing the editor interface")) ?></p>
+ <p>
+ <input type="checkbox" class="checkbox" id="onlyofficeForcesave"
+ <?php if ($_["forcesave"]) { ?>checked="checked"<?php } ?> />
+ <label for="onlyofficeForcesave"><?php p($l->t("Keep intermediate versions when editing")) ?></label>
+ </p>
+
+ <p class="onlyoffice-header">
+ <?php p($l->t("The customization section allows personalizing the editor interface")) ?>
+ </p>
<p>
<input type="checkbox" class="checkbox" id="onlyofficeChat"