Plugin: OnlyOffice: Add OnlyOffice plugin v1.1.1 (9da9fba3a2) from the OnlyOffice team - refs BT#18599
parent
4f13a887ae
commit
31dfbabd2d
@ -0,0 +1,5 @@ |
||||
Chamilo ONLYOFFICE integration plugin uses code from the following 3rd party projects: |
||||
|
||||
JWT - JSON Web Token implementation (https://github.com/firebase/php-jwt/blob/master/LICENSE) |
||||
License: BSD |
||||
License File: 3rdparty/jwt/LICENSE |
||||
@ -0,0 +1,7 @@ |
||||
<?php |
||||
namespace Firebase\JWT; |
||||
|
||||
class BeforeValidException extends \UnexpectedValueException |
||||
{ |
||||
|
||||
} |
||||
@ -0,0 +1,7 @@ |
||||
<?php |
||||
namespace Firebase\JWT; |
||||
|
||||
class ExpiredException extends \UnexpectedValueException |
||||
{ |
||||
|
||||
} |
||||
@ -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); |
||||
} |
||||
} |
||||
@ -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. |
||||
@ -0,0 +1,7 @@ |
||||
<?php |
||||
namespace Firebase\JWT; |
||||
|
||||
class SignatureInvalidException extends \UnexpectedValueException |
||||
{ |
||||
|
||||
} |
||||
@ -0,0 +1,4 @@ |
||||
# Authors |
||||
|
||||
* Ascensio System SIA: <integration@onlyoffice.com> |
||||
|
||||
@ -0,0 +1,17 @@ |
||||
# Change Log |
||||
|
||||
This plugin is developed and maintained at https://github.com/ONLYOFFICE/onlyoffice-chamilo. |
||||
|
||||
## 2021-08 1.1.1 |
||||
- Add security filtering |
||||
- Minor documentation and code style changes |
||||
|
||||
## 2021-04 1.1.0 |
||||
- View option for DOCX, XLSX, PPTX. |
||||
- JWT support |
||||
|
||||
## 2021-03 1.0.0 |
||||
- Ability to create documents |
||||
- Edit option for DOCX, XLSX, PPTX. |
||||
- Collaboration editing |
||||
- Configuration page |
||||
@ -0,0 +1,201 @@ |
||||
Apache License |
||||
Version 2.0, January 2004 |
||||
http://www.apache.org/licenses/ |
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION |
||||
|
||||
1. Definitions. |
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, |
||||
and distribution as defined by Sections 1 through 9 of this document. |
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by |
||||
the copyright owner that is granting the License. |
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all |
||||
other entities that control, are controlled by, or are under common |
||||
control with that entity. For the purposes of this definition, |
||||
"control" means (i) the power, direct or indirect, to cause the |
||||
direction or management of such entity, whether by contract or |
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the |
||||
outstanding shares, or (iii) beneficial ownership of such entity. |
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity |
||||
exercising permissions granted by this License. |
||||
|
||||
"Source" form shall mean the preferred form for making modifications, |
||||
including but not limited to software source code, documentation |
||||
source, and configuration files. |
||||
|
||||
"Object" form shall mean any form resulting from mechanical |
||||
transformation or translation of a Source form, including but |
||||
not limited to compiled object code, generated documentation, |
||||
and conversions to other media types. |
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or |
||||
Object form, made available under the License, as indicated by a |
||||
copyright notice that is included in or attached to the work |
||||
(an example is provided in the Appendix below). |
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object |
||||
form, that is based on (or derived from) the Work and for which the |
||||
editorial revisions, annotations, elaborations, or other modifications |
||||
represent, as a whole, an original work of authorship. For the purposes |
||||
of this License, Derivative Works shall not include works that remain |
||||
separable from, or merely link (or bind by name) to the interfaces of, |
||||
the Work and Derivative Works thereof. |
||||
|
||||
"Contribution" shall mean any work of authorship, including |
||||
the original version of the Work and any modifications or additions |
||||
to that Work or Derivative Works thereof, that is intentionally |
||||
submitted to Licensor for inclusion in the Work by the copyright owner |
||||
or by an individual or Legal Entity authorized to submit on behalf of |
||||
the copyright owner. For the purposes of this definition, "submitted" |
||||
means any form of electronic, verbal, or written communication sent |
||||
to the Licensor or its representatives, including but not limited to |
||||
communication on electronic mailing lists, source code control systems, |
||||
and issue tracking systems that are managed by, or on behalf of, the |
||||
Licensor for the purpose of discussing and improving the Work, but |
||||
excluding communication that is conspicuously marked or otherwise |
||||
designated in writing by the copyright owner as "Not a Contribution." |
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity |
||||
on behalf of whom a Contribution has been received by Licensor and |
||||
subsequently incorporated within the Work. |
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of |
||||
this License, each Contributor hereby grants to You a perpetual, |
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||
copyright license to reproduce, prepare Derivative Works of, |
||||
publicly display, publicly perform, sublicense, and distribute the |
||||
Work and such Derivative Works in Source or Object form. |
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of |
||||
this License, each Contributor hereby grants to You a perpetual, |
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||
(except as stated in this section) patent license to make, have made, |
||||
use, offer to sell, sell, import, and otherwise transfer the Work, |
||||
where such license applies only to those patent claims licensable |
||||
by such Contributor that are necessarily infringed by their |
||||
Contribution(s) alone or by combination of their Contribution(s) |
||||
with the Work to which such Contribution(s) was submitted. If You |
||||
institute patent litigation against any entity (including a |
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work |
||||
or a Contribution incorporated within the Work constitutes direct |
||||
or contributory patent infringement, then any patent licenses |
||||
granted to You under this License for that Work shall terminate |
||||
as of the date such litigation is filed. |
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the |
||||
Work or Derivative Works thereof in any medium, with or without |
||||
modifications, and in Source or Object form, provided that You |
||||
meet the following conditions: |
||||
|
||||
(a) You must give any other recipients of the Work or |
||||
Derivative Works a copy of this License; and |
||||
|
||||
(b) You must cause any modified files to carry prominent notices |
||||
stating that You changed the files; and |
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works |
||||
that You distribute, all copyright, patent, trademark, and |
||||
attribution notices from the Source form of the Work, |
||||
excluding those notices that do not pertain to any part of |
||||
the Derivative Works; and |
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its |
||||
distribution, then any Derivative Works that You distribute must |
||||
include a readable copy of the attribution notices contained |
||||
within such NOTICE file, excluding those notices that do not |
||||
pertain to any part of the Derivative Works, in at least one |
||||
of the following places: within a NOTICE text file distributed |
||||
as part of the Derivative Works; within the Source form or |
||||
documentation, if provided along with the Derivative Works; or, |
||||
within a display generated by the Derivative Works, if and |
||||
wherever such third-party notices normally appear. The contents |
||||
of the NOTICE file are for informational purposes only and |
||||
do not modify the License. You may add Your own attribution |
||||
notices within Derivative Works that You distribute, alongside |
||||
or as an addendum to the NOTICE text from the Work, provided |
||||
that such additional attribution notices cannot be construed |
||||
as modifying the License. |
||||
|
||||
You may add Your own copyright statement to Your modifications and |
||||
may provide additional or different license terms and conditions |
||||
for use, reproduction, or distribution of Your modifications, or |
||||
for any such Derivative Works as a whole, provided Your use, |
||||
reproduction, and distribution of the Work otherwise complies with |
||||
the conditions stated in this License. |
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise, |
||||
any Contribution intentionally submitted for inclusion in the Work |
||||
by You to the Licensor shall be under the terms and conditions of |
||||
this License, without any additional terms or conditions. |
||||
Notwithstanding the above, nothing herein shall supersede or modify |
||||
the terms of any separate license agreement you may have executed |
||||
with Licensor regarding such Contributions. |
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade |
||||
names, trademarks, service marks, or product names of the Licensor, |
||||
except as required for reasonable and customary use in describing the |
||||
origin of the Work and reproducing the content of the NOTICE file. |
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or |
||||
agreed to in writing, Licensor provides the Work (and each |
||||
Contributor provides its Contributions) on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
||||
implied, including, without limitation, any warranties or conditions |
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A |
||||
PARTICULAR PURPOSE. You are solely responsible for determining the |
||||
appropriateness of using or redistributing the Work and assume any |
||||
risks associated with Your exercise of permissions under this License. |
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory, |
||||
whether in tort (including negligence), contract, or otherwise, |
||||
unless required by applicable law (such as deliberate and grossly |
||||
negligent acts) or agreed to in writing, shall any Contributor be |
||||
liable to You for damages, including any direct, indirect, special, |
||||
incidental, or consequential damages of any character arising as a |
||||
result of this License or out of the use or inability to use the |
||||
Work (including but not limited to damages for loss of goodwill, |
||||
work stoppage, computer failure or malfunction, or any and all |
||||
other commercial damages or losses), even if such Contributor |
||||
has been advised of the possibility of such damages. |
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing |
||||
the Work or Derivative Works thereof, You may choose to offer, |
||||
and charge a fee for, acceptance of support, warranty, indemnity, |
||||
or other liability obligations and/or rights consistent with this |
||||
License. However, in accepting such obligations, You may act only |
||||
on Your own behalf and on Your sole responsibility, not on behalf |
||||
of any other Contributor, and only if You agree to indemnify, |
||||
defend, and hold each Contributor harmless for any liability |
||||
incurred by, or claims asserted against, such Contributor by reason |
||||
of your accepting any such warranty or additional liability. |
||||
|
||||
END OF TERMS AND CONDITIONS |
||||
|
||||
APPENDIX: How to apply the Apache License to your work. |
||||
|
||||
To apply the Apache License to your work, attach the following |
||||
boilerplate notice, with the fields enclosed by brackets "[]" |
||||
replaced with your own identifying information. (Don't include |
||||
the brackets!) The text should be enclosed in the appropriate |
||||
comment syntax for the file format. We also recommend that a |
||||
file or class name and description of purpose be included on the |
||||
same "printed page" as the copyright notice for easier |
||||
identification within third-party archives. |
||||
|
||||
Copyright [yyyy] [name of copyright owner] |
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
you may not use this file except in compliance with the License. |
||||
You may obtain a copy of the License at |
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
||||
Unless required by applicable law or agreed to in writing, software |
||||
distributed under the License is distributed on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
See the License for the specific language governing permissions and |
||||
limitations under the License. |
||||
@ -0,0 +1,140 @@ |
||||
# Chamilo ONLYOFFICE integration plugin |
||||
|
||||
This app enables users to edit office documents from [Chamilo](https://chamilo.org) using ONLYOFFICE Docs packaged as Document Server - [Community or Enterprise Edition](#onlyoffice-docs-editions). |
||||
|
||||
The app is compatible with Chamilo v1.11.16 or newer. |
||||
|
||||
## Features |
||||
|
||||
The plugin allows teachers to: |
||||
|
||||
* Create and edit text documents, spreadsheets, and presentations. |
||||
* Co-edit documents in real-time: use two co-editing modes (Fast and Strict), Track Changes, comments, and built-in chat. |
||||
|
||||
Supported formats: |
||||
|
||||
* For editing: DOCX, XLSX, PPTX. |
||||
|
||||
## Installing ONLYOFFICE Docs |
||||
|
||||
You will need an instance of ONLYOFFICE Docs (Document Server) that is resolvable and connectable both from Chamilo and any end clients. ONLYOFFICE Document Server must also be able to POST to Chamilo directly. |
||||
|
||||
ONLYOFFICE Document Server and Chamilo 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 Chamilo work on port 80. |
||||
|
||||
You can install the free Community version of ONLYOFFICE Docs or scalable Enterprise Edition with pro features. |
||||
|
||||
To install the 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. |
||||
|
||||
To install the Enterprise Edition, follow instructions [here](https://helpcenter.onlyoffice.com/server/integration-edition/index.aspx). |
||||
|
||||
The Community Edition vs Enterprise Edition comparison can be found [here](#onlyoffice-docs-editions). |
||||
|
||||
To use ONLYOFFICE behind a proxy, please refer to [this article](https://helpcenter.onlyoffice.com/server/document/document-server-proxy.aspx). |
||||
|
||||
## Installing Chamilo ONLYOFFICE integration plugin |
||||
|
||||
The plugin comes integrated into Chamilo 1.11.16. |
||||
|
||||
To enable, go to the plugins list, select the ONLYOFFICE plugin, and click _Enable_ the selected plugins. |
||||
|
||||
If you want more up-to-date versions of the plugin, you can update the plugin/onlyoffice/ folder with the original plugin code [here](https://github.com/ONLYOFFICE/onlyoffice-chamilo) together with the compilation instructions. |
||||
|
||||
## Configuring Chamilo ONLYOFFICE integration plugin |
||||
|
||||
On the Plugins page, find ONLYOFFICE and click _Configure_. You'll see the _Settings_ page. Enable the plugin and specify the _Document Server address_. |
||||
|
||||
## How it works |
||||
|
||||
* To create a new file, the teacher opens the necessary folder and clicks the ONLYOFFICE icon "Create new". |
||||
* The user is redirected to the file creation page where they need to enter the file name and format (text document, spreadsheet, or presentation). The browser calls `/plugin/onlyoffice/create.php` method. It adds the copy of the empty file to the course folder. |
||||
* To open an existing file, the user chooses the _Open with ONLYOFFICE_ icon. |
||||
* The request is being sent to `/plugin/onlyoffice/editor.php?docId=«document identificator»`. The server processes the request, generates the editor initialization configuration with the properties: |
||||
|
||||
* **url** - the URL that ONLYOFFICE Document Server uses to download the document; |
||||
* **callbackUrl** - the URL at which ONLYOFFICE Document Server informs Chamilo about the 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 etag to instruct ONLYOFFICE Document Server whether to download the document again or not; |
||||
|
||||
* The server returns a page with a script to open the editor. |
||||
* The browser opens this page and loads the editor. |
||||
* The browser makes a request to Document Server and passes the document configuration to it. |
||||
* Document Server loads the document and the user starts editing. |
||||
* Document Server sends a POST request to **callbackUrl** to inform Chamilo that the user is editing the document. |
||||
* When all users have finished editing, they close the editor window. |
||||
* After 10 seconds, Document Server makes a POST request to **callbackUrl** with the information that editing has ended and sends a link to the new document version. |
||||
* Chamilo loads a new version of the document and overwrites the file. |
||||
|
||||
More information on integration ONLYOFFICE Docs can be found in the [API documentation](https://api.onlyoffice.com/editors/basic). |
||||
|
||||
## ONLYOFFICE Docs editions |
||||
|
||||
ONLYOFFICE offers different versions of its online document editors that can be deployed on your own servers. |
||||
|
||||
* Community Edition (`onlyoffice-documentserver` package) |
||||
* Enterprise Edition (`onlyoffice-documentserver-ee` package) |
||||
|
||||
The table below will help you to make the right choice. |
||||
|
||||
| Pricing and licensing | Community Edition | Enterprise Edition | |
||||
| ------------- | ------------- | ------------- | |
||||
| | [Get it now](https://www.onlyoffice.com/download.aspx?utm_source=github&utm_medium=cpc&utm_campaign=GitHubChamilo) | [Start Free Trial](https://www.onlyoffice.com/enterprise-edition-free.aspx?utm_source=github&utm_medium=cpc&utm_campaign=GitHubChamilo) | |
||||
| Cost | FREE | [Go to the pricing page](https://www.onlyoffice.com/docs-enterprise-prices.aspx?utm_source=github&utm_medium=cpc&utm_campaign=GitHubChamilo) | |
||||
| 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** | **Enterprise Edition** | |
||||
| Documentation | [Help Center](https://helpcenter.onlyoffice.com/installation/docs-community-index.aspx) | [Help Center](https://helpcenter.onlyoffice.com/installation/docs-enterprise-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=GitHubChamilo) | [Buy Now](https://www.onlyoffice.com/support.aspx?utm_source=github&utm_medium=cpc&utm_campaign=GitHubChamilo) | |
||||
| **Services** | **Community Edition** | **Enterprise Edition** | |
||||
| Conversion Service | + | + | |
||||
| Document Builder Service | + | + | |
||||
| **Interface** | **Community Edition** | **Enterprise Edition** | |
||||
| Tabbed interface | + | + | |
||||
| Dark theme | + | + | |
||||
| 150% scaling | + | + | |
||||
| White Label | - | - | |
||||
| Integrated test example (node.js)* | - | + | |
||||
| Mobile web editors | - | + | |
||||
| Access to pro features via desktop | - | + | |
||||
| **Plugins & Macros** | **Community Edition** | **Enterprise Edition** | |
||||
| Plugins | + | + | |
||||
| Macros | + | + | |
||||
| **Collaborative capabilities** | **Community Edition** | **Enterprise 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** | **Enterprise 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** | **Enterprise Edition** | |
||||
| Font and paragraph formatting | + | + | |
||||
| Object insertion | + | + | |
||||
| Functions, formulas, equations | + | + | |
||||
| Table templates | + | + | |
||||
| Pivot tables | + | + | |
||||
| Data validation | + | + | |
||||
| Conditional formatting for viewing | +** | +** | |
||||
| Sheet Views | - | + | |
||||
| **Presentation Editor features** | **Community Edition** | **Enterprise Edition** | |
||||
| Font and paragraph formatting | + | + | |
||||
| Object insertion | + | + | |
||||
| Transitions | + | + | |
||||
| Presenter mode | + | + | |
||||
| Notes | + | + | |
||||
| | [Get it now](https://www.onlyoffice.com/download.aspx?utm_source=github&utm_medium=cpc&utm_campaign=GitHubChamilo) | [Start Free Trial](https://www.onlyoffice.com/enterprise-edition-free.aspx?utm_source=github&utm_medium=cpc&utm_campaign=GitHubChamilo) | |
||||
|
||||
\* It's possible to add documents for comparison from your local drive, from URL and from Chamilo storage. |
||||
|
||||
\** Support for all conditions and gradient. Adding/Editing capabilities are coming soon |
||||
|
||||
@ -0,0 +1,4 @@ |
||||
# Authors |
||||
|
||||
* Ascensio System SIA: <integration@onlyoffice.com> |
||||
|
||||
@ -0,0 +1,201 @@ |
||||
Apache License |
||||
Version 2.0, January 2004 |
||||
http://www.apache.org/licenses/ |
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION |
||||
|
||||
1. Definitions. |
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, |
||||
and distribution as defined by Sections 1 through 9 of this document. |
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by |
||||
the copyright owner that is granting the License. |
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all |
||||
other entities that control, are controlled by, or are under common |
||||
control with that entity. For the purposes of this definition, |
||||
"control" means (i) the power, direct or indirect, to cause the |
||||
direction or management of such entity, whether by contract or |
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the |
||||
outstanding shares, or (iii) beneficial ownership of such entity. |
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity |
||||
exercising permissions granted by this License. |
||||
|
||||
"Source" form shall mean the preferred form for making modifications, |
||||
including but not limited to software source code, documentation |
||||
source, and configuration files. |
||||
|
||||
"Object" form shall mean any form resulting from mechanical |
||||
transformation or translation of a Source form, including but |
||||
not limited to compiled object code, generated documentation, |
||||
and conversions to other media types. |
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or |
||||
Object form, made available under the License, as indicated by a |
||||
copyright notice that is included in or attached to the work |
||||
(an example is provided in the Appendix below). |
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object |
||||
form, that is based on (or derived from) the Work and for which the |
||||
editorial revisions, annotations, elaborations, or other modifications |
||||
represent, as a whole, an original work of authorship. For the purposes |
||||
of this License, Derivative Works shall not include works that remain |
||||
separable from, or merely link (or bind by name) to the interfaces of, |
||||
the Work and Derivative Works thereof. |
||||
|
||||
"Contribution" shall mean any work of authorship, including |
||||
the original version of the Work and any modifications or additions |
||||
to that Work or Derivative Works thereof, that is intentionally |
||||
submitted to Licensor for inclusion in the Work by the copyright owner |
||||
or by an individual or Legal Entity authorized to submit on behalf of |
||||
the copyright owner. For the purposes of this definition, "submitted" |
||||
means any form of electronic, verbal, or written communication sent |
||||
to the Licensor or its representatives, including but not limited to |
||||
communication on electronic mailing lists, source code control systems, |
||||
and issue tracking systems that are managed by, or on behalf of, the |
||||
Licensor for the purpose of discussing and improving the Work, but |
||||
excluding communication that is conspicuously marked or otherwise |
||||
designated in writing by the copyright owner as "Not a Contribution." |
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity |
||||
on behalf of whom a Contribution has been received by Licensor and |
||||
subsequently incorporated within the Work. |
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of |
||||
this License, each Contributor hereby grants to You a perpetual, |
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||
copyright license to reproduce, prepare Derivative Works of, |
||||
publicly display, publicly perform, sublicense, and distribute the |
||||
Work and such Derivative Works in Source or Object form. |
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of |
||||
this License, each Contributor hereby grants to You a perpetual, |
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||
(except as stated in this section) patent license to make, have made, |
||||
use, offer to sell, sell, import, and otherwise transfer the Work, |
||||
where such license applies only to those patent claims licensable |
||||
by such Contributor that are necessarily infringed by their |
||||
Contribution(s) alone or by combination of their Contribution(s) |
||||
with the Work to which such Contribution(s) was submitted. If You |
||||
institute patent litigation against any entity (including a |
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work |
||||
or a Contribution incorporated within the Work constitutes direct |
||||
or contributory patent infringement, then any patent licenses |
||||
granted to You under this License for that Work shall terminate |
||||
as of the date such litigation is filed. |
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the |
||||
Work or Derivative Works thereof in any medium, with or without |
||||
modifications, and in Source or Object form, provided that You |
||||
meet the following conditions: |
||||
|
||||
(a) You must give any other recipients of the Work or |
||||
Derivative Works a copy of this License; and |
||||
|
||||
(b) You must cause any modified files to carry prominent notices |
||||
stating that You changed the files; and |
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works |
||||
that You distribute, all copyright, patent, trademark, and |
||||
attribution notices from the Source form of the Work, |
||||
excluding those notices that do not pertain to any part of |
||||
the Derivative Works; and |
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its |
||||
distribution, then any Derivative Works that You distribute must |
||||
include a readable copy of the attribution notices contained |
||||
within such NOTICE file, excluding those notices that do not |
||||
pertain to any part of the Derivative Works, in at least one |
||||
of the following places: within a NOTICE text file distributed |
||||
as part of the Derivative Works; within the Source form or |
||||
documentation, if provided along with the Derivative Works; or, |
||||
within a display generated by the Derivative Works, if and |
||||
wherever such third-party notices normally appear. The contents |
||||
of the NOTICE file are for informational purposes only and |
||||
do not modify the License. You may add Your own attribution |
||||
notices within Derivative Works that You distribute, alongside |
||||
or as an addendum to the NOTICE text from the Work, provided |
||||
that such additional attribution notices cannot be construed |
||||
as modifying the License. |
||||
|
||||
You may add Your own copyright statement to Your modifications and |
||||
may provide additional or different license terms and conditions |
||||
for use, reproduction, or distribution of Your modifications, or |
||||
for any such Derivative Works as a whole, provided Your use, |
||||
reproduction, and distribution of the Work otherwise complies with |
||||
the conditions stated in this License. |
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise, |
||||
any Contribution intentionally submitted for inclusion in the Work |
||||
by You to the Licensor shall be under the terms and conditions of |
||||
this License, without any additional terms or conditions. |
||||
Notwithstanding the above, nothing herein shall supersede or modify |
||||
the terms of any separate license agreement you may have executed |
||||
with Licensor regarding such Contributions. |
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade |
||||
names, trademarks, service marks, or product names of the Licensor, |
||||
except as required for reasonable and customary use in describing the |
||||
origin of the Work and reproducing the content of the NOTICE file. |
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or |
||||
agreed to in writing, Licensor provides the Work (and each |
||||
Contributor provides its Contributions) on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
||||
implied, including, without limitation, any warranties or conditions |
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A |
||||
PARTICULAR PURPOSE. You are solely responsible for determining the |
||||
appropriateness of using or redistributing the Work and assume any |
||||
risks associated with Your exercise of permissions under this License. |
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory, |
||||
whether in tort (including negligence), contract, or otherwise, |
||||
unless required by applicable law (such as deliberate and grossly |
||||
negligent acts) or agreed to in writing, shall any Contributor be |
||||
liable to You for damages, including any direct, indirect, special, |
||||
incidental, or consequential damages of any character arising as a |
||||
result of this License or out of the use or inability to use the |
||||
Work (including but not limited to damages for loss of goodwill, |
||||
work stoppage, computer failure or malfunction, or any and all |
||||
other commercial damages or losses), even if such Contributor |
||||
has been advised of the possibility of such damages. |
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing |
||||
the Work or Derivative Works thereof, You may choose to offer, |
||||
and charge a fee for, acceptance of support, warranty, indemnity, |
||||
or other liability obligations and/or rights consistent with this |
||||
License. However, in accepting such obligations, You may act only |
||||
on Your own behalf and on Your sole responsibility, not on behalf |
||||
of any other Contributor, and only if You agree to indemnify, |
||||
defend, and hold each Contributor harmless for any liability |
||||
incurred by, or claims asserted against, such Contributor by reason |
||||
of your accepting any such warranty or additional liability. |
||||
|
||||
END OF TERMS AND CONDITIONS |
||||
|
||||
APPENDIX: How to apply the Apache License to your work. |
||||
|
||||
To apply the Apache License to your work, attach the following |
||||
boilerplate notice, with the fields enclosed by brackets "[]" |
||||
replaced with your own identifying information. (Don't include |
||||
the brackets!) The text should be enclosed in the appropriate |
||||
comment syntax for the file format. We also recommend that a |
||||
file or class name and description of purpose be included on the |
||||
same "printed page" as the copyright notice for easier |
||||
identification within third-party archives. |
||||
|
||||
Copyright [yyyy] [name of copyright owner] |
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
you may not use this file except in compliance with the License. |
||||
You may obtain a copy of the License at |
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
||||
Unless required by applicable law or agreed to in writing, software |
||||
distributed under the License is distributed on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
See the License for the specific language governing permissions and |
||||
limitations under the License. |
||||
@ -0,0 +1,8 @@ |
||||
## Overview |
||||
|
||||
Template files used to create new documents and documents with sample content in: |
||||
|
||||
* [Chamilo ONLYOFFICE integration plugin](https://github.com/onlyoffice/onlyoffice-chamilo) |
||||
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,280 @@ |
||||
<?php |
||||
/** |
||||
* |
||||
* (c) Copyright Ascensio System SIA 2021 |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
require_once __DIR__.'/../../main/inc/global.inc.php'; |
||||
|
||||
use ChamiloSession as Session; |
||||
|
||||
/** |
||||
* Status of the document |
||||
*/ |
||||
const TrackerStatus_Editing = 1; |
||||
const TrackerStatus_MustSave = 2; |
||||
const TrackerStatus_Corrupted = 3; |
||||
const TrackerStatus_Closed = 4; |
||||
const TrackerStatus_ForceSave = 6; |
||||
const TrackerStatus_CorruptedForceSave = 7; |
||||
|
||||
$plugin = OnlyofficePlugin::create(); |
||||
|
||||
if (isset($_GET["hash"]) && !empty($_GET["hash"])) { |
||||
$callbackResponseArray = []; |
||||
@header( 'Content-Type: application/json; charset==utf-8'); |
||||
@header( 'X-Robots-Tag: noindex' ); |
||||
@header( 'X-Content-Type-Options: nosniff' ); |
||||
|
||||
list ($hashData, $error) = Crypt::ReadHash($_GET["hash"]); |
||||
if ($hashData === null) { |
||||
$callbackResponseArray["status"] = "error"; |
||||
$callbackResponseArray["error"] = $error; |
||||
die(json_encode($callbackResponseArray)); |
||||
} |
||||
|
||||
$type = $hashData->type; |
||||
$courseId = $hashData->courseId; |
||||
$userId = $hashData->userId; |
||||
$docId = $hashData->docId; |
||||
$groupId = $hashData->groupId; |
||||
$sessionId = $hashData->sessionId; |
||||
|
||||
$courseInfo = api_get_course_info_by_id($courseId); |
||||
$courseCode = $courseInfo["code"]; |
||||
|
||||
if (!empty($userId)) { |
||||
$userInfo = api_get_user_info($userId); |
||||
} else { |
||||
$result["error"] = "User not found"; |
||||
die (json_encode($result)); |
||||
} |
||||
|
||||
if (api_is_anonymous()) { |
||||
$loggedUser = [ |
||||
"user_id" => $userInfo["id"], |
||||
"status" => $userInfo["status"], |
||||
"uidReset" => true, |
||||
]; |
||||
|
||||
Session::write("_user", $loggedUser); |
||||
Login::init_user($loggedUser["user_id"], true); |
||||
} else { |
||||
$userId = api_get_user_id(); |
||||
} |
||||
|
||||
switch($type) { |
||||
case "track": |
||||
$callbackResponseArray = track(); |
||||
die (json_encode($callbackResponseArray)); |
||||
case "download": |
||||
$callbackResponseArray = download(); |
||||
die (json_encode($callbackResponseArray)); |
||||
default: |
||||
$callbackResponseArray["status"] = "error"; |
||||
$callbackResponseArray["error"] = "404 Method not found"; |
||||
die(json_encode($callbackResponseArray)); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Handle request from the document server with the document status information |
||||
*/ |
||||
function track(): array |
||||
{ |
||||
$result = []; |
||||
|
||||
global $plugin; |
||||
global $courseCode; |
||||
global $userId; |
||||
global $docId; |
||||
global $groupId; |
||||
global $sessionId; |
||||
global $courseInfo; |
||||
|
||||
if (($body_stream = file_get_contents("php://input")) === false) { |
||||
$result["error"] = "Bad Request"; |
||||
return $result; |
||||
} |
||||
|
||||
$data = json_decode($body_stream, true); |
||||
|
||||
if ($data === null) { |
||||
$result["error"] = "Bad Response"; |
||||
return $result; |
||||
} |
||||
|
||||
if (!empty($plugin->get("jwt_secret"))) { |
||||
|
||||
if (!empty($data["token"])) { |
||||
try { |
||||
$payload = \Firebase\JWT\JWT::decode($data["token"], $plugin->get("jwt_secret"), array("HS256")); |
||||
} catch (\UnexpectedValueException $e) { |
||||
$result["status"] = "error"; |
||||
$result["error"] = "403 Access denied"; |
||||
return $result; |
||||
} |
||||
} else { |
||||
$token = substr($_SERVER[AppConfig::JwtHeader()], strlen("Bearer ")); |
||||
try { |
||||
$decodeToken = \Firebase\JWT\JWT::decode($token, $plugin->get("jwt_secret"), array("HS256")); |
||||
$payload = $decodeToken->payload; |
||||
} catch (\UnexpectedValueException $e) { |
||||
$result["status"] = "error"; |
||||
$result["error"] = "403 Access denied"; |
||||
return $result; |
||||
} |
||||
} |
||||
|
||||
$data["url"] = isset($payload->url) ? $payload->url : null; |
||||
$data["status"] = $payload->status; |
||||
} |
||||
|
||||
$status = $data["status"]; |
||||
|
||||
$track_result = 1; |
||||
switch ($status) { |
||||
case TrackerStatus_MustSave: |
||||
case TrackerStatus_Corrupted: |
||||
|
||||
$downloadUri = $data["url"]; |
||||
|
||||
if (!empty($docId) && !empty($courseCode)) { |
||||
$docInfo = DocumentManager::get_document_data_by_id($docId, $courseCode, false, $sessionId); |
||||
|
||||
if ($docInfo === false) { |
||||
$result["error"] = "File not found"; |
||||
return $result; |
||||
} |
||||
|
||||
$filePath = $docInfo["absolute_path"]; |
||||
} else { |
||||
$result["error"] = "Bad Request"; |
||||
return $result; |
||||
} |
||||
|
||||
list ($isAllowToEdit, $isMyDir, $isGroupAccess, $isReadonly) = getPermissions($docInfo, $userId, $courseCode, $groupId, $sessionId); |
||||
|
||||
if ($isReadonly) { |
||||
break; |
||||
} |
||||
|
||||
if (($new_data = file_get_contents($downloadUri)) === false) { |
||||
break; |
||||
} |
||||
|
||||
if ($isAllowToEdit || $isMyDir || $isGroupAccess) { |
||||
$groupInfo = GroupManager::get_group_properties($groupId); |
||||
|
||||
if ($fp = @fopen($filePath, "w")) { |
||||
fputs($fp, $new_data); |
||||
fclose($fp); |
||||
api_item_property_update($courseInfo, |
||||
TOOL_DOCUMENT, |
||||
$docId, |
||||
"DocumentUpdated", |
||||
$userId, |
||||
$groupInfo, |
||||
null, |
||||
null, |
||||
null, |
||||
$sessionId); |
||||
update_existing_document($courseInfo, |
||||
$docId, |
||||
filesize($filePath), |
||||
false); |
||||
$track_result = 0; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
case TrackerStatus_Editing: |
||||
case TrackerStatus_Closed: |
||||
|
||||
$track_result = 0; |
||||
break; |
||||
} |
||||
|
||||
$result["error"] = $track_result; |
||||
return $result; |
||||
} |
||||
|
||||
/** |
||||
* Downloading file by the document service |
||||
*/ |
||||
function download() |
||||
{ |
||||
global $plugin; |
||||
global $courseCode; |
||||
global $userId; |
||||
global $docId; |
||||
global $groupId; |
||||
global $sessionId; |
||||
global $courseInfo; |
||||
|
||||
if (!empty($plugin->get("jwt_secret"))) { |
||||
$token = substr($_SERVER[AppConfig::JwtHeader()], strlen("Bearer ")); |
||||
try { |
||||
$payload = \Firebase\JWT\JWT::decode($token, $plugin->get("jwt_secret"), array("HS256")); |
||||
|
||||
} catch (\UnexpectedValueException $e) { |
||||
$result["status"] = "error"; |
||||
$result["error"] = "403 Access denied"; |
||||
return $result; |
||||
} |
||||
} |
||||
|
||||
if (!empty($docId) && !empty($courseCode)) { |
||||
$docInfo = DocumentManager::get_document_data_by_id($docId, $courseCode, false, $sessionId); |
||||
|
||||
if ($docInfo === false) { |
||||
$result["error"] = "File not found"; |
||||
return $result; |
||||
} |
||||
|
||||
$filePath = $docInfo["absolute_path"]; |
||||
} else { |
||||
$result["error"] = "File not found"; |
||||
return $result; |
||||
} |
||||
|
||||
@header("Content-Type: application/octet-stream"); |
||||
@header("Content-Disposition: attachment; filename=" . $docInfo["title"]); |
||||
|
||||
readfile($filePath); |
||||
} |
||||
|
||||
/** |
||||
* Method checks access rights to document and returns permissions |
||||
*/ |
||||
function getPermissions(array $docInfo, int $userId, string $courseCode, int $groupId = null, int $sessionId = null): array |
||||
{ |
||||
$isAllowToEdit = api_is_allowed_to_edit(true, true); |
||||
$isMyDir = DocumentManager::is_my_shared_folder($userId, $docInfo["absolute_parent_path"], $sessionId); |
||||
|
||||
$isGroupAccess = false; |
||||
if (!empty($groupId)) { |
||||
$courseInfo = api_get_course_info($courseCode); |
||||
Session::write("_real_cid", $courseInfo["real_id"]); |
||||
$groupProperties = GroupManager::get_group_properties($groupId); |
||||
$docInfoGroup = api_get_item_property_info($courseInfo["real_id"], "document", $docInfo["id"], $sessionId); |
||||
$isGroupAccess = GroupManager::allowUploadEditDocument($userId, $courseCode, $groupProperties, $docInfoGroup); |
||||
} |
||||
|
||||
$isReadonly = $docInfo["readonly"]; |
||||
|
||||
return [$isAllowToEdit, $isMyDir, $isGroupAccess, $isReadonly]; |
||||
} |
||||
@ -0,0 +1,180 @@ |
||||
<?php |
||||
/** |
||||
* |
||||
* (c) Copyright Ascensio System SIA 2021 |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
require_once __DIR__.'/../../main/inc/global.inc.php'; |
||||
|
||||
use ChamiloSession as Session; |
||||
|
||||
$plugin = OnlyofficePlugin::create(); |
||||
|
||||
$mapFileFormat = [ |
||||
"text" => $plugin->get_lang("document"), |
||||
"spreadsheet" => $plugin->get_lang("spreadsheet"), |
||||
"presentation" => $plugin->get_lang("presentation") |
||||
]; |
||||
|
||||
$userId = $_GET["userId"]; |
||||
$sessionId = $_GET["sessionId"]; |
||||
$docId = $_GET["folderId"]; |
||||
$courseId = $_GET["courseId"]; |
||||
|
||||
$courseInfo = api_get_course_info_by_id($courseId); |
||||
$courseCode = $courseInfo["code"]; |
||||
|
||||
$docInfo = DocumentManager::get_document_data_by_id( |
||||
$docId, |
||||
$courseCode, |
||||
true, |
||||
$sessionId |
||||
); |
||||
|
||||
$groupRights = Session::read('group_member_with_upload_rights'); |
||||
$isAllowToEdit = api_is_allowed_to_edit(true, true); |
||||
$isMyDir = DocumentManager::is_my_shared_folder( |
||||
$userId, |
||||
$docInfo["absolute_path"], |
||||
$sessionId |
||||
); |
||||
if (!($isAllowToEdit || $isMyDir || $groupRights)) { |
||||
api_not_allowed(true); |
||||
} |
||||
|
||||
$form = new FormValidator( |
||||
"doc_create", |
||||
"post", |
||||
api_get_path(WEB_PLUGIN_PATH) . "onlyoffice/create.php" |
||||
); |
||||
|
||||
$form->addText("fileName", $plugin->get_lang("title"), true); |
||||
$form->addSelect("fileFormat", $plugin->get_lang("chooseFileFormat"), $mapFileFormat); |
||||
$form->addButtonCreate($plugin->get_lang("create")); |
||||
|
||||
$form->addHidden("groupId", (int) $_GET["groupId"]); |
||||
$form->addHidden("courseId", (int) $_GET["courseId"]); |
||||
$form->addHidden("sessionId", (int) $_GET["sessionId"]); |
||||
$form->addHidden("userId", (int) $_GET["userId"]); |
||||
$form->addHidden("folderId", (int) $_GET["folderId"]); |
||||
$form->addHidden("goBackUrl", Security::remove_XSS($_SERVER["HTTP_REFERER"])); |
||||
|
||||
if ($form->validate()) { |
||||
$values = $form->exportValues(); |
||||
|
||||
$folderId = $values["folderId"]; |
||||
$userId = $values["userId"]; |
||||
$groupId = $values["groupId"]; |
||||
$sessionId = $values["sessionId"]; |
||||
$courseId = $values["courseId"]; |
||||
$goBackUrl = Security::remove_XSS($values["goBackUrl"]); |
||||
|
||||
$fileType = $values["fileFormat"]; |
||||
$fileExt = FileUtility::getDocExt($fileType); |
||||
$fileTitle = $values["fileName"] . "." . $fileExt; |
||||
|
||||
$courseInfo = api_get_course_info_by_id($courseId); |
||||
$courseCode = $courseInfo["code"]; |
||||
|
||||
$fileNamePrefix = DocumentManager::getDocumentSuffix($courseInfo, $sessionId, $groupId); |
||||
$fileName = $values["fileName"] . $fileNamePrefix . "." . $fileExt; |
||||
|
||||
$groupInfo = GroupManager::get_group_properties($groupId); |
||||
|
||||
$emptyTemplatePath = TemplateManager::getEmptyTemplate($fileExt); |
||||
|
||||
$folderPath = ''; |
||||
$fileRelatedPath = "/"; |
||||
if (!empty($folderId)) { |
||||
$document_data = DocumentManager::get_document_data_by_id( |
||||
$folderId, |
||||
$courseCode, |
||||
true, |
||||
$sessionId |
||||
); |
||||
$folderPath = $document_data["absolute_path"]; |
||||
$fileRelatedPath = $fileRelatedPath . substr($document_data["absolute_path_from_document"], 10) . "/" . $fileName; |
||||
} else { |
||||
$folderPath = api_get_path(SYS_COURSE_PATH) . api_get_course_path($courseCode) . "/document"; |
||||
if (!empty($groupId)) { |
||||
$folderPath = $folderPath . "/" . $groupInfo["directory"]; |
||||
$fileRelatedPath = $groupInfo["directory"] . "/"; |
||||
} |
||||
$fileRelatedPath = $fileRelatedPath . $fileName; |
||||
} |
||||
$filePath = $folderPath . "/" . $fileName; |
||||
|
||||
if (file_exists($filePath)) { |
||||
Display::addFlash(Display::return_message($plugin->get_lang("fileIsExist"), "error")); |
||||
goto display; |
||||
} |
||||
if (!Security::check_abs_path($filePath, $folderPath)) { |
||||
Display::addFlash(Display::return_message(get_lang('SendFileError'), 'error')); |
||||
goto display; |
||||
} |
||||
|
||||
if ($fp = @fopen($filePath, "w")) { |
||||
$content = file_get_contents($emptyTemplatePath); |
||||
fputs($fp, $content); |
||||
fclose($fp); |
||||
|
||||
chmod($filePath, api_get_permissions_for_new_files()); |
||||
|
||||
$documentId = add_document( |
||||
$courseInfo, |
||||
$fileRelatedPath, |
||||
"file", |
||||
filesize($filePath), |
||||
$fileTitle, |
||||
null, |
||||
false |
||||
); |
||||
if ($documentId) { |
||||
api_item_property_update( |
||||
$courseInfo, |
||||
TOOL_DOCUMENT, |
||||
$documentId, |
||||
"DocumentAdded", |
||||
$userId, |
||||
$groupInfo, |
||||
null, |
||||
null, |
||||
null, |
||||
$sessionId |
||||
); |
||||
|
||||
header("Location: " . $goBackUrl); |
||||
exit(); |
||||
} |
||||
|
||||
} else { |
||||
Display::addFlash( |
||||
Display::return_message( |
||||
$plugin->get_lang("impossibleCreateFile"), |
||||
"error" |
||||
) |
||||
); |
||||
} |
||||
} |
||||
|
||||
display: |
||||
$goBackUrl = $goBackUrl ?: Security::remove_XSS($_SERVER["HTTP_REFERER"]); |
||||
$actionsLeft = '<a href="'. $goBackUrl . '">' . Display::return_icon("back.png", get_lang("Back") . " " . get_lang("To") . " " . get_lang("DocumentsOverview"), "", ICON_SIZE_MEDIUM) . "</a>"; |
||||
|
||||
Display::display_header($plugin->get_lang("createNewDocument")); |
||||
echo Display::toolbarAction("actions-documents", [$actionsLeft]); |
||||
echo $form->returnForm(); |
||||
Display::display_footer(); |
||||
@ -0,0 +1,246 @@ |
||||
<?php |
||||
/** |
||||
* |
||||
* (c) Copyright Ascensio System SIA 2021 |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
require_once __DIR__.'/../../main/inc/global.inc.php'; |
||||
|
||||
const USER_AGENT_MOBILE = "/android|avantgo|playbook|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od|ad)|iris|kindle|lge |maemo|midp|mmp|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\\/|plucker|pocket|psp|symbian|treo|up\\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i"; |
||||
|
||||
$plugin = OnlyofficePlugin::create(); |
||||
|
||||
$isEnable = $plugin->get("enable_onlyoffice_plugin") === 'true'; |
||||
if (!$isEnable) { |
||||
die ("Document server isn't enabled"); |
||||
return; |
||||
} |
||||
|
||||
$documentServerUrl = $plugin->get("document_server_url"); |
||||
if (empty($documentServerUrl)) { |
||||
die ("Document server isn't configured"); |
||||
return; |
||||
} |
||||
|
||||
$config = []; |
||||
|
||||
$docApiUrl = $documentServerUrl . "/web-apps/apps/api/documents/api.js"; |
||||
|
||||
$docId = $_GET["docId"]; |
||||
$groupId = isset($_GET["groupId"]) && !empty($_GET["groupId"]) ? $_GET["groupId"] : null; |
||||
|
||||
$userId = api_get_user_id(); |
||||
|
||||
$userInfo = api_get_user_info($userId); |
||||
|
||||
$sessionId = api_get_session_id(); |
||||
$courseId = api_get_course_int_id(); |
||||
$courseInfo = api_get_course_info(); |
||||
$courseCode = $courseInfo["code"]; |
||||
|
||||
$docInfo = DocumentManager::get_document_data_by_id($docId, $courseCode, false, $sessionId); |
||||
|
||||
$extension = strtolower(pathinfo($docInfo["title"], PATHINFO_EXTENSION)); |
||||
|
||||
$langInfo = LangManager::getLangUser(); |
||||
|
||||
$docType = FileUtility::getDocType($extension); |
||||
$key = FileUtility::getKey($courseCode, $docId); |
||||
$fileUrl = FileUtility::getFileUrl($courseId, $userId, $docId, $sessionId, $groupId); |
||||
|
||||
$config = [ |
||||
"type" => "desktop", |
||||
"documentType" => $docType, |
||||
"document" => [ |
||||
"fileType" => $extension, |
||||
"key" => $key, |
||||
"title" => $docInfo["title"], |
||||
"url" => $fileUrl |
||||
], |
||||
"editorConfig" => [ |
||||
"lang" => $langInfo["isocode"], |
||||
"region" => $langInfo["isocode"], |
||||
"user" => [ |
||||
"id" => strval($userId), |
||||
"name" => $userInfo["username"] |
||||
], |
||||
"customization" => [ |
||||
"goback" => [ |
||||
"blank" => false, |
||||
"requestClose" => false, |
||||
"text" => get_lang("Back"), |
||||
"url" => Security::remove_XSS($_SERVER["HTTP_REFERER"]) |
||||
], |
||||
"compactHeader" => true, |
||||
"toolbarNoTabs" => true |
||||
] |
||||
] |
||||
]; |
||||
|
||||
$userAgent = $_SERVER['HTTP_USER_AGENT']; |
||||
|
||||
$isMobileAgent = preg_match(USER_AGENT_MOBILE, $userAgent); |
||||
if ($isMobileAgent) { |
||||
$config['type'] = 'mobile'; |
||||
} |
||||
|
||||
$isAllowToEdit = api_is_allowed_to_edit(true, true); |
||||
$isMyDir = DocumentManager::is_my_shared_folder( |
||||
$userId, |
||||
$docInfo["absolute_parent_path"], |
||||
$sessionId |
||||
); |
||||
|
||||
$isGroupAccess = false; |
||||
if (!empty($groupId)) { |
||||
$groupProperties = GroupManager::get_group_properties($groupId); |
||||
$docInfoGroup = api_get_item_property_info( |
||||
api_get_course_int_id(), |
||||
'document', |
||||
$docId, |
||||
$sessionId |
||||
); |
||||
$isGroupAccess = GroupManager::allowUploadEditDocument( |
||||
$userId, |
||||
$courseCode, |
||||
$groupProperties, |
||||
$docInfoGroup |
||||
); |
||||
|
||||
$isMemberGroup = GroupManager::is_user_in_group($userId, $groupProperties); |
||||
|
||||
if (!$isGroupAccess) { |
||||
if (!$groupProperties["status"]) { |
||||
api_not_allowed(true); |
||||
} |
||||
if (!$isMemberGroup && $groupProperties["doc_state"] != 1) { |
||||
api_not_allowed(true); |
||||
} |
||||
} |
||||
} |
||||
|
||||
$accessRights = $isAllowToEdit || $isMyDir || $isGroupAccess; |
||||
$canEdit = in_array($extension, FileUtility::$can_edit_types); |
||||
|
||||
$isVisible = DocumentManager::check_visibility_tree($docId, $courseInfo, $sessionId, $userId, $groupId); |
||||
$isReadonly = $docInfo["readonly"]; |
||||
|
||||
if (!$isVisible) { |
||||
api_not_allowed(true); |
||||
} |
||||
|
||||
if ($canEdit && $accessRights && !$isReadonly) { |
||||
$config["editorConfig"]["mode"] = "edit"; |
||||
$config["editorConfig"]["callbackUrl"] = getCallbackUrl( |
||||
$docId, |
||||
$userId, |
||||
$courseId, |
||||
$sessionId, |
||||
$groupId |
||||
); |
||||
} else { |
||||
$canView = in_array($extension, FileUtility::$can_view_types); |
||||
if ($canView) { |
||||
$config["editorConfig"]["mode"] = "view"; |
||||
} else { |
||||
api_not_allowed(true); |
||||
} |
||||
} |
||||
$config["document"]["permissions"]["edit"] = $accessRights && !$isReadonly; |
||||
|
||||
if (!empty($plugin->get("jwt_secret"))) { |
||||
$token = \Firebase\JWT\JWT::encode($config, $plugin->get("jwt_secret")); |
||||
$config["token"] = $token; |
||||
} |
||||
|
||||
/** |
||||
* Return callback url |
||||
*/ |
||||
function getCallbackUrl(int $docId, int $userId, int $courseId, int $sessionId, int $groupId = null): string |
||||
{ |
||||
$url = ""; |
||||
|
||||
$data = [ |
||||
"type" => "track", |
||||
"courseId" => $courseId, |
||||
"userId" => $userId, |
||||
"docId" => $docId, |
||||
"sessionId" => $sessionId |
||||
]; |
||||
|
||||
if (!empty($groupId)) { |
||||
$data["groupId"] = $groupId; |
||||
} |
||||
|
||||
$hashUrl = Crypt::GetHash($data); |
||||
|
||||
return $url . api_get_path(WEB_PLUGIN_PATH) . "onlyoffice/callback.php?hash=" . $hashUrl; |
||||
} |
||||
|
||||
?> |
||||
<title>ONLYOFFICE</title> |
||||
<style> |
||||
#app > iframe { |
||||
height: calc(100% - 140px); |
||||
} |
||||
body { |
||||
height: 100%; |
||||
} |
||||
.chatboxheadmain, |
||||
.pull-right, |
||||
.breadcrumb { |
||||
display: none; |
||||
} |
||||
</style> |
||||
<script type="text/javascript" src=<?php echo $docApiUrl?>></script>
|
||||
<script type="text/javascript"> |
||||
var onAppReady = function () { |
||||
innerAlert("Document editor ready"); |
||||
}; |
||||
var connectEditor = function () { |
||||
$("#cm-content")[0].remove(".container"); |
||||
$("#main").append('<div id="app-onlyoffice">' + |
||||
'<div id="app">' + |
||||
'<div id="iframeEditor">' + |
||||
'</div>' + |
||||
'</div>' + |
||||
'</div>'); |
||||
|
||||
var config = <?php echo json_encode($config)?>;
|
||||
var isMobileAgent = <?php echo json_encode($isMobileAgent)?>;
|
||||
|
||||
config.events = { |
||||
"onAppReady": onAppReady |
||||
}; |
||||
|
||||
docEditor = new DocsAPI.DocEditor("iframeEditor", config); |
||||
|
||||
$(".navbar").css({"margin-bottom": "0px"}); |
||||
$("body").css({"margin": "0 0 0px"}); |
||||
if (isMobileAgent) { |
||||
var frameEditor = $("#app > iframe")[0]; |
||||
$(frameEditor).css({"height": "100%", "top": "0px"}); |
||||
} |
||||
} |
||||
|
||||
if (window.addEventListener) { |
||||
window.addEventListener("load", connectEditor); |
||||
} else if (window.attachEvent) { |
||||
window.attachEvent("load", connectEditor); |
||||
} |
||||
|
||||
</script> |
||||
<?php echo Display::display_header(); ?> |
||||
@ -0,0 +1,25 @@ |
||||
<?php |
||||
/** |
||||
* |
||||
* (c) Copyright Ascensio System SIA 2021 |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
require_once __DIR__.'/../../main/inc/global.inc.php'; |
||||
|
||||
/** |
||||
* @package chamilo.plugin.onlyoffice |
||||
*/ |
||||
OnlyofficePlugin::create()->install(); |
||||
@ -0,0 +1,21 @@ |
||||
<?php |
||||
/** |
||||
* @author ASENSIO SYSTEM SIA |
||||
* |
||||
*/ |
||||
$strings["plugin_title"] = "ONLYOFFICE"; |
||||
$strings['plugin_comment'] = "ONLYOFFICE конекторът ви позволява да преглеждате, редактирате и да си сътрудничите с текстови документи, таблици и презентациив рамките на Chamilo с помощта на ONLYOFFICE Docs."; |
||||
$strings["enable_onlyoffice_plugin"] = "Активирай"; |
||||
$strings["document_server_url"] = "Адрес на ONLYOFFICE Docs"; |
||||
$strings["jwt_secret"] = "Секретен ключ (оставете празно за забрана)"; |
||||
$strings["openByOnlyoffice"] = "Отвори с ONLYOFFICE"; |
||||
$strings["createNew"] = "Създай нов"; |
||||
$strings["title"] = "Заглавие"; |
||||
$strings["chooseFileFormat"] = "Избери формат на файла"; |
||||
$strings["document"] = "Документ"; |
||||
$strings["spreadsheet"] = "Таблица"; |
||||
$strings["presentation"] = "Презентация"; |
||||
$strings["create"] = "Създай"; |
||||
$strings["fileIsExist"] = "Файлът вече съществува"; |
||||
$strings["impossibleCreateFile"] = "Не може да се създаде файл"; |
||||
$strings["createNewDocument"] = "Създай нов документ"; |
||||
@ -0,0 +1,21 @@ |
||||
<?php |
||||
/** |
||||
* @author ASENSIO SYSTEM SIA |
||||
* |
||||
*/ |
||||
$strings["plugin_title"] = "ONLYOFFICE"; |
||||
$strings['plugin_comment'] = "ONLYOFFICE connector maakt het mogelijk om tekstdocumenten, spreadsheets en presentaties te bekijken, te bewerken en samen te werken binnen Chamilo met behulp van ONLYOFFICE Docs."; |
||||
$strings["enable_onlyoffice_plugin"] = "Inschakelen"; |
||||
$strings["document_server_url"] = "ONLYOFFICE Docs adres"; |
||||
$strings["jwt_secret"] = "Geheime sleutel (leeg laten om niet te te gebruiken)"; |
||||
$strings["openByOnlyoffice"] = "Open met ONLYOFFICE"; |
||||
$strings["createNew"] = "Nieuwe maken"; |
||||
$strings["title"] = "Titel"; |
||||
$strings["chooseFileFormat"] = "Kies bestandsformaat"; |
||||
$strings["document"] = "Document"; |
||||
$strings["spreadsheet"] = "Spreadsheet"; |
||||
$strings["presentation"] = "Presentatie"; |
||||
$strings["create"] = "Maak"; |
||||
$strings["fileIsExist"] = "Bestand bestaat al"; |
||||
$strings["impossibleCreateFile"] = "Onmogelijk om bestand te maken"; |
||||
$strings["createNewDocument"] = "Nieuw document maken"; |
||||
@ -0,0 +1,21 @@ |
||||
<?php |
||||
/** |
||||
* @author ASENSIO SYSTEM SIA |
||||
* |
||||
*/ |
||||
$strings["plugin_title"] = "ONLYOFFICE"; |
||||
$strings['plugin_comment'] = "ONLYOFFICE connector allows you to view, edit, and collaborate on text documents, spreadsheets, and presentations within Chamilo using ONLYOFFICE Docs."; |
||||
$strings["enable_onlyoffice_plugin"] = "Enable"; |
||||
$strings["document_server_url"] = "ONLYOFFICE Docs address"; |
||||
$strings["jwt_secret"] = "Secret key (leave blank to disable)"; |
||||
$strings["openByOnlyoffice"] = "Open with ONLYOFFICE"; |
||||
$strings["createNew"] = "Create new"; |
||||
$strings["title"] = "Title"; |
||||
$strings["chooseFileFormat"] = "Choose file format"; |
||||
$strings["document"] = "Document"; |
||||
$strings["spreadsheet"] = "Spreadsheet"; |
||||
$strings["presentation"] = "Presentation"; |
||||
$strings["create"] = "Create"; |
||||
$strings["fileIsExist"] = "File already exists"; |
||||
$strings["impossibleCreateFile"] = "Impossible to create file"; |
||||
$strings["createNewDocument"] = "Create new document"; |
||||
@ -0,0 +1,21 @@ |
||||
<?php |
||||
/** |
||||
* @author ASENSIO SYSTEM SIA |
||||
* |
||||
*/ |
||||
$strings["plugin_title"] = "ONLYOFFICE"; |
||||
$strings['plugin_comment'] = "Connecteur ONLYOFFICE vous permet d’afficher, éditer et coéditer les documents texte, les feuilles de calcul et les présentations au sein de Chamilo en utilisant ONLYOFFICE Docs."; |
||||
$strings["enable_onlyoffice_plugin"] = "Activer"; |
||||
$strings["document_server_url"] = "L'URL du serveur ONLYOFFICE Docs"; |
||||
$strings["jwt_secret"] = "Clé secrète (laisser vide pour désactiver)"; |
||||
$strings["openByOnlyoffice"] = "Ouvrir avec ONLYOFFICE"; |
||||
$strings["createNew"] = "Créer nouveau"; |
||||
$strings["title"] = "Titre"; |
||||
$strings["chooseFileFormat"] = "Sélectionner le format du fichier"; |
||||
$strings["document"] = "Document"; |
||||
$strings["spreadsheet"] = "Classeur"; |
||||
$strings["presentation"] = "Présentation"; |
||||
$strings["create"] = "Créer"; |
||||
$strings["fileIsExist"] = "Le fichier existe déjà"; |
||||
$strings["impossibleCreateFile"] = "Impossible de créer le fichier"; |
||||
$strings["createNewDocument"] = "Créer un nouveau document"; |
||||
@ -0,0 +1,21 @@ |
||||
<?php |
||||
/** |
||||
* @author ASENSIO SYSTEM SIA |
||||
* |
||||
*/ |
||||
$strings["plugin_title"] = "ONLYOFFICE"; |
||||
$strings['plugin_comment'] = "Mit dem ONLYOFFICE-Konnektor können Sie in Chamilo Textdokumente, Tabellenkalkulationen und Präsentationen mit ONLYOFFICE Docs öffnen und bearbeiten"; |
||||
$strings["enable_onlyoffice_plugin"] = "Aktivieren"; |
||||
$strings["document_server_url"] = "Die Adresse von ONLYOFFICE Docs"; |
||||
$strings["jwt_secret"] = "Geheimer Schlüssel (freilassen, um zu deaktivieren)"; |
||||
$strings["openByOnlyoffice"] = "In ONLYOFFICE öffnen"; |
||||
$strings["createNew"] = "Neu erstellen"; |
||||
$strings["title"] = "Titel"; |
||||
$strings["chooseFileFormat"] = "Dateiformat auswählen"; |
||||
$strings["document"] = "Dokument"; |
||||
$strings["spreadsheet"] = "Tabellenkalkulation"; |
||||
$strings["presentation"] = "Präsentation"; |
||||
$strings["create"] = "Erstellen"; |
||||
$strings["fileIsExist"] = "Die Datei ist bereits vorhanden"; |
||||
$strings["impossibleCreateFile"] = "Datei kann nicht erstellt werden"; |
||||
$strings["createNewDocument"] = "Neues Dokument erstellen"; |
||||
@ -0,0 +1,20 @@ |
||||
<?php |
||||
/** |
||||
* @author ASENSIO SYSTEM SIA |
||||
* |
||||
*/ |
||||
$strings["plugin_title"] = "ONLYOFFICE"; |
||||
$strings['plugin_comment'] = "Ο συνδετήρας ONLYOFFICE σάς επιτρέπει να προβάλετε, να επεξεργαστείτε και να συνεργαστείτε σε έγγραφα κειμένου, υπολογιστικά φύλλα και παρουσιάσεις στο Chamilo χρησιμοποιώντας έγγραφα ONLYOFFICE."; |
||||
$strings["enable_onlyoffice_plugin"] = "Ενεργοποίηση"; |
||||
$strings["document_server_url"] = "Διεύθυνση ONLYOFFICE Docs"; |
||||
$strings["openByOnlyoffice"] = "Άνοιγμα με ONLYOFFICE"; |
||||
$strings["createNew"] = "Δημιουργία νέου"; |
||||
$strings["title"] = "Τίτλος"; |
||||
$strings["chooseFileFormat"] = "Επιλογή μορφής αρχείου"; |
||||
$strings["document"] = "Έγγραφο"; |
||||
$strings["spreadsheet"] = "Υπολογιστικό φύλλο"; |
||||
$strings["presentation"] = "Παρουσίαση"; |
||||
$strings["create"] = "Δημιουργία"; |
||||
$strings["fileIsExist"] = "Το αρχείο υπάρχει ήδη"; |
||||
$strings["impossibleCreateFile"] = "Η δημιουργία αρχείου είναι αδύνατη"; |
||||
$strings["createNewDocument"] = "Δημιουργία νέου εγγράφου"; |
||||
@ -0,0 +1,21 @@ |
||||
<?php |
||||
/** |
||||
* @author ASENSIO SYSTEM SIA |
||||
* |
||||
*/ |
||||
$strings["plugin_title"] = "ONLYOFFICE"; |
||||
$strings['plugin_comment'] = "Il connettore ONLYOFFICE ti consente di visualizzare, modificare e collaborare su documenti di testo, fogli di calcolo e presentazioni all'interno di Chamilo con ONLYOFFICE Docs."; |
||||
$strings["enable_onlyoffice_plugin"] = "Attiva"; |
||||
$strings["document_server_url"] = "Indirizzo del ONLYOFFICE Docs"; |
||||
$strings["jwt_secret"] = "Chiave segreta (lasciare vuoto per disabilitare)"; |
||||
$strings["openByOnlyoffice"] = "Apri con ONLYOFFICE"; |
||||
$strings["createNew"] = "Crea ora"; |
||||
$strings["title"] = "Titolo "; |
||||
$strings["chooseFileFormat"] = "Seleziona formato file"; |
||||
$strings["document"] = "Documento"; |
||||
$strings["spreadsheet"] = "Foglio di calcolo"; |
||||
$strings["presentation"] = "Presentazione"; |
||||
$strings["create"] = "Crea"; |
||||
$strings["fileIsExist"] = "Il file esiste già"; |
||||
$strings["impossibleCreateFile"] = "Impossibile creare file"; |
||||
$strings["createNewDocument"] = "Crea nuovo documento"; |
||||
@ -0,0 +1,21 @@ |
||||
<?php |
||||
/** |
||||
* @author ASENSIO SYSTEM SIA |
||||
* |
||||
*/ |
||||
$strings["plugin_title"] = "ONLYOFFICE"; |
||||
$strings['plugin_comment'] = "ONLYOFFICE connector pozwala na przeglądanie, edytowanie oraz współpracę nad dokumentami tekstowymi, arkuszami kalkulacyjnymi i prezentacjami w Chamilo za pośrednictwem ONLYOFFICE Docs."; |
||||
$strings["enable_onlyoffice_plugin"] = "Włącz"; |
||||
$strings["document_server_url"] = "Adres ONLYOFFICE Docs"; |
||||
$strings["jwt_secret"] = "Klucz zabezpieczeń (pozostaw puste aby wyłączyć)"; |
||||
$strings["openByOnlyoffice"] = "Otwórz w ONLYOFFICE"; |
||||
$strings["createNew"] = "Utwórz nowy"; |
||||
$strings["title"] = "Tytuł"; |
||||
$strings["chooseFileFormat"] = "Wybierz format pliku"; |
||||
$strings["document"] = "Dokument"; |
||||
$strings["spreadsheet"] = "Arkusz kalkulacyjny"; |
||||
$strings["presentation"] = "Prezentacja"; |
||||
$strings["create"] = "Utwórz"; |
||||
$strings["fileIsExist"] = "Plik już istnieje"; |
||||
$strings["impossibleCreateFile"] = "Nie można utworzyć pliku"; |
||||
$strings["createNewDocument"] = "Utwórz nowy dokument"; |
||||
@ -0,0 +1,21 @@ |
||||
<?php |
||||
/** |
||||
* @author ASENSIO SYSTEM SIA |
||||
* |
||||
*/ |
||||
$strings["plugin_title"] = "ONLYOFFICE"; |
||||
$strings['plugin_comment'] = "ONLYOFFICE connector permite que você visualize, edite e colabore em documentos de texto, planilhas e apresentações dentro do Chamilo usando o ONLYOFFICE Docs."; |
||||
$strings["enable_onlyoffice_plugin"] = "Habilitar"; |
||||
$strings["document_server_url"] = "Endereço do ONLYOFFICE Docs"; |
||||
$strings["jwt_secret"] = "Chave secreta (deixe em branco para desativar)"; |
||||
$strings["openByOnlyoffice"] = "Abrir com ONLYOFFICE"; |
||||
$strings["createNew"] = "Criar novo"; |
||||
$strings["title"] = "Título"; |
||||
$strings["chooseFileFormat"] = "Escolher formato de arquivo"; |
||||
$strings["document"] = "Documento"; |
||||
$strings["spreadsheet"] = "Planilha"; |
||||
$strings["presentation"] = "Apresentação"; |
||||
$strings["create"] = "Criar"; |
||||
$strings["fileIsExist"] = "Arquivo já existe"; |
||||
$strings["impossibleCreateFile"] = "Impossível criar arquivo"; |
||||
$strings["createNewDocument"] = "Criar novo documento"; |
||||
@ -0,0 +1,21 @@ |
||||
<?php |
||||
/** |
||||
* @author ASENSIO SYSTEM SIA |
||||
* |
||||
*/ |
||||
$strings["plugin_title"] = "ONLYOFFICE"; |
||||
$strings['plugin_comment'] = "Коннектор ONLYOFFICE позволяет просматривать и редактировать текстовые документы, электронные таблицы и презентации и работать над ними совместно в Chamilo, используя ONLYOFFICE Docs."; |
||||
$strings["enable_onlyoffice_plugin"] = "Включить"; |
||||
$strings["document_server_url"] = "Адрес ONLYOFFICE Docs"; |
||||
$strings["jwt_secret"] = "Секретный ключ (оставьте пустым для отключения)"; |
||||
$strings["openByOnlyoffice"] = "Открыть с помощью ONLYOFFICE"; |
||||
$strings["createNew"] = "Создать новый"; |
||||
$strings["title"] = "Заголовок"; |
||||
$strings["chooseFileFormat"] = "Выбрать формат файла"; |
||||
$strings["document"] = "Документ "; |
||||
$strings["spreadsheet"] = "Таблица "; |
||||
$strings["presentation"] = "Презентация "; |
||||
$strings["create"] = "Создать "; |
||||
$strings["fileIsExist"] = "Файл уже существует"; |
||||
$strings["impossibleCreateFile"] = "Невозможно создать файл"; |
||||
$strings["createNewDocument"] = "Создать новый документ"; |
||||
@ -0,0 +1,21 @@ |
||||
<?php |
||||
/** |
||||
* @author ASENSIO SYSTEM SIA |
||||
* |
||||
*/ |
||||
$strings["plugin_title"] = "ONLYOFFICE"; |
||||
$strings['plugin_comment'] = "El conector de ONLYOFFICE le permite ver, editar y colaborar en documentos de texto, hojas de cálculo y presentaciones dentro de Chamilo utilizando ONLYOFFICE Docs."; |
||||
$strings["enable_onlyoffice_plugin"] = "Habilitar"; |
||||
$strings["document_server_url"] = "URL del servidor ONLYOFFICE Docs"; |
||||
$strings["jwt_secret"] = "Clave secreta (deje en blanco o desactive)"; |
||||
$strings["openByOnlyoffice"] = "Abrir con ONLYOFFICE"; |
||||
$strings["createNew"] = "Crear nuevo"; |
||||
$strings["title"] = "Título"; |
||||
$strings["chooseFileFormat"] = "Elegir el formato del archivo"; |
||||
$strings["document"] = "Documento"; |
||||
$strings["spreadsheet"] = "Hoja de cálculo"; |
||||
$strings["presentation"] = "Presentación"; |
||||
$strings["create"] = "Crear"; |
||||
$strings["fileIsExist"] = "El archivo ya existe"; |
||||
$strings["impossibleCreateFile"] = "No es posible crear el archivo"; |
||||
$strings["createNewDocument"] = "Crear documento nuevo"; |
||||
@ -0,0 +1,45 @@ |
||||
<?php |
||||
/** |
||||
* |
||||
* (c) Copyright Ascensio System SIA 2021 |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
require_once __DIR__ . "/../../../main/inc/global.inc.php"; |
||||
|
||||
class AppConfig { |
||||
|
||||
/** |
||||
* The config key for the jwt header |
||||
* |
||||
* @var string |
||||
*/ |
||||
private const jwtHeader = "onlyoffice_jwt_header"; |
||||
|
||||
/** |
||||
* Get the jwt header setting |
||||
* |
||||
* @return string |
||||
*/ |
||||
public static function JwtHeader() |
||||
{ |
||||
$header = api_get_configuration_value(self::jwtHeader); |
||||
if (empty($header)) { |
||||
$header = "Authorization"; |
||||
} |
||||
|
||||
return $header; |
||||
} |
||||
} |
||||
@ -0,0 +1,57 @@ |
||||
<?php |
||||
/** |
||||
* |
||||
* (c) Copyright Ascensio System SIA 2021 |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
require_once __DIR__ . "/../../../main/inc/global.inc.php"; |
||||
|
||||
class Crypt { |
||||
|
||||
/** |
||||
* Generate token for the object |
||||
* |
||||
* @param array $object - object to signature |
||||
* |
||||
* @return string |
||||
*/ |
||||
public static function GetHash($object) |
||||
{ |
||||
return \Firebase\JWT\JWT::encode($object, api_get_security_key()); |
||||
} |
||||
|
||||
/** |
||||
* Create an object from the token |
||||
* |
||||
* @param string $token - token |
||||
* |
||||
* @return array |
||||
*/ |
||||
public static function ReadHash($token) |
||||
{ |
||||
$result = null; |
||||
$error = null; |
||||
if ($token === null) { |
||||
return [$result, "token is empty"]; |
||||
} |
||||
try { |
||||
$result = \Firebase\JWT\JWT::decode($token, api_get_security_key(), array("HS256")); |
||||
} catch (\UnexpectedValueException $e) { |
||||
$error = $e->getMessage(); |
||||
} |
||||
return [$result, $error]; |
||||
} |
||||
} |
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue