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-06-05 18:26:04 +0300
committerGitHub <noreply@github.com>2020-06-05 18:26:04 +0300
commitf91753d3a861644f6eb62576bd6fa9061987a937 (patch)
treeb4cb6d18347086f3376125ebe0734bfc7651db1f
parentf9e427ea53866568271a950c27343ca0ebee8aae (diff)
parent30422499ff2f27e41e47937cc9d55c03b4417f08 (diff)
Merge pull request #295 from ONLYOFFICE/developv4.2.0
Release/4.2.0
-rw-r--r--CHANGELOG.md7
-rw-r--r--README.md194
-rw-r--r--appinfo/application.php5
-rw-r--r--appinfo/info.xml4
-rw-r--r--controller/callbackcontroller.php137
-rw-r--r--controller/editorcontroller.php156
-rw-r--r--controller/federationcontroller.php8
-rw-r--r--controller/settingscontroller.php30
-rw-r--r--css/settings.css5
-rw-r--r--js/editor.js10
-rw-r--r--js/settings.js4
-rw-r--r--l10n/de.js5
-rw-r--r--l10n/de.json5
-rw-r--r--l10n/de_DE.js5
-rw-r--r--l10n/de_DE.json5
-rw-r--r--l10n/es.js5
-rw-r--r--l10n/es.json5
-rw-r--r--l10n/fr.js5
-rw-r--r--l10n/fr.json5
-rw-r--r--l10n/ru.js6
-rw-r--r--l10n/ru.json6
-rw-r--r--lib/appconfig.php78
-rw-r--r--lib/crypt.php10
-rw-r--r--lib/directeditor.php27
-rw-r--r--lib/documentservice.php18
-rw-r--r--lib/filecreator.php6
-rw-r--r--lib/fileutility.php52
-rw-r--r--templates/editor.php7
-rw-r--r--templates/settings.php27
29 files changed, 480 insertions, 357 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fc5df76..b8918c0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
# Change Log
+## 4.2.0
+## Added
+- review display settings
+
+## Changed
+- compatible with Nextcloud 19
+
## 4.1.4
## Changed
- fix file opening in Nextcloud Android mobile application
diff --git a/README.md b/README.md
index aee1828..99f82de 100644
--- a/README.md
+++ b/README.md
@@ -1,104 +1,45 @@
-# ![](screenshots/icon.png) Nextcloud-ONLYOFFICE integration app
+# ![](screenshots/icon.png) Nextcloud ONLYOFFICE integration app
-* [Overview](#overview)
-* [Installing ONLYOFFICE Document Server](#installing-onlyoffice-document-server)
-* [ONLYOFFICE Document Server editions](#onlyoffice-document-server-editions)
-* [Installing Nextcloud-ONLYOFFICE integration app](#installing-nextcloud-onlyoffice-integration-app)
-* [Configuring Nextcloud-ONLYOFFICE integration app](#configuring-nextcloud-onlyoffice-integration-app)
-* [How it works](#how-it-works)
-* [Known issues](#known-issues)
+This app enables users to edit office documents from [Nextcloud](https://nextcloud.com) using ONLYOFFICE Document Server - [Community or Integration Edition](#onlyoffice-document-server-editions).
-# Overview
+## Features
-This app enables users to edit office documents from [Nextcloud](https://nextcloud.com) using ONLYOFFICE Document Server.
-Currently the following document formats can be edited with this app: csv, docx, pptx, txt, xlsx.
-The above mentioned formats are also available for viewing together with pdf.
-The edited files of the corresponding type can be converted into the Office Open XML formats: doc, docm, dot, dotx, epub, htm, html, odp, odt, pot, potm, potx, pps, ppsm, ppsx, ppt, pptm, rtf, xls, xlsm, xlsx, xlt, xltm, xltx.
+The app allows to:
-The app will create an item in the `new` (+) menu to create **Document**, **Spreadsheet**, **Presentation**.
-It will also create a new **Open in ONLYOFFICE** menu option within the document library for Office documents.
-This allows multiple users to collaborate in real time and to save back those changes to Nextcloud.
+* Create and edit text documents, spreadsheets, and presentations.
+* Share files to other users.
+* Protect documents with watermarks.
+* Co-edit documents in real-time: use two co-editing modes (Fast and Strict), Track Changes, comments, and built-in chat. Co-editing is also available between several federated Nextcloud instances connected to one Document Server.
-You can also use our **[Docker installation](https://github.com/ONLYOFFICE/docker-onlyoffice-nextcloud)** to get installed and configured Document Server and Nextcloud installation with a couple of commands.
+Supported formats:
+* For viewing and editing: DOCX, XLSX, PPTX, CSV, TXT.
+* For viewing only: PDF.
+* For converting to Office Open XML formats: DOC, DOCM, DOT, DOTX, EPUB, HTM, HTML, ODP, ODT, POT, POTM, POTX, PPS, PPSM, PPSX, PPT, PPTM, RTF, XLS, XLSM, XLT, XLTM, XLTX.
## Installing ONLYOFFICE Document Server
-You will need an instance of ONLYOFFICE Document Server that is resolvable and connectable both from Nextcloud and any end clients (version 4.2.7 and later are supported for use with the app).
-If that is not the case, use the official ONLYOFFICE Document Server documentation page: [Document Server for Linux](https://helpcenter.onlyoffice.com/server/linux/document/linux-installation.aspx). ONLYOFFICE Document Server must also be able to POST to Nextcloud directly.
+You will need an instance of ONLYOFFICE Document Server that is resolvable and connectable both from Nextcloud and any end clients (version 4.2.7 and later are supported for use with the app).ONLYOFFICE Document Server must also be able to POST to Nextcloud directly.
-Starting with version 4.3.0, ONLYOFFICE Document Server and Nextcloud can be installed either on different computers, or on the same machine.
-In case you select the latter variant, you will need to set up a custom port for Document Server as by default both ONLYOFFICE Document Server and Nextcloud work on port 80.
-Or you can use Document Server behind a proxy, please refer to [this article](https://helpcenter.onlyoffice.com/server/document/document-server-proxy.aspx) to learn how you can configure it.
+Starting with version 4.3.0, ONLYOFFICE Document Server and Nextcloud can be installed either on different computers, or on the same machine. If you use one machine, set up a custom port for Document Server as by default both ONLYOFFICE Document Server and Nextcloud work on port 80.
-The easiest way to start an instance of ONLYOFFICE Document Server is to use [Docker](https://github.com/ONLYOFFICE/Docker-DocumentServer).
+You can install free Community version of ONLYOFFICE Document Server or scalable enterprise-level Integration Edition.
-## ONLYOFFICE Document Server editions
-
-ONLYOFFICE offers different versions of its online document editors that can be deployed on your own servers.
-
-ONLYOFFICE Document Server:
-* Community Edition (`onlyoffice-documentserver` package)
+To install free Community version, use [Docker](https://github.com/onlyoffice/Docker-DocumentServer) (recommended) or follow [these instructions](https://helpcenter.onlyoffice.com/server/linux/document/linux-installation.aspx) for Debian, Ubuntu, or derivatives.
-* Integration Edition (`onlyoffice-documentserver-ie` package)
+To install Integration Edition, follow instructions [here](https://helpcenter.onlyoffice.com/server/integration-edition/index.aspx).
-The table below will help you to make the right choice.
+Community Edition vs Integration Edition comparison can be found [here](#onlyoffice-document-server-editions).
-| Pricing and licensing | Community Edition | Integration Edition |
-| ------------- | ------------- | ------------- |
-| | [Get it now](https://www.onlyoffice.com/download.aspx?utm_source=github&utm_medium=cpc&utm_campaign=GitHubNextcloud) | [Start Free Trial](https://www.onlyoffice.com/connectors-request.aspx?utm_source=github&utm_medium=cpc&utm_campaign=GitHubNextcloud) |
-| Cost | FREE | [Go to the pricing page](https://www.onlyoffice.com/integration-edition-prices.aspx?utm_source=github&utm_medium=cpc&utm_campaign=GitHubNextcloud) |
-| Simultaneous connections | up to 20 maximum | As in chosen pricing plan |
-| Number of users | up to 20 recommended | As in chosen pricing plan |
-| License | GNU AGPL v.3 | Proprietary |
-| **Support** | **Community Edition** | **Integration Edition** |
-| Documentation | [Help Center](https://helpcenter.onlyoffice.com/server/docker/opensource/index.aspx) | [Help Center](https://helpcenter.onlyoffice.com/server/integration-edition/index.aspx) |
-| Standard support | [GitHub](https://github.com/ONLYOFFICE/DocumentServer/issues) or paid | One year support included |
-| Premium support | [Buy Now](https://www.onlyoffice.com/support.aspx?utm_source=github&utm_medium=cpc&utm_campaign=GitHubNextcloud) | [Buy Now](https://www.onlyoffice.com/support.aspx?utm_source=github&utm_medium=cpc&utm_campaign=GitHubNextcloud) |
-| **Services** | **Community Edition** | **Integration Edition** |
-| Conversion Service | + | + |
-| Document Builder Service | + | + |
-| **Interface** | **Community Edition** | **Integration Edition** |
-| Tabbed interface | + | + |
-| White Label | - | - |
-| Integrated test example (node.js)* | - | + |
-| **Plugins & Macros** | **Community Edition** | **Integration Edition** |
-| Plugins | + | + |
-| Macros | + | + |
-| **Collaborative capabilities** | **Community Edition** | **Integration Edition** |
-| Two co-editing modes | + | + |
-| Comments | + | + |
-| Built-in chat | + | + |
-| Review and tracking changes | + | + |
-| Display modes of tracking changes | + | + |
-| Version history | + | + |
-| **Document Editor features** | **Community Edition** | **Integration Edition** |
-| Font and paragraph formatting | + | + |
-| Object insertion | + | + |
-| Content control | + | + |
-| Layout tools | + | + |
-| Table of contents | + | + |
-| Navigation panel | + | + |
-| Mail Merge | + | + |
-| **Spreadsheet Editor features** | **Community Edition** | **Integration Edition** |
-| Font and paragraph formatting | + | + |
-| Object insertion | + | + |
-| Functions, formulas, equations | + | + |
-| Table templates | + | + |
-| Pivot tables | +** | +** |
-| **Presentation Editor features** | **Community Edition** | **Integration Edition** |
-| Font and paragraph formatting | + | + |
-| Object insertion | + | + |
-| Animations | + | + |
-| Presenter mode | + | + |
-| Notes | + | + |
-| | [Get it now](https://www.onlyoffice.com/download.aspx?utm_source=github&utm_medium=cpc&utm_campaign=GitHubNextcloud) | [Start Free Trial](https://www.onlyoffice.com/connectors-request.aspx?utm_source=github&utm_medium=cpc&utm_campaign=GitHubNextcloud) |
+To use ONLYOFFICE behind a proxy, please refer to [this article](https://helpcenter.onlyoffice.com/server/document/document-server-proxy.aspx).
+You can also use our **[Docker installation](https://github.com/ONLYOFFICE/docker-onlyoffice-nextcloud)** to install pre-configured Document Server (free version) and Nextcloud with a couple of commands.
-## Installing Nextcloud-ONLYOFFICE integration app
+## Installing Nextcloud ONLYOFFICE integration app
The Nextcloud administrator can install the integration app from the in-built application market.
For that go to the user name and select **Apps**.
+
After that find **ONLYOFFICE** in the list of available applications and install it.
If the server with the Nextcloud installed does not have an Internet access, or if you need it for some other reason, the administrator can install the application manually.
@@ -108,7 +49,6 @@ To start using ONLYOFFICE Document Server with Nextcloud, the following steps mu
```
cd apps/
```
-
2. Get the Nextcloud ONLYOFFICE integration app.
There are several ways to do that:
@@ -125,11 +65,9 @@ There are several ways to do that:
```
chown -R www-data:www-data onlyoffice
```
+3. In Nextcloud open the `~/index.php/settings/apps/disabled` page with _Not enabled_ apps by administrator and click _Enable_ for the **ONLYOFFICE** application.
-3. In Nextcloud open the `~/index.php/settings/apps?category=disabled` page with _Not enabled_ apps by administrator and click _Enable_ for the **ONLYOFFICE** application.
-
-
-## Configuring Nextcloud-ONLYOFFICE integration app
+## Configuring Nextcloud ONLYOFFICE integration app
In Nextcloud open the `~/index.php/settings/admin/onlyoffice` page with administrative settings for **ONLYOFFICE** section.
Enter the following address to connect ONLYOFFICE Document Server:
@@ -155,7 +93,6 @@ Enable or disable the _Open file in the same tab_ setting.
The **Open in ONLYOFFICE** action will be added to the file context menu.
You can specify this action as default and it will be used when the file name is clicked for the selected file types.
-
## How it works
The ONLYOFFICE integration follows the API documented here https://api.onlyoffice.com/editors/basic:
@@ -172,12 +109,9 @@ This method adds the copy of the file from the assets folder to the folder the u
* The app prepares a JSON object with the following properties:
* **url** - the URL that ONLYOFFICE Document Server uses to download the document;
- * **callback** - the URL that ONLYOFFICE Document Server informs about status of the document editing;
+ * **callbackUrl** - the URL that ONLYOFFICE Document Server informs about status of the document editing;
* **documentServerUrl** - the URL that the client needs to respond to ONLYOFFICE Document Server (can be set at the administrative settings page);
- * **key** - the UUID+Modified Timestamp to instruct ONLYOFFICE Document Server whether to download the document again or not;
- * **fileName** - the document Title (name);
- * **userId** - the identification of the user;
- * **userName** - the name of the user.
+ * **key** - the etag to instruct ONLYOFFICE Document Server whether to download the document again or not;
* Nextcloud takes this object and constructs a page from `templates/editor.php` template, filling in all of those values so that the client browser can load up the editor.
@@ -185,20 +119,17 @@ This method adds the copy of the file from the assets folder to the folder the u
* Then ONLYOFFICE Document Server downloads the document from Nextcloud and the user begins editing.
-* ONLYOFFICE Document Server sends a POST request to the _callback_ URL to inform Nextcloud that a user is editing the document.
+* ONLYOFFICE Document Server sends a POST request to the _callbackUrl_ to inform Nextcloud that a user is editing the document.
* When all users and client browsers are done with editing, they close the editing window.
-* After [10 seconds](https://api.onlyoffice.com/editors/save#savedelay) of inactivity, ONLYOFFICE Document Server sends a POST to the _callback_ URL letting Nextcloud know that the clients have finished editing the document and closed it.
+* After [10 seconds](https://api.onlyoffice.com/editors/save#savedelay) of inactivity, ONLYOFFICE Document Server sends a POST to the _callbackUrl_ letting Nextcloud know that the clients have finished editing the document and closed it.
* Nextcloud downloads the new version of the document, replacing the old one.
## Known issues
-* If the document is shared using the **Federated Cloud Sharing** app, the co-editing among the servers will not be avaialble.
-The users from one and the same server can edit the document in the co-editing mode, but the users from two (or more) different servers will not be able to collaborate on the same document in real time.
-
* Adding the storage using the **External storages** app has issues with the co-editing in some cases.
If the connection is made using the same authorization keys (the _Username and password_ or _Global credentials_ authentication type is selected), then the co-editing is available for the users.
If different authorization keys are used (_Log-in credentials, save in database_ or _User entered, store in database_ authentication options), the co-editing is not available.
@@ -217,3 +148,72 @@ The instruction on enabling _master key_ based encryption is available in the of
)
```
This will disable the certificate verification and allow Nextcloud to establish connection with **Document Server**, but you must remember that this is a temporary insecure solution and we strongly recommend that you replace the certificate with the one issued by some CA. Once you do that, do not forget to remove the above section from Nextcloud config file.
+
+## ONLYOFFICE Document Server editions
+
+ONLYOFFICE offers different versions of its online document editors that can be deployed on your own servers.
+
+ONLYOFFICE Document Server:
+* Community Edition (`onlyoffice-documentserver` package)
+
+* Integration Edition (`onlyoffice-documentserver-ie` package)
+
+The table below will help you to make the right choice.
+
+| Pricing and licensing | Community Edition | Integration Edition |
+| ------------- | ------------- | ------------- |
+| | [Get it now](https://www.onlyoffice.com/download.aspx?utm_source=github&utm_medium=cpc&utm_campaign=GitHubNextcloud) | [Start Free Trial](https://www.onlyoffice.com/connectors-request.aspx?utm_source=github&utm_medium=cpc&utm_campaign=GitHubNextcloud) |
+| Cost | FREE | [Go to the pricing page](https://www.onlyoffice.com/integration-edition-prices.aspx?utm_source=github&utm_medium=cpc&utm_campaign=GitHubNextcloud) |
+| Simultaneous connections | up to 20 maximum | As in chosen pricing plan |
+| Number of users | up to 20 recommended | As in chosen pricing plan |
+| License | GNU AGPL v.3 | Proprietary |
+| **Support** | **Community Edition** | **Integration Edition** |
+| Documentation | [Help Center](https://helpcenter.onlyoffice.com/server/docker/opensource/index.aspx) | [Help Center](https://helpcenter.onlyoffice.com/server/integration-edition/index.aspx) |
+| Standard support | [GitHub](https://github.com/ONLYOFFICE/DocumentServer/issues) or paid | One year support included |
+| Premium support | [Buy Now](https://www.onlyoffice.com/support.aspx?utm_source=github&utm_medium=cpc&utm_campaign=GitHubNextcloud) | [Buy Now](https://www.onlyoffice.com/support.aspx?utm_source=github&utm_medium=cpc&utm_campaign=GitHubNextcloud) |
+| **Services** | **Community Edition** | **Integration Edition** |
+| Conversion Service | + | + |
+| Document Builder Service | + | + |
+| **Interface** | **Community Edition** | **Integration Edition** |
+| Tabbed interface | + | + |
+| White Label | - | - |
+| Integrated test example (node.js)* | - | + |
+| Mobile web editors | - | + |
+| **Plugins & Macros** | **Community Edition** | **Integration Edition** |
+| Plugins | + | + |
+| Macros | + | + |
+| **Collaborative capabilities** | **Community Edition** | **Integration Edition** |
+| Two co-editing modes | + | + |
+| Comments | + | + |
+| Built-in chat | + | + |
+| Review and tracking changes | + | + |
+| Display modes of tracking changes | + | + |
+| Version history | + | + |
+| **Document Editor features** | **Community Edition** | **Integration Edition** |
+| Font and paragraph formatting | + | + |
+| Object insertion | + | + |
+| Adding Content control | - | + |
+| Editing Content control | + | + |
+| Layout tools | + | + |
+| Table of contents | + | + |
+| Navigation panel | + | + |
+| Mail Merge | + | + |
+| Comparing Documents | - | +* |
+| **Spreadsheet Editor features** | **Community Edition** | **Integration Edition** |
+| Font and paragraph formatting | + | + |
+| Object insertion | + | + |
+| Functions, formulas, equations | + | + |
+| Table templates | + | + |
+| Pivot tables | +** | +** |
+| **Presentation Editor features** | **Community Edition** | **Integration Edition** |
+| Font and paragraph formatting | + | + |
+| Object insertion | + | + |
+| Animations | + | + |
+| Presenter mode | + | + |
+| Notes | + | + |
+| | [Get it now](https://www.onlyoffice.com/download.aspx?utm_source=github&utm_medium=cpc&utm_campaign=GitHubNextcloud) | [Start Free Trial](https://www.onlyoffice.com/connectors-request.aspx?utm_source=github&utm_medium=cpc&utm_campaign=GitHubNextcloud) |
+
+\* It's possible to add documents for comparison from your local drive, from URL and from Nextcloud storage.
+
+\** Changing style and deleting (Full support coming soon)
+
diff --git a/appinfo/application.php b/appinfo/application.php
index af63b6a..fe897d7 100644
--- a/appinfo/application.php
+++ b/appinfo/application.php
@@ -31,7 +31,6 @@ namespace OCA\Onlyoffice\AppInfo;
use OCP\AppFramework\App;
use OCP\DirectEditing\RegisterDirectEditorEvent;
-use OCP\Share\IManager;
use OCP\Util;
use OCA\Onlyoffice\AppConfig;
@@ -46,14 +45,14 @@ class Application extends App {
/**
* Application configuration
*
- * @var OCA\Onlyoffice\AppConfig
+ * @var AppConfig
*/
public $appConfig;
/**
* Hash generator
*
- * @var OCA\Onlyoffice\Crypt
+ * @var Crypt
*/
public $crypt;
diff --git a/appinfo/info.xml b/appinfo/info.xml
index f4c8a4b..b65838d 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>agpl</licence>
<author mail="dev@onlyoffice.com" homepage="https://www.onlyoffice.com/">Ascensio System SIA</author>
- <version>4.1.4</version>
+ <version>4.2.0</version>
<namespace>Onlyoffice</namespace>
<types>
<filesystem/>
@@ -29,7 +29,7 @@
<screenshot>https://raw.githubusercontent.com/ONLYOFFICE/onlyoffice-nextcloud/master/screenshots/new.png</screenshot>
<screenshot>https://raw.githubusercontent.com/ONLYOFFICE/onlyoffice-nextcloud/master/screenshots/open.png</screenshot>
<dependencies>
- <nextcloud min-version="16" max-version="18"/>
+ <nextcloud min-version="16" max-version="19"/>
</dependencies>
<settings>
<admin>OCA\Onlyoffice\AdminSettings</admin>
diff --git a/controller/callbackcontroller.php b/controller/callbackcontroller.php
index 6cfd234..555cfab 100644
--- a/controller/callbackcontroller.php
+++ b/controller/callbackcontroller.php
@@ -33,7 +33,6 @@ use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataDownloadResponse;
use OCP\AppFramework\Http\JSONResponse;
-use OCP\Constants;
use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\IRootFolder;
@@ -97,21 +96,21 @@ class CallbackController extends Controller {
/**
* Application configuration
*
- * @var OCA\Onlyoffice\AppConfig
+ * @var AppConfig
*/
private $config;
/**
* Hash generator
*
- * @var OCA\Onlyoffice\Crypt
+ * @var Crypt
*/
private $crypt;
/**
* Share manager
*
- * @var OCP\Share\IManager
+ * @var IManager
*/
private $shareManager;
@@ -120,13 +119,13 @@ class CallbackController extends Controller {
*
* @var Array
*/
- private $_trackerStatus = array(
+ private $_trackerStatus = [
0 => "NotFound",
1 => "Editing",
2 => "MustSave",
3 => "Corrupted",
4 => "Closed"
- );
+ ];
/**
* @param string $AppName - application name
@@ -136,8 +135,8 @@ class CallbackController extends Controller {
* @param IUserManager $userManager - user manager
* @param IL10N $trans - l10n service
* @param ILogger $logger - logger
- * @param OCA\Onlyoffice\AppConfig $config - application configuration
- * @param OCA\Onlyoffice\Crypt $crypt - hash generator
+ * @param AppConfig $config - application configuration
+ * @param Crypt $crypt - hash generator
* @param IManager $shareManager - Share manager
*/
public function __construct($AppName,
@@ -169,7 +168,7 @@ class CallbackController extends Controller {
*
* @param string $doc - verification token with the file identifier
*
- * @return DataDownloadResponse
+ * @return DataDownloadResponse|JSONResponse
*
* @NoAdminRequired
* @NoCSRFRequired
@@ -179,23 +178,23 @@ class CallbackController extends Controller {
public function download($doc) {
list ($hashData, $error) = $this->crypt->ReadHash($doc);
- if ($hashData === NULL) {
- $this->logger->error("Download with empty or not correct hash: $error", array("app" => $this->appName));
+ if ($hashData === null) {
+ $this->logger->error("Download with empty or not correct hash: $error", ["app" => $this->appName]);
return new JSONResponse(["message" => $this->trans->t("Access denied")], Http::STATUS_FORBIDDEN);
}
if ($hashData->action !== "download") {
- $this->logger->error("Download with other action", array("app" => $this->appName));
+ $this->logger->error("Download with other action", ["app" => $this->appName]);
return new JSONResponse(["message" => $this->trans->t("Invalid request")], Http::STATUS_BAD_REQUEST);
}
$fileId = $hashData->fileId;
- $this->logger->debug("Download: $fileId", array("app" => $this->appName));
+ $this->logger->debug("Download: $fileId", ["app" => $this->appName]);
if (!$this->userSession->isLoggedIn()) {
if (!empty($this->config->GetDocumentServerSecret())) {
$header = \OC::$server->getRequest()->getHeader($this->config->JwtHeader());
if (empty($header)) {
- $this->logger->error("Download without jwt", array("app" => $this->appName));
+ $this->logger->error("Download without jwt", ["app" => $this->appName]);
return new JSONResponse(["message" => $this->trans->t("Access denied")], Http::STATUS_FORBIDDEN);
}
@@ -204,7 +203,7 @@ class CallbackController extends Controller {
try {
$decodedHeader = \Firebase\JWT\JWT::decode($header, $this->config->GetDocumentServerSecret(), array("HS256"));
} catch (\UnexpectedValueException $e) {
- $this->logger->error("Download with invalid jwt: " . $e->getMessage(), array("app" => $this->appName));
+ $this->logger->logException($e, ["message" => "Download with invalid jwt", "app" => $this->appName]);
return new JSONResponse(["message" => $this->trans->t("Access denied")], Http::STATUS_FORBIDDEN);
}
}
@@ -224,7 +223,7 @@ class CallbackController extends Controller {
}
}
- $shareToken = isset($hashData->shareToken) ? $hashData->shareToken : NULL;
+ $shareToken = isset($hashData->shareToken) ? $hashData->shareToken : null;
list ($file, $error) = empty($shareToken) ? $this->getFile($userId, $fileId) : $this->getFileByToken($fileId, $shareToken);
if (isset($error)) {
@@ -232,14 +231,14 @@ class CallbackController extends Controller {
}
if ($this->userSession->isLoggedIn() && !$file->isReadable()) {
- $this->logger->error("Download without access right", array("app" => $this->appName));
+ $this->logger->error("Download without access right", ["app" => $this->appName]);
return new JSONResponse(["message" => $this->trans->t("Access denied")], Http::STATUS_FORBIDDEN);
}
try {
return new DataDownloadResponse($file->getContent(), $file->getName(), $file->getMimeType());
} catch (NotPermittedException $e) {
- $this->logger->error("Download Not permitted: $fileId " . $e->getMessage(), array("app" => $this->appName));
+ $this->logger->logException($e, ["message" => "Download Not permitted: $fileId", "app" => $this->appName]);
return new JSONResponse(["message" => $this->trans->t("Not permitted")], Http::STATUS_FORBIDDEN);
}
return new JSONResponse(["message" => $this->trans->t("Download failed")], Http::STATUS_INTERNAL_SERVER_ERROR);
@@ -250,7 +249,7 @@ class CallbackController extends Controller {
*
* @param string $doc - verification token with the file identifier
*
- * @return OCA\Onlyoffice\DownloadResponse
+ * @return DataDownloadResponse|JSONResponse
*
* @NoAdminRequired
* @NoCSRFRequired
@@ -258,22 +257,22 @@ class CallbackController extends Controller {
* @CORS
*/
public function emptyfile($doc) {
- $this->logger->debug("Download empty", array("app" => $this->appName));
+ $this->logger->debug("Download empty", ["app" => $this->appName]);
list ($hashData, $error) = $this->crypt->ReadHash($doc);
- if ($hashData === NULL) {
- $this->logger->error("Download empty with empty or not correct hash: $error", array("app" => $this->appName));
+ if ($hashData === null) {
+ $this->logger->error("Download empty with empty or not correct hash: $error", ["app" => $this->appName]);
return new JSONResponse(["message" => $this->trans->t("Access denied")], Http::STATUS_FORBIDDEN);
}
if ($hashData->action !== "empty") {
- $this->logger->error("Download empty with other action", array("app" => $this->appName));
+ $this->logger->error("Download empty with other action", ["app" => $this->appName]);
return new JSONResponse(["message" => $this->trans->t("Invalid request")], Http::STATUS_BAD_REQUEST);
}
if (!empty($this->config->GetDocumentServerSecret())) {
$header = \OC::$server->getRequest()->getHeader($this->config->JwtHeader());
if (empty($header)) {
- $this->logger->error("Download empty without jwt", array("app" => $this->appName));
+ $this->logger->error("Download empty without jwt", ["app" => $this->appName]);
return new JSONResponse(["message" => $this->trans->t("Access denied")], Http::STATUS_FORBIDDEN);
}
@@ -282,7 +281,7 @@ class CallbackController extends Controller {
try {
$decodedHeader = \Firebase\JWT\JWT::decode($header, $this->config->GetDocumentServerSecret(), array("HS256"));
} catch (\UnexpectedValueException $e) {
- $this->logger->error("Download empty with invalid jwt: " . $e->getMessage(), array("app" => $this->appName));
+ $this->logger->logException($e, ["message" => "Download empty with invalid jwt", "app" => $this->appName]);
return new JSONResponse(["message" => $this->trans->t("Access denied")], Http::STATUS_FORBIDDEN);
}
}
@@ -291,14 +290,14 @@ class CallbackController extends Controller {
$template = file_get_contents($templatePath);
if (!$template) {
- $this->logger->info("Template for download empty not found: $templatePath", array("app" => $this->appName));
+ $this->logger->info("Template for download empty not found: $templatePath", ["app" => $this->appName]);
return new JSONResponse(["message" => $this->trans->t("File not found")], Http::STATUS_NOT_FOUND);
}
try {
return new DataDownloadResponse($template, "new.docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
} catch (NotPermittedException $e) {
- $this->logger->error("Download Not permitted: $fileId " . $e->getMessage(), array("app" => $this->appName));
+ $this->logger->logException($e, ["message" => "Download Not permitted", "app" => $this->appName]);
return new JSONResponse(["message" => $this->trans->t("Not permitted")], Http::STATUS_FORBIDDEN);
}
return new JSONResponse(["message" => $this->trans->t("Download failed")], Http::STATUS_INTERNAL_SERVER_ERROR);
@@ -324,30 +323,30 @@ class CallbackController extends Controller {
public function track($doc, $users, $key, $status, $url, $token) {
list ($hashData, $error) = $this->crypt->ReadHash($doc);
- if ($hashData === NULL) {
- $this->logger->error("Track with empty or not correct hash: $error", array("app" => $this->appName));
+ if ($hashData === null) {
+ $this->logger->error("Track with empty or not correct hash: $error", ["app" => $this->appName]);
return new JSONResponse(["message" => $this->trans->t("Access denied")], Http::STATUS_FORBIDDEN);
}
if ($hashData->action !== "track") {
- $this->logger->error("Track with other action", array("app" => $this->appName));
+ $this->logger->error("Track with other action", ["app" => $this->appName]);
return new JSONResponse(["message" => $this->trans->t("Invalid request")], Http::STATUS_BAD_REQUEST);
}
$fileId = $hashData->fileId;
- $this->logger->debug("Track: $fileId status $status", array("app" => $this->appName));
+ $this->logger->debug("Track: $fileId status $status", ["app" => $this->appName]);
if (!empty($this->config->GetDocumentServerSecret())) {
if (!empty($token)) {
try {
$payload = \Firebase\JWT\JWT::decode($token, $this->config->GetDocumentServerSecret(), array("HS256"));
} catch (\UnexpectedValueException $e) {
- $this->logger->error("Track with invalid jwt in body: " . $e->getMessage(), array("app" => $this->appName));
+ $this->logger->logException($e, ["message" => "Track with invalid jwt in body", "app" => $this->appName]);
return new JSONResponse(["message" => $this->trans->t("Access denied")], Http::STATUS_FORBIDDEN);
}
} else {
$header = \OC::$server->getRequest()->getHeader($this->config->JwtHeader());
if (empty($header)) {
- $this->logger->error("Track without jwt", array("app" => $this->appName));
+ $this->logger->error("Track without jwt", ["app" => $this->appName]);
return new JSONResponse(["message" => $this->trans->t("Access denied")], Http::STATUS_FORBIDDEN);
}
@@ -358,15 +357,15 @@ class CallbackController extends Controller {
$payload = $decodedHeader->payload;
} catch (\UnexpectedValueException $e) {
- $this->logger->error("Track with invalid jwt: " . $e->getMessage(), array("app" => $this->appName));
+ $this->logger->logException($e, ["message" => "Track with invalid jwt", "app" => $this->appName]);
return new JSONResponse(["message" => $this->trans->t("Access denied")], Http::STATUS_FORBIDDEN);
}
}
- $users = isset($payload->users) ? $payload->users : NULL;
+ $users = isset($payload->users) ? $payload->users : null;
$key = $payload->key;
$status = $payload->status;
- $url = isset($payload->url) ? $payload->url : NULL;
+ $url = isset($payload->url) ? $payload->url : null;
}
$trackerStatus = $this->_trackerStatus[$status];
@@ -376,12 +375,12 @@ class CallbackController extends Controller {
case "MustSave":
case "Corrupted":
if (empty($url)) {
- $this->logger->error("Track without url: $fileId status $trackerStatus", array("app" => $this->appName));
+ $this->logger->error("Track without url: $fileId status $trackerStatus", ["app" => $this->appName]);
return new JSONResponse(["message" => "Url not found"], Http::STATUS_BAD_REQUEST);
}
try {
- $shareToken = isset($hashData->shareToken) ? $hashData->shareToken : NULL;
+ $shareToken = isset($hashData->shareToken) ? $hashData->shareToken : null;
$filePath = null;
\OC_Util::tearDownFS();
@@ -402,7 +401,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", array("app" => $this->appName));
+ $this->logger->debug("Track for $userId: $fileId status $trackerStatus", ["app" => $this->appName]);
$user = $this->userManager->get($userId);
if (!empty($user)) {
@@ -412,14 +411,14 @@ class CallbackController extends Controller {
$filePath = $hashData->filePath;
}
} else {
- $this->logger->debug("Track $fileId by token for $userId", array("app" => $this->appName));
+ $this->logger->debug("Track $fileId by token for $userId", ["app" => $this->appName]);
}
}
list ($file, $error) = empty($shareToken) ? $this->getFile($userId, $fileId, $filePath) : $this->getFileByToken($fileId, $shareToken);
if (isset($error)) {
- $this->logger->error("track error $fileId " . json_encode($error->getData()), array("app" => $this->appName));
+ $this->logger->error("track error $fileId " . json_encode($error->getData()), ["app" => $this->appName]);
return $error;
}
@@ -434,24 +433,24 @@ class CallbackController extends Controller {
$key = DocumentService::GenerateRevisionId($fileId . $url);
try {
- $this->logger->debug("Converted from $downloadExt to $curExt", array("app" => $this->appName));
+ $this->logger->debug("Converted from $downloadExt to $curExt", ["app" => $this->appName]);
$url = $documentService->GetConvertedUri($url, $downloadExt, $curExt, $key);
} catch (\Exception $e) {
- $this->logger->error("Converted on save error: " . $e->getMessage(), array("app" => $this->appName));
+ $this->logger->logException($e, ["message" => "Converted on save error", "app" => $this->appName]);
return new JSONResponse(["message" => $e->getMessage()], Http::STATUS_INTERNAL_SERVER_ERROR);
}
}
$newData = $documentService->Request($url);
- $this->logger->debug("Track put content " . $file->getPath(), array("app" => $this->appName));
+ $this->logger->debug("Track put content " . $file->getPath(), ["app" => $this->appName]);
$this->retryOperation(function () use ($file, $newData){
return $file->putContent($newData);
});
$result = 0;
} catch (\Exception $e) {
- $this->logger->error("Track $trackerStatus error: " . $e->getMessage(), array("app" => $this->appName));
+ $this->logger->logException($e, ["message" => "Track $trackerStatus error", "app" => $this->appName]);
}
break;
@@ -461,7 +460,7 @@ class CallbackController extends Controller {
break;
}
- $this->logger->debug("Track: $fileId status $status result $result", array("app" => $this->appName));
+ $this->logger->debug("Track: $fileId status $status result $result", ["app" => $this->appName]);
return new JSONResponse(["error" => $result], Http::STATUS_OK);
}
@@ -476,21 +475,21 @@ class CallbackController extends Controller {
*
* @return array
*/
- private function getFile($userId, $fileId, $filePath = NULL) {
+ private function getFile($userId, $fileId, $filePath = null) {
if (empty($fileId)) {
- return [NULL, new JSONResponse(["message" => $this->trans->t("FileId is empty")], Http::STATUS_BAD_REQUEST)];
+ return [null, new JSONResponse(["message" => $this->trans->t("FileId is empty")], Http::STATUS_BAD_REQUEST)];
}
try {
$files = $this->root->getUserFolder($userId)->getById($fileId);
} catch (\Exception $e) {
- $this->logger->error("getFile: $fileId " . $e->getMessage(), array("app" => $this->appName));
- return [NULL, new JSONResponse(["message" => $this->trans->t("Invalid request")], Http::STATUS_BAD_REQUEST)];
+ $this->logger->errorlogException($e, ["message" => "getFile: $fileId", "app" => $this->appName]);
+ return [null, new JSONResponse(["message" => $this->trans->t("Invalid request")], Http::STATUS_BAD_REQUEST)];
}
if (empty($files)) {
- $this->logger->error("Files not found: $fileId", array("app" => $this->appName));
- return [NULL, new JSONResponse(["message" => $this->trans->t("Files not found")], Http::STATUS_NOT_FOUND)];
+ $this->logger->error("Files not found: $fileId", ["app" => $this->appName]);
+ return [null, new JSONResponse(["message" => $this->trans->t("Files not found")], Http::STATUS_NOT_FOUND)];
}
$file = $files[0];
@@ -506,11 +505,11 @@ class CallbackController extends Controller {
}
if (!($file instanceof File)) {
- $this->logger->error("File not found: $fileId", array("app" => $this->appName));
- return [NULL, new JSONResponse(["message" => $this->trans->t("File not found")], Http::STATUS_NOT_FOUND)];
+ $this->logger->error("File not found: $fileId", ["app" => $this->appName]);
+ return [null, new JSONResponse(["message" => $this->trans->t("File not found")], Http::STATUS_NOT_FOUND)];
}
- return [$file, NULL];
+ return [$file, null];
}
/**
@@ -525,33 +524,33 @@ class CallbackController extends Controller {
list ($share, $error) = $this->getShare($shareToken);
if (isset($error)) {
- return [NULL, $error];
+ return [null, $error];
}
try {
$node = $share->getNode();
} catch (NotFoundException $e) {
- $this->logger->error("getFileByToken error: " . $e->getMessage(), array("app" => $this->appName));
- return [NULL, new JSONResponse(["message" => $this->trans->t("File not found")], Http::STATUS_NOT_FOUND)];
+ $this->logger->logException($e, ["message" => "getFileByToken error", "app" => $this->appName]);
+ return [null, new JSONResponse(["message" => $this->trans->t("File not found")], Http::STATUS_NOT_FOUND)];
}
if ($node instanceof Folder) {
try {
$files = $node->getById($fileId);
} catch (\Exception $e) {
- $this->logger->error("getFileByToken: $fileId " . $e->getMessage(), array("app" => $this->appName));
- return [NULL, new JSONResponse(["message" => $this->trans->t("Invalid request")], Http::STATUS_NOT_FOUND)];
+ $this->logger->logException($e, ["message" => "getFileByToken: $fileId", "app" => $this->appName]);
+ return [null, new JSONResponse(["message" => $this->trans->t("Invalid request")], Http::STATUS_NOT_FOUND)];
}
if (empty($files)) {
- return [NULL, new JSONResponse(["message" => $this->trans->t("File not found")], Http::STATUS_NOT_FOUND)];
+ return [null, new JSONResponse(["message" => $this->trans->t("File not found")], Http::STATUS_NOT_FOUND)];
}
$file = $files[0];
} else {
$file = $node;
}
- return [$file, NULL];
+ return [$file, null];
}
/**
@@ -563,22 +562,22 @@ class CallbackController extends Controller {
*/
private function getShare($shareToken) {
if (empty($shareToken)) {
- return [NULL, new JSONResponse(["message" => $this->trans->t("FileId is empty")], Http::STATUS_BAD_REQUEST)];
+ return [null, new JSONResponse(["message" => $this->trans->t("FileId is empty")], Http::STATUS_BAD_REQUEST)];
}
- $share;
+ $share = null;
try {
$share = $this->shareManager->getShareByToken($shareToken);
} catch (ShareNotFound $e) {
- $this->logger->error("getShare error: " . $e->getMessage(), array("app" => $this->appName));
- $share = NULL;
+ $this->logger->logException($e, ["message" => "getShare error", "app" => $this->appName]);
+ $share = null;
}
- if ($share === NULL || $share === false) {
- return [NULL, new JSONResponse(["message" => $this->trans->t("You do not have enough permissions to view the file")], Http::STATUS_FORBIDDEN)];
+ if ($share === null || $share === false) {
+ return [null, new JSONResponse(["message" => $this->trans->t("You do not have enough permissions to view the file")], Http::STATUS_FORBIDDEN)];
}
- return [$share, NULL];
+ return [$share, null];
}
/**
diff --git a/controller/editorcontroller.php b/controller/editorcontroller.php
index adbca48..f20dd1f 100644
--- a/controller/editorcontroller.php
+++ b/controller/editorcontroller.php
@@ -33,13 +33,10 @@ use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\ContentSecurityPolicy;
use OCP\AppFramework\Http\RedirectResponse;
use OCP\AppFramework\Http\TemplateResponse;
-use OCP\AutoloadNotAllowedException;
use OCP\Constants;
-use OCP\Files\FileInfo;
use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\IRootFolder;
-use OCP\Files\NotFoundException;
use OCP\IL10N;
use OCP\ILogger;
use OCP\IRequest;
@@ -47,17 +44,13 @@ use OCP\ISession;
use OCP\IURLGenerator;
use OCP\IUserManager;
use OCP\IUserSession;
-use OCP\Share\Exceptions\ShareNotFound;
use OCP\Share\IManager;
-use OC\Files\Filesystem;
-
use OCA\Files\Helper;
use OCA\Onlyoffice\AppConfig;
use OCA\Onlyoffice\Crypt;
use OCA\Onlyoffice\DocumentService;
-use OCA\Onlyoffice\FileCreator;
use OCA\Onlyoffice\FileUtility;
use OCA\Onlyoffice\TemplateManager;
@@ -111,21 +104,21 @@ class EditorController extends Controller {
/**
* Application configuration
*
- * @var OCA\Onlyoffice\AppConfig
+ * @var AppConfig
*/
private $config;
/**
* Hash generator
*
- * @var OCA\Onlyoffice\Crypt
+ * @var Crypt
*/
private $crypt;
/**
* File utility
*
- * @var OCA\Onlyoffice\FileUtility
+ * @var FileUtility
*/
private $fileUtility;
@@ -143,8 +136,8 @@ class EditorController extends Controller {
* @param IURLGenerator $urlGenerator - url generator service
* @param IL10N $trans - l10n service
* @param ILogger $logger - logger
- * @param OCA\Onlyoffice\AppConfig $config - application configuration
- * @param OCA\Onlyoffice\Crypt $crypt - hash generator
+ * @param AppConfig $config - application configuration
+ * @param Crypt $crypt - hash generator
* @param IManager $shareManager - Share manager
* @param IManager $ISession - Session
*/
@@ -187,8 +180,8 @@ class EditorController extends Controller {
* @NoAdminRequired
* @PublicPage
*/
- public function create($name, $dir, $shareToken = NULL) {
- $this->logger->debug("Create: $name", array("app" => $this->appName));
+ public function create($name, $dir, $shareToken = null) {
+ $this->logger->debug("Create: $name", ["app" => $this->appName]);
if (empty($shareToken) && !$this->config->isUserAllowedToUse()) {
return ["error" => $this->trans->t("Not permitted")];
@@ -201,7 +194,7 @@ class EditorController extends Controller {
list ($userFolder, $error, $share) = $this->fileUtility->getNodeByToken($shareToken);
if (isset($error)) {
- $this->logger->error("Create: $error", array("app" => $this->appName));
+ $this->logger->error("Create: $error", ["app" => $this->appName]);
return ["error" => $error];
}
@@ -210,36 +203,40 @@ class EditorController extends Controller {
}
if (!empty($shareToken) && ($share->getPermissions() & Constants::PERMISSION_CREATE) === 0) {
- $this->logger->error("Create in public folder without access: $fileId", array("app" => $this->appName));
+ $this->logger->error("Create in public folder without access", ["app" => $this->appName]);
return ["error" => $this->trans->t("You do not have enough permissions to view the file")];
}
}
$folder = $userFolder->get($dir);
- if ($folder === NULL) {
- $this->logger->error("Folder for file creation was not found: $dir", array("app" => $this->appName));
+ if ($folder === null) {
+ $this->logger->error("Folder for file creation was not found: $dir", ["app" => $this->appName]);
return ["error" => $this->trans->t("The required folder was not found")];
}
if (!$folder->isCreatable()) {
- $this->logger->error("Folder for file creation without permission: $dir", array("app" => $this->appName));
+ $this->logger->error("Folder for file creation without permission: $dir", ["app" => $this->appName]);
return ["error" => $this->trans->t("You don't have enough permission to create")];
}
$template = TemplateManager::GetTemplate($name);
if (!$template) {
- $this->logger->error("Template for file creation not found: $templatePath", array("app" => $this->appName));
+ $this->logger->error("Template for file creation not found: $name", ["app" => $this->appName]);
return ["error" => $this->trans->t("Template not found")];
}
$name = $folder->getNonExistingName($name);
try {
- $file = $folder->newFile($name);
+ if (\version_compare(\implode(".", \OCP\Util::getVersion()), "19", "<")) {
+ $file = $folder->newFile($name);
- $file->putContent($template);
+ $file->putContent($template);
+ } else {
+ $file = $folder->newFile($name, $template);
+ }
} catch (NotPermittedException $e) {
- $this->logger->error("Can't create file: $name", array("app" => $this->appName));
+ $this->logger->logException($e, ["message" => "Can't create file: $name", "app" => $this->appName]);
return ["error" => $this->trans->t("Can't create file")];
}
@@ -260,15 +257,15 @@ class EditorController extends Controller {
* @NoAdminRequired
* @PublicPage
*/
- public function convert($fileId, $shareToken = NULL) {
- $this->logger->debug("Convert: $fileId", array("app" => $this->appName));
+ public function convert($fileId, $shareToken = null) {
+ $this->logger->debug("Convert: $fileId", ["app" => $this->appName]);
if (empty($shareToken) && !$this->config->isUserAllowedToUse()) {
return ["error" => $this->trans->t("Not permitted")];
}
$user = $this->userSession->getUser();
- $userId = NULL;
+ $userId = null;
if (!empty($user)) {
$userId = $user->getUID();
}
@@ -276,12 +273,12 @@ class EditorController extends Controller {
list ($file, $error, $share) = empty($shareToken) ? $this->getFile($userId, $fileId) : $this->fileUtility->getFileByToken($fileId, $shareToken);
if (isset($error)) {
- $this->logger->error("Convertion: $fileId $error", array("app" => $this->appName));
+ $this->logger->error("Convertion: $fileId $error", ["app" => $this->appName]);
return ["error" => $error];
}
if (!empty($shareToken) && ($share->getPermissions() & Constants::PERMISSION_CREATE) === 0) {
- $this->logger->error("Convertion in public folder without access: $fileId", array("app" => $this->appName));
+ $this->logger->error("Convertion in public folder without access: $fileId", ["app" => $this->appName]);
return ["error" => $this->trans->t("You do not have enough permissions to view the file")];
}
@@ -289,12 +286,12 @@ class EditorController extends Controller {
$ext = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
$format = $this->config->FormatsSetting()[$ext];
if (!isset($format)) {
- $this->logger->info("Format for convertion not supported: $fileName", array("app" => $this->appName));
+ $this->logger->info("Format for convertion not supported: $fileName", ["app" => $this->appName]);
return ["error" => $this->trans->t("Format is not supported")];
}
if (!isset($format["conv"]) || $format["conv"] !== true) {
- $this->logger->info("Conversion is not required: $fileName", array("app" => $this->appName));
+ $this->logger->info("Conversion is not required: $fileName", ["app" => $this->appName]);
return ["error" => $this->trans->t("Conversion is not required")];
}
@@ -308,14 +305,14 @@ class EditorController extends Controller {
break;
}
- $newFileUri;
+ $newFileUri = null;
$documentService = new DocumentService($this->trans, $this->config);
$key = $this->fileUtility->getKey($file);
$fileUrl = $this->getUrl($file, $user, $shareToken);
try {
$newFileUri = $documentService->GetConvertedUri($fileUrl, $ext, $internalExtension, $key);
} catch (\Exception $e) {
- $this->logger->error("GetConvertedUri: " . $file->getId() . " " . $e->getMessage(), array("app" => $this->appName));
+ $this->logger->logException($e, ["GetConvertedUri: " . $file->getId(), "app" => $this->appName]);
return ["error" => $e->getMessage()];
}
@@ -327,7 +324,7 @@ class EditorController extends Controller {
try {
$newData = $documentService->Request($newFileUri);
} catch (\Exception $e) {
- $this->logger->error("Failed to download converted file: " . $e->getMessage(), array("app" => $this->appName));
+ $this->logger->logException($e, ["Failed to download converted file", "app" => $this->appName]);
return ["error" => $this->trans->t("Failed to download converted file")];
}
@@ -339,7 +336,7 @@ class EditorController extends Controller {
$file->putContent($newData);
} catch (NotPermittedException $e) {
- $this->logger->error("Can't create file: $newFileName", array("app" => $this->appName));
+ $this->logger->logException($e, ["Can't create file: $newFileName", "app" => $this->appName]);
return ["error" => $this->trans->t("Can't create file")];
}
@@ -361,7 +358,7 @@ class EditorController extends Controller {
* @NoAdminRequired
*/
public function save($name, $dir, $url) {
- $this->logger->debug("Save: $name", array("app" => $this->appName));
+ $this->logger->debug("Save: $name", ["app" => $this->appName]);
if (!$this->config->isUserAllowedToUse()) {
return ["error" => $this->trans->t("Not permitted")];
@@ -372,12 +369,12 @@ class EditorController extends Controller {
$folder = $userFolder->get($dir);
- if ($folder === NULL) {
- $this->logger->error("Folder for saving file was not found: $dir", array("app" => $this->appName));
+ if ($folder === null) {
+ $this->logger->error("Folder for saving file was not found: $dir", ["app" => $this->appName]);
return ["error" => $this->trans->t("The required folder was not found")];
}
if (!$folder->isCreatable()) {
- $this->logger->error("Folder for saving file without permission: $dir", array("app" => $this->appName));
+ $this->logger->error("Folder for saving file without permission: $dir", ["app" => $this->appName]);
return ["error" => $this->trans->t("You don't have enough permission to create")];
}
@@ -387,7 +384,7 @@ class EditorController extends Controller {
$documentService = new DocumentService($this->trans, $this->config);
$newData = $documentService->Request($url);
} catch (\Exception $e) {
- $this->logger->error("Failed to download file for saving: $url " . $e->getMessage(), array("app" => $this->appName));
+ $this->logger->logException($e, ["Failed to download file for saving: $url", "app" => $this->appName]);
return ["error" => $this->trans->t("Download failed")];
}
@@ -398,7 +395,7 @@ class EditorController extends Controller {
$file->putContent($newData);
} catch (NotPermittedException $e) {
- $this->logger->error("Can't save file: $name", array("app" => $this->appName));
+ $this->logger->logException($e, ["Can't save file: $name", "app" => $this->appName]);
return ["error" => $this->trans->t("Can't create file")];
}
@@ -418,7 +415,7 @@ class EditorController extends Controller {
* @NoAdminRequired
*/
public function url($filePath) {
- $this->logger->debug("Request url for: $filePath", array("app" => $this->appName));
+ $this->logger->debug("Request url for: $filePath", ["app" => $this->appName]);
if (!$this->config->isUserAllowedToUse()) {
return ["error" => $this->trans->t("Not permitted")];
@@ -430,12 +427,12 @@ class EditorController extends Controller {
$file = $userFolder->get($filePath);
- if ($file === NULL) {
- $this->logger->error("File for generate presigned url was not found: $dir", array("app" => $this->appName));
+ if ($file === null) {
+ $this->logger->error("File for generate presigned url was not found: $filePath", ["app" => $this->appName]);
return ["error" => $this->trans->t("File not found")];
}
if (!$file->isReadable()) {
- $this->logger->error("Folder for saving file without permission: $dir", array("app" => $this->appName));
+ $this->logger->error("Folder for saving file without permission: $filePath", ["app" => $this->appName]);
return ["error" => $this->trans->t("You do not have enough permissions to view the file")];
}
@@ -469,8 +466,8 @@ class EditorController extends Controller {
* @NoAdminRequired
* @NoCSRFRequired
*/
- public function index($fileId, $filePath = NULL, $shareToken = NULL, $inframe = false) {
- $this->logger->debug("Open: $fileId $filePath", array("app" => $this->appName));
+ public function index($fileId, $filePath = null, $shareToken = null, $inframe = false) {
+ $this->logger->debug("Open: $fileId $filePath", ["app" => $this->appName]);
if (empty($shareToken) && !$this->userSession->isLoggedIn()) {
$redirectUrl = $this->urlGenerator->linkToRoute("core.login.showLoginForm", [
@@ -486,7 +483,7 @@ class EditorController extends Controller {
$documentServerUrl = $this->config->GetDocumentServerUrl();
if (empty($documentServerUrl)) {
- $this->logger->error("documentServerUrl is empty", array("app" => $this->appName));
+ $this->logger->error("documentServerUrl is empty", ["app" => $this->appName]);
return $this->renderError($this->trans->t("ONLYOFFICE app is not configured. Please contact admin"));
}
@@ -552,16 +549,16 @@ class EditorController extends Controller {
* @NoAdminRequired
* @PublicPage
*/
- public function config($fileId, $filePath = NULL, $shareToken = NULL, $directToken = null, $inframe = 0, $desktop = false) {
+ public function config($fileId, $filePath = null, $shareToken = null, $directToken = null, $inframe = 0, $desktop = false) {
if (!empty($directToken)) {
list ($directData, $error) = $this->crypt->ReadHash($directToken);
- if ($directData === NULL) {
- $this->logger->error("Config for directEditor with empty or not correct hash: $error", array("app" => $this->appName));
+ if ($directData === null) {
+ $this->logger->error("Config for directEditor with empty or not correct hash: $error", ["app" => $this->appName]);
return ["error" => $this->trans->t("Not permitted")];
}
if ($directData->action !== "direct") {
- $this->logger->error("Config for directEditor with other data", array("app" => $this->appName));
+ $this->logger->error("Config for directEditor with other data", ["app" => $this->appName]);
return ["error" => $this->trans->t("Invalid request")];
}
@@ -584,7 +581,7 @@ class EditorController extends Controller {
}
$user = $this->userSession->getUser();
- $userId = NULL;
+ $userId = null;
if (!empty($user)) {
$userId = $user->getUID();
}
@@ -593,7 +590,7 @@ class EditorController extends Controller {
list ($file, $error, $share) = empty($shareToken) ? $this->getFile($userId, $fileId, $filePath) : $this->fileUtility->getFileByToken($fileId, $shareToken);
if (isset($error)) {
- $this->logger->error("Config: $fileId $error", array("app" => $this->appName));
+ $this->logger->error("Config: $fileId $error", ["app" => $this->appName]);
return ["error" => $error];
}
@@ -601,7 +598,7 @@ class EditorController extends Controller {
$ext = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
$format = $this->config->FormatsSetting()[$ext];
if (!isset($format)) {
- $this->logger->info("Format is not supported for editing: $fileName", array("app" => $this->appName));
+ $this->logger->info("Format is not supported for editing: $fileName", ["app" => $this->appName]);
return ["error" => $this->trans->t("Format is not supported")];
}
@@ -657,7 +654,7 @@ class EditorController extends Controller {
];
}
- $folderLink = NULL;
+ $folderLink = null;
if (!empty($shareToken)) {
if (method_exists($share, "getHideDownload") && $share->getHideDownload()) {
@@ -690,7 +687,7 @@ class EditorController extends Controller {
}
}
- if ($folderLink !== NULL && $inframe !== 2) {
+ if ($folderLink !== null && $inframe !== 2) {
$params["editorConfig"]["customization"]["goback"] = [
"url" => $folderLink
];
@@ -698,9 +695,10 @@ class EditorController extends Controller {
if (!$desktop) {
if ($this->config->GetSameTab()) {
$params["editorConfig"]["customization"]["goback"]["blank"] = false;
- if ($inframe === 1 || !empty($directToken)) {
- $params["editorConfig"]["customization"]["goback"]["requestClose"] = true;
- }
+ }
+
+ if ($inframe === 1 || !empty($directToken)) {
+ $params["editorConfig"]["customization"]["goback"]["requestClose"] = true;
}
}
}
@@ -722,7 +720,7 @@ class EditorController extends Controller {
$params["token"] = $token;
}
- $this->logger->debug("Config is generated for: $fileId with key $key", array("app" => $this->appName));
+ $this->logger->debug("Config is generated for: $fileId with key $key", ["app" => $this->appName]);
return $params;
}
@@ -736,21 +734,21 @@ class EditorController extends Controller {
*
* @return array
*/
- private function getFile($userId, $fileId, $filePath = NULL) {
+ private function getFile($userId, $fileId, $filePath = null) {
if (empty($fileId)) {
- return [NULL, $this->trans->t("FileId is empty"), NULL];
+ return [null, $this->trans->t("FileId is empty"), null];
}
try {
$files = $this->root->getUserFolder($userId)->getById($fileId);
} catch (\Exception $e) {
- $this->logger->error("getFile: $fileId " . $e->getMessage(), array("app" => $this->appName));
- return [NULL, $this->trans->t("Invalid request"), NULL];
+ $this->logger->logException($e, ["getFile: $fileId", "app" => $this->appName]);
+ return [null, $this->trans->t("Invalid request"), null];
}
if (empty($files)) {
- $this->logger->info("Files not found: $fileId", array("app" => $this->appName));
- return [NULL, $this->trans->t("File not found"), NULL];
+ $this->logger->info("Files not found: $fileId", ["app" => $this->appName]);
+ return [null, $this->trans->t("File not found"), null];
}
$file = $files[0];
@@ -766,10 +764,10 @@ class EditorController extends Controller {
}
if (!$file->isReadable()) {
- return [NULL, $this->trans->t("You do not have enough permissions to view the file"), NULL];
+ return [null, $this->trans->t("You do not have enough permissions to view the file"), null];
}
- return [$file, NULL, NULL];
+ return [$file, null, null];
}
/**
@@ -781,8 +779,8 @@ class EditorController extends Controller {
*
* @return string
*/
- private function getUrl($file, $user = NULL, $shareToken = NULL) {
- $userId = NULL;
+ private function getUrl($file, $user = null, $shareToken = null) {
+ $userId = null;
if (!empty($user)) {
$userId = $user->getUID();
}
@@ -844,6 +842,12 @@ class EditorController extends Controller {
$params["editorConfig"]["customization"]["toolbarNoTabs"] = true;
}
+ //default is original
+ $reviewDisplay = $this->config->GetCustomizationReviewDisplay();
+ if ($reviewDisplay !== "original") {
+ $params["editorConfig"]["customization"]["reviewDisplay"] = $reviewDisplay;
+ }
+
/* from system config */
@@ -1010,13 +1014,13 @@ class EditorController extends Controller {
* @return TemplateResponse
*/
private function renderError($error, $hint = "") {
- return new TemplateResponse("", "error", array(
- "errors" => array(
- array(
+ return new TemplateResponse("", "error", [
+ "errors" => [
+ [
"error" => $error,
"hint" => $hint
- )
- )
- ), "error");
+ ]
+ ]
+ ], "error");
}
}
diff --git a/controller/federationcontroller.php b/controller/federationcontroller.php
index 36c9f50..07aae6c 100644
--- a/controller/federationcontroller.php
+++ b/controller/federationcontroller.php
@@ -62,14 +62,14 @@ class FederationController extends OCSController {
/**
* Application configuration
*
- * @var OCA\Onlyoffice\AppConfig
+ * @var AppConfig
*/
public $config;
/**
* File utility
*
- * @var OCA\Onlyoffice\FileUtility
+ * @var FileUtility
*/
private $fileUtility;
@@ -113,13 +113,13 @@ class FederationController extends OCSController {
list ($file, $error, $share) = $this->fileUtility->getFileByToken(null, $shareToken, $path);
if (isset($error)) {
- $this->logger->error("Federated getFileByToken: $error", array("app" => $this->appName));
+ $this->logger->error("Federated getFileByToken: $error", ["app" => $this->appName]);
return new DataResponse(["error" => $error]);
}
$key = $this->fileUtility->getKey($file, true);
- $this->logger->debug("Federated request get for " . $file->getId() . " key $key", array("app" => $this->appName));
+ $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 7292ce6..332fd13 100644
--- a/controller/settingscontroller.php
+++ b/controller/settingscontroller.php
@@ -63,7 +63,7 @@ class SettingsController extends Controller {
/**
* Application configuration
*
- * @var OCA\Onlyoffice\AppConfig
+ * @var AppConfig
*/
private $config;
@@ -77,7 +77,7 @@ class SettingsController extends Controller {
/**
* Hash generator
*
- * @var OCA\Onlyoffice\Crypt
+ * @var Crypt
*/
private $crypt;
@@ -87,8 +87,8 @@ class SettingsController extends Controller {
* @param IURLGenerator $urlGenerator - url generator service
* @param IL10N $trans - l10n service
* @param ILogger $logger - logger
- * @param OCA\Onlyoffice\AppConfig $config - application configuration
- * @param OCA\Onlyoffice\Crypt $crypt - hash generator
+ * @param AppConfig $config - application configuration
+ * @param Crypt $crypt - hash generator
*/
public function __construct($AppName,
IRequest $request,
@@ -130,7 +130,8 @@ class SettingsController extends Controller {
"toolbarNoTabs" => $this->config->GetCustomizationToolbarNoTabs(),
"successful" => $this->config->SettingsAreSuccessful(),
"watermark" => $this->config->GetWatermarkSettings(),
- "tagsEnabled" => App::isEnabled("systemtags")
+ "tagsEnabled" => App::isEnabled("systemtags"),
+ "reviewDisplay" => $this->config->GetCustomizationReviewDisplay()
];
return new TemplateResponse($this->appName, "settings", $data, "blank");
}
@@ -192,6 +193,7 @@ class SettingsController extends Controller {
* @param bool $feedback - display feedback
* @param bool $help - display help
* @param bool $toolbarNoTabs - display toolbar tab
+ * @param string $reviewDisplay - review viewing mode
*
* @return array
*/
@@ -203,7 +205,8 @@ class SettingsController extends Controller {
$compactHeader,
$feedback,
$help,
- $toolbarNoTabs
+ $toolbarNoTabs,
+ $reviewDisplay
) {
$this->config->SetDefaultFormats($defFormats);
@@ -215,6 +218,7 @@ class SettingsController extends Controller {
$this->config->SetCustomizationFeedback($feedback);
$this->config->SetCustomizationHelp($help);
$this->config->SetCustomizationToolbarNoTabs($toolbarNoTabs);
+ $this->config->SetCustomizationReviewDisplay($reviewDisplay);
return [
];
@@ -273,7 +277,7 @@ class SettingsController extends Controller {
}
} catch (\Exception $e) {
- $this->logger->error("Protocol on check error: " . $e->getMessage(), array("app" => $this->appName));
+ $this->logger->logException($e, ["Protocol on check error", "app" => $this->appName]);
return $e->getMessage();
}
@@ -287,7 +291,7 @@ class SettingsController extends Controller {
}
} catch (\Exception $e) {
- $this->logger->error("HealthcheckRequest on check error: " . $e->getMessage(), array("app" => $this->appName));
+ $this->logger->logException($e, ["HealthcheckRequest on check error", "app" => $this->appName]);
return $e->getMessage();
}
@@ -297,7 +301,7 @@ class SettingsController extends Controller {
$commandResponse = $documentService->CommandRequest("version");
- $this->logger->debug("CommandRequest on check: " . json_encode($commandResponse), array("app" => $this->appName));
+ $this->logger->debug("CommandRequest on check: " . json_encode($commandResponse), ["app" => $this->appName]);
if (empty($commandResponse)) {
throw new \Exception($this->trans->t("Error occurred in the document service"));
@@ -309,11 +313,11 @@ class SettingsController extends Controller {
}
} catch (\Exception $e) {
- $this->logger->error("CommandRequest on check error: " . $e->getMessage(), array("app" => $this->appName));
+ $this->logger->logException($e, ["CommandRequest on check error", "app" => $this->appName]);
return $e->getMessage();
}
- $convertedFileUri;
+ $convertedFileUri = null;
try {
$hashUrl = $this->crypt->GetHash(["action" => "empty"]);
@@ -325,14 +329,14 @@ class SettingsController extends Controller {
$convertedFileUri = $documentService->GetConvertedUri($fileUrl, "docx", "docx", "check_" . rand());
} catch (\Exception $e) {
- $this->logger->error("GetConvertedUri on check error: " . $e->getMessage(), array("app" => $this->appName));
+ $this->logger->logException($e, ["GetConvertedUri on check error", "app" => $this->appName]);
return $e->getMessage();
}
try {
$documentService->Request($convertedFileUri);
} catch (\Exception $e) {
- $this->logger->error("Request converted file on check error: " . $e->getMessage(), array("app" => $this->appName));
+ $this->logger->logException($e, ["Request converted file on check error", "app" => $this->appName]);
return $e->getMessage();
}
diff --git a/css/settings.css b/css/settings.css
index 12ba0cc..4732f22 100644
--- a/css/settings.css
+++ b/css/settings.css
@@ -69,3 +69,8 @@
-moz-column-width: 100px;
-webkit-column-width: 100px;
}
+.onlyoffice-tables {
+ column-width: 140px;
+ -moz-column-width: 140px;
+ -webkit-column-width: 140px;
+}
diff --git a/js/editor.js b/js/editor.js
index 5cb0c46..36d9cf1 100644
--- a/js/editor.js
+++ b/js/editor.js
@@ -109,10 +109,6 @@
return;
}
- if (config.editorConfig.tenant) {
- displayError(t(OCA.Onlyoffice.AppName, "You are using public demo ONLYOFFICE Document Server. Please do not store private sensitive data."));
- }
-
var docIsChanged = null;
var docIsChangedTimeout = null;
@@ -138,6 +134,12 @@
"onDocumentStateChange": setPageTitle,
};
+ if (config.editorConfig.tenant) {
+ config.events.onAppReady = function() {
+ OCA.Onlyoffice.docEditor.showMessage(t(OCA.Onlyoffice.AppName, "You are using public demo ONLYOFFICE Document Server. Please do not store private sensitive data."));
+ };
+ }
+
if (OCA.Onlyoffice.inframe && !shareToken
|| OC.currentUser) {
config.events.onRequestSaveAs = OCA.Onlyoffice.onRequestSaveAs;
diff --git a/js/settings.js b/js/settings.js
index a2f3633..b080e22 100644
--- a/js/settings.js
+++ b/js/settings.js
@@ -208,6 +208,7 @@
var feedback = $("#onlyofficeFeedback").is(":checked");
var help = $("#onlyofficeHelp").is(":checked");
var toolbarNoTabs = !$("#onlyofficeToolbarNoTabs").is(":checked");
+ var reviewDisplay = $("input[type='radio'][name='reviewDisplay']:checked").attr("id").replace("onlyofficeReviewDisplay_", "");
$.ajax({
method: "PUT",
@@ -221,7 +222,8 @@
compactHeader: compactHeader,
feedback: feedback,
help: help,
- toolbarNoTabs: toolbarNoTabs
+ toolbarNoTabs: toolbarNoTabs,
+ reviewDisplay: reviewDisplay
},
success: function onSuccess(response) {
$(".section-onlyoffice").removeClass("icon-loading");
diff --git a/l10n/de.js b/l10n/de.js
index 10899c6..936a634 100644
--- a/l10n/de.js
+++ b/l10n/de.js
@@ -78,6 +78,9 @@ OC.L10N.register(
"Show watermark for read only link shares" : "Wasserzeichen auf schreibgeschützten Link-Freigaben anzeigen",
"Show watermark on link shares with specific system tags" : "Wasserzeichen auf Link-Freigaben mit bestimmten System-Tags anzeigen",
"Select tag" : "Wähle einen Tag",
- "Select file to compare" : "Datei zum Vergleich auswählen"
+ "Select file to compare" : "Datei zum Vergleich auswählen",
+ "Markup": "Markup",
+ "Final": "Endgültig",
+ "Original": "Original"
},
"nplurals=2; plural=(n != 1);");
diff --git a/l10n/de.json b/l10n/de.json
index df18125..c194c9c 100644
--- a/l10n/de.json
+++ b/l10n/de.json
@@ -76,6 +76,9 @@
"Show watermark for read only link shares" : "Wasserzeichen auf schreibgeschützten Link-Freigaben anzeigen",
"Show watermark on link shares with specific system tags" : "Wasserzeichen auf Link-Freigaben mit bestimmten System-Tags anzeigen",
"Select tag" : "Wähle einen Tag",
- "Select file to compare" : "Datei zum Vergleich auswählen"
+ "Select file to compare" : "Datei zum Vergleich auswählen",
+ "Markup": "Markup",
+ "Final": "Endgültig",
+ "Original": "Original"
},"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 96f994d..b0e573e 100644
--- a/l10n/de_DE.js
+++ b/l10n/de_DE.js
@@ -78,6 +78,9 @@ OC.L10N.register(
"Show watermark for read only link shares" : "Wasserzeichen auf schreibgeschützten Link-Freigaben anzeigen",
"Show watermark on link shares with specific system tags" : "Wasserzeichen auf Link-Freigaben mit bestimmten System-Tags anzeigen",
"Select tag" : "Wählen Sie ein Tag",
- "Select file to compare" : "Datei zum Vergleich auswählen"
+ "Select file to compare" : "Datei zum Vergleich auswählen",
+ "Markup": "Markup",
+ "Final": "Endgültig",
+ "Original": "Original"
},
"nplurals=2; plural=(n != 1);");
diff --git a/l10n/de_DE.json b/l10n/de_DE.json
index 9b3d891..b6a53f4 100644
--- a/l10n/de_DE.json
+++ b/l10n/de_DE.json
@@ -76,6 +76,9 @@
"Show watermark for read only link shares" : "Wasserzeichen auf schreibgeschützten Link-Freigaben anzeigen",
"Show watermark on link shares with specific system tags" : "Wasserzeichen auf Link-Freigaben mit bestimmten System-Tags anzeigen",
"Select tag" : "Wählen Sie ein Tag",
- "Select file to compare" : "Datei zum Vergleich auswählen"
+ "Select file to compare" : "Datei zum Vergleich auswählen",
+ "Markup": "Markup",
+ "Final": "Endgültig",
+ "Original": "Original"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
} \ No newline at end of file
diff --git a/l10n/es.js b/l10n/es.js
index d1f7e37..7f13393 100644
--- a/l10n/es.js
+++ b/l10n/es.js
@@ -61,6 +61,9 @@ OC.L10N.register(
"Secure view enables you to secure documents by embedding a watermark" : "La vista segura permite asegurar documentos mediante la inserción de una marca de agua en ellos",
"Supported placeholders" : "Marcadores soportados",
"Select tag" : "Seleccionar etiqueta",
- "Select file to compare" : "Seleccione un archivo para comparar"
+ "Select file to compare" : "Seleccione un archivo para comparar",
+ "Markup": "Cambios",
+ "Final": "Final",
+ "Original": "Original"
},
"nplurals=2; plural=(n != 1);");
diff --git a/l10n/es.json b/l10n/es.json
index 2c9598c..54728f9 100644
--- a/l10n/es.json
+++ b/l10n/es.json
@@ -59,6 +59,9 @@
"Secure view enables you to secure documents by embedding a watermark" : "La vista segura permite asegurar documentos mediante la inserción de una marca de agua en ellos",
"Supported placeholders" : "Marcadores soportados",
"Select tag" : "Seleccionar etiqueta",
- "Select file to compare" : "Seleccione un archivo para comparar"
+ "Select file to compare" : "Seleccione un archivo para comparar",
+ "Markup": "Cambios",
+ "Final": "Final",
+ "Original": "Original"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
} \ No newline at end of file
diff --git a/l10n/fr.js b/l10n/fr.js
index 0ecc924..c8ae66f 100644
--- a/l10n/fr.js
+++ b/l10n/fr.js
@@ -59,6 +59,9 @@ OC.L10N.register(
"Insert image" : "Insérer une image",
"Select recipients" : "Sélectionner les destinataires",
"Select tag" : "Sélectionner une étiquette",
- "Select file to compare" : "Sélectionner fichier à comparer"
+ "Select file to compare" : "Sélectionner fichier à comparer",
+ "Markup": "Balisage",
+ "Final": "Finale",
+ "Original": "Original"
},
"nplurals=2; plural=(n != 1);");
diff --git a/l10n/fr.json b/l10n/fr.json
index 6c609af..43eec6f 100644
--- a/l10n/fr.json
+++ b/l10n/fr.json
@@ -57,6 +57,9 @@
"Insert image" : "Insérer une image",
"Select recipients" : "Sélectionner les destinataires",
"Select tag" : "Sélectionner une étiquette",
- "Select file to compare" : "Sélectionner fichier à comparer"
+ "Select file to compare" : "Sélectionner fichier à comparer",
+ "Markup": "Balisage",
+ "Final": "Finale",
+ "Original": "Original"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}
diff --git a/l10n/ru.js b/l10n/ru.js
index 779b7b9..ad2d0fa 100644
--- a/l10n/ru.js
+++ b/l10n/ru.js
@@ -78,6 +78,10 @@ OC.L10N.register(
"Show watermark for read only link shares" : "Отображать водяной знак при доступе только на чтение по ссылке",
"Show watermark on link shares with specific system tags" : "Отображать водяной знак при доступе по ссылке с определенными системными метками",
"Select tag" : "Выберите метку",
- "Select file to compare" : "Выбрать файл для сравнения"
+ "Select file to compare" : "Выбрать файл для сравнения",
+ "Review mode for viewing": "Режим рецензирования при просмотре",
+ "Markup": "Изменения",
+ "Final": "Изменённый документ",
+ "Original": "Исходный документ"
},
"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 7f393ce..ae1ee6e 100644
--- a/l10n/ru.json
+++ b/l10n/ru.json
@@ -76,6 +76,10 @@
"Show watermark for read only link shares" : "Отображать водяной знак при доступе только на чтение по ссылке",
"Show watermark on link shares with specific system tags" : "Отображать водяной знак при доступе по ссылке с определенными системными метками",
"Select tag" : "Выберите метку",
- "Select file to compare" : "Выбрать файл для сравнения"
+ "Select file to compare" : "Выбрать файл для сравнения",
+ "Review mode for viewing": "Режим рецензирования при просмотре",
+ "Markup": "Изменения",
+ "Final": "Изменённый документ",
+ "Original": "Исходный документ"
},"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/lib/appconfig.php b/lib/appconfig.php
index 88a101e..c419428 100644
--- a/lib/appconfig.php
+++ b/lib/appconfig.php
@@ -52,14 +52,14 @@ class AppConfig {
/**
* Config service
*
- * @var OCP\IConfig
+ * @var IConfig
*/
private $config;
/**
* Logger
*
- * @var OCP\ILogger
+ * @var ILogger
*/
private $logger;
@@ -155,6 +155,13 @@ class AppConfig {
private $_customizationToolbarNoTabs = "customizationToolbarNoTabs";
/**
+ * The config key for the review mode setting
+ *
+ * @var string
+ */
+ private $_customizationReviewDisplay = "customizationReviewDisplay";
+
+ /**
* The config key for the setting limit groups
*
* @var string
@@ -279,7 +286,7 @@ class AppConfig {
&& array_key_exists($key, $this->config->getSystemValue($this->appName))) {
return $this->config->getSystemValue($this->appName)[$key];
}
- return NULL;
+ return null;
}
/**
@@ -290,12 +297,12 @@ class AppConfig {
* @return bool
*/
public function SelectDemo($value) {
- $this->logger->info("Select demo: " . json_encode($value), array("app" => $this->appName));
+ $this->logger->info("Select demo: " . json_encode($value), ["app" => $this->appName]);
$data = $this->GetDemoData();
if ($value === true && !$data["available"]) {
- $this->logger->info("Trial demo is overdue: " . json_encode($data), array("app" => $this->appName));
+ $this->logger->info("Trial demo is overdue: " . json_encode($data), ["app" => $this->appName]);
return false;
}
@@ -324,7 +331,7 @@ class AppConfig {
}
$data = json_decode($data, true);
- $overdue = new DateTime(isset($data["start"]) ? $data["start"]["date"] : NULL);
+ $overdue = new DateTime(isset($data["start"]) ? $data["start"]["date"] : null);
$overdue->add(new DateInterval("P" . $this->DEMO_PARAM["TRIAL"] . "D"));
if ($overdue > new DateTime()) {
$data["available"] = true;
@@ -360,7 +367,7 @@ class AppConfig {
}
}
- $this->logger->info("SetDocumentServerUrl: $documentServer", array("app" => $this->appName));
+ $this->logger->info("SetDocumentServerUrl: $documentServer", ["app" => $this->appName]);
$this->config->setAppValue($this->appName, $this->_documentserver, $documentServer);
}
@@ -404,7 +411,7 @@ class AppConfig {
}
}
- $this->logger->info("SetDocumentServerInternalUrl: $documentServerInternal", array("app" => $this->appName));
+ $this->logger->info("SetDocumentServerInternalUrl: $documentServerInternal", ["app" => $this->appName]);
$this->config->setAppValue($this->appName, $this->_documentserverInternal, $documentServerInternal);
}
@@ -450,7 +457,7 @@ class AppConfig {
if ($from !== $documentServerUrl)
{
- $this->logger->debug("Replace url from $from to $documentServerUrl", array("app" => $this->appName));
+ $this->logger->debug("Replace url from $from to $documentServerUrl", ["app" => $this->appName]);
$url = str_replace($from, $documentServerUrl, $url);
}
}
@@ -472,7 +479,7 @@ class AppConfig {
}
}
- $this->logger->info("SetStorageUrl: $storageUrl", array("app" => $this->appName));
+ $this->logger->info("SetStorageUrl: $storageUrl", ["app" => $this->appName]);
$this->config->setAppValue($this->appName, $this->_storageUrl, $storageUrl);
}
@@ -498,9 +505,9 @@ class AppConfig {
public function SetDocumentServerSecret($secret) {
$secret = trim($secret);
if (empty($secret)) {
- $this->logger->info("Clear secret key", array("app" => $this->appName));
+ $this->logger->info("Clear secret key", ["app" => $this->appName]);
} else {
- $this->logger->info("Set secret key", array("app" => $this->appName));
+ $this->logger->info("Set secret key", ["app" => $this->appName]);
}
$this->config->setAppValue($this->appName, $this->_jwtSecret, $secret);
@@ -545,7 +552,7 @@ class AppConfig {
*/
public function SetDefaultFormats($formats) {
$value = json_encode($formats);
- $this->logger->info("Set default formats: $value", array("app" => $this->appName));
+ $this->logger->info("Set default formats: $value", ["app" => $this->appName]);
$this->config->setAppValue($this->appName, $this->_defFormats, $value);
}
@@ -570,7 +577,7 @@ class AppConfig {
*/
public function SetEditableFormats($formats) {
$value = json_encode($formats);
- $this->logger->info("Set editing formats: $value", array("app" => $this->appName));
+ $this->logger->info("Set editing formats: $value", ["app" => $this->appName]);
$this->config->setAppValue($this->appName, $this->_editFormats, $value);
}
@@ -594,7 +601,7 @@ class AppConfig {
* @param bool $value - same tab
*/
public function SetSameTab($value) {
- $this->logger->info("Set opening in a same tab: " . json_encode($value), array("app" => $this->appName));
+ $this->logger->info("Set opening in a same tab: " . json_encode($value), ["app" => $this->appName]);
$this->config->setAppValue($this->appName, $this->_sameTab, json_encode($value));
}
@@ -614,7 +621,7 @@ class AppConfig {
* @param bool $value - display chat
*/
public function SetCustomizationChat($value) {
- $this->logger->info("Set chat display: " . json_encode($value), array("app" => $this->appName));
+ $this->logger->info("Set chat display: " . json_encode($value), ["app" => $this->appName]);
$this->config->setAppValue($this->appName, $this->_customizationChat, json_encode($value));
}
@@ -634,7 +641,7 @@ class AppConfig {
* @param bool $value - display compact header
*/
public function SetCustomizationCompactHeader($value) {
- $this->logger->info("Set compact header display: " . json_encode($value), array("app" => $this->appName));
+ $this->logger->info("Set compact header display: " . json_encode($value), ["app" => $this->appName]);
$this->config->setAppValue($this->appName, $this->_customizationCompactHeader, json_encode($value));
}
@@ -654,7 +661,7 @@ class AppConfig {
* @param bool $value - display feedback
*/
public function SetCustomizationFeedback($value) {
- $this->logger->info("Set feedback display: " . json_encode($value), array("app" => $this->appName));
+ $this->logger->info("Set feedback display: " . json_encode($value), ["app" => $this->appName]);
$this->config->setAppValue($this->appName, $this->_customizationFeedback, json_encode($value));
}
@@ -674,7 +681,7 @@ class AppConfig {
* @param bool $value - display help
*/
public function SetCustomizationHelp($value) {
- $this->logger->info("Set help display: " . json_encode($value), array("app" => $this->appName));
+ $this->logger->info("Set help display: " . json_encode($value), ["app" => $this->appName]);
$this->config->setAppValue($this->appName, $this->_customizationHelp, json_encode($value));
}
@@ -694,7 +701,7 @@ class AppConfig {
* @param bool $value - without tabs
*/
public function SetCustomizationToolbarNoTabs($value) {
- $this->logger->info("Set without tabs: " . json_encode($value), array("app" => $this->appName));
+ $this->logger->info("Set without tabs: " . json_encode($value), ["app" => $this->appName]);
$this->config->setAppValue($this->appName, $this->_customizationToolbarNoTabs, json_encode($value));
}
@@ -709,11 +716,40 @@ class AppConfig {
}
/**
+ * Save review viewing mode setting
+ *
+ * @param string $value - review mode
+ */
+ public function SetCustomizationReviewDisplay($value) {
+ $this->logger->info("Set review mode: " . $value, array("app" => $this->appName));
+
+ $this->config->setAppValue($this->appName, $this->_customizationReviewDisplay, $value);
+ }
+
+ /**
+ * Get review viewing mode setting
+ *
+ * @return string
+ */
+ public function GetCustomizationReviewDisplay() {
+ $value = $this->config->getAppValue($this->appName, $this->_customizationReviewDisplay, "original");
+ if ($value === "markup") {
+ return "markup";
+ }
+ if ($value === "final") {
+ return "final";
+ }
+ return "original";
+ }
+
+ /**
* Save watermark settings
*
* @param array $settings - watermark settings
*/
public function SetWatermarkSettings($settings) {
+ $this->logger->info("Set watermark enabled: " . $settings["enabled"], ["app" => $this->appName]);
+
if ($settings["enabled"] !== "true") {
$this->config->setAppValue(AppConfig::WATERMARK_APP_NAMESPACE, "watermark_enabled", "no");
return;
@@ -806,7 +842,7 @@ class AppConfig {
$groups = array();
}
$value = json_encode($groups);
- $this->logger->info("Set groups: $value", array("app" => $this->appName));
+ $this->logger->info("Set groups: $value", ["app" => $this->appName]);
$this->config->setAppValue($this->appName, $this->_groups, $value);
}
diff --git a/lib/crypt.php b/lib/crypt.php
index ccece98..7a5e4ea 100644
--- a/lib/crypt.php
+++ b/lib/crypt.php
@@ -41,12 +41,12 @@ class Crypt {
/**
* Application configuration
*
- * @var OCA\Onlyoffice\AppConfig
+ * @var AppConfig
*/
private $config;
/**
- * @param OCA\Onlyoffice\AppConfig $config - application configutarion
+ * @param AppConfig $config - application configutarion
*/
public function __construct(AppConfig $appConfig) {
$this->config = $appConfig;
@@ -71,9 +71,9 @@ class Crypt {
* @return array
*/
public function ReadHash($token) {
- $result = NULL;
- $error = NULL;
- if ($token === NULL) {
+ $result = null;
+ $error = null;
+ if ($token === null) {
return [$result, "token is empty"];
}
try {
diff --git a/lib/directeditor.php b/lib/directeditor.php
index 11b99c1..dc8da51 100644
--- a/lib/directeditor.php
+++ b/lib/directeditor.php
@@ -30,7 +30,6 @@
namespace OCA\Onlyoffice;
use OCP\AppFramework\Http\ContentSecurityPolicy;
-use OCP\AppFramework\Http\NotFoundResponse;
use OCP\AppFramework\Http\Response;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\DirectEditing\IEditor;
@@ -81,14 +80,14 @@ class DirectEditor implements IEditor {
/**
* Application configuration
*
- * @var OCA\Onlyoffice\AppConfig
+ * @var AppConfig
*/
private $config;
/**
* Hash generator
*
- * @var OCA\Onlyoffice\Crypt
+ * @var Crypt
*/
private $crypt;
@@ -97,8 +96,8 @@ class DirectEditor implements IEditor {
* @param IURLGenerator $urlGenerator - url generator service
* @param IL10N $trans - l10n service
* @param ILogger $logger - logger
- * @param OCA\Onlyoffice\AppConfig $config - application configuration
- * @param OCA\Onlyoffice\Crypt $crypt - hash generator
+ * @param AppConfig $config - application configuration
+ * @param Crypt $crypt - hash generator
*/
public function __construct($AppName,
IURLGenerator $urlGenerator,
@@ -218,12 +217,12 @@ class DirectEditor implements IEditor {
$token->useTokenScope();
$file = $token->getFile();
$fileId = $file->getId();
- $this->logger->debug("DirectEditor open: $fileId", array("app" => $this->appName));
+ $this->logger->debug("DirectEditor open: $fileId", ["app" => $this->appName]);
$documentServerUrl = $this->config->GetDocumentServerUrl();
if (empty($documentServerUrl)) {
- $this->logger->error("documentServerUrl is empty", array("app" => $this->appName));
+ $this->logger->error("documentServerUrl is empty", ["app" => $this->appName]);
return $this->renderError($this->trans->t("ONLYOFFICE app is not configured. Please contact admin"));
}
@@ -263,7 +262,7 @@ class DirectEditor implements IEditor {
return $response;
} catch (\Exception $e) {
- $this->logger->error("DirectEditor open: " . $e->getMessage(), array("app" => $this->appName));
+ $this->logger->logException($e, ["DirectEditor open", "app" => $this->appName]);
return $this->renderError($e->getMessage());
}
}
@@ -277,13 +276,13 @@ class DirectEditor implements IEditor {
* @return TemplateResponse
*/
private function renderError($error, $hint = "") {
- return new TemplateResponse("", "error", array(
- "errors" => array(
- array(
+ return new TemplateResponse("", "error", [
+ "errors" => [
+ [
"error" => $error,
"hint" => $hint
- )
- )
- ), "error");
+ ]
+ ]
+ ], "error");
}
}
diff --git a/lib/documentservice.php b/lib/documentservice.php
index 1fbb7cf..a084d59 100644
--- a/lib/documentservice.php
+++ b/lib/documentservice.php
@@ -50,13 +50,13 @@ class DocumentService {
/**
* Application configuration
*
- * @var OCA\Onlyoffice\AppConfig
+ * @var AppConfig
*/
private $config;
/**
* @param IL10N $trans - l10n service
- * @param OCA\Onlyoffice\AppConfig $config - application configutarion
+ * @param AppConfig $config - application configutarion
*/
public function __construct(IL10N $trans, AppConfig $appConfig) {
$this->trans = $trans;
@@ -99,7 +99,7 @@ class DocumentService {
$isEndConvert = $responceFromConvertService->EndConvert;
- if ($isEndConvert !== NULL && strtolower($isEndConvert) === "true") {
+ if ($isEndConvert !== null && strtolower($isEndConvert) === "true") {
return $responceFromConvertService->FileUrl;
}
@@ -151,13 +151,13 @@ class DocumentService {
$data["tenant"] = $this->config->GetSystemValue("instanceid", true);
}
- $opts = array(
+ $opts = [
"timeout" => "120",
"headers" => [
"Content-type" => "application/json"
],
"body" => json_encode($data)
- );
+ ];
if (!empty($this->config->GetDocumentServerSecret())) {
$params = [
@@ -279,12 +279,12 @@ class DocumentService {
"c" => $method
];
- $opts = array(
+ $opts = [
"headers" => [
"Content-type" => "application/json"
],
"body" => json_encode($data)
- );
+ ];
if (!empty($this->config->GetDocumentServerSecret())) {
$params = [
@@ -347,11 +347,11 @@ class DocumentService {
*
* @return string
*/
- public function Request($url, $method = "get", $opts = NULL) {
+ public function Request($url, $method = "get", $opts = null) {
$httpClientService = \OC::$server->getHTTPClientService();
$client = $httpClientService->newClient();
- if (NULL === $opts) {
+ if (null === $opts) {
$opts = array();
}
if (substr($url, 0, strlen("https")) === "https" && $this->config->TurnOffVerification()) {
diff --git a/lib/filecreator.php b/lib/filecreator.php
index 31c18e8..8cf2d21 100644
--- a/lib/filecreator.php
+++ b/lib/filecreator.php
@@ -143,20 +143,20 @@ class FileCreator extends ACreateEmpty {
* @param string $templateId - teamplate id
*/
public function create(File $file, string $creatorId = null, string $templateId = null): void {
- $this->logger->debug("FileCreator: " . $file->getId() . " " . $file->getName() . " $creatorId $templateId", array("app" => $this->appName));
+ $this->logger->debug("FileCreator: " . $file->getId() . " " . $file->getName() . " $creatorId $templateId", ["app" => $this->appName]);
$fileName = $file->getName();
$template = TemplateManager::GetTemplate($fileName);
if (!$template) {
- $this->logger->error("FileCreator: Template for file creation not found: $templatePath", array("app" => $this->appName));
+ $this->logger->error("FileCreator: Template for file creation not found: $templateId", ["app" => $this->appName]);
return;
}
try {
$file->putContent($template);
} catch (NotPermittedException $e) {
- $this->logger->error("FileCreator: Can't create file: $name", array("app" => $this->appName));
+ $this->logger->logException($e, ["FileCreator: Can't create file: $fileName", "app" => $this->appName]);
}
}
}
diff --git a/lib/fileutility.php b/lib/fileutility.php
index 75606b1..ada72a4 100644
--- a/lib/fileutility.php
+++ b/lib/fileutility.php
@@ -86,7 +86,7 @@ class FileUtility {
/**
* Application configuration
*
- * @var OCA\Onlyoffice\AppConfig
+ * @var AppConfig
*/
private $config;
@@ -94,7 +94,7 @@ class FileUtility {
* @param string $AppName - application name
* @param IL10N $trans - l10n service
* @param ILogger $logger - logger
- * @param OCA\Onlyoffice\AppConfig $config - application configuration
+ * @param AppConfig $config - application configuration
* @param IManager $shareManager - Share manager
* @param IManager $ISession - Session
*/
@@ -125,7 +125,7 @@ class FileUtility {
list ($node, $error, $share) = $this->getNodeByToken($shareToken);
if (isset($error)) {
- return [NULL, $error, NULL];
+ return [null, $error, null];
}
if ($node instanceof Folder) {
@@ -133,28 +133,28 @@ class FileUtility {
try {
$files = $node->getById($fileId);
} catch (\Exception $e) {
- $this->logger->error("getFileByToken: $fileId " . $e->getMessage(), array("app" => $this->appName));
- return [NULL, $this->trans->t("Invalid request"), NULL];
+ $this->logger->logException($e, ["getFileByToken: $fileId", "app" => $this->appName]);
+ return [null, $this->trans->t("Invalid request"), null];
}
if (empty($files)) {
- $this->logger->info("Files not found: $fileId", array("app" => $this->appName));
- return [NULL, $this->trans->t("File not found"), NULL];
+ $this->logger->info("Files not found: $fileId", ["app" => $this->appName]);
+ return [null, $this->trans->t("File not found"), null];
}
$file = $files[0];
} else {
try {
$file = $node->get($path);
} catch (\Exception $e) {
- $this->logger->error("getFileByToken for path: $path " . $e->getMessage(), array("app" => $this->appName));
- return [NULL, $this->trans->t("Invalid request"), NULL];
+ $this->logger->logException($e, ["getFileByToken for path: $path", "app" => $this->appName]);
+ return [null, $this->trans->t("Invalid request"), null];
}
}
} else {
$file = $node;
}
- return [$file, NULL, $share];
+ return [$file, null, $share];
}
/**
@@ -168,21 +168,21 @@ class FileUtility {
list ($share, $error) = $this->getShare($shareToken);
if (isset($error)) {
- return [NULL, $error, NULL];
+ return [null, $error, null];
}
if (($share->getPermissions() & Constants::PERMISSION_READ) === 0) {
- return [NULL, $this->trans->t("You do not have enough permissions to view the file"), NULL];
+ return [null, $this->trans->t("You do not have enough permissions to view the file"), null];
}
try {
$node = $share->getNode();
} catch (NotFoundException $e) {
- $this->logger->error("getNodeByToken error: " . $e->getMessage(), array("app" => $this->appName));
- return [NULL, $this->trans->t("File not found"), NULL];
+ $this->logger->logException($e, ["getNodeByToken error", "app" => $this->appName]);
+ return [null, $this->trans->t("File not found"), null];
}
- return [$node, NULL, $share];
+ return [$node, null, $share];
}
/**
@@ -194,28 +194,28 @@ class FileUtility {
*/
private function getShare($shareToken) {
if (empty($shareToken)) {
- return [NULL, $this->trans->t("FileId is empty")];
+ return [null, $this->trans->t("FileId is empty")];
}
- $share;
+ $share = null;
try {
$share = $this->shareManager->getShareByToken($shareToken);
} catch (ShareNotFound $e) {
- $this->logger->error("getShare error: " . $e->getMessage(), array("app" => $this->appName));
- $share = NULL;
+ $this->logger->logException($e, ["getShare error", "app" => $this->appName]);
+ $share = null;
}
- if ($share === NULL || $share === false) {
- return [NULL, $this->trans->t("You do not have enough permissions to view the file")];
+ if ($share === null || $share === false) {
+ return [null, $this->trans->t("You do not have enough permissions to view the file")];
}
if ($share->getPassword()
&& (!$this->session->exists("public_link_authenticated")
|| $this->session->get("public_link_authenticated") !== (string) $share->getId())) {
- return [NULL, $this->trans->t("You do not have enough permissions to view the file")];
+ return [null, $this->trans->t("You do not have enough permissions to view the file")];
}
- return [$share, NULL];
+ return [$share, null];
}
/**
@@ -237,7 +237,7 @@ class FileUtility {
return $key;
}
} catch (\Exception $e) {
- $this->logger->error("Failed to request federated key " . $file->getId() . " " . $e->getMessage(), array("app" => $this->appName));
+ $this->logger->logException($e, ["Failed to request federated key " . $file->getId(), "app" => $this->appName]);
}
}
@@ -273,12 +273,12 @@ class FileUtility {
$data = $body["ocs"]["data"];
if (!empty($data["error"])) {
- $this->logger->error("Error federated key " . $data["error"], array("app" => $this->appName));
+ $this->logger->error("Error federated key " . $data["error"], ["app" => $this->appName]);
return null;
}
$key = $data["key"];
- $this->logger->debug("Federated key: $key", array("app" => $this->appName));
+ $this->logger->debug("Federated key: $key", ["app" => $this->appName]);
return $key;
}
diff --git a/templates/editor.php b/templates/editor.php
index 17fac75..381c316 100644
--- a/templates/editor.php
+++ b/templates/editor.php
@@ -50,3 +50,10 @@
<?php } ?>
</div>
+
+<?php if (!empty($_["directToken"])) { ?>
+<script>
+ document.querySelector("meta[name='viewport']")
+ .setAttribute("content", "width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no");
+</script>
+<?php } ?>
diff --git a/templates/settings.php b/templates/settings.php
index ba816e1..64b7b56 100644
--- a/templates/settings.php
+++ b/templates/settings.php
@@ -185,6 +185,33 @@
<label for="onlyofficeToolbarNoTabs"><?php p($l->t("Display toolbar tabs")) ?></label>
</p>
+ <p class="onlyoffice-header">
+ <?php p($l->t("Review mode for viewing")) ?>
+ </p>
+ <div class="onlyoffice-tables">
+ <div>
+ <input type="radio" class="radio"
+ id="onlyofficeReviewDisplay_markup"
+ name="reviewDisplay"
+ <?php if ($_["reviewDisplay"] === "markup") { ?>checked="checked"<?php } ?> />
+ <label for="onlyofficeReviewDisplay_markup"><?php p($l->t("Markup")) ?></label>
+ </div>
+ <div>
+ <input type="radio" class="radio"
+ id="onlyofficeReviewDisplay_final"
+ name="reviewDisplay"
+ <?php if ($_["reviewDisplay"] === "final") { ?>checked="checked"<?php } ?> />
+ <label for="onlyofficeReviewDisplay_final"><?php p($l->t("Final")) ?></label>
+ </div>
+ <div>
+ <input type="radio" class="radio"
+ id="onlyofficeReviewDisplay_original"
+ name="reviewDisplay"
+ <?php if ($_["reviewDisplay"] === "original") { ?>checked="checked"<?php } ?> />
+ <label for="onlyofficeReviewDisplay_original"><?php p($l->t("Original")) ?></label>
+ </div>
+ </div>
+
<br />
<p><button id="onlyofficeSave" class="button"><?php p($l->t("Save")) ?></button></p>
</div>