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>2017-04-06 15:25:46 +0300
committerGitHub <noreply@github.com>2017-04-06 15:25:46 +0300
commit0b846f09e437f83aa8c42e4c51504566236cea8d (patch)
treef499267dd08ff2dc883d1811246bb317c3c34a00
parent0a38a6a2f12716f08a66b84130b73a261bfbc33f (diff)
parent25de35b9565db11ae7a8bdada61797b9edbf4357 (diff)
Merge pull request #40 from ONLYOFFICE/develop
Release/1.0.2
-rw-r--r--3rdparty/jwt/BeforeValidException.php7
-rw-r--r--3rdparty/jwt/ExpiredException.php7
-rw-r--r--3rdparty/jwt/JWT.php370
-rw-r--r--3rdparty/jwt/LICENSE30
-rw-r--r--3rdparty/jwt/SignatureInvalidException.php7
-rw-r--r--CHANGELOG.md15
-rw-r--r--README.md2
-rw-r--r--appinfo/app.php2
-rw-r--r--appinfo/application.php17
-rw-r--r--appinfo/info.xml7
-rw-r--r--appinfo/routes.php3
-rw-r--r--controller/callbackcontroller.php74
-rw-r--r--controller/editorcontroller.php123
-rw-r--r--controller/settingscontroller.php84
-rw-r--r--css/settings.css3
-rw-r--r--js/editor.js55
-rw-r--r--js/settings.js27
-rw-r--r--lib/adminsettings.php2
-rw-r--r--lib/appconfig.php83
-rw-r--r--lib/crypt.php4
-rw-r--r--lib/documentservice.php181
-rw-r--r--lib/downloadresponse.php64
-rw-r--r--lib/errorresponse.php61
-rw-r--r--screenshots/icon.pngbin990 -> 587 bytes
-rw-r--r--screenshots/main.pngbin68010 -> 68411 bytes
-rw-r--r--screenshots/new.pngbin18956 -> 17772 bytes
-rw-r--r--screenshots/settings.pngbin26685 -> 14761 bytes
-rw-r--r--templates/editor.php14
-rw-r--r--templates/settings.php9
29 files changed, 941 insertions, 310 deletions
diff --git a/3rdparty/jwt/BeforeValidException.php b/3rdparty/jwt/BeforeValidException.php
new file mode 100644
index 0000000..a6ee2f7
--- /dev/null
+++ b/3rdparty/jwt/BeforeValidException.php
@@ -0,0 +1,7 @@
+<?php
+namespace Firebase\JWT;
+
+class BeforeValidException extends \UnexpectedValueException
+{
+
+}
diff --git a/3rdparty/jwt/ExpiredException.php b/3rdparty/jwt/ExpiredException.php
new file mode 100644
index 0000000..3597370
--- /dev/null
+++ b/3rdparty/jwt/ExpiredException.php
@@ -0,0 +1,7 @@
+<?php
+namespace Firebase\JWT;
+
+class ExpiredException extends \UnexpectedValueException
+{
+
+}
diff --git a/3rdparty/jwt/JWT.php b/3rdparty/jwt/JWT.php
new file mode 100644
index 0000000..6d30e94
--- /dev/null
+++ b/3rdparty/jwt/JWT.php
@@ -0,0 +1,370 @@
+<?php
+
+namespace Firebase\JWT;
+use \DomainException;
+use \InvalidArgumentException;
+use \UnexpectedValueException;
+use \DateTime;
+
+/**
+ * JSON Web Token implementation, based on this spec:
+ * http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-06
+ *
+ * PHP version 5
+ *
+ * @category Authentication
+ * @package Authentication_JWT
+ * @author Neuman Vong <neuman@twilio.com>
+ * @author Anant Narayanan <anant@php.net>
+ * @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
+ * @link https://github.com/firebase/php-jwt
+ */
+class JWT
+{
+
+ /**
+ * When checking nbf, iat or expiration times,
+ * we want to provide some extra leeway time to
+ * account for clock skew.
+ */
+ public static $leeway = 0;
+
+ /**
+ * Allow the current timestamp to be specified.
+ * Useful for fixing a value within unit testing.
+ *
+ * Will default to PHP time() value if null.
+ */
+ public static $timestamp = null;
+
+ public static $supported_algs = array(
+ 'HS256' => array('hash_hmac', 'SHA256'),
+ 'HS512' => array('hash_hmac', 'SHA512'),
+ 'HS384' => array('hash_hmac', 'SHA384'),
+ 'RS256' => array('openssl', 'SHA256'),
+ );
+
+ /**
+ * Decodes a JWT string into a PHP object.
+ *
+ * @param string $jwt The JWT
+ * @param string|array $key The key, or map of keys.
+ * If the algorithm used is asymmetric, this is the public key
+ * @param array $allowed_algs List of supported verification algorithms
+ * Supported algorithms are 'HS256', 'HS384', 'HS512' and 'RS256'
+ *
+ * @return object The JWT's payload as a PHP object
+ *
+ * @throws UnexpectedValueException Provided JWT was invalid
+ * @throws SignatureInvalidException Provided JWT was invalid because the signature verification failed
+ * @throws BeforeValidException Provided JWT is trying to be used before it's eligible as defined by 'nbf'
+ * @throws BeforeValidException Provided JWT is trying to be used before it's been created as defined by 'iat'
+ * @throws ExpiredException Provided JWT has since expired, as defined by the 'exp' claim
+ *
+ * @uses jsonDecode
+ * @uses urlsafeB64Decode
+ */
+ public static function decode($jwt, $key, $allowed_algs = array())
+ {
+ $timestamp = is_null(static::$timestamp) ? time() : static::$timestamp;
+
+ if (empty($key)) {
+ throw new InvalidArgumentException('Key may not be empty');
+ }
+ if (!is_array($allowed_algs)) {
+ throw new InvalidArgumentException('Algorithm not allowed');
+ }
+ $tks = explode('.', $jwt);
+ if (count($tks) != 3) {
+ throw new UnexpectedValueException('Wrong number of segments');
+ }
+ list($headb64, $bodyb64, $cryptob64) = $tks;
+ if (null === ($header = static::jsonDecode(static::urlsafeB64Decode($headb64)))) {
+ throw new UnexpectedValueException('Invalid header encoding');
+ }
+ if (null === $payload = static::jsonDecode(static::urlsafeB64Decode($bodyb64))) {
+ throw new UnexpectedValueException('Invalid claims encoding');
+ }
+ $sig = static::urlsafeB64Decode($cryptob64);
+
+ if (empty($header->alg)) {
+ throw new UnexpectedValueException('Empty algorithm');
+ }
+ if (empty(static::$supported_algs[$header->alg])) {
+ throw new UnexpectedValueException('Algorithm not supported');
+ }
+ if (!in_array($header->alg, $allowed_algs)) {
+ throw new UnexpectedValueException('Algorithm not allowed');
+ }
+ if (is_array($key) || $key instanceof \ArrayAccess) {
+ if (isset($header->kid)) {
+ $key = $key[$header->kid];
+ } else {
+ throw new UnexpectedValueException('"kid" empty, unable to lookup correct key');
+ }
+ }
+
+ // Check the signature
+ if (!static::verify("$headb64.$bodyb64", $sig, $key, $header->alg)) {
+ throw new SignatureInvalidException('Signature verification failed');
+ }
+
+ // Check if the nbf if it is defined. This is the time that the
+ // token can actually be used. If it's not yet that time, abort.
+ if (isset($payload->nbf) && $payload->nbf > ($timestamp + static::$leeway)) {
+ throw new BeforeValidException(
+ 'Cannot handle token prior to ' . date(DateTime::ISO8601, $payload->nbf)
+ );
+ }
+
+ // Check that this token has been created before 'now'. This prevents
+ // using tokens that have been created for later use (and haven't
+ // correctly used the nbf claim).
+ if (isset($payload->iat) && $payload->iat > ($timestamp + static::$leeway)) {
+ throw new BeforeValidException(
+ 'Cannot handle token prior to ' . date(DateTime::ISO8601, $payload->iat)
+ );
+ }
+
+ // Check if this token has expired.
+ if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) {
+ throw new ExpiredException('Expired token');
+ }
+
+ return $payload;
+ }
+
+ /**
+ * Converts and signs a PHP object or array into a JWT string.
+ *
+ * @param object|array $payload PHP object or array
+ * @param string $key The secret key.
+ * If the algorithm used is asymmetric, this is the private key
+ * @param string $alg The signing algorithm.
+ * Supported algorithms are 'HS256', 'HS384', 'HS512' and 'RS256'
+ * @param mixed $keyId
+ * @param array $head An array with header elements to attach
+ *
+ * @return string A signed JWT
+ *
+ * @uses jsonEncode
+ * @uses urlsafeB64Encode
+ */
+ public static function encode($payload, $key, $alg = 'HS256', $keyId = null, $head = null)
+ {
+ $header = array('typ' => 'JWT', 'alg' => $alg);
+ if ($keyId !== null) {
+ $header['kid'] = $keyId;
+ }
+ if ( isset($head) && is_array($head) ) {
+ $header = array_merge($head, $header);
+ }
+ $segments = array();
+ $segments[] = static::urlsafeB64Encode(static::jsonEncode($header));
+ $segments[] = static::urlsafeB64Encode(static::jsonEncode($payload));
+ $signing_input = implode('.', $segments);
+
+ $signature = static::sign($signing_input, $key, $alg);
+ $segments[] = static::urlsafeB64Encode($signature);
+
+ return implode('.', $segments);
+ }
+
+ /**
+ * Sign a string with a given key and algorithm.
+ *
+ * @param string $msg The message to sign
+ * @param string|resource $key The secret key
+ * @param string $alg The signing algorithm.
+ * Supported algorithms are 'HS256', 'HS384', 'HS512' and 'RS256'
+ *
+ * @return string An encrypted message
+ *
+ * @throws DomainException Unsupported algorithm was specified
+ */
+ public static function sign($msg, $key, $alg = 'HS256')
+ {
+ if (empty(static::$supported_algs[$alg])) {
+ throw new DomainException('Algorithm not supported');
+ }
+ list($function, $algorithm) = static::$supported_algs[$alg];
+ switch($function) {
+ case 'hash_hmac':
+ return hash_hmac($algorithm, $msg, $key, true);
+ case 'openssl':
+ $signature = '';
+ $success = openssl_sign($msg, $signature, $key, $algorithm);
+ if (!$success) {
+ throw new DomainException("OpenSSL unable to sign data");
+ } else {
+ return $signature;
+ }
+ }
+ }
+
+ /**
+ * Verify a signature with the message, key and method. Not all methods
+ * are symmetric, so we must have a separate verify and sign method.
+ *
+ * @param string $msg The original message (header and body)
+ * @param string $signature The original signature
+ * @param string|resource $key For HS*, a string key works. for RS*, must be a resource of an openssl public key
+ * @param string $alg The algorithm
+ *
+ * @return bool
+ *
+ * @throws DomainException Invalid Algorithm or OpenSSL failure
+ */
+ private static function verify($msg, $signature, $key, $alg)
+ {
+ if (empty(static::$supported_algs[$alg])) {
+ throw new DomainException('Algorithm not supported');
+ }
+
+ list($function, $algorithm) = static::$supported_algs[$alg];
+ switch($function) {
+ case 'openssl':
+ $success = openssl_verify($msg, $signature, $key, $algorithm);
+ if (!$success) {
+ throw new DomainException("OpenSSL unable to verify data: " . openssl_error_string());
+ } else {
+ return $signature;
+ }
+ case 'hash_hmac':
+ default:
+ $hash = hash_hmac($algorithm, $msg, $key, true);
+ if (function_exists('hash_equals')) {
+ return hash_equals($signature, $hash);
+ }
+ $len = min(static::safeStrlen($signature), static::safeStrlen($hash));
+
+ $status = 0;
+ for ($i = 0; $i < $len; $i++) {
+ $status |= (ord($signature[$i]) ^ ord($hash[$i]));
+ }
+ $status |= (static::safeStrlen($signature) ^ static::safeStrlen($hash));
+
+ return ($status === 0);
+ }
+ }
+
+ /**
+ * Decode a JSON string into a PHP object.
+ *
+ * @param string $input JSON string
+ *
+ * @return object Object representation of JSON string
+ *
+ * @throws DomainException Provided string was invalid JSON
+ */
+ public static function jsonDecode($input)
+ {
+ if (version_compare(PHP_VERSION, '5.4.0', '>=') && !(defined('JSON_C_VERSION') && PHP_INT_SIZE > 4)) {
+ /** In PHP >=5.4.0, json_decode() accepts an options parameter, that allows you
+ * to specify that large ints (like Steam Transaction IDs) should be treated as
+ * strings, rather than the PHP default behaviour of converting them to floats.
+ */
+ $obj = json_decode($input, false, 512, JSON_BIGINT_AS_STRING);
+ } else {
+ /** Not all servers will support that, however, so for older versions we must
+ * manually detect large ints in the JSON string and quote them (thus converting
+ *them to strings) before decoding, hence the preg_replace() call.
+ */
+ $max_int_length = strlen((string) PHP_INT_MAX) - 1;
+ $json_without_bigints = preg_replace('/:\s*(-?\d{'.$max_int_length.',})/', ': "$1"', $input);
+ $obj = json_decode($json_without_bigints);
+ }
+
+ if (function_exists('json_last_error') && $errno = json_last_error()) {
+ static::handleJsonError($errno);
+ } elseif ($obj === null && $input !== 'null') {
+ throw new DomainException('Null result with non-null input');
+ }
+ return $obj;
+ }
+
+ /**
+ * Encode a PHP object into a JSON string.
+ *
+ * @param object|array $input A PHP object or array
+ *
+ * @return string JSON representation of the PHP object or array
+ *
+ * @throws DomainException Provided object could not be encoded to valid JSON
+ */
+ public static function jsonEncode($input)
+ {
+ $json = json_encode($input);
+ if (function_exists('json_last_error') && $errno = json_last_error()) {
+ static::handleJsonError($errno);
+ } elseif ($json === 'null' && $input !== null) {
+ throw new DomainException('Null result with non-null input');
+ }
+ return $json;
+ }
+
+ /**
+ * Decode a string with URL-safe Base64.
+ *
+ * @param string $input A Base64 encoded string
+ *
+ * @return string A decoded string
+ */
+ public static function urlsafeB64Decode($input)
+ {
+ $remainder = strlen($input) % 4;
+ if ($remainder) {
+ $padlen = 4 - $remainder;
+ $input .= str_repeat('=', $padlen);
+ }
+ return base64_decode(strtr($input, '-_', '+/'));
+ }
+
+ /**
+ * Encode a string with URL-safe Base64.
+ *
+ * @param string $input The string you want encoded
+ *
+ * @return string The base64 encode of what you passed in
+ */
+ public static function urlsafeB64Encode($input)
+ {
+ return str_replace('=', '', strtr(base64_encode($input), '+/', '-_'));
+ }
+
+ /**
+ * Helper method to create a JSON error.
+ *
+ * @param int $errno An error number from json_last_error()
+ *
+ * @return void
+ */
+ private static function handleJsonError($errno)
+ {
+ $messages = array(
+ JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
+ JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
+ JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON'
+ );
+ throw new DomainException(
+ isset($messages[$errno])
+ ? $messages[$errno]
+ : 'Unknown JSON error: ' . $errno
+ );
+ }
+
+ /**
+ * Get the number of bytes in cryptographic strings.
+ *
+ * @param string
+ *
+ * @return int
+ */
+ private static function safeStrlen($str)
+ {
+ if (function_exists('mb_strlen')) {
+ return mb_strlen($str, '8bit');
+ }
+ return strlen($str);
+ }
+}
diff --git a/3rdparty/jwt/LICENSE b/3rdparty/jwt/LICENSE
new file mode 100644
index 0000000..cb0c49b
--- /dev/null
+++ b/3rdparty/jwt/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2011, Neuman Vong
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the name of Neuman Vong nor the names of other
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/3rdparty/jwt/SignatureInvalidException.php b/3rdparty/jwt/SignatureInvalidException.php
new file mode 100644
index 0000000..27332b2
--- /dev/null
+++ b/3rdparty/jwt/SignatureInvalidException.php
@@ -0,0 +1,7 @@
+<?php
+namespace Firebase\JWT;
+
+class SignatureInvalidException extends \UnexpectedValueException
+{
+
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e0cc51d..9db56e1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,18 @@
+# Change Log
+
+## 1.0.2
+### Added
+- logging
+- checking Document Server address on save
+- checking version of onlyoffice
+- set language of editor
+
+### Changed
+- replace own Response class to OCP\AppFramework\Http class from core
+
+### Security
+- jwt signature for request to Document Server
+
## 1.0.1
- fix exception when versions app is disabled
- adding protocol to document server url
diff --git a/README.md b/README.md
index f00249e..de82948 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@ The app will create an item in the `new` (+) menu to create **Document**, **Spre
## Installing ONLYOFFICE Document Server
-You will need an instance of ONLYOFFICE Document Server that is resolvable and connectable both from ownCloud/Nextcloud and any end clients (version 3.0 and later are supported for use with the app). If that is not the case, use the official ONLYOFFICE Document Server documetnations page: [Document Server for Linux](http://helpcenter.onlyoffice.com/server/linux/document/linux-installation.aspx). ONLYOFFICE Document Server must also be able to POST to ownCloud directly.
+You will need an instance of ONLYOFFICE Document Server that is resolvable and connectable both from ownCloud/Nextcloud and any end clients (version 4.2 and later are supported for use with the app). If that is not the case, use the official ONLYOFFICE Document Server documetnations page: [Document Server for Linux](http://helpcenter.onlyoffice.com/server/linux/document/linux-installation.aspx). ONLYOFFICE Document Server must also be able to POST to ownCloud/Nextcloud directly.
The easiest way to start an instance of ONLYOFFICE Document Server is to use [Docker](https://github.com/ONLYOFFICE/Docker-DocumentServer).
diff --git a/appinfo/app.php b/appinfo/app.php
index 469955d..68ebda6 100644
--- a/appinfo/app.php
+++ b/appinfo/app.php
@@ -22,7 +22,7 @@
* in every copy of the program you distribute.
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
*
-*/
+ */
namespace OCA\Onlyoffice\AppInfo;
diff --git a/appinfo/application.php b/appinfo/application.php
index ffb400a..23e2fd0 100644
--- a/appinfo/application.php
+++ b/appinfo/application.php
@@ -22,7 +22,7 @@
* in every copy of the program you distribute.
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
*
-*/
+ */
namespace OCA\Onlyoffice\AppInfo;
@@ -60,7 +60,8 @@ class Application extends App {
$this->crypt = new Crypt($this->appConfig);
// Default script and style if configured
- if (!empty($this->appConfig->GetDocumentServerUrl()))
+ if (!empty($this->appConfig->GetDocumentServerUrl())
+ && array_key_exists("REQUEST_URI", \OC::$server->getRequest()->server))
{
$url = \OC::$server->getRequest()->server["REQUEST_URI"];
@@ -72,6 +73,8 @@ class Application extends App {
}
}
+ require_once __DIR__ . "/../3rdparty/jwt/JWT.php";
+
$container = $this->getContainer();
$container->registerService("L10N", function($c) {
@@ -86,12 +89,18 @@ class Application extends App {
return $c->query("ServerContainer")->getUserSession();
});
+ $container->registerService("Logger", function($c) {
+ return $c->query("ServerContainer")->getLogger();
+ });
+
// Controllers
$container->registerService("SettingsController", function($c) {
return new SettingsController(
$c->query("AppName"),
$c->query("Request"),
+ $c->query("L10N"),
+ $c->query("Logger"),
$this->appConfig
);
});
@@ -101,9 +110,10 @@ class Application extends App {
$c->query("AppName"),
$c->query("Request"),
$c->query("RootStorage"),
- $c->query("UserSession")->getUser(),
+ $c->query("UserSession"),
$c->query("ServerContainer")->getURLGenerator(),
$c->query("L10N"),
+ $c->query("Logger"),
$this->appConfig,
$this->crypt
);
@@ -117,6 +127,7 @@ class Application extends App {
$c->query("UserSession"),
$c->query("ServerContainer")->getUserManager(),
$c->query("L10N"),
+ $c->query("Logger"),
$this->appConfig,
$this->crypt
);
diff --git a/appinfo/info.xml b/appinfo/info.xml
index 212e1c1..02a4547 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -6,7 +6,7 @@
<summary>ownCloud ONLYOFFICE integration app</summary>
<description>ONLYOFFICE integration app enables users to edit Office documents within ONLYOFFICE from OwnCloud. 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 OwnCloud.</description>
<licence>AGPL</licence>
- <author mail="dev@onlyoffice.com" homepage="https://www.onlyoffice.com/">Ascensio System SIA</author>
+ <author>Ascensio System SIA</author>
<version>1.0.1</version>
<namespace>Onlyoffice</namespace>
<types>
@@ -15,6 +15,11 @@
<documentation>
<admin>https://api.onlyoffice.com/editors/owncloud</admin>
</documentation>
+ <category>files</category>
+ <category>integration</category>
+ <category>office</category>
+ <category>organization</category>
+ <category>tools</category>
<category>tool</category>
<website>https://www.onlyoffice.com</website>
<bugs>https://github.com/ONLYOFFICE/onlyoffice-owncloud/issues</bugs>
diff --git a/appinfo/routes.php b/appinfo/routes.php
index cfd973a..e81b5d8 100644
--- a/appinfo/routes.php
+++ b/appinfo/routes.php
@@ -22,13 +22,14 @@
* in every copy of the program you distribute.
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
*
-*/
+ */
return [
"routes" => [
["name" => "callback#download", "url" => "/download", "verb" => "GET"],
["name" => "callback#track", "url" => "/track", "verb" => "POST"],
["name" => "editor#index", "url" => "/{fileId}", "verb" => "GET"],
+ ["name" => "editor#config", "url" => "/ajax/config/{fileId}", "verb" => "GET"],
["name" => "editor#create", "url" => "/ajax/new", "verb" => "POST"],
["name" => "editor#convert", "url" => "/ajax/convert", "verb" => "POST"],
["name" => "settings#settings", "url" => "/ajax/settings", "verb" => "PUT"],
diff --git a/controller/callbackcontroller.php b/controller/callbackcontroller.php
index 0eeeb3c..f69901f 100644
--- a/controller/callbackcontroller.php
+++ b/controller/callbackcontroller.php
@@ -22,15 +22,18 @@
* in every copy of the program you distribute.
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
*
-*/
+ */
namespace OCA\Onlyoffice\Controller;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
+use OCP\AppFramework\Http\DataDownloadResponse;
+use OCP\AppFramework\Http\JSONResponse;
use OCP\Files\File;
use OCP\Files\IRootFolder;
use OCP\IL10N;
+use OCP\ILogger;
use OCP\IRequest;
use OCP\IUserManager;
use OCP\IUserSession;
@@ -38,8 +41,6 @@ use OCP\IUserSession;
use OCA\Onlyoffice\AppConfig;
use OCA\Onlyoffice\Crypt;
use OCA\Onlyoffice\DocumentService;
-use OCA\Onlyoffice\DownloadResponse;
-use OCA\Onlyoffice\ErrorResponse;
/**
* Callback handler for the document server.
@@ -77,6 +78,13 @@ class CallbackController extends Controller {
private $trans;
/**
+ * Logger
+ *
+ * @var OCP\ILogger
+ */
+ private $logger;
+
+ /**
* Application configuration
*
* @var OCA\Onlyoffice\AppConfig
@@ -104,13 +112,15 @@ class CallbackController extends Controller {
);
/**
- * @param string $AppName application name
- * @param IRequest $request request object
- * @param IRootFolder $root root folder
- * @param IUserSession $userSession user session
- * @param IUserManager $userManager user manager
- * @param IL10N $l10n l10n service
- * @param OCA\Onlyoffice\Crypt $crypt hash generator
+ * @param string $AppName - application name
+ * @param IRequest $request - request object
+ * @param IRootFolder $root - root folder
+ * @param IUserSession $userSession - user session
+ * @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
*/
public function __construct($AppName,
IRequest $request,
@@ -118,6 +128,7 @@ class CallbackController extends Controller {
IUserSession $userSession,
IUserManager $userManager,
IL10N $trans,
+ ILogger $logger,
AppConfig $config,
Crypt $crypt
) {
@@ -127,6 +138,7 @@ class CallbackController extends Controller {
$this->userSession = $userSession;
$this->userManager = $userManager;
$this->trans = $trans;
+ $this->logger = $logger;
$this->config = $config;
$this->crypt = $crypt;
}
@@ -137,7 +149,7 @@ class CallbackController extends Controller {
*
* @param string $doc - verification token with the file identifier
*
- * @return OCA\Onlyoffice\DownloadResponse
+ * @return DataDownloadResponse
*
* @NoAdminRequired
* @NoCSRFRequired
@@ -148,31 +160,36 @@ class CallbackController extends Controller {
$hashData = $this->crypt->ReadHash($doc);
if ($hashData === NULL) {
- return new ErrorResponse(Http::STATUS_FORBIDDEN, $this->trans->t("Access deny"));
+ $this->logger->info("Download with empty or not correct hash", array("app" => $this->appName));
+ return new JSONResponse(["message" => $this->trans->t("Access deny")], Http::STATUS_FORBIDDEN);
}
if ($hashData->action !== "download") {
- return new ErrorResponse(Http::STATUS_BAD_REQUEST, $this->trans->t("Invalid request"));
+ $this->logger->info("Download with other action", array("app" => $this->appName));
+ return new JSONResponse(["message" => $this->trans->t("Invalid request")], Http::STATUS_BAD_REQUEST);
}
$fileId = $hashData->fileId;
$ownerId = $hashData->ownerId;
$files = $this->root->getUserFolder($ownerId)->getById($fileId);
- if(empty($files)) {
- return new ErrorResponse(Http::STATUS_NOT_FOUND, $this->trans->t("Files not found"));
+ if (empty($files)) {
+ $this->logger->info("Files for download not found: " . $fileId, array("app" => $this->appName));
+ return new JSONResponse(["message" => $this->trans->t("Files not found")], Http::STATUS_NOT_FOUND);
}
$file = $files[0];
if (! $file instanceof File) {
- return new ErrorResponse(Http::STATUS_NOT_FOUND, $this->trans->t("File not found"));
+ $this->logger->info("File for download not found: " . $fileId, array("app" => $this->appName));
+ return new JSONResponse(["message" => $this->trans->t("File not found")], Http::STATUS_NOT_FOUND);
}
try {
- return new DownloadResponse($file);
+ return new DataDownloadResponse($file->getContent(), $file->getName(), $file->getMimeType());
} catch(\OCP\Files\NotPermittedException $e) {
- return new ErrorResponse(Http::STATUS_FORBIDDEN, $this->trans->t("Not permitted"));
+ $this->logger->info("Download Not permitted: " . $fileId . " " . $e->getMessage(), array("app" => $this->appName));
+ return new JSONResponse(["message" => $this->trans->t("Not permitted")], Http::STATUS_FORBIDDEN);
}
- return new ErrorResponse(Http::STATUS_INTERNAL_SERVER_ERROR, $this->trans->t("Download failed"));
+ return new JSONResponse(["message" => $this->trans->t("Download failed")], Http::STATUS_INTERNAL_SERVER_ERROR);
}
/**
@@ -194,10 +211,12 @@ class CallbackController extends Controller {
$hashData = $this->crypt->ReadHash($doc);
if ($hashData === NULL) {
- return ["message" => $this->trans->t("Access deny")];
+ $this->logger->info("Track with empty or not correct hash", array("app" => $this->appName));
+ return new JSONResponse(["message" => $this->trans->t("Access deny")], Http::STATUS_FORBIDDEN);
}
if ($hashData->action !== "track") {
- return ["message" => $this->trans->t("Invalid request")];
+ $this->logger->info("Track with other action", array("app" => $this->appName));
+ return new JSONResponse(["message" => $this->trans->t("Invalid request")], Http::STATUS_BAD_REQUEST);
}
$trackerStatus = $this->_trackerStatus[$status];
@@ -211,13 +230,15 @@ class CallbackController extends Controller {
$ownerId = $hashData->ownerId;
$files = $this->root->getUserFolder($ownerId)->getById($fileId);
- if(empty($files)) {
- return ["message" => $this->trans->t("Files not found")];
+ if (empty($files)) {
+ $this->logger->info("Files for track not found: " . $fileId, array("app" => $this->appName));
+ return new JSONResponse(["message" => $this->trans->t("Files not found")], Http::STATUS_NOT_FOUND);
}
$file = $files[0];
if (! $file instanceof File) {
- return ["message" => $this->trans->t("File not found")];
+ $this->logger->info("File for track not found: " . $fileId, array("app" => $this->appName));
+ return new JSONResponse(["message" => $this->trans->t("File not found")], Http::STATUS_NOT_FOUND);
}
$fileName = $file->getName();
@@ -233,7 +254,8 @@ class CallbackController extends Controller {
$documentService->GetConvertedUri($url, $downloadExt, $curExt, $key, FALSE, $newFileUri);
$url = $newFileUri;
} catch (\Exception $e) {
- return ["message" => $e->getMessage()];
+ $this->logger->error("GetConvertedUri in track: " . $url . " " . $e->getMessage(), array("app" => $this->appName));
+ return new JSONResponse(["message" => $e->getMessage()], Http::STATUS_INTERNAL_SERVER_ERROR);
}
}
@@ -251,6 +273,6 @@ class CallbackController extends Controller {
break;
}
- return ["error" => $error];
+ return new JSONResponse(["error" => $error], ($error === 0 ? Http::STATUS_OK : Http::STATUS_BAD_REQUEST));
}
} \ No newline at end of file
diff --git a/controller/editorcontroller.php b/controller/editorcontroller.php
index 5828e6d..9253744 100644
--- a/controller/editorcontroller.php
+++ b/controller/editorcontroller.php
@@ -22,7 +22,7 @@
* in every copy of the program you distribute.
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
*
-*/
+ */
namespace OCA\Onlyoffice\Controller;
@@ -34,9 +34,10 @@ use OCP\AutoloadNotAllowedException;
use OCP\Files\FileInfo;
use OCP\Files\IRootFolder;
use OCP\IL10N;
+use OCP\ILogger;
use OCP\IRequest;
use OCP\IURLGenerator;
-use OCP\IUser;
+use OCP\IUserSession;
use OC\Files\Filesystem;
use OC\Files\View;
@@ -55,11 +56,11 @@ use OCA\Onlyoffice\DocumentService;
class EditorController extends Controller {
/**
- * Current user
+ * Current user session
*
- * @var IUser
+ * @var IUserSession
*/
- private $user;
+ private $userSession;
/**
* Root folder
@@ -83,6 +84,13 @@ class EditorController extends Controller {
private $trans;
/**
+ * Logger
+ *
+ * @var ILogger
+ */
+ private $logger;
+
+ /**
* Application configuration
*
* @var OCA\Onlyoffice\AppConfig
@@ -97,30 +105,33 @@ class EditorController extends Controller {
private $crypt;
/**
- * @param string $AppName application name
- * @param IRequest $request request object
- * @param IRootFolder $root root folder
- * @param IUser $user current user
- * @param IURLGenerator $urlGenerator url generator service
- * @param IL10N $l10n l10n service
- * @param OCA\Onlyoffice\AppConfig $config application configuration
- * @param OCA\Onlyoffice\Crypt $crypt hash generator
+ * @param string $AppName - application name
+ * @param IRequest $request - request object
+ * @param IRootFolder $root - root folder
+ * @param IUserSession $userSession - current user session
+ * @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
*/
public function __construct($AppName,
IRequest $request,
IRootFolder $root,
- IUser $user,
+ IUserSession $userSession,
IURLGenerator $urlGenerator,
IL10N $trans,
+ ILogger $logger,
AppConfig $config,
Crypt $crypt
) {
parent::__construct($AppName, $request);
- $this->user = $user;
+ $this->userSession = $userSession;
$this->root = $root;
$this->urlGenerator = $urlGenerator;
$this->trans = $trans;
+ $this->logger = $logger;
$this->config = $config;
$this->crypt = $crypt;
}
@@ -137,14 +148,16 @@ class EditorController extends Controller {
*/
public function create($name, $dir) {
- $userId = $this->user->getUID();
+ $userId = $this->userSession->getUser()->getUID();
$userFolder = $this->root->getUserFolder($userId);
$folder = $userFolder->get($dir);
if ($folder === NULL) {
+ $this->logger->info("Folder for file creation was not found: " . $dir, array("app" => $this->appName));
return ["error" => $this->trans->t("The required folder was not found")];
}
if (!$folder->isCreatable()) {
+ $this->logger->info("Folder for file creation without permission: " . $dir, array("app" => $this->appName));
return ["error" => $this->trans->t("You don't have enough permission to create")];
}
@@ -155,17 +168,20 @@ class EditorController extends Controller {
$template = file_get_contents($templatePath);
if (!$template) {
+ $this->logger->info("Template for file creation not found: " . $templatePath, array("app" => $this->appName));
return ["error" => $this->trans->t("Template not found")];
}
$view = Filesystem::getView();
if (!$view->file_put_contents($filePath, $template)) {
+ $this->logger->error("Can't create file: " . $filePath, array("app" => $this->appName));
return ["error" => $this->trans->t("Can't create file")];
}
$fileInfo = $view->getFileInfo($filePath);
if ($fileInfo === false) {
+ $this->logger->info("File not found: " . $filePath, array("app" => $this->appName));
return ["error" => $this->trans->t("File not found")];
}
@@ -186,6 +202,7 @@ class EditorController extends Controller {
list ($file, $error) = $this->getFile($fileId);
if (isset($error)) {
+ $this->logger->error("Convertion: " . $fileId . " " . $error, array("app" => $this->appName));
return ["error" => $error];
}
@@ -193,10 +210,12 @@ class EditorController extends Controller {
$ext = pathinfo($fileName, PATHINFO_EXTENSION);
$format = $this->config->formats[$ext];
if (!isset($format)) {
+ $this->logger->info("Format for convertion not supported: " . $fileName, array("app" => $this->appName));
return ["error" => $this->trans->t("Format do not supported")];
}
- if(!isset($format["conv"]) || $format["conv"] !== TRUE) {
+ if (!isset($format["conv"]) || $format["conv"] !== TRUE) {
+ $this->logger->debug("Conversion not required: " . $fileName, array("app" => $this->appName));
return ["error" => $this->trans->t("Conversion not required")];
}
@@ -217,10 +236,11 @@ class EditorController extends Controller {
try {
$documentService->GetConvertedUri($fileUrl, $ext, $internalExtension, $key, FALSE, $newFileUri);
} catch (\Exception $e) {
+ $this->logger->error("GetConvertedUri: " . $fileId . " " . $e->getMessage(), array("app" => $this->appName));
return ["error" => $e->getMessage()];
}
- $userId = $this->user->getUID();
+ $userId = $this->userSession->getUser()->getUID();
$folder = $file->getParent();
if (!$folder->isCreatable()) {
$folder = $this->root->getUserFolder($userId);
@@ -232,19 +252,22 @@ class EditorController extends Controller {
$newFileName = $folder->getNonExistingName($fileNameWithoutExt . "." . $internalExtension);
$newFilePath = $newFolderPath . DIRECTORY_SEPARATOR . $newFileName;
-
+
if (($newData = file_get_contents($newFileUri)) === FALSE){
+ $this->logger->error("Failed download converted file: " . $newFileUri, array("app" => $this->appName));
return ["error" => $this->trans->t("Failed download converted file")];
}
$view = Filesystem::getView();
if (!$view->file_put_contents($newFilePath, $newData)) {
+ $this->logger->error("Can't create file after convertion: " . $newFilePath, array("app" => $this->appName));
return ["error" => $this->trans->t("Can't create file")];
}
$fileInfo = $view->getFileInfo($newFilePath);
if ($fileInfo === false) {
+ $this->logger->info("File not found: " . $newFilePath, array("app" => $this->appName));
return ["error" => $this->trans->t("File not found")];
}
@@ -263,14 +286,23 @@ class EditorController extends Controller {
* @NoCSRFRequired
*/
public function index($fileId) {
- $params = $this->getParam($fileId);
+ $documentServerUrl = $this->config->GetDocumentServerUrl();
+
+ if (empty($documentServerUrl)) {
+ $this->logger->error("documentServerUrl is empty", array("app" => $this->appName));
+ return ["error" => $this->trans->t("ONLYOFFICE app not configured. Please contact admin")];
+ }
+
+ $params = [
+ "documentServerUrl" => $documentServerUrl,
+ "fileId" => $fileId
+ ];
$response = new TemplateResponse($this->appName, "editor", $params);
$csp = new ContentSecurityPolicy();
$csp->allowInlineScript(true);
- $documentServerUrl = $params["documentServerUrl"];
if (isset($documentServerUrl) && !empty($documentServerUrl)) {
$csp->addAllowedScriptDomain($documentServerUrl);
$csp->addAllowedFrameDomain($documentServerUrl);
@@ -286,11 +318,14 @@ class EditorController extends Controller {
* @param integer $fileId - file identifier
*
* @return array
+ *
+ * @NoAdminRequired
*/
- private function getParam($fileId) {
+ public function config($fileId) {
list ($file, $error) = $this->getFile($fileId);
if (isset($error)) {
+ $this->logger->error("Convertion: " . $fileId . " " . $error, array("app" => $this->appName));
return ["error" => $error];
}
@@ -298,16 +333,11 @@ class EditorController extends Controller {
$ext = pathinfo($fileName, PATHINFO_EXTENSION);
$format = $this->config->formats[$ext];
if (!isset($format)) {
+ $this->logger->info("Format do not supported for editing: " . $fileName, array("app" => $this->appName));
return ["error" => $this->trans->t("Format do not supported")];
}
- $documentServerUrl = $this->config->GetDocumentServerUrl();
-
- if (empty($documentServerUrl)) {
- return ["error" => $this->trans->t("ONLYOFFICE app not configured. Please contact admin")];
- }
-
- $userId = $this->user->getUID();
+ $userId = $this->userSession->getUser()->getUID();
$ownerId = $file->getOwner()->getUID();
try {
$this->root->getUserFolder($ownerId);
@@ -321,19 +351,32 @@ class EditorController extends Controller {
$key = $this->getKey($file);
$canEdit = isset($format["edit"]) && $format["edit"];
+ $callback = ($file->isUpdateable() && $canEdit ? $this->urlGenerator->linkToRouteAbsolute($this->appName . ".callback.track", ["doc" => $hashCallback]) : NULL);
$params = [
- "documentServerUrl" => $documentServerUrl,
-
- "callback" => ($file->isUpdateable() && $canEdit ? $this->urlGenerator->linkToRouteAbsolute($this->appName . ".callback.track", ["doc" => $hashCallback]) : NULL),
- "fileName" => $fileName,
- "key" => DocumentService::GenerateRevisionId($key),
- "url" => $fileUrl,
- "userId" => $userId,
- "userName" => $this->user->getDisplayName(),
- "documentType" => $format["type"]
+ "document" => [
+ "fileType" => pathinfo($fileName, PATHINFO_EXTENSION),
+ "key" => DocumentService::GenerateRevisionId($key),
+ "title" => $fileName,
+ "url" => $fileUrl,
+ ],
+ "documentType" => $format["type"],
+ "editorConfig" => [
+ "callbackUrl" => $callback,
+ "lang" => \OC::$server->getL10NFactory("")->get("")->getLanguageCode(),
+ "mode" => ($callback === NULL ? "view" : "edit"),
+ "user" => [
+ "id" => $userId,
+ "name" => $this->userSession->getUser()->getDisplayName()
+ ]
+ ]
];
+ if (!empty($this->config->GetDocumentServerSecret())) {
+ $token = \Firebase\JWT\JWT::encode($params, $this->config->GetDocumentServerSecret());
+ $params["token"] = $token;
+ }
+
return $params;
}
@@ -350,7 +393,7 @@ class EditorController extends Controller {
}
$files = $this->root->getById($fileId);
- if(empty($files)) {
+ if (empty($files)) {
return [NULL, $this->trans->t("File not found")];
}
$file = $files[0];
@@ -375,7 +418,7 @@ class EditorController extends Controller {
try {
$this->root->getUserFolder($ownerId);
} catch (NoUserException $e) {
- $ownerId = $this->user->getUID();
+ $ownerId = $this->userSession->getUser()->getUID();
}
$key = $fileId . $file->getMtime();
diff --git a/controller/settingscontroller.php b/controller/settingscontroller.php
index 84a70e9..33290b8 100644
--- a/controller/settingscontroller.php
+++ b/controller/settingscontroller.php
@@ -22,15 +22,18 @@
* in every copy of the program you distribute.
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
*
-*/
+ */
namespace OCA\Onlyoffice\Controller;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\TemplateResponse;
+use OCP\IL10N;
+use OCP\ILogger;
use OCP\IRequest;
use OCA\Onlyoffice\AppConfig;
+use OCA\Onlyoffice\DocumentService;
/**
* Settings controller for the administration page
@@ -38,6 +41,20 @@ use OCA\Onlyoffice\AppConfig;
class SettingsController extends Controller {
/**
+ * l10n service
+ *
+ * @var IL10N
+ */
+ private $trans;
+
+ /**
+ * Logger
+ *
+ * @var ILogger
+ */
+ private $logger;
+
+ /**
* Application configuration
*
* @var OCA\Onlyoffice\AppConfig
@@ -45,16 +62,22 @@ class SettingsController extends Controller {
private $config;
/**
- * @param string $AppName application name
- * @param IRequest $request request object
- * @param OCA\Onlyoffice\AppConfig $config application configuration
+ * @param string $AppName - application name
+ * @param IRequest $request - request object
+ * @param IL10N $trans - l10n service
+ * @param ILogger $logger - logger
+ * @param OCA\Onlyoffice\AppConfig $config - application configuration
*/
- public function __construct($AppName,
+ public function __construct($AppName,
IRequest $request,
+ IL10N $trans,
+ ILogger $logger,
AppConfig $config
) {
parent::__construct($AppName, $request);
+ $this->trans = $trans;
+ $this->logger = $logger;
$this->config = $config;
}
@@ -64,20 +87,34 @@ class SettingsController extends Controller {
* @return TemplateResponse
*/
public function index() {
- $data = ["documentserver" => $this->config->GetDocumentServerUrl()];
+ $data = [
+ "documentserver" => $this->config->GetDocumentServerUrl(),
+ "secret" => $this->config->GetDocumentServerSecret()
+ ];
return new TemplateResponse($this->appName, "settings", $data, "blank");
}
/**
* Save the document server address
*
- * @param string $documentserver application name
+ * @param string $documentserver - document service address
+ * @param string $secret - secret key for signature
*
* @return array
*/
- public function settings($documentserver) {
+ public function settings($documentserver, $secret) {
$this->config->SetDocumentServerUrl($documentserver);
- return ["documentserver" => $this->config->GetDocumentServerUrl()];
+ $this->config->SetDocumentServerSecret($secret);
+
+ $documentserver = $this->config->GetDocumentServerUrl();
+ if (!empty($documentserver)) {
+ $error = $this->checkDocServiceUrl();
+ }
+
+ return [
+ "documentserver" => $this->config->GetDocumentServerUrl(),
+ "error" => $error
+ ];
}
/**
@@ -90,4 +127,33 @@ class SettingsController extends Controller {
public function formats(){
return $this->config->formats;
}
+
+
+ /**
+ * Checking document service location
+ *
+ * @param string $documentServer - document service address
+ *
+ * @return string
+ */
+ private function checkDocServiceUrl() {
+
+ $documentService = new DocumentService($this->trans, $this->config);
+
+ try {
+ $commandResponse = $documentService->CommandRequest("version");
+
+ $this->logger->debug("CommandRequest on check: " . json_encode($commandResponse), array("app" => $this->appName));
+
+ $version = floatval($commandResponse->version);
+ if ($version < 4.2) {
+ throw new \Exception($this->trans->t("Not supported version"));
+ }
+ } catch (\Exception $e) {
+ $this->logger->error("CommandRequest on check error: " . $e->getMessage(), array("app" => $this->appName));
+ return $e->getMessage();
+ }
+
+ return "";
+ }
}
diff --git a/css/settings.css b/css/settings.css
index 8c45562..4be07de 100644
--- a/css/settings.css
+++ b/css/settings.css
@@ -34,3 +34,6 @@
margin: 6px 0 4px;
width: 250px;
}
+.onlyoffice-hide {
+ display: none;
+}
diff --git a/js/editor.js b/js/editor.js
index 2a7851d..949744a 100644
--- a/js/editor.js
+++ b/js/editor.js
@@ -32,41 +32,40 @@
};
}
- OCA.Onlyoffice.OpenEditor = function (data) {
- if (typeof DocsAPI === "undefined" && !data.error.length) {
- data.error = t(OCA.Onlyoffice.AppName, "ONLYOFFICE app not configured. Please contact admin");
+ OCA.Onlyoffice.OpenEditor = function (fileId, error) {
+
+ var displayError = function (error) {
+ $("#iframeEditor").text(error).addClass("error");
+ };
+
+ if (error.length) {
+ displayError(error)
+ return;
}
- if (data.error.length) {
- $("#iframeEditor").text(data.error).addClass("error");
+ if (!fileId.length) {
+ displayError(t(OCA.Onlyoffice.AppName, "FileId is empty"));
return;
}
- var ext = (data.title || "").toLowerCase().split(".").pop();
+ if (typeof DocsAPI === "undefined" && !error.length) {
+ displayError(t(OCA.Onlyoffice.AppName, "ONLYOFFICE not reached. Please contact admin"));
+ return;
+ }
- var config = {
- "document": {
- "fileType": ext,
- "key": data.key,
- "title": data.title,
- "url": data.url
- },
- "documentType": data.documentType,
- "editorConfig": {
- "callbackUrl": (data.callbackUrl ? data.callbackUrl : null),
- "lang": "en-US",
- "mode": (data.callbackUrl ? "edit" : "view"),
- "user": {
- "id": data.userId,
- "name": data.userName
- }
- },
- "height": "100%",
- "type": "desktop",
- "width": "100%"
- };
+ $.ajax({
+ url: OC.generateUrl("apps/onlyoffice/ajax/config/" + fileId),
+ success: function onSuccess(config) {
+ if (config) {
+ if (config.error != null) {
+ displayError(config.error);
+ return;
+ }
- var docEditor = new DocsAPI.DocEditor("iframeEditor", config);
+ var docEditor = new DocsAPI.DocEditor("iframeEditor", config);
+ }
+ }
+ });
};
})(jQuery, OCA); \ No newline at end of file
diff --git a/js/settings.js b/js/settings.js
index 47c8bf1..69c4174 100644
--- a/js/settings.js
+++ b/js/settings.js
@@ -34,16 +34,31 @@
}
$("#onlyofficeSave").click(function () {
- var docServiceUrlApi = $("#docServiceUrlApi").val().trim();
+ var onlyofficeUrl = $("#onlyofficeUrl").val().trim();
+
+ if (!onlyofficeUrl.length) {
+ $("#onlyofficeSecret").val("");
+ }
+ var onlyofficeSecret = $("#onlyofficeSecret").val();
$.ajax({
method: "PUT",
url: OC.generateUrl("apps/onlyoffice/ajax/settings"),
- data: { documentserver: docServiceUrlApi },
+ data: {
+ documentserver: onlyofficeUrl,
+ secret: onlyofficeSecret
+ },
success: function onSuccess(response) {
if (response && response.documentserver != null) {
- $("#docServiceUrlApi").val(response.documentserver);
- var row = OC.Notification.show(t(OCA.Onlyoffice.AppName, "Settings have been successfully updated"));
+ $("#onlyofficeUrl").val(response.documentserver);
+
+ $("#onlyofficeSecretPanel").toggleClass("onlyoffice-hide", !response.documentserver.length);
+
+ var message =
+ response.error
+ ? (t(OCA.Onlyoffice.AppName, "Error when trying to connect") + " (" + response.error + ")")
+ : t(OCA.Onlyoffice.AppName, "Settings have been successfully updated");
+ var row = OC.Notification.show(message);
setTimeout(function () {
OC.Notification.hide(row);
}, 3000);
@@ -52,7 +67,7 @@
});
});
- $("#docServiceUrlApi").keypress(function (e) {
+ $("#onlyofficeUrl, #onlyofficeSecret").keypress(function (e) {
var code = e.keyCode || e.which;
if (code === 13) {
$("#onlyofficeSave").click();
@@ -60,4 +75,4 @@
});
});
-})(jQuery, OC); \ No newline at end of file
+})(jQuery, OC);
diff --git a/lib/adminsettings.php b/lib/adminsettings.php
index 7497e75..11c0f55 100644
--- a/lib/adminsettings.php
+++ b/lib/adminsettings.php
@@ -22,7 +22,7 @@
* in every copy of the program you distribute.
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
*
-*/
+ */
namespace OCA\Onlyoffice;
diff --git a/lib/appconfig.php b/lib/appconfig.php
index b97cfc9..3cf78be 100644
--- a/lib/appconfig.php
+++ b/lib/appconfig.php
@@ -22,11 +22,12 @@
* in every copy of the program you distribute.
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
*
-*/
+ */
namespace OCA\Onlyoffice;
use OCP\IConfig;
+use OCP\ILogger;
/**
* Application configutarion
@@ -36,6 +37,21 @@ use OCP\IConfig;
class AppConfig {
/**
+ * Definition url on server
+ *
+ * @var string
+ */
+ private $predefDocumentServerUrl = "";
+
+ /**
+ * Definition url on server
+ *
+ * @var string
+ */
+ private $predefDocumentServerSecret = "";
+
+
+ /**
* Application name
*
* @var string
@@ -50,6 +66,13 @@ class AppConfig {
private $config;
/**
+ * Logger
+ *
+ * @var OCP\ILogger
+ */
+ private $logger;
+
+ /**
* The config key for the document server address
*
* @var string
@@ -57,6 +80,13 @@ class AppConfig {
private $_documentserver = "DocumentServerUrl";
/**
+ * The config key for the secret key in jwt
+ *
+ * @var string
+ */
+ private $_secret = "DocumentServerSecret";
+
+ /**
* The config key for the secret key
*
* @var string
@@ -64,12 +94,14 @@ class AppConfig {
private $_cryptSecret = "skey";
/**
- * @param string $AppName application name
+ * @param string $AppName - application name
*/
public function __construct($AppName) {
-
+
$this->appName = $AppName;
+
$this->config = \OC::$server->getConfig();
+ $this->logger = \OC::$server->getLogger();
}
/**
@@ -82,6 +114,9 @@ class AppConfig {
if (strlen($documentServer) > 0 && !preg_match("/^https?:\/\//i", $documentServer)) {
$documentServer = "http://" . $documentServer;
}
+
+ $this->logger->info("SetDocumentServerUrl: " . $documentServer, array("app" => $this->appName));
+
$this->config->setAppValue($this->appName, $this->_documentserver, $documentServer);
$this->DropSKey();
}
@@ -92,7 +127,39 @@ class AppConfig {
* @return string
*/
public function GetDocumentServerUrl() {
- return $this->config->getAppValue($this->appName, $this->_documentserver, "");
+ $url = $this->config->getAppValue($this->appName, $this->_documentserver, "");
+ if (empty($url)) {
+ $url = $this->predefDocumentServerUrl;
+ }
+ return $url;
+ }
+
+ /**
+ * Save the document service secret key to the application configuration
+ *
+ * @param string $secret - secret key
+ */
+ public function SetDocumentServerSecret($secret) {
+ if (empty($secret)) {
+ $this->logger->info("Clear secret key", array("app" => $this->appName));
+ } else {
+ $this->logger->info("Set secret key", array("app" => $this->appName));
+ }
+
+ $this->config->setAppValue($this->appName, $this->_secret, $secret);
+ }
+
+ /**
+ * Get the document service secret key from the application configuration
+ *
+ * @return string
+ */
+ public function GetDocumentServerSecret() {
+ $secret = $this->config->getAppValue($this->appName, $this->_secret, "");
+ if (empty($secret)) {
+ $secret = $this->predefDocumentServerSecret;
+ }
+ return $secret;
}
/**
@@ -111,15 +178,9 @@ class AppConfig {
/**
* Regenerate the secret key
- *
- * @return string
*/
private function DropSKey() {
- $skey = $this->config->getAppValue($this->appName, $this->_cryptSecret, "");
- if (!empty($skey)) {
- $skey = number_format(round(microtime(true) * 1000), 0, ".", "");
- $this->config->setAppValue($this->appName, $this->_cryptSecret, $skey);
- }
+ $this->config->setAppValue($this->appName, $this->_cryptSecret, "");
}
diff --git a/lib/crypt.php b/lib/crypt.php
index 28c888b..14222d2 100644
--- a/lib/crypt.php
+++ b/lib/crypt.php
@@ -22,7 +22,7 @@
* in every copy of the program you distribute.
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
*
-*/
+ */
namespace OCA\Onlyoffice;
@@ -43,7 +43,7 @@ class Crypt {
private $skey;
/**
- * @param OCA\Onlyoffice\AppConfig $config application configutarion
+ * @param OCA\Onlyoffice\AppConfig $config - application configutarion
*/
public function __construct(AppConfig $appConfig) {
$this->skey = $appConfig->GetSKey();
diff --git a/lib/documentservice.php b/lib/documentservice.php
index e3b0703..a0c0140 100644
--- a/lib/documentservice.php
+++ b/lib/documentservice.php
@@ -22,7 +22,7 @@
* in every copy of the program you distribute.
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
*
-*/
+ */
namespace OCA\Onlyoffice;
@@ -52,7 +52,8 @@ class DocumentService {
private $config;
/**
- * @param OCA\Onlyoffice\AppConfig $config application configutarion
+ * @param IL10N $trans - l10n service
+ * @param OCA\Onlyoffice\AppConfig $config - application configutarion
*/
public function __construct(IL10N $trans, AppConfig $appConfig) {
$this->trans = $trans;
@@ -60,12 +61,12 @@ class DocumentService {
}
/**
- * Translation key to a supported form.
- *
- * @param string $expected_key Expected key
- *
- * @return Supported key
- */
+ * Translation key to a supported form.
+ *
+ * @param string $expected_key - Expected key
+ *
+ * @return string
+ */
public static function GenerateRevisionId($expected_key) {
if (strlen($expected_key) > 20) {
$expected_key = crc32( $expected_key);
@@ -76,17 +77,17 @@ class DocumentService {
}
/**
- * The method is to convert the file to the required format
- *
- * @param string $document_uri - Uri for the document to convert
- * @param string $from_extension - Document extension
- * @param string $to_extension - Extension to which to convert
- * @param string $document_revision_id - Key for caching on service
- * @param bool $is_async - Perform conversions asynchronously
- * @param string $converted_document_uri - Uri to the converted document
- *
- * @return The percentage of completion of conversion
- */
+ * The method is to convert the file to the required format and return the percentage of completion
+ *
+ * @param string $document_uri - Uri for the document to convert
+ * @param string $from_extension - Document extension
+ * @param string $to_extension - Extension to which to convert
+ * @param string $document_revision_id - Key for caching on service
+ * @param bool $is_async - Perform conversions asynchronously
+ * @param string $converted_document_uri - Uri to the converted document
+ *
+ * @return int
+ */
function GetConvertedUri($document_uri, $from_extension, $to_extension, $document_revision_id, $is_async, &$converted_document_uri) {
$converted_document_uri = "";
$responceFromConvertService = $this->SendRequestToConvertService($document_uri, $from_extension, $to_extension, $document_revision_id, $is_async);
@@ -110,16 +111,16 @@ class DocumentService {
}
/**
- * Request for conversion to a service
- *
- * @param string $document_uri - Uri for the document to convert
- * @param string $from_extension - Document extension
- * @param string $to_extension - Extension to which to convert
- * @param string $document_revision_id - Key for caching on service
- * @param bool - $is_async - Perform conversions asynchronously
- *
- * @return Xml document request result of conversion
- */
+ * Request for conversion to a service
+ *
+ * @param string $document_uri - Uri for the document to convert
+ * @param string $from_extension - Document extension
+ * @param string $to_extension - Extension to which to convert
+ * @param string $document_revision_id - Key for caching on service
+ * @param bool - $is_async - Perform conversions asynchronously
+ *
+ * @return array
+ */
function SendRequestToConvertService($document_uri, $from_extension, $to_extension, $document_revision_id, $is_async) {
if (empty($from_extension)) {
$path_parts = pathinfo($document_uri);
@@ -167,6 +168,14 @@ class DocumentService {
)
);
+ if (!empty($this->config->GetDocumentServerSecret())) {
+ $params = [
+ "payload" => $data
+ ];
+ $token = \Firebase\JWT\JWT::encode($params, $this->config->GetDocumentServerSecret());
+ $opts["http"]["header"] = $opts["http"]["header"] . "Authorization: Bearer " . $token . "\r\n";
+ }
+
if (substr($urlToConverter, 0, strlen("https")) === "https") {
$opts["ssl"] = array( "verify_peer" => FALSE );
}
@@ -200,40 +209,43 @@ class DocumentService {
}
/**
- * Generate an error code table
- *
- * @param string $errorCode - Error code
- *
- * @return null
- */
+ * Generate an error code table of convertion
+ *
+ * @param string $errorCode - Error code
+ *
+ * @return null
+ */
function ProcessConvServResponceError($errorCode) {
$errorMessageTemplate = $this->trans->t("Error occurred in the document service: ");
$errorMessage = "";
switch ($errorCode) {
+ case -20:
+ $errorMessage = $errorMessageTemplate . "Error encrypt signature";
+ break;
case -8:
- $errorMessage = $errorMessageTemplate . "Error document VKey";
+ $errorMessage = $errorMessageTemplate . "Invalid token";
break;
case -7:
$errorMessage = $errorMessageTemplate . "Error document request";
break;
case -6:
- $errorMessage = $errorMessageTemplate . "Error database";
+ $errorMessage = $errorMessageTemplate . "Error while accessing the conversion result database";
break;
case -5:
$errorMessage = $errorMessageTemplate . "Error unexpected guid";
break;
case -4:
- $errorMessage = $errorMessageTemplate . "Error download error";
+ $errorMessage = $errorMessageTemplate . "Error while downloading the document file to be converted.";
break;
case -3:
- $errorMessage = $errorMessageTemplate . "Error convertation error";
+ $errorMessage = $errorMessageTemplate . "Conversion error";
break;
case -2:
- $errorMessage = $errorMessageTemplate . "Error convertation timeout";
+ $errorMessage = $errorMessageTemplate . "Timeout conversion error";
break;
case -1:
- $errorMessage = $errorMessageTemplate . "Error convertation unknown";
+ $errorMessage = $errorMessageTemplate . "Unknown error";
break;
case 0:
break;
@@ -244,4 +256,91 @@ class DocumentService {
throw new \Exception($errorMessage);
}
+
+ /**
+ * Send command
+ *
+ * @param string $method - type of command
+ *
+ * @return array
+ */
+ function CommandRequest($method) {
+
+ $documentServerUrl = $this->config->GetDocumentServerUrl();
+
+ if (empty($documentServerUrl)) {
+ throw new \Exception($this->trans->t("ONLYOFFICE app not configured. Please contact admin"));
+ }
+
+ $urlCommand = $documentServerUrl . "/coauthoring/CommandService.ashx";
+
+ $data = json_encode(
+ array(
+ "c" => $method
+ )
+ );
+
+ $opts = array("http" => array(
+ "method" => "POST",
+ "timeout" => "120000",
+ "header"=> "Content-type: application/json\r\n",
+ "content" => $data
+ )
+ );
+
+ if (!empty($this->config->GetDocumentServerSecret())) {
+ $params = [
+ "payload" => $data
+ ];
+ $token = \Firebase\JWT\JWT::encode($params, $this->config->GetDocumentServerSecret());
+ $opts["http"]["header"] = $opts["http"]["header"] . "Authorization: Bearer " . $token . "\r\n";
+ }
+
+ if (substr($urlCommand, 0, strlen("https")) === "https") {
+ $opts["ssl"] = array( "verify_peer" => FALSE );
+ }
+
+ $context = stream_context_create($opts);
+
+ if (($response = file_get_contents($urlCommand, FALSE, $context)) === FALSE){
+ throw new \Exception ($this->trans->t("Bad Request or timeout error"));
+ }
+
+ $data = json_decode($response);
+
+ $this->ProcessCommandServResponceError($data->error);
+
+ return $data;
+ }
+
+ /**
+ * Generate an error code table of command
+ *
+ * @param string $errorCode - Error code
+ *
+ * @return null
+ */
+ function ProcessCommandServResponceError($errorCode) {
+ $errorMessageTemplate = $this->trans->t("Error occurred in the document service: ");
+ $errorMessage = "";
+
+ switch ($errorCode) {
+ case 6:
+ $errorMessage = $errorMessageTemplate . "Invalid token";
+ break;
+ case 5:
+ $errorMessage = $errorMessageTemplate . "Command not correсt";
+ break;
+ case 3:
+ $errorMessage = $errorMessageTemplate . "Internal server error";
+ break;
+ case 0:
+ return;
+ default:
+ $errorMessage = $errorMessageTemplate . "ErrorCode = " . $errorCode;
+ break;
+ }
+
+ throw new \Exception($errorMessage);
+ }
}
diff --git a/lib/downloadresponse.php b/lib/downloadresponse.php
deleted file mode 100644
index c37bd59..0000000
--- a/lib/downloadresponse.php
+++ /dev/null
@@ -1,64 +0,0 @@
-<?php
-/**
- *
- * (c) Copyright Ascensio System Limited 2010-2017
- *
- * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU
- * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html).
- * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that
- * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights.
- *
- * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR
- * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html
- *
- * You can contact Ascensio System SIA by email at sales@onlyoffice.com
- *
- * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display
- * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3.
- *
- * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains
- * relevant author attributions when distributing the software. If the display of the logo in its graphic
- * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE"
- * in every copy of the program you distribute.
- * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
- *
-*/
-
-namespace OCA\Onlyoffice;
-
-use OCP\AppFramework\Http\Response;
-use OCP\AppFramework\Http;
-use OCP\Files\File;
-
-/**
- * A renderer for the file
- *
- * @package OCA\Onlyoffice
- */
-class DownloadResponse extends Response {
-
- /**
- * @var string
- */
- private $content;
-
- /**
- * @param OCP\Files\File $file the file to be downloaded
- */
- public function __construct(File $file) {
- $this->setStatus(Http::STATUS_OK);
- $this->content = $file->getContent();
-
- $this->addHeader("Content-type", $file->getMimeType() . "; charset=utf-8");
- $this->addHeader("Content-Disposition", "attachment; filename*=UTF-8''" . rawurlencode($file->getName()) . "; filename=\"" . rawurlencode($file->getName()) . "\"");
- }
-
- /**
- * Returns the rendered file
- *
- * @return string the file
- */
- public function render() {
- return $this->content;
- }
-}
diff --git a/lib/errorresponse.php b/lib/errorresponse.php
deleted file mode 100644
index 512257a..0000000
--- a/lib/errorresponse.php
+++ /dev/null
@@ -1,61 +0,0 @@
-<?php
-/**
- *
- * (c) Copyright Ascensio System Limited 2010-2017
- *
- * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU
- * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html).
- * In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that
- * Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights.
- *
- * THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR
- * FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html
- *
- * You can contact Ascensio System SIA by email at sales@onlyoffice.com
- *
- * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display
- * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3.
- *
- * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains
- * relevant author attributions when distributing the software. If the display of the logo in its graphic
- * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE"
- * in every copy of the program you distribute.
- * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
- *
-*/
-
-namespace OCA\Onlyoffice;
-
-use OCP\AppFramework\Http\Response;
-use OCP\AppFramework\Http;
-
-/**
- * A renderer for error
- *
- * @package OCA\Onlyoffice
- */
-class ErrorResponse extends Response {
-
- /**
- * @var string
- */
- private $message;
-
- /**
- * @param int $statusCode the HTTP status code, defaults to 200
- * @param string $message error description
- */
- public function __construct($statusCode = Http::STATUS_OK, $message = NULL) {
- $this->setStatus($statusCode);
- $this->message = $message;
- }
-
- /**
- * Returns the rendered file
- *
- * @return string the file
- */
- public function render() {
- return $this->message;
- }
-} \ No newline at end of file
diff --git a/screenshots/icon.png b/screenshots/icon.png
index 8a648f2..03bc00a 100644
--- a/screenshots/icon.png
+++ b/screenshots/icon.png
Binary files differ
diff --git a/screenshots/main.png b/screenshots/main.png
index 1f2cb78..43e5017 100644
--- a/screenshots/main.png
+++ b/screenshots/main.png
Binary files differ
diff --git a/screenshots/new.png b/screenshots/new.png
index d318827..e25c939 100644
--- a/screenshots/new.png
+++ b/screenshots/new.png
Binary files differ
diff --git a/screenshots/settings.png b/screenshots/settings.png
index b054fd0..dd760dd 100644
--- a/screenshots/settings.png
+++ b/screenshots/settings.png
Binary files differ
diff --git a/templates/editor.php b/templates/editor.php
index 371cf55..724f745 100644
--- a/templates/editor.php
+++ b/templates/editor.php
@@ -22,7 +22,7 @@
* in every copy of the program you distribute.
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
*
-*/
+ */
style("onlyoffice", "editor");
script("onlyoffice", "editor");
@@ -39,17 +39,7 @@
} ?>
<script type="text/javascript" nonce="<?php p(base64_encode($_["requesttoken"])) ?>">
- OCA.Onlyoffice.OpenEditor ({
- error: "<?php empty($_["error"]) ? "" : p($_["error"]) ?>",
-
- callbackUrl: "<?php print_unescaped($_["callback"]) ?>",
- key: "<?php p($_["key"]) ?>",
- title: "<?php p($_["fileName"]) ?>",
- url: "<?php print_unescaped($_["url"]) ?>",
- userId: "<?php p($_["userId"]) ?>",
- userName: "<?php p($_["userName"]) ?>",
- documentType: "<?php p($_["documentType"]) ?>",
- });
+ OCA.Onlyoffice.OpenEditor("<?php p($_["fileId"]) ?>", "<?php empty($_["error"]) ? "" : p($_["error"]) ?>");
</script>
</div>
diff --git a/templates/settings.php b/templates/settings.php
index b721ba4..c34368c 100644
--- a/templates/settings.php
+++ b/templates/settings.php
@@ -22,7 +22,7 @@
* in every copy of the program you distribute.
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
*
-*/
+ */
style("onlyoffice", "settings");
script("onlyoffice", "settings");
@@ -34,7 +34,12 @@
<p><?php p($l->t("ONLYOFFICE Document Service Location specifies the address of the server with the document services installed. Please change the '<documentserver>' for the server address in the below line.")) ?></p>
<p class="onlyoffice-header"><?php p($l->t("Document Editing Service Address")) ?></p>
- <input id="docServiceUrlApi" value="<?php p($_["documentserver"]) ?>" placeholder="https://<documentserver>" type="text">
+ <input id="onlyofficeUrl" value="<?php p($_["documentserver"]) ?>" placeholder="https://<documentserver>" type="text">
+
+ <div id="onlyofficeSecretPanel" class="<?php echo (empty($_["documentserver"]) ? "onlyoffice-hide" : "") ?>">
+ <p class="onlyoffice-header"><?php p($l->t("Secret key (leave blank to disable)")) ?></p>
+ <input id="onlyofficeSecret" value="<?php p($_["secret"]) ?>" placeholder="secret" type="text">
+ </div>
<br />
<a id="onlyofficeSave" class="button"><?php p($l->t("Save")) ?></a>