Compare commits

...

151 Commits

Author SHA1 Message Date
c9baa474be python-raw: add sr-only 2023-08-11 18:42:23 +04:00
48185674d4 python-raw: up html 2023-08-11 18:42:12 +04:00
dbca11e85c python-raw: move js 2023-08-11 18:41:45 +04:00
d21a732859 python-raw: move css 2023-08-11 18:40:57 +04:00
b96a6fac53 python-raw: add js 2023-08-08 14:43:34 +04:00
b01af348a7 python-raw: add html 2023-08-08 14:43:17 +04:00
c2443d3106 python-raw: add styles 2023-08-08 14:42:56 +04:00
5fa3f4c57f python-raw: add formats endpoint 2023-08-08 14:41:58 +04:00
efd7e1b6e7 python-raw: add format methods 2023-08-08 14:41:20 +04:00
259ee63eea python-raw: temp hide server 2023-08-08 14:41:00 +04:00
453b9835b7 python-raw: fix formats 2023-08-08 14:40:27 +04:00
89e6544b79 Merge branch 'feature/python-matrix-formats' into feature/python-conversion 2023-08-04 12:31:53 +04:00
32a60c09aa python: add public and private document-server urls to the compose 2023-08-02 10:49:34 +04:00
5a03024cd3 python: add support for the internal network for the track endpoint 2023-08-02 10:49:34 +04:00
cb6416c481 python: sort imports 2023-08-02 10:49:34 +04:00
55e4247687 python: add tests for proxy manager 2023-08-02 10:49:34 +04:00
a0620905b2 python: add proxy manager to resolve the document-server url 2023-08-02 10:49:34 +04:00
3fcef4096a python: remove the strict CORS hint 2023-08-02 10:49:34 +04:00
4e5349c1c4 python: add tests for specifying public and private document-server urls 2023-08-02 10:49:34 +04:00
538eec1ff3 python: add support for specifying public and private document-server urls 2023-08-02 10:49:34 +04:00
df7cfda2fb Merge pull request #429 from ONLYOFFICE/fix/java-spring-save-previous
java-spring: add a step to save the previous version of the file during processing save
2023-07-31 15:37:55 +03:00
9ebef04f3d Merge pull request #415 from ONLYOFFICE/feature/ruby-docker
ruby: add support for containerization
2023-07-31 15:36:46 +03:00
a6ed970bca Merge pull request #413 from ONLYOFFICE/feature/python-docker
python: add support for containerization
2023-07-31 15:36:06 +03:00
7f659bc95e Merge pull request #428 from ONLYOFFICE/fix/php-lint-action
php: update php lint action to latest changes
2023-07-31 09:46:00 +03:00
3c4a1c1c31 Merge pull request #425 from ONLYOFFICE/feature/php-server-configuration
php: simplify server configuration
2023-07-29 15:42:55 +03:00
2bae0567ad nodejs: fix for ie (496a150620) 2023-07-28 17:26:53 +03:00
c06f2a5a29 python: replace the previous implementation of formats 2023-07-28 18:01:05 +04:00
8aed04753c python: add tests for the format module 2023-07-28 18:01:01 +04:00
15ed02e556 python: add format module 2023-07-28 12:06:30 +04:00
fb66dc8423 python: add tests for the codable module 2023-07-28 12:06:30 +04:00
541ace8132 python: add codable module 2023-07-28 12:06:30 +04:00
8b5cebeb3f python: add document-formats submodule 2023-07-28 12:06:30 +04:00
419fbf90f9 python: update paths to the new location of templates 2023-07-27 17:10:50 +04:00
2d637b88df python: move document-templates to the assets directory 2023-07-27 17:10:50 +04:00
13e2d5b33c python: add recipes to work with docker 2023-07-27 16:12:43 +04:00
f208fe1939 python: add docker files 2023-07-27 16:12:43 +04:00
b3cc35cb5e python: add support for the ADDRESS env variable 2023-07-27 16:12:43 +04:00
8b316b0786 python: move the lint recipe to the bottom of file 2023-07-27 16:12:43 +04:00
c57fcbfd90 python: delete recipes sorting in the help recipe 2023-07-27 16:12:43 +04:00
1939b85c8c ruby: disable strict hosts policy 2023-07-27 11:26:40 +04:00
ee0ac33820 ruby: add recipes to work with docker 2023-07-27 11:26:40 +04:00
c93579d98f ruby: add docker files 2023-07-27 11:26:40 +04:00
61a9e122cd ruby: move the lint recipe to the bottom of file 2023-07-27 11:26:40 +04:00
e88a9f5bed ruby: delete recipes sorting in the help recipe 2023-07-27 11:26:40 +04:00
f1ce0fb415 ruby: rename server recipes 2023-07-27 11:26:40 +04:00
3b142d92fc php: update urls to new endpoints 2023-07-26 17:42:48 +04:00
1898da660e php: simplify server configuration 2023-07-26 17:42:48 +04:00
0629cf0797 php: add HTTPStatus enum 2023-07-26 17:42:48 +04:00
7737670a6d php: delete IIS config 2023-07-26 17:42:48 +04:00
5bdcac42e3 php: add recipes to start the server 2023-07-26 17:42:48 +04:00
4b8ffa26cc php: add fpm config 2023-07-26 17:41:53 +04:00
a4b6fcb512 java-spring: add a step to save the previous version of the file during processing save 2023-07-26 17:34:07 +04:00
523b10c979 php: update php lint action to latest changes 2023-07-26 17:03:50 +04:00
1d4bc95c29 Merge pull request #424 from ONLYOFFICE/feature/php-configuration-manager
php: add support for configuration via environment variables
2023-07-26 15:31:05 +03:00
e94a4863aa php: replace json config with configuration manager 2023-07-26 12:07:58 +04:00
edda981ab7 php: append configuration directory to test recipe 2023-07-26 12:07:55 +04:00
65ebf9a5b1 php: add configuration manager tests 2023-07-26 12:07:55 +04:00
a081cd1f06 php: add configuration manager 2023-07-26 12:07:55 +04:00
04c5ee8cd7 php: add URL tests 2023-07-26 12:07:55 +04:00
3ebf3fabee php: add URL class 2023-07-26 12:07:52 +04:00
403efc1325 php: add tests for the path absolute function 2023-07-26 11:27:50 +04:00
7deedcaa5e php: enrich Path with new methods 2023-07-26 11:27:50 +04:00
161e26dd8a Merge pull request #423 from ONLYOFFICE/feature/php-missed-tests
php: add missed path tests and rewrite path methods a bit
2023-07-26 10:21:30 +03:00
09804349fa php: update path instances due to new methods 2023-07-24 14:22:28 +04:00
83a83f4f5d php: fix make test receipt 2023-07-24 14:22:28 +04:00
79ee987003 php: add path tests 2023-07-24 14:22:28 +04:00
434484cac3 php: separate path normalization from join 2023-07-24 14:22:25 +04:00
a0bafb0e4b php: simplify path string generation 2023-07-24 12:41:43 +04:00
184aded727 php: remove path separator from constructor options 2023-07-24 12:41:40 +04:00
7db708e559 Merge pull request #422 from ONLYOFFICE/feature/php-normalize-dependencies
php: pin and normalize existing dependencies, add new ones
2023-07-21 14:42:41 +03:00
3b3116f56e php: license link 2023-07-21 14:42:18 +03:00
15c70f43d7 Merge pull request #421 from ONLYOFFICE/feature/php-restore-endpoint
php: add restore endpoint
2023-07-21 09:49:00 +03:00
39af4be045 php: restore to changelog 2023-07-21 09:48:35 +03:00
c9dc4862ed php: add ignore file 2023-07-20 16:15:46 +04:00
122d41e307 php: add lint and test recieps 2023-07-20 16:15:44 +04:00
2880db7383 php: add install recipes 2023-07-20 16:14:24 +04:00
c0f23a4fc2 php: add makefile 2023-07-20 16:14:24 +04:00
1a49d8dd03 php: normalize codesniffer config file 2023-07-20 16:14:24 +04:00
180f287c07 php: delete composer scripts 2023-07-20 16:14:24 +04:00
37c33965c8 php: move autoload to the production section 2023-07-20 16:14:24 +04:00
0861732dbe php: replace local jwt with jwt as a dependency 2023-07-20 16:14:24 +04:00
d3220c7a91 php: add phpunit 2023-07-20 16:14:24 +04:00
450cafdae3 php: move mbstring extension to production dependencies 2023-07-20 16:14:24 +04:00
62abe3e569 php: pin dependency versions 2023-07-20 16:14:24 +04:00
8b2fbff3b7 php: update lock file 2023-07-20 16:14:24 +04:00
acf243ee3e php: add restore endpoint 2023-07-19 15:29:27 +04:00
66c38ded4a php: add the common module with Path class 2023-07-19 15:29:27 +04:00
58a73190d4 Merge pull request #420 from ONLYOFFICE/fix/php-assets-path
php: fix a new path to the assets directory
2023-07-19 09:41:21 +03:00
ae7a208831 php: fix a new path to the assets directory 2023-07-18 13:52:13 +04:00
2d83f3f39c Merge pull request #419 from ONLYOFFICE/feature/ruby-restore-endpoint
ruby: add restore endpoint
2023-07-18 12:20:46 +03:00
2aed1e3a83 ruby: restore to changelog 2023-07-18 12:19:57 +03:00
001a99b502 Merge pull request #418 from ONLYOFFICE/fix/ruby-sorbet-commands
ruby: hide sorbet commands for unsupported platforms
2023-07-18 12:18:26 +03:00
a7a9f997d5 ruby: add restore endpoint 2023-07-18 10:35:31 +04:00
4c3dc96e30 Merge pull request #417 from ONLYOFFICE/fix/ruby-config-manager-typo
ruby: fix typo in config manager name
2023-07-18 09:19:46 +03:00
7fe1ae27b4 Merge pull request #416 from ONLYOFFICE/feature/ruby-configuration-manager-tests
ruby: add missed configuration manager tests
2023-07-18 09:19:09 +03:00
dce840c627 ruby: hide sorbet commands for unsupported platforms 2023-07-17 18:50:17 +04:00
4f832ac2f7 ruby: fix typo in config manager name 2023-07-17 18:42:59 +04:00
e7623e29d7 ruby: add test recipe 2023-07-17 18:26:09 +04:00
16dd8a55e5 ruby: add missed configuration manager tests 2023-07-17 18:26:09 +04:00
dca68bf0d1 ruby: add missed imports 2023-07-17 18:26:09 +04:00
f028f1ba2f ruby: add test-unit as a dependency to satisfy rake 2023-07-17 18:26:09 +04:00
5c8867dd20 ruby: move the configuration to its directory 2023-07-17 18:26:09 +04:00
27eea60fc3 Merge pull request #414 from ONLYOFFICE/feature/python-restore-endpoint
python: add restore endpoint
2023-07-17 15:12:00 +03:00
c285b49d3b python: restore to changelog 2023-07-17 15:11:28 +03:00
70c86f3054 python: add onRequestRestore callback 2023-07-14 14:55:57 +04:00
00e5466a0c python: add restore endpoint 2023-07-14 14:55:57 +04:00
12a366a28a python: sort imports 2023-07-14 14:55:12 +04:00
b4918cf493 python: delete unused imports 2023-07-14 14:55:12 +04:00
b2b21980d1 python: add error response 2023-07-14 14:55:12 +04:00
09a97c0f66 python: add tests for the http functions 2023-07-14 14:55:12 +04:00
5db65ef062 python: add functions to declare the http method for the endpoint 2023-07-14 14:55:12 +04:00
5164380336 python: add a function to find a user by his ID 2023-07-14 14:55:12 +04:00
26c5cd0a83 python: remove unused import 2023-07-14 14:55:12 +04:00
bff8c83283 Merge pull request #410 from ONLYOFFICE/feature/python-server-configuration
python: simplify server configuration
2023-07-14 10:43:29 +03:00
827bd3981f python: add missed django_settings_module 2023-07-13 17:10:10 +04:00
e8599c7c56 python: add recipes to start the server 2023-07-13 17:10:06 +04:00
0a3b951eb6 Merge pull request #412 from ONLYOFFICE/feature/ruby-respect-gitignore
ruby: respect gitignore
2023-07-13 15:25:59 +03:00
988da27e3f python: simplify server configuration 2023-07-13 16:23:09 +04:00
a697219dcf ruby: add receipt stages to generate bin files 2023-07-13 16:04:54 +04:00
7649e7f51e ruby: delete files that should be ignored 2023-07-13 16:02:24 +04:00
108b5ea317 ruby: move example-specific ignored files to the root of the example 2023-07-13 16:02:24 +04:00
4ea0760803 Merge pull request #411 from ONLYOFFICE/feature/ruby-server-configuration
ruby: simplify server configuration
2023-07-13 14:51:07 +03:00
c558098deb ruby: delete sqlite3 2023-07-13 14:48:57 +03:00
4a61093343 Merge pull request #409 from ONLYOFFICE/feature/python-configuration-manager
python: add support for configuration via environment variables
2023-07-13 14:46:34 +03:00
2af1f56aa7 ruby: delete sqlite3 as it's not used 2023-07-13 14:02:30 +04:00
6780feafa6 ruby: add recipes to start the server 2023-07-13 14:02:26 +04:00
23b4ffc104 ruby: simplify server configuration 2023-07-12 13:44:20 +04:00
46a9fffb0e 7.4 formats 2023-07-12 11:46:15 +03:00
0ccc98e83f python: standardize the format of URLs 2023-07-10 16:29:59 +04:00
7f59c15112 python: replace the global config with the configuration manager 2023-07-10 16:29:59 +04:00
455f2ee823 python: add tests for the configuration manager 2023-07-10 16:29:51 +04:00
3076a9bc0e python: add configuration manager 2023-07-10 16:05:30 +04:00
c605607b5e python: add test recipe 2023-07-10 14:52:21 +04:00
e05afc931a python: add tests for the string boolean function 2023-07-10 14:49:25 +04:00
53aeb19e2f python: add the common module with string boolean function 2023-07-10 14:46:33 +04:00
14a557be69 Merge pull request #396 from ONLYOFFICE/feature/ruby-configuration-environment
ruby: support for configuration via environment variables
2023-07-10 10:28:24 +03:00
666b6f8a03 Merge pull request #408 from ONLYOFFICE/fix/ruby-sorbet-platforms
ruby: fix sorbet installation on unsupported platforms
2023-07-07 15:33:10 +03:00
ebddc8114a ruby: fix sorbet installation on unsupported platforms 2023-07-05 12:26:00 +04:00
fa76819b2d Merge pull request #403 from ONLYOFFICE/feature/python-normalize-dependencies
python: pin and add new dependencies
2023-07-05 10:02:00 +03:00
69aa25880a python: 3rd party licenses 2023-07-05 10:00:21 +03:00
7cd79280ff python: add help, dev, lint, prod recipes to Makefile 2023-07-04 16:57:03 +04:00
4535bfe661 python: add development dependencies 2023-07-04 16:56:57 +04:00
01cabdf1e5 python: pin python version 2023-07-04 14:12:11 +04:00
8b15a0e417 python: pin dependency versions 2023-07-04 14:12:11 +04:00
451b3b7a38 ruby: standardize the format of the URIs in the configuration 2023-07-04 13:52:40 +04:00
952b495c4d ruby: support for configuration via environment variables 2023-07-04 13:52:37 +04:00
1cd3642de8 Merge pull request #407 from ONLYOFFICE/feature/reference-source
Feature/reference source
2023-07-04 12:49:58 +03:00
a4f49cb86b nodejs: change reference source to changelog 2023-07-04 12:45:15 +03:00
9ca722329f nodejs: 1.6.0 (50a4ddbd11) 2023-07-04 10:51:24 +03:00
496a150620 nodejs: onRequestReferenceSource method 2023-06-30 11:29:38 +03:00
983a92d9e0 nodejs: function for getting of file extension 2023-06-30 11:27:48 +03:00
181 changed files with 8716 additions and 1959 deletions

View File

@ -25,7 +25,5 @@ jobs:
php-version: '8.2'
tools: cs2pr, phpcs
- name: Run phpcs
run: |
phpcs --version
phpcs -q --extensions=php,module,inc,install,test,profile,theme,info --ignore=node_modules,bower_components,vendor,css,js,lib --standard=./ruleset.xml ./
- name: Lint
run: phpcs .

5
.gitignore vendored
View File

@ -22,8 +22,3 @@
/web/documentserver-example/csharp/packages
/web/documentserver-example/csharp-mvc/packages
/web/documentserver-example/java-spring/documents/
/web/documentserver-example/ruby/.bundle
/web/documentserver-example/ruby/db/*.sqlite3
/web/documentserver-example/ruby/db/*.sqlite3-journal
/web/documentserver-example/ruby/log/*
/web/documentserver-example/ruby/tmp

12
.gitmodules vendored
View File

@ -2,10 +2,6 @@
path = web/documentserver-example/java/src/main/resources/assets
url = https://github.com/ONLYOFFICE/document-templates
branch = main/en
[submodule "web/documentserver-example/python/assets"]
path = web/documentserver-example/python/assets
url = https://github.com/ONLYOFFICE/document-templates
branch = main/en
[submodule "web/documentserver-example/csharp-mvc/assets"]
path = web/documentserver-example/csharp-mvc/assets
url = https://github.com/ONLYOFFICE/document-templates
@ -38,3 +34,11 @@
path = web/documentserver-example/php/assets/document-formats
url = https://github.com/ONLYOFFICE/document-formats
branch = master
[submodule "web/documentserver-example/python/assets/document-templates"]
path = web/documentserver-example/python/assets/document-templates
url = https://github.com/ONLYOFFICE/document-templates
branch = main/en
[submodule "web/documentserver-example/python/assets/document-formats"]
path = web/documentserver-example/python/assets/document-formats
url = https://github.com/ONLYOFFICE/document-formats
branch = master

View File

@ -303,13 +303,25 @@ PHP_CodeSniffer - PHP_CodeSniffer is a set of two PHP scripts; the main phpcs sc
License: BSD-3-Clause
License File: PHP_CodeSniffer.license
PHPUnit - The PHP Unit Testing framework. (https://github.com/sebastianbergmann/phpunit/blob/main/LICENSE)
License: BSD 3-Clause
License File: phpunit.license
web/documentserver-example/python
django-stubs - PEP-484 stubs for Django. (https://github.com/typeddjango/django-stubs/blob/master/LICENSE.md)
License: MIT
License File: django-stubs.license
Django - Django is a high-level Python web framework that encourages rapid development and clean, pragmatic design. Thanks for checking it out. (https://github.com/django/django/blob/main/LICENSE)
License: BSD-3-Clause
License File: Django.license
flake8 - flake8 is a python tool that glues together pycodestyle, pyflakes, mccabe, and third-party plugins to check the style and quality of some python code. (https://github.com/PyCQA/flake8/blob/main/LICENSE)
License: MIT
License File: flake8.license
jQuery.BlockUI - The jQuery BlockUI Plugin lets you simulate synchronous behavior when using AJAX, without locking the browser. (https://github.com/malsup/blockui/)
License: MIT, GPL
License File: jQuery.BlockUI.license
@ -334,6 +346,10 @@ jQuery.UI - jQuery UI is an open source library of interface components —
License: MIT
License File: jQuery.UI.license
mypy - Optional static typing for Python. (https://github.com/python/mypy/blob/master/LICENSE)
License: MIT
License File: mypy.license
PyJWT - A Python implementation of RFC 7519. (https://github.com/jpadilla/pyjwt/blob/master/LICENSE)
License: MIT
License File: PyJWT.license
@ -346,6 +362,10 @@ requests - Requests allows you to send HTTP/1.1 requests extremely easily.
License: Apache 2.0
License File: requests.license
typeshed - Collection of library stubs for Python, with static types. (https://github.com/python/typeshed/blob/main/LICENSE)
License: Apache 2.0
License File: typeshed.license
web/documentserver-example/ruby
@ -417,10 +437,6 @@ sorbet - A fast, powerful type checker designed for Ruby. (https://github
License: Apache License 2.0
License File: sorbet.license
sqlite3 - This module allows Ruby programs to interface with the SQLite3 database engine (www.sqlite.org). (https://github.com/sparklemotion/sqlite3-ruby/blob/master/LICENSE)
License: BSD-3-Clause
License File: sqlite3.license
tapioca - The swiss army knife of RBI generation. (https://github.com/Shopify/tapioca/blob/v0.11.6/LICENSE.txt)
License: MIT
License File: tapioca.license

View File

@ -1,10 +1,14 @@
# Change Log
- nodejs: change reference source
- php: using a repo with a list of formats
- nodejs: using a repo with a list of formats
- nodejs: delete file without reloading the page
- nodejs: getting history by a separate request
- nodejs: restore from history
- python: restore from history
- ruby: restore from history
- php: restore from history
## 1.6.0
- nodejs: setUsers for region protection

View File

@ -163,6 +163,8 @@ public class DefaultCallbackManager implements CallbackManager {
storageMutator.createDirectory(ver); // create the file version directory
lastVersion.toFile().renameTo(new File(versionDir + File.separator + "prev" + curExt));
saveFile(byteArrayFile, toSave); // save document file
byte[] byteArrayChanges = getDownloadFile(changesUri); // download file changes

View File

@ -1,6 +1,6 @@
{
"name": "OnlineEditorsExampleNodeJS",
"version": "4.1.0",
"version": "1.6.0",
"private": false,
"scripts": {
"start": "node ./bin/www",

View File

@ -227,6 +227,53 @@
}
};
var onRequestReferenceSource = function (event) {
innerAlert("onRequestReferenceSource");
let xhr = new XMLHttpRequest();
xhr.open("GET", "files");
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send();
xhr.onload = function () {
if (xhr.status === 200) {
innerAlert(JSON.parse(xhr.responseText));
let fileList = JSON.parse(xhr.responseText);
let firstXlsxName;
let file;
for (var i = 0; i < fileList.length; i++) {
file = fileList[i];
if (file["title"]) {
if (getFileExt(file["title"]) === "xlsx")
{
firstXlsxName = file["title"];
break;
}
}
}
if (firstXlsxName) {
let data = {
directUrl : "<%- file.directUrl %>" || false,
path : firstXlsxName
};
let xhr = new XMLHttpRequest();
xhr.open("POST", "reference");
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(JSON.stringify(data));
xhr.onload = function () {
if (xhr.status === 200) {
docEditor.setReferenceSource(JSON.parse(xhr.responseText));
} else {
innerAlert("/reference - bad status");
}
}
} else {
innerAlert("No *.xlsx files");
}
} else {
innerAlert("/files - bad status");
}
}
};
var onRequestSaveAs = function (event) { // the user is trying to save file by clicking Save Copy as... button
var title = event.data.title;
var url = event.data.url;
@ -287,6 +334,7 @@
config.events.onRequestUsers = onRequestUsers;
config.events.onRequestSendNotify = onRequestSendNotify;
config.events.onRequestReferenceData = onRequestReferenceData;
config.events.onRequestReferenceSource = onRequestReferenceSource;
}
if (config.editorConfig.createUrl) {
@ -326,6 +374,13 @@
}
};
const getFileExt = function (fileName) {
if (fileName.indexOf(".")) {
return fileName.split('.').reverse()[0];
}
return false;
};
if (window.addEventListener) {
window.addEventListener("load", connectEditor);
window.addEventListener("resize", fixSize);

View File

@ -0,0 +1,2 @@
logs
vendor

View File

@ -0,0 +1 @@
8.1.21

View File

@ -31,3 +31,7 @@ License File: jwt.license
PHP_CodeSniffer - PHP_CodeSniffer is a set of two PHP scripts; the main phpcs script that tokenizes PHP, JavaScript and CSS files to detect violations of a defined coding standard, and a second phpcbf script to automatically correct coding standard violations. PHP_CodeSniffer is an essential development tool that ensures your code remains clean and consistent. (https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt)
License: BSD-3-Clause
License File: PHP_CodeSniffer.license
PHPUnit - The PHP Unit Testing framework. (https://github.com/sebastianbergmann/phpunit/blob/main/LICENSE)
License: BSD 3-Clause
License File: phpunit.license

View File

@ -0,0 +1,80 @@
.DEFAULT_GOAL := help
ADDRESS := $(ADDRESS)
PORT := $(PORT)
.PHONY: help
help: # Show help message for each of the Makefile recipes.
@grep -E "^[a-z-]+: #" $(MAKEFILE_LIST) | \
awk 'BEGIN {FS = ": # "}; {printf "%s: %s\n", $$1, $$2}'
.PHONY: dev
dev: # Install development dependencies.
@composer install
.PHONY: prod
prod: # Install production dependencies.
@composer install --no-dev
ifeq ($(ADDRESS),)
server-dev: \
export ADDRESS := localhost
else
server-dev: \
export ADDRESS := $(ADDRESS)
endif
ifeq ($(PORT),)
server-dev: \
export PORT := 9000
else
server-dev: \
export PORT := $(PORT)
endif
.PHONY: server-dev
server-dev: # Start the development server on localhost at $PORT (default: 9000).
@php --server $(ADDRESS):$(PORT)
ifeq ($(ADDRESS),)
server-prod: \
export ADDRESS := 0.0.0.0
else
server-prod: \
export ADDRESS := $(ADDRESS)
endif
ifeq ($(PORT),)
server-prod: \
export PORT := 9000
else
server-prod: \
export PORT := $(PORT)
endif
.PHONY: server-prod
server-prod: # Start the production server on 0.0.0.0 at $PORT (default: 9000).
@php-fpm --fpm-config php-fpm.conf
.PHONY: lint
lint: # Lint the source code for the style.
@./vendor/bin/phpcs .
.PHONY: test
test: # Run tests recursively.
@./vendor/bin/phpunit \
--test-suffix "Tests.php" \
--display-incomplete \
--display-deprecations \
--display-errors \
--display-notices \
--display-warnings \
common
@./vendor/bin/phpunit \
--test-suffix "Tests.php" \
--display-incomplete \
--display-deprecations \
--display-errors \
--display-notices \
--display-warnings \
configuration

View File

@ -18,6 +18,8 @@
namespace OnlineEditorsExamplePhp;
use Exception;
use OnlineEditorsExamplePhp\Common\Path;
use OnlineEditorsExamplePhp\Configuration\ConfigurationManager;
use OnlineEditorsExamplePhp\Helpers\ConfigManager;
use OnlineEditorsExamplePhp\Helpers\ExampleUsers;
use OnlineEditorsExamplePhp\Helpers\JwtManager;
@ -47,41 +49,6 @@ function getHttpOrigin()
return $origin;
}
/**
* Set headers that prevent caching in all the browsers
*
* @return void
*/
function nocacheHeaders()
{
$headers = [
'Expires' => 'Wed, 11 Jan 1984 05:00:00 GMT',
'Cache-Control' => 'no-cache, must-revalidate, max-age=0',
'Pragma' => 'no-cache',
];
$headers['Last-Modified'] = false;
unset($headers['Last-Modified']);
// In PHP 5.3+, make sure we are not sending a Last-Modified header.
if (function_exists('header_remove')) {
@header_remove('Last-Modified');
} else {
// In PHP 5.2, send an empty Last-Modified header, but only as a
// last resort to override a header already sent. #WP23021
foreach (headers_list() as $header) {
if (0 === mb_stripos($header, 'Last-Modified')) {
$headers['Last-Modified'] = '';
break;
}
}
}
foreach ($headers as $name => $field_value) {
@header("{$name}: {$field_value}");
}
}
/**
* Save copy as...
*
@ -90,6 +57,8 @@ function nocacheHeaders()
function saveas()
{
try {
$config_manager = new ConfigurationManager();
$post = json_decode(file_get_contents('php://input'), true);
$fileurl = $post["url"];
$title = $post["title"];
@ -106,8 +75,7 @@ function saveas()
$content_length = $headers["Content-Length"];
$data = file_get_contents(str_replace(" ", "%20", $fileurl));
if ($data === false || $content_length <= 0 || $content_length >
$configManager->getConfig("fileSizeMax")) {
if ($data === false || $content_length <= 0 || $content_length > $config_manager->maximum_file_size()) {
$result["error"] = "File size is incorrect";
return $result;
}
@ -133,6 +101,8 @@ function saveas()
*/
function upload()
{
$config_manager = new ConfigurationManager();
$configManager = new ConfigManager();
if ($_FILES['files']['error'] > 0) {
$result["error"] = 'Error ' . json_encode($_FILES['files']['error']);
@ -154,7 +124,7 @@ function upload()
$ext = mb_strtolower(pathinfo($_FILES['files']['name'], PATHINFO_EXTENSION)); // get file extension
// check if the file size is correct (it should be less than the max file size, but greater than 0)
if ($filesize <= 0 || $filesize > $configManager->getConfig("fileSizeMax")) {
if ($filesize <= 0 || $filesize > $config_manager->maximum_file_size()) {
$result["error"] = 'File size is incorrect'; // if not, then an error occurs
return $result;
}
@ -203,7 +173,15 @@ function track()
return $data;
}
global $_trackerStatus;
$_trackerStatus = [
0 => 'NotFound',
1 => 'Editing',
2 => 'MustSave',
3 => 'Corrupted',
4 => 'Closed',
6 => 'MustForceSave',
7 => 'CorruptedForceSave',
];
$status = $_trackerStatus[$data->status]; // get status from the request body
$userAddress = $_GET["userAddress"];
@ -256,9 +234,8 @@ function convert()
$fileUri = $post["fileUri"];
if ($fileUri == null || $fileUri == "") {
$fileUri = serverPath(true) . '/'
. "webeditor-ajax.php"
. "?type=download"
. "&fileName=" . urlencode($fileName)
. "download"
. "?fileName=" . urlencode($fileName)
. "&userAddress=" . getClientIp();
}
$key = getDocEditorKey($fileName);
@ -391,6 +368,8 @@ function csv()
function historyDownload()
{
try {
$config_manager = new ConfigurationManager();
$fileName = basename($_GET["fileName"]); // get the file name
$userAddress = $_GET["userAddress"];
@ -400,8 +379,7 @@ function historyDownload()
$jwtManager = new JwtManager();
if ($jwtManager->isJwtEnabled()) {
$configManager = new ConfigManager();
$jwtHeader = $configManager->getConfig("docServJwtHeader") == "" ?
"Authorization" : $configManager->getConfig("docServJwtHeader");
$jwtHeader = $config_manager->jwt_header();
if (!empty(apache_request_headers()[$jwtHeader])) {
$token = $jwtManager->jwtDecode(mb_substr(
apache_request_headers()[$jwtHeader],
@ -438,17 +416,15 @@ function historyDownload()
function download()
{
try {
$configManager = new ConfigManager();
$fileName = realpath($configManager->getConfig("storagePath"))
=== $configManager->getConfig("storagePath") ?
$_GET["fileName"] : basename($_GET["fileName"]); // get the file name
$config_manager = new ConfigurationManager();
$fileName = $_GET["fileName"];
$userAddress = $_GET["userAddress"] ?? null;
$isEmbedded = $_GET["&dmode"] ?? null;
$jwtManager = new JwtManager();
if ($jwtManager->isJwtEnabled() && $isEmbedded == null && $userAddress) {
$jwtHeader = $configManager->getConfig("docServJwtHeader") == "" ?
"Authorization" : $configManager->getConfig("docServJwtHeader");
$jwtHeader = $config_manager->jwt_header();
if (!empty(apache_request_headers()[$jwtHeader])) {
$token = $jwtManager->jwtDecode(mb_substr(
apache_request_headers()[$jwtHeader],
@ -605,3 +581,74 @@ function reference()
return $data;
}
function restore()
{
try {
$input = file_get_contents('php://input');
$body = json_decode($input);
$source_basename = $body->fileName;
$version = $body->version;
$user_id = $body->userId;
$source_file = getStoragePath($source_basename);
$history_directory = getHistoryDir($source_file);
$bumped_version = getFileVersion($history_directory);
$bumped_version_string_directory = getVersionDir($history_directory, $bumped_version);
if (!file_exists($bumped_version_string_directory)) {
mkdir($bumped_version_string_directory);
}
$bumped_version_directory = new Path($bumped_version_string_directory);
$bumped_key_file = $bumped_version_directory->join_path('key.txt');
$bumped_key_string_file = $bumped_key_file->string();
$bumped_key = getDocEditorKey($source_basename);
file_put_contents($bumped_key_string_file, $bumped_key, LOCK_EX);
$users = new ExampleUsers();
$user = $users->getUser($user_id);
$bumped_changes_file = $bumped_version_directory->join_path('changes.json');
$bumped_changes_string_file = $bumped_changes_file->string();
$bumped_changes = [
'serverVersion' => null,
'changes' => array(
[
'created' => date('Y-m-d H:i:s'),
'user' => [
'id' => $user->id,
'name' => $user->name
]
]
)
];
$bumped_changes_content = json_encode($bumped_changes, JSON_PRETTY_PRINT);
file_put_contents($bumped_changes_string_file, $bumped_changes_content, LOCK_EX);
$source_extension = pathinfo($source_basename, PATHINFO_EXTENSION);
$previous_basename = "prev.{$source_extension}";
$bumped_file = $bumped_version_directory->join_path($previous_basename);
$bumped_string_file = $bumped_file->string();
copy($source_file, $bumped_string_file);
$recovery_version_string_directory = getVersionDir($history_directory, $version);
$recovery_version_directory = new Path($recovery_version_string_directory);
$recovery_file = $recovery_version_directory->join_path($previous_basename);
$recovery_string_file = $recovery_file->string();
copy($recovery_string_file, $source_file);
return [
'error' => null,
'success' => true
];
} catch (Exception $error) {
$message = $error->getMessage();
return [
'error' => $message,
'success' => false
];
}
}

View File

@ -0,0 +1,22 @@
<?php
//
// (c) Copyright Ascensio System SIA 2023
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
namespace OnlineEditorsExamplePhp\Common;
enum HTTPStatus: int {
case not_found = 404;
}

View File

@ -0,0 +1,127 @@
<?php
//
// (c) Copyright Ascensio System SIA 2023
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
namespace OnlineEditorsExamplePhp\Common;
final class Path {
private string $separator = DIRECTORY_SEPARATOR;
private string $string;
public function __construct(string $path) {
$this->string = $path;
}
public function string(): string {
return $this->string;
}
public function dirname(): ?string {
$string = $this->string();
$parsed = pathinfo($string, PATHINFO_DIRNAME);
return $parsed ?: null;
}
public function basename(): ?string {
$string = $this->string();
$parsed = pathinfo($string, PATHINFO_BASENAME);
return $parsed ?: null;
}
public function extension(): ?string {
$string = $this->string();
$parsed = pathinfo($string, PATHINFO_EXTENSION);
return $parsed ?: null;
}
public function filename(): ?string {
$string = $this->string();
$parsed = pathinfo($string, PATHINFO_FILENAME);
return $parsed ?: null;
}
public function normalize(): self {
$string = $this->string();
$filtered = array();
$slugs = explode($this->separator, $string);
foreach ($slugs as $slug) {
if ($slug === '.') {
continue;
}
if ($slug === '..') {
array_pop($filtered);
continue;
}
$filtered[] = $slug;
}
$joined = implode($this->separator, $filtered);
$escaped_separator = preg_quote($this->separator, $this->separator);
$separator_regex = "/{$escaped_separator}{2,}/";
$separated = preg_replace($separator_regex, $this->separator, $joined);
return new Path($separated);
}
public function join_path(string $path): self {
$string = $this->string();
$separator =
str_ends_with($string, $this->separator) ||
str_starts_with($path, $this->separator)
? ''
: $this->separator;
return new Path("{$string}{$separator}{$path}");
}
public function absolute(): bool {
$string = $this->string();
$nt_regex = '/^[A-Za-z]:\\\\/';
if (preg_match($nt_regex, $string)) {
return true;
}
$nt_separator = '\\';
if (str_starts_with($string, $nt_separator)) {
return true;
}
$posix_separator = '/';
if (str_starts_with($string, $posix_separator)) {
return true;
}
return false;
}
public function exists(): bool {
$string = $this->string();
return file_exists($string);
}
public function contents(): string {
$string = $this->string();
return file_get_contents($string);
}
public function directory(): bool {
$string = $this->string();
return is_dir($string);
}
public function make_directory(): bool {
$string = $this->string();
return mkdir($string);
}
}

View File

@ -0,0 +1,39 @@
<?php
//
// (c) Copyright Ascensio System SIA 2023
//
// 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.
//
use PHPUnit\Framework\TestCase;
use OnlineEditorsExamplePhp\Common\Path;
final class PathAbsolutePOSIXTests extends TestCase {
public function test_recognizes_an_empty_as_a_non_absolute() {
$path = new Path('');
$absolute = $path->absolute();
$this->assertFalse($absolute);
}
public function test_recognizes_a_relative_as_a_non_absolute() {
$path = new Path('.');
$absolute = $path->absolute();
$this->assertFalse($absolute);
}
public function test_recognizes_an_absolute_as_an_absolute() {
$path = new Path('/');
$absolute = $path->absolute();
$this->assertTrue($absolute);
}
}

View File

@ -0,0 +1,84 @@
<?php
//
// (c) Copyright Ascensio System SIA 2023
//
// 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.
//
use PHPUnit\Framework\TestCase;
use OnlineEditorsExamplePhp\Common\Path;
final class PathJoinPOSIXTests extends TestCase {
public function test_joins_a_relative_to_an_empty_one() {
$path = new Path('');
$joined = $path->join_path('srv');
$this->assertEquals($joined->dirname(), '/');
$this->assertEquals($joined->basename(), 'srv');
$this->assertEquals($joined->extension(), null);
$this->assertEquals($joined->filename(), 'srv');
}
public function test_joins_a_relative_to_a_relative_one() {
$path = new Path('.');
$joined = $path->join_path('srv');
$this->assertEquals($joined->dirname(), '.');
$this->assertEquals($joined->basename(), 'srv');
$this->assertEquals($joined->extension(), null);
$this->assertEquals($joined->filename(), 'srv');
}
public function test_joins_a_relative_to_an_absolute_one() {
$path = new Path('/');
$joined = $path->join_path('srv');
$this->assertEquals($joined->dirname(), '/');
$this->assertEquals($joined->basename(), 'srv');
$this->assertEquals($joined->extension(), null);
$this->assertEquals($joined->filename(), 'srv');
}
public function test_joins_an_absolute_to_an_empty_one() {
$path = new Path('');
$joined = $path->join_path('/srv');
$this->assertEquals($joined->dirname(), '/');
$this->assertEquals($joined->basename(), 'srv');
$this->assertEquals($joined->extension(), null);
$this->assertEquals($joined->filename(), 'srv');
}
public function test_joins_an_absolute_to_a_relative_one() {
$path = new Path('.');
$joined = $path->join_path('/srv');
$this->assertEquals($joined->dirname(), '.');
$this->assertEquals($joined->basename(), 'srv');
$this->assertEquals($joined->extension(), null);
$this->assertEquals($joined->filename(), 'srv');
}
public function test_joins_an_absolute_to_an_absolute_one() {
$path = new Path('/');
$joined = $path->join_path('/srv');
$this->assertEquals($joined->dirname(), '/');
$this->assertEquals($joined->basename(), 'srv');
$this->assertEquals($joined->extension(), null);
$this->assertEquals($joined->filename(), 'srv');
}
public function test_joins_an_unnormalized() {
$path = new Path('');
$joined = $path->join_path('../srv');
$this->assertEquals($joined->dirname(), '/..');
$this->assertEquals($joined->basename(), 'srv');
$this->assertEquals($joined->extension(), null);
$this->assertEquals($joined->filename(), 'srv');
}
}

View File

@ -0,0 +1,30 @@
<?php
//
// (c) Copyright Ascensio System SIA 2023
//
// 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.
//
use PHPUnit\Framework\TestCase;
use OnlineEditorsExamplePhp\Common\Path;
final class PathNormalizePOSIXTests extends TestCase {
public function test_normalizes() {
$path = new Path('./srv///sub/.././sub/file.docx');
$normalized = $path->normalize();
$this->assertEquals($normalized->dirname(), 'srv/sub');
$this->assertEquals($normalized->basename(), 'file.docx');
$this->assertEquals($normalized->extension(), 'docx');
$this->assertEquals($normalized->filename(), 'file');
}
}

View File

@ -0,0 +1,150 @@
<?php
//
// (c) Copyright Ascensio System SIA 2023
//
// 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.
//
use PHPUnit\Framework\TestCase;
use OnlineEditorsExamplePhp\Common\Path;
final class PathStringPOSIXTests extends TestCase {
public function test_generates_with_an_empty() {
$path = new Path('');
$string = $path->string();
$this->assertEquals($string, '');
}
public function test_generates_with_an_empty_relative() {
$path = new Path('.');
$string = $path->string();
$this->assertEquals($string, '.');
}
public function test_generates_with_an_empty_absolute() {
$path = new Path('/');
$string = $path->string();
$this->assertEquals($string, '/');
}
public function test_generates_with_a_relative() {
$path = new Path('srv');
$string = $path->string();
$this->assertEquals($string, 'srv');
}
public function test_generates_with_a_relative_containing_a_directory() {
$path = new Path('srv/sub');
$string = $path->string();
$this->assertEquals($string, 'srv/sub');
}
public function test_generates_with_a_relative_containing_a_file() {
$path = new Path('srv/file.json');
$string = $path->string();
$this->assertEquals($string, 'srv/file.json');
}
public function test_generates_with_a_relative_containing_a_directory_with_a_file() {
$path = new Path('srv/sub/file.json');
$string = $path->string();
$this->assertEquals($string, 'srv/sub/file.json');
}
public function test_generates_with_an_unnormalized_relative() {
$path = new Path('srv////sub///file.json');
$string = $path->string();
$this->assertEquals($string, 'srv////sub///file.json');
}
public function test_generates_with_an_normalized_relative() {
$path = new Path('srv////sub///file.json');
$normalized = $path->normalize();
$string = $normalized->string();
$this->assertEquals($string, 'srv/sub/file.json');
}
public function test_generates_with_an_explicit_relative() {
$path = new Path('./srv');
$string = $path->string();
$this->assertEquals($string, './srv');
}
public function test_generates_with_an_explicit_relative_containing_a_directory() {
$path = new Path('./srv/sub');
$string = $path->string();
$this->assertEquals($string, './srv/sub');
}
public function test_generates_with_an_explicit_relative_containing_a_file() {
$path = new Path('./srv/file.json');
$string = $path->string();
$this->assertEquals($string, './srv/file.json');
}
public function test_generates_with_an_explicit_relative_containing_a_directory_with_a_file() {
$path = new Path('./srv/sub/file.json');
$string = $path->string();
$this->assertEquals($string, './srv/sub/file.json');
}
public function test_generates_with_an_explicit_unnormalized_relative() {
$path = new Path('./srv////sub///file.json');
$string = $path->string();
$this->assertEquals($string, './srv////sub///file.json');
}
public function test_generates_with_an_explicit_normalized_relative() {
$path = new Path('./srv////sub///file.json');
$normalized = $path->normalize();
$string = $normalized->string();
$this->assertEquals($string, 'srv/sub/file.json');
}
public function test_generates_with_an_absolute() {
$path = new Path('/srv');
$string = $path->string();
$this->assertEquals($string, '/srv');
}
public function test_generates_with_an_absolute_containing_a_directory() {
$path = new Path('/srv/sub');
$string = $path->string();
$this->assertEquals($string, '/srv/sub');
}
public function test_generates_with_an_absolute_containing_a_file() {
$path = new Path('/srv/file.json');
$string = $path->string();
$this->assertEquals($string, '/srv/file.json');
}
public function test_generates_with_an_absolute_containing_a_directory_with_a_file() {
$path = new Path('/srv/sub/file.json');
$string = $path->string();
$this->assertEquals($string, '/srv/sub/file.json');
}
public function test_generates_with_an_unnormalized_absolute() {
$path = new Path('/srv////sub///file.json');
$string = $path->string();
$this->assertEquals($string, '/srv////sub///file.json');
}
public function test_generates_with_an_normalized_absolute() {
$path = new Path('/srv////sub///file.json');
$normalized = $path->normalize();
$string = $normalized->string();
$this->assertEquals($string, '/srv/sub/file.json');
}
}

View File

@ -0,0 +1,132 @@
<?php
//
// (c) Copyright Ascensio System SIA 2023
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
namespace OnlineEditorsExamplePhp\Common;
final class URL {
private string $string;
public function __construct(string $url) {
$this->string = $url;
}
public function string(): string {
return $this->string;
}
public function scheme(): ?string {
$string = $this->string();
return parse_url($string, PHP_URL_SCHEME) ?: null;
}
public function host(): ?string {
$string = $this->string();
return parse_url($string, PHP_URL_HOST) ?: null;
}
public function port(): ?int {
$string = $this->string();
return parse_url($string, PHP_URL_PORT) ?: null;
}
public function user(): ?string {
$string = $this->string();
return parse_url($string, PHP_URL_USER) ?: null;
}
public function pass(): ?string {
$string = $this->string();
return parse_url($string, PHP_URL_PASS) ?: null;
}
public function path(): ?string {
$string = $this->string();
return parse_url($string, PHP_URL_PATH) ?: null;
}
public function query(): ?string {
$string = $this->string();
return parse_url($string, PHP_URL_QUERY) ?: null;
}
public function fragment(): ?string {
$string = $this->string();
return parse_url($string, PHP_URL_FRAGMENT) ?: null;
}
public static function from_components(
?string $scheme,
?string $host,
?int $port,
?string $user,
?string $pass,
?string $path,
?string $query,
?string $fragment
): URL {
$string = '';
if ($scheme) {
$string .= "{$scheme}://";
}
if ($user) {
$string .= $user;
}
if ($pass) {
$string .= ":{$pass}@";
}
if ($host) {
$string .= $host;
}
if ($port) {
$string .= ":{$port}";
}
if ($path) {
$string .= $path;
}
if ($query) {
$string .= "?{$query}";
}
if ($fragment) {
$string .= "#{$fragment}";
}
return new URL($string);
}
public function join_path(string $path): self {
$current_path = $this->path();
$separator =
$current_path &&
(
str_ends_with($current_path, '/') ||
str_starts_with($path, '/')
) ||
!$current_path && str_starts_with($path, '/')
? ''
: '/';
$separated = "{$current_path}{$separator}{$path}";
return URL::from_components(
$this->scheme(),
$this->host(),
$this->port(),
$this->user(),
$this->pass(),
$separated,
$this->query(),
$this->fragment()
);
}
}

View File

@ -0,0 +1,42 @@
<?php
//
// (c) Copyright Ascensio System SIA 2023
//
// 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.
//
use PHPUnit\Framework\TestCase;
use OnlineEditorsExamplePhp\Common\URL;
final class URLFromComponentsTests extends TestCase {
public function test_creates() {
$url = URL::from_components(
'http',
'localhost',
8080,
'user',
'password',
'/path',
'q=value',
'fragment'
);
$this->assertEquals('http', $url->scheme());
$this->assertEquals('localhost', $url->host());
$this->assertEquals(8080, $url->port());
$this->assertEquals('user', $url->user());
$this->assertEquals('password', $url->pass());
$this->assertEquals('/path', $url->path());
$this->assertEquals('q=value', $url->query());
$this->assertEquals('fragment', $url->fragment());
}
}

View File

@ -0,0 +1,73 @@
<?php
//
// (c) Copyright Ascensio System SIA 2023
//
// 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.
//
use PHPUnit\Framework\TestCase;
use OnlineEditorsExamplePhp\Common\URL;
final class URLJoinPathTests extends TestCase {
public function test_joins_a_relative_to_an_empty_one() {
$url = new URL('http://localhost');
$joined = $url->join_path('first');
$this->assertEquals('http', $joined->scheme());
$this->assertEquals('localhost', $joined->host());
$this->assertEquals(null, $joined->port());
$this->assertEquals(null, $joined->user());
$this->assertEquals(null, $joined->pass());
$this->assertEquals('/first', $joined->path());
$this->assertEquals(null, $joined->query());
$this->assertEquals(null, $joined->fragment());
}
public function test_joins_a_relative() {
$url = new URL('http://localhost/first');
$joined = $url->join_path('second');
$this->assertEquals('http', $joined->scheme());
$this->assertEquals('localhost', $joined->host());
$this->assertEquals(null, $joined->port());
$this->assertEquals(null, $joined->user());
$this->assertEquals(null, $joined->pass());
$this->assertEquals('/first/second', $joined->path());
$this->assertEquals(null, $joined->query());
$this->assertEquals(null, $joined->fragment());
}
public function test_joins_an_absolute_to_an_empty_one() {
$url = new URL('http://localhost');
$joined = $url->join_path('/first');
$this->assertEquals('http', $joined->scheme());
$this->assertEquals('localhost', $joined->host());
$this->assertEquals(null, $joined->port());
$this->assertEquals(null, $joined->user());
$this->assertEquals(null, $joined->pass());
$this->assertEquals('/first', $joined->path());
$this->assertEquals(null, $joined->query());
$this->assertEquals(null, $joined->fragment());
}
public function test_joins_an_absolute() {
$url = new URL('http://localhost/first');
$joined = $url->join_path('/second');
$this->assertEquals('http', $joined->scheme());
$this->assertEquals('localhost', $joined->host());
$this->assertEquals(null, $joined->port());
$this->assertEquals(null, $joined->user());
$this->assertEquals(null, $joined->pass());
$this->assertEquals('/first/second', $joined->path());
$this->assertEquals(null, $joined->query());
$this->assertEquals(null, $joined->fragment());
}
}

View File

@ -0,0 +1,30 @@
<?php
//
// (c) Copyright Ascensio System SIA 2023
//
// 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.
//
use PHPUnit\Framework\TestCase;
use OnlineEditorsExamplePhp\Common\URL;
final class URLStringTests extends TestCase {
public function test_generates() {
$url = new URL('http://user:password@localhost:8080/path?q=value#fragment');
$string = $url->string();
$this->assertEquals(
'http://user:password@localhost:8080/path?q=value#fragment',
$string
);
}
}

View File

@ -1,26 +1,19 @@
{
"require": {
"ext-mbstring": "*",
"firebase/php-jwt": "^6.8.1"
},
"require-dev": {
"squizlabs/php_codesniffer": "*",
"ext-mbstring": "*"
"squizlabs/php_codesniffer": "^3.7.2",
"phpunit/phpunit": "^10.2.6"
},
"scripts": {
"code-sniffer": [
"./vendor/bin/phpcs --config-set default_standard ruleset.xml",
"./vendor/bin/phpcs --config-set colors 1"
],
"post-install-cmd": [
"@code-sniffer"
],
"post-update-cmd": [
"@code-sniffer"
]
},
"autoload-dev": {
"autoload": {
"psr-4": {
"OnlineEditorsExamplePhp\\" : "",
"OnlineEditorsExamplePhp\\Common\\": "common/",
"OnlineEditorsExamplePhp\\Configuration\\" : "configuration/",
"OnlineEditorsExamplePhp\\Helpers\\" : "helpers/",
"OnlineEditorsExamplePhp\\Views\\" : "views/",
"Firebase\\JWT\\" : "lib/jwt/"
"OnlineEditorsExamplePhp\\Views\\" : "views/"
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,66 +0,0 @@
{
"version": "1.6.0",
"fileSizeMax": 5242880,
"storagePath": "",
"alone": false,
"docServTimeout": "120000",
"docServSiteUrl": "https://documentserver/",
"docServConverterUrl": "ConvertService.ashx",
"docServApiUrl": "web-apps/apps/api/documents/api.js",
"docServPreloaderUrl": "web-apps/apps/api/documents/cache-scripts.html",
"docServCommandUrl": "coauthoring/CommandService.ashx",
"docServJwtSecret": "",
"docServJwtHeader": "Authorization",
"docServJwtUseForRequest": true,
"docServVerifyPeerOff": true,
"exampleUrl": "",
"mobileRegex": "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",
"languages": {
"en": "English",
"hy": "Armenian",
"az": "Azerbaijani",
"eu": "Basque",
"be": "Belarusian",
"bg": "Bulgarian",
"ca": "Catalan",
"zh": "Chinese (Simplified)",
"zh-TW": "Chinese (Traditional)",
"cs": "Czech",
"da": "Danish",
"nl": "Dutch",
"fi": "Finnish",
"fr": "French",
"gl": "Galego",
"de": "German",
"el": "Greek",
"hu": "Hungarian",
"id": "Indonesian",
"it": "Italian",
"ja": "Japanese",
"ko": "Korean",
"lo": "Lao",
"lv": "Latvian",
"ms": "Malay (Malaysia)",
"no": "Norwegian",
"pl": "Polish",
"pt": "Portuguese (Brazil)",
"pt-PT": "Portuguese (Portugal)",
"ro": "Romanian",
"ru": "Russian",
"si": "Sinhala (Sri Lanka)",
"sk": "Slovak",
"sl": "Slovenian",
"es": "Spanish",
"sv": "Swedish",
"tr": "Turkish",
"uk": "Ukrainian",
"vi": "Vietnamese",
"aa-AA": "Test Language"
}
}

View File

@ -0,0 +1,177 @@
<?php
//
// (c) Copyright Ascensio System SIA 2023
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
namespace OnlineEditorsExamplePhp\Configuration;
use OnlineEditorsExamplePhp\Common\Path;
use OnlineEditorsExamplePhp\Common\URL;
final class ConfigurationManager {
public string $version = '1.6.0';
public function example_url(): ?URL {
$url = getenv('EXAMPLE_URL');
if (!$url) {
return null;
}
return new URL($url);
}
public function document_server_url(): URL {
$url = getenv('DOCUMENT_SERVER_URL') ?: 'http://document-server';
return new URL($url);
}
public function document_server_api_url(): URL {
$server_url = $this->document_server_url();
$path = getenv('DOCUMENT_SERVER_API_PATH')
?: 'web-apps/apps/api/documents/api.js';
return $server_url->join_path($path);
}
public function document_server_preloader_url(): URL {
$server_url = $this->document_server_url();
$path = getenv('DOCUMENT_SERVER_PRELOADER_PATH')
?: 'web-apps/apps/api/documents/cache-scripts.html';
return $server_url->join_path($path);
}
public function document_server_command_url(): URL {
$server_url = $this->document_server_url();
$path = getenv('DOCUMENT_SERVER_COMMAND_PATH')
?: 'coauthoring/CommandService.ashx';
return $server_url->join_path($path);
}
public function document_server_converter_url(): URL {
$server_url = $this->document_server_url();
$path = getenv('DOCUMENT_SERVER_CONVERTER_PATH')
?: 'ConvertService.ashx';
return $server_url->join_path($path);
}
public function jwt_secret(): string {
return getenv('JWT_SECRET') ?: '';
}
public function jwt_header(): string {
return getenv('JWT_HEADER') ?: 'Authorization';
}
public function jwt_use_for_request(): bool {
$use = getenv('JWT_USE_FOR_REQUEST');
if (!$use) {
return true;
}
return filter_var($use, FILTER_VALIDATE_BOOLEAN);
}
public function ssl_verify_peer_mode_enabled(): bool {
$enabled = getenv('SSL_VERIFY_PEER_MODE_ENABLED');
if (!$enabled) {
return false;
}
return filter_var($enabled, FILTER_VALIDATE_BOOLEAN);
}
public function storage_path(): Path {
$storage_path = getenv('STORAGE_PATH') ?: 'storage';
$storage_directory = new Path($storage_path);
if ($storage_directory->absolute()) {
return $storage_directory;
}
$storage_string_directory = $storage_directory->string();
$current_directory = new Path(__DIR__);
$directory = $current_directory
->join_path('..')
->join_path($storage_string_directory);
return $directory->normalize();
}
public function single_user(): bool {
$single = getenv('SINGLE_USER');
if (!$single) {
return false;
}
return filter_var($single, FILTER_VALIDATE_BOOLEAN);
}
public function maximum_file_size(): int {
$size = getenv('MAXIMUM_FILE_SIZE');
if (!$size) {
return 5 * 1024 * 1024;
}
return intval($size);
}
public function conversion_timeout(): int {
$timeout = getenv('CONVERSION_TIMEOUT');
if (!$timeout) {
return 120 * 1000;
}
return intval($timeout);
}
/**
* @return string[]
*/
public function languages(): array {
return [
'en' => "English",
'hy' => 'Armenian',
'az' => 'Azerbaijani',
'eu' => 'Basque',
'be' => 'Belarusian',
'bg' => 'Bulgarian',
'ca' => 'Catalan',
'zh' => 'Chinese (Simplified)',
'zh-TW' => 'Chinese (Traditional)',
'cs' => 'Czech',
'da' => 'Danish',
'nl' => 'Dutch',
'fi' => 'Finnish',
'fr' => 'French',
'gl' => 'Galego',
'de' => 'German',
'el' => 'Greek',
'hu' => 'Hungarian',
'id' => 'Indonesian',
'it' => 'Italian',
'ja' => 'Japanese',
'ko' => 'Korean',
'lo' => 'Lao',
'lv' => 'Latvian',
'ms' => 'Malay (Malaysia)',
'no' => 'Norwegian',
'pl' => 'Polish',
'pt' => 'Portuguese (Brazil)',
'pt-PT' => 'Portuguese (Portugal)',
'ro' => 'Romanian',
'ru' => 'Russian',
'si' => 'Sinhala (Sri Lanka)',
'sk' => 'Slovak',
'sl' => 'Slovenian',
'es' => 'Spanish',
'sv' => 'Swedish',
'tr' => 'Turkish',
'uk' => 'Ukrainian',
'vi' => 'Vietnamese',
'aa-AA' => 'Test Language'
];
}
}

View File

@ -0,0 +1,47 @@
<?php
//
// (c) Copyright Ascensio System SIA 2023
//
// 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.
//
use PHPUnit\Framework\TestCase;
use OnlineEditorsExamplePhp\Configuration\ConfigurationManager;
final class ConfigurationManagerConversionTimeoutTests extends TestCase {
public array $env;
public function __construct(string $name) {
$this->env = getenv();
parent::__construct($name);
}
protected function setUp(): void {
foreach ($this->env as $key => $value) {
putenv("{$key}={$value}");
}
}
public function test_assigns_a_default_value() {
$config_manager = new ConfigurationManager();
$timeout = $config_manager->conversion_timeout();
$this->assertEquals(120_000, $timeout);
}
public function test_assigns_a_value_from_the_environment() {
putenv('CONVERSION_TIMEOUT=10');
$config_manager = new ConfigurationManager();
$timeout = $config_manager->conversion_timeout();
$this->assertEquals(10, $timeout);
}
}

View File

@ -0,0 +1,53 @@
<?php
//
// (c) Copyright Ascensio System SIA 2023
//
// 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.
//
use PHPUnit\Framework\TestCase;
use OnlineEditorsExamplePhp\Configuration\ConfigurationManager;
final class ConfigurationManagerDocumentServerAPIURLTests extends TestCase {
public array $env;
public function __construct(string $name) {
$this->env = getenv();
parent::__construct($name);
}
protected function setUp(): void {
foreach ($this->env as $key => $value) {
putenv("{$key}={$value}");
}
}
public function test_assigns_a_default_value() {
$config_manager = new ConfigurationManager();
$url = $config_manager->document_server_api_url();
$this->assertEquals(
'http://document-server/web-apps/apps/api/documents/api.js',
$url->string()
);
}
public function test_assigns_a_value_from_the_environment() {
putenv('DOCUMENT_SERVER_API_PATH=/api');
$config_manager = new ConfigurationManager();
$url = $config_manager->document_server_api_url();
$this->assertEquals(
'http://document-server/api',
$url->string()
);
}
}

View File

@ -0,0 +1,53 @@
<?php
//
// (c) Copyright Ascensio System SIA 2023
//
// 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.
//
use PHPUnit\Framework\TestCase;
use OnlineEditorsExamplePhp\Configuration\ConfigurationManager;
final class ConfigurationManagerDocumentServerCommandURLTests extends TestCase {
public array $env;
public function __construct(string $name) {
$this->env = getenv();
parent::__construct($name);
}
protected function setUp(): void {
foreach ($this->env as $key => $value) {
putenv("{$key}={$value}");
}
}
public function test_assigns_a_default_value() {
$config_manager = new ConfigurationManager();
$url = $config_manager->document_server_command_url();
$this->assertEquals(
'http://document-server/coauthoring/CommandService.ashx',
$url->string()
);
}
public function test_assigns_a_value_from_the_environment() {
putenv('DOCUMENT_SERVER_COMMAND_PATH=/command');
$config_manager = new ConfigurationManager();
$url = $config_manager->document_server_command_url();
$this->assertEquals(
'http://document-server/command',
$url->string()
);
}
}

View File

@ -0,0 +1,53 @@
<?php
//
// (c) Copyright Ascensio System SIA 2023
//
// 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.
//
use PHPUnit\Framework\TestCase;
use OnlineEditorsExamplePhp\Configuration\ConfigurationManager;
final class ConfigurationManagerDocumentServerConverterURLTests extends TestCase {
public array $env;
public function __construct(string $name) {
$this->env = getenv();
parent::__construct($name);
}
protected function setUp(): void {
foreach ($this->env as $key => $value) {
putenv("{$key}={$value}");
}
}
public function test_assigns_a_default_value() {
$config_manager = new ConfigurationManager();
$url = $config_manager->document_server_converter_url();
$this->assertEquals(
'http://document-server/ConvertService.ashx',
$url->string()
);
}
public function test_assigns_a_value_from_the_environment() {
putenv('DOCUMENT_SERVER_CONVERTER_PATH=/converter');
$config_manager = new ConfigurationManager();
$url = $config_manager->document_server_converter_url();
$this->assertEquals(
'http://document-server/converter',
$url->string()
);
}
}

View File

@ -0,0 +1,53 @@
<?php
//
// (c) Copyright Ascensio System SIA 2023
//
// 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.
//
use PHPUnit\Framework\TestCase;
use OnlineEditorsExamplePhp\Configuration\ConfigurationManager;
final class ConfigurationManagerDocumentServerPreloaderURLTests extends TestCase {
public array $env;
public function __construct(string $name) {
$this->env = getenv();
parent::__construct($name);
}
protected function setUp(): void {
foreach ($this->env as $key => $value) {
putenv("{$key}={$value}");
}
}
public function test_assigns_a_default_value() {
$config_manager = new ConfigurationManager();
$url = $config_manager->document_server_preloader_url();
$this->assertEquals(
'http://document-server/web-apps/apps/api/documents/cache-scripts.html',
$url->string()
);
}
public function test_assigns_a_value_from_the_environment() {
putenv('DOCUMENT_SERVER_PRELOADER_PATH=/preloader');
$config_manager = new ConfigurationManager();
$url = $config_manager->document_server_preloader_url();
$this->assertEquals(
'http://document-server/preloader',
$url->string()
);
}
}

View File

@ -0,0 +1,47 @@
<?php
//
// (c) Copyright Ascensio System SIA 2023
//
// 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.
//
use PHPUnit\Framework\TestCase;
use OnlineEditorsExamplePhp\Configuration\ConfigurationManager;
final class ConfigurationManagerDocumentServerURLTests extends TestCase {
public array $env;
public function __construct(string $name) {
$this->env = getenv();
parent::__construct($name);
}
protected function setUp(): void {
foreach ($this->env as $key => $value) {
putenv("{$key}={$value}");
}
}
public function test_assigns_a_default_value() {
$config_manager = new ConfigurationManager();
$url = $config_manager->document_server_url();
$this->assertEquals('http://document-server', $url->string());
}
public function test_assigns_a_value_from_the_environment() {
putenv('DOCUMENT_SERVER_URL=http://localhost');
$config_manager = new ConfigurationManager();
$url = $config_manager->document_server_url();
$this->assertEquals('http://localhost', $url->string());
}
}

View File

@ -0,0 +1,47 @@
<?php
//
// (c) Copyright Ascensio System SIA 2023
//
// 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.
//
use PHPUnit\Framework\TestCase;
use OnlineEditorsExamplePhp\Configuration\ConfigurationManager;
final class ConfigurationManagerExampleURLTests extends TestCase {
public array $env;
public function __construct(string $name) {
$this->env = getenv();
parent::__construct($name);
}
protected function setUp(): void {
foreach ($this->env as $key => $value) {
putenv("{$key}={$value}");
}
}
public function test_assigns_a_default_value() {
$config_manager = new ConfigurationManager();
$url = $config_manager->example_url();
$this->assertNull($url);
}
public function test_assigns_a_value_from_the_environment() {
putenv('EXAMPLE_URL=http://localhost');
$config_manager = new ConfigurationManager();
$url = $config_manager->example_url();
$this->assertEquals('http://localhost', $url->string());
}
}

View File

@ -0,0 +1,47 @@
<?php
//
// (c) Copyright Ascensio System SIA 2023
//
// 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.
//
use PHPUnit\Framework\TestCase;
use OnlineEditorsExamplePhp\Configuration\ConfigurationManager;
final class ConfigurationManagerJWTHeaderTests extends TestCase {
public array $env;
public function __construct(string $name) {
$this->env = getenv();
parent::__construct($name);
}
protected function setUp(): void {
foreach ($this->env as $key => $value) {
putenv("{$key}={$value}");
}
}
public function test_assigns_a_default_value() {
$config_manager = new ConfigurationManager();
$header = $config_manager->jwt_header();
$this->assertEquals('Authorization', $header);
}
public function test_assigns_a_value_from_the_environment() {
putenv('JWT_HEADER=Proxy-Authorization');
$config_manager = new ConfigurationManager();
$header = $config_manager->jwt_header();
$this->assertEquals('Proxy-Authorization', $header);
}
}

View File

@ -0,0 +1,47 @@
<?php
//
// (c) Copyright Ascensio System SIA 2023
//
// 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.
//
use PHPUnit\Framework\TestCase;
use OnlineEditorsExamplePhp\Configuration\ConfigurationManager;
final class ConfigurationManagerJWTSecretTests extends TestCase {
public array $env;
public function __construct(string $name) {
$this->env = getenv();
parent::__construct($name);
}
protected function setUp(): void {
foreach ($this->env as $key => $value) {
putenv("{$key}={$value}");
}
}
public function test_assigns_a_default_value() {
$config_manager = new ConfigurationManager();
$secret = $config_manager->jwt_secret();
$this->assertEquals('', $secret);
}
public function test_assigns_a_value_from_the_environment() {
putenv('JWT_SECRET=your-256-bit-secret');
$config_manager = new ConfigurationManager();
$secret = $config_manager->jwt_secret();
$this->assertEquals('your-256-bit-secret', $secret);
}
}

View File

@ -0,0 +1,47 @@
<?php
//
// (c) Copyright Ascensio System SIA 2023
//
// 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.
//
use PHPUnit\Framework\TestCase;
use OnlineEditorsExamplePhp\Configuration\ConfigurationManager;
final class ConfigurationManagerJWTUseForRequest extends TestCase {
public array $env;
public function __construct(string $name) {
$this->env = getenv();
parent::__construct($name);
}
protected function setUp(): void {
foreach ($this->env as $key => $value) {
putenv("{$key}={$value}");
}
}
public function test_assigns_a_default_value() {
$config_manager = new ConfigurationManager();
$use = $config_manager->jwt_use_for_request();
$this->assertTrue($use);
}
public function test_assigns_a_value_from_the_environment() {
putenv('JWT_USE_FOR_REQUEST=false');
$config_manager = new ConfigurationManager();
$use = $config_manager->jwt_use_for_request();
$this->assertFalse($use);
}
}

View File

@ -0,0 +1,47 @@
<?php
//
// (c) Copyright Ascensio System SIA 2023
//
// 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.
//
use PHPUnit\Framework\TestCase;
use OnlineEditorsExamplePhp\Configuration\ConfigurationManager;
final class ConfigurationManagerMaximumFileSizeTests extends TestCase {
public array $env;
public function __construct(string $name) {
$this->env = getenv();
parent::__construct($name);
}
protected function setUp(): void {
foreach ($this->env as $key => $value) {
putenv("{$key}={$value}");
}
}
public function test_assigns_a_default_value() {
$config_manager = new ConfigurationManager();
$size = $config_manager->maximum_file_size();
$this->assertEquals(5_242_880, $size);
}
public function test_assigns_a_value_from_the_environment() {
putenv('MAXIMUM_FILE_SIZE=10');
$config_manager = new ConfigurationManager();
$size = $config_manager->maximum_file_size();
$this->assertEquals(10, $size);
}
}

View File

@ -0,0 +1,47 @@
<?php
//
// (c) Copyright Ascensio System SIA 2023
//
// 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.
//
use PHPUnit\Framework\TestCase;
use OnlineEditorsExamplePhp\Configuration\ConfigurationManager;
final class ConfigurationManagerSSLTests extends TestCase {
public array $env;
public function __construct(string $name) {
$this->env = getenv();
parent::__construct($name);
}
protected function setUp(): void {
foreach ($this->env as $key => $value) {
putenv("{$key}={$value}");
}
}
public function test_assigns_a_default_value() {
$config_manager = new ConfigurationManager();
$enabled = $config_manager->ssl_verify_peer_mode_enabled();
$this->assertFalse($enabled);
}
public function test_assigns_a_value_from_the_environment() {
putenv('SSL_VERIFY_PEER_MODE_ENABLED=true');
$config_manager = new ConfigurationManager();
$enabled = $config_manager->ssl_verify_peer_mode_enabled();
$this->assertTrue($enabled);
}
}

View File

@ -0,0 +1,47 @@
<?php
//
// (c) Copyright Ascensio System SIA 2023
//
// 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.
//
use PHPUnit\Framework\TestCase;
use OnlineEditorsExamplePhp\Configuration\ConfigurationManager;
final class ConfigurationManagerSingleUserTests extends TestCase {
public array $env;
public function __construct(string $name) {
$this->env = getenv();
parent::__construct($name);
}
protected function setUp(): void {
foreach ($this->env as $key => $value) {
putenv("{$key}={$value}");
}
}
public function test_assigns_a_default_value() {
$config_manager = new ConfigurationManager();
$single = $config_manager->single_user();
$this->assertFalse($single);
}
public function test_assigns_a_value_from_the_environment() {
putenv('SINGLE_USER=true');
$config_manager = new ConfigurationManager();
$single = $config_manager->single_user();
$this->assertTrue($single);
}
}

View File

@ -0,0 +1,56 @@
<?php
//
// (c) Copyright Ascensio System SIA 2023
//
// 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.
//
use PHPUnit\Framework\TestCase;
use OnlineEditorsExamplePhp\Configuration\ConfigurationManager;
final class ConfigurationManagerStoragePathTests extends TestCase {
public array $env;
public function __construct(string $name) {
$this->env = getenv();
parent::__construct($name);
}
protected function setUp(): void {
foreach ($this->env as $key => $value) {
putenv("{$key}={$value}");
}
}
public function test_assigns_a_default_value() {
$config_manager = new ConfigurationManager();
$path = $config_manager->storage_path();
$this->assertTrue($path->absolute());
$this->assertEquals($path->basename(), 'storage');
}
public function test_assigns_a_relative_path_from_the_environment() {
putenv('STORAGE_PATH=directory');
$config_manager = new ConfigurationManager();
$path = $config_manager->storage_path();
$this->assertTrue($path->absolute());
$this->assertEquals($path->basename(), 'directory');
}
public function test_assigns_an_absolute_path_from_the_environment() {
putenv('STORAGE_PATH=/directory');
$config_manager = new ConfigurationManager();
$path = $config_manager->storage_path();
$this->assertEquals($path->string(), '/directory');
}
}

View File

@ -0,0 +1,26 @@
<?php
//
// (c) Copyright Ascensio System SIA 2023
//
// 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.
//
use PHPUnit\Framework\TestCase;
use OnlineEditorsExamplePhp\Configuration\ConfigurationManager;
final class ConfigurationManagerTests extends TestCase {
public function test_corresponds_the_latest_version() {
$config_manager = new ConfigurationManager();
$this->assertEquals('1.6.0', $config_manager->version);
}
}

View File

@ -1,26 +0,0 @@
<?php
/**
* (c) Copyright Ascensio System SIA 2023
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace OnlineEditorsExamplePhp;
use OnlineEditorsExamplePhp\Views\DocEditorView;
require_once dirname(__FILE__) . '/functions.php';
require_once dirname(__FILE__) . '/vendor/autoload.php';
$docEditorView = new DocEditorView($_REQUEST);
$docEditorView->render();

View File

@ -18,6 +18,7 @@
namespace OnlineEditorsExamplePhp;
use Exception;
use OnlineEditorsExamplePhp\Configuration\ConfigurationManager;
use OnlineEditorsExamplePhp\Helpers\ConfigManager;
use OnlineEditorsExamplePhp\Helpers\ExampleUsers;
use OnlineEditorsExamplePhp\Helpers\JwtManager;
@ -92,10 +93,10 @@ function getClientIp()
*/
function serverPath($forDocumentServer = null)
{
$configManager = new ConfigManager();
return $forDocumentServer && $configManager->getConfig("storagePath") != null
&& $configManager->getConfig("exampleUrl") != ""
? $configManager->getConfig("exampleUrl")
$config_manager = new ConfigurationManager();
$example_url = $config_manager->example_url();
return $forDocumentServer && $example_url
? $example_url->string()
: (getScheme() . '://' . $_SERVER['HTTP_HOST']);
}
@ -108,11 +109,8 @@ function serverPath($forDocumentServer = null)
*/
function getCurUserHostAddress($userAddress = null)
{
$configManager = new ConfigManager();
if ($configManager->getConfig("alone")) {
if (empty($configManager->getConfig("storagePath"))) {
return "Storage";
}
$config_manager = new ConfigurationManager();
if ($config_manager->single_user()) {
return "";
}
if (is_null($userAddress)) {
@ -222,39 +220,21 @@ function getScheme()
*/
function getStoragePath($fileName, $userAddress = null)
{
$configManager = new ConfigManager();
$storagePath = trim(
str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $configManager->getConfig("storagePath")),
DIRECTORY_SEPARATOR
);
if (!empty($storagePath) && !file_exists($storagePath) && !is_dir($storagePath)) {
mkdir($storagePath);
$config_manager = new ConfigurationManager();
$storage_path = $config_manager->storage_path();
if (!$storage_path->exists()) {
$storage_path->make_directory();
}
if (realpath($storagePath) === $storagePath) {
$directory = $storagePath;
} else {
$directory = __DIR__ . DIRECTORY_SEPARATOR . $storagePath;
$user_ip = getCurUserHostAddress($userAddress);
$user_directory = $storage_path->join_path($user_ip);
if (!$user_directory->exists()) {
$user_directory->make_directory();
}
if ($storagePath != "") {
$directory = $directory . DIRECTORY_SEPARATOR;
// if the file directory doesn't exist, make it
if (!file_exists($directory) && !is_dir($directory)) {
mkdir($directory);
}
}
if (realpath($storagePath) !== $storagePath) {
$directory = $directory . getCurUserHostAddress($userAddress) . DIRECTORY_SEPARATOR;
}
if (!file_exists($directory) && !is_dir($directory)) {
mkdir($directory);
}
sendlog("getStoragePath result: " . $directory . basename($fileName), "common.log");
return realpath($storagePath) === $storagePath ? $directory . $fileName : $directory . basename($fileName);
$file = $user_directory->join_path($fileName);
return $file->string();
}
/**
@ -268,43 +248,30 @@ function getStoragePath($fileName, $userAddress = null)
*/
function getForcesavePath($fileName, $userAddress, $create)
{
$configManager = new ConfigManager();
$storagePath = trim(
str_replace(
['/', '\\'],
DIRECTORY_SEPARATOR,
$configManager->getConfig("storagePath")
),
DIRECTORY_SEPARATOR
);
$config_manager = new ConfigurationManager();
$storage_path = $config_manager->storage_path();
// create the directory to this file version
if (realpath($storagePath) === $storagePath) {
$directory = $storagePath . DIRECTORY_SEPARATOR;
} else {
$directory = __DIR__ . DIRECTORY_SEPARATOR . $storagePath . getCurUserHostAddress($userAddress) .
DIRECTORY_SEPARATOR;
$user_ip = getCurUserHostAddress($userAddress);
$user_directory = $storage_path->join_path($user_ip);
if (!$user_directory->exists()) {
return '';
}
if (!is_dir($directory)) {
return "";
$history_directory = $user_directory->join_path("{$fileName}-hist");
if (!$history_directory->exists()) {
if ($create) {
$history_directory->make_directory();
} else {
return '';
}
}
// create the directory to the history of this file version
$directory = $directory . $fileName . "-hist" . DIRECTORY_SEPARATOR;
if (!$create && !is_dir($directory)) {
return "";
$file = $history_directory->join_path($fileName);
if (!$file->exists() && !$create) {
return '';
}
if (!file_exists($directory) && !is_dir($directory)) {
mkdir($directory);
}
$directory = $directory . $fileName;
if (!$create && !file_exists($directory)) {
return "";
}
return $directory;
return $file->string();
}
/**
@ -370,38 +337,21 @@ function getFileVersion($histDir)
function getStoredFiles()
{
$configManager = new ConfigManager();
$storagePath = trim(str_replace(
['/', '\\'],
DIRECTORY_SEPARATOR,
$configManager->getConfig("storagePath")
), DIRECTORY_SEPARATOR);
if (!empty($storagePath) && !file_exists($storagePath) && !is_dir($storagePath)) {
mkdir($storagePath);
$config_manager = new ConfigurationManager();
$storage_path = $config_manager->storage_path();
if (!$storage_path->exists()) {
$storage_path->make_directory();
}
if (realpath($storagePath) === $storagePath) {
$directory = $storagePath;
} else {
$directory = __DIR__ . DIRECTORY_SEPARATOR . $storagePath;
$user_ip = getCurUserHostAddress();
$user_directory = $storage_path->join_path($user_ip);
if (!$user_directory->exists()) {
$user_directory->make_directory();
}
// get the storage path and check if it exists
$result = [];
if ($storagePath != "") {
$directory = $directory . DIRECTORY_SEPARATOR;
if (!file_exists($directory) && !is_dir($directory)) {
return $result;
}
}
if (realpath($storagePath) !== $storagePath) {
$directory = $directory . getCurUserHostAddress() . DIRECTORY_SEPARATOR;
}
if (!file_exists($directory) && !is_dir($directory)) {
return $result;
}
$directory = $user_directory->string();
$cdir = scandir($directory); // get all the files and folders from the directory
$result = [];
@ -432,21 +382,8 @@ function getStoredFiles()
*/
function getVirtualPath($forDocumentServer)
{
$configManager = new ConfigManager();
$storagePath = trim(str_replace(
['/', '\\'],
'/',
$configManager->getConfig("storagePath")
), '/');
$storagePath = $storagePath != "" ? $storagePath . '/' : "";
if (realpath($storagePath) === $storagePath) {
$virtPath = serverPath($forDocumentServer) . '/' . $storagePath . '/';
} else {
$virtPath = serverPath($forDocumentServer) . '/' . $storagePath . getCurUserHostAddress() . '/';
}
sendlog("getVirtualPath virtPath: " . $virtPath, "common.log");
return $virtPath;
$server_url = serverPath($forDocumentServer);
return $server_url . '/' . getCurUserHostAddress() . '/';
}
/**
@ -689,6 +626,8 @@ function sendRequestToConvertService(
$filePass,
$lang
) {
$config_manager = new ConfigurationManager();
if (empty($from_extension)) {
$path_parts = pathinfo($document_uri);
$from_extension = mb_strtolower($path_parts['extension']);
@ -707,9 +646,7 @@ function sendRequestToConvertService(
// generate document token
$document_revision_id = generateRevisionId($document_revision_id);
$configManager = new ConfigManager();
$urlToConverter = $configManager->getConfig("docServSiteUrl").
$configManager->getConfig("docServConverterUrl");
$urlToConverter = $config_manager->document_server_converter_url()->string();
$arr = [
"async" => $is_async,
@ -724,8 +661,7 @@ function sendRequestToConvertService(
// add header token
$headerToken = "";
$jwtHeader = $configManager->getConfig("docServJwtHeader") ==
"" ? "Authorization" : $configManager->getConfig("docServJwtHeader");
$jwtHeader = $config_manager->jwt_header();
$jwtManager = new JwtManager();
if ($jwtManager->isJwtEnabled() && $jwtManager->tokenUseForRequest()) {
@ -738,7 +674,7 @@ function sendRequestToConvertService(
// request parameters
$opts = ['http' => [
'method' => 'POST',
'timeout' => $configManager->getConfig("docServTimeout"),
'timeout' => $config_manager->conversion_timeout(),
'header' => "Content-type: application/json\r\n" .
"Accept: application/json\r\n" .
(empty($headerToken) ? "" : $jwtHeader.": Bearer $headerToken\r\n"),
@ -747,7 +683,7 @@ function sendRequestToConvertService(
];
if (mb_substr($urlToConverter, 0, mb_strlen("https")) === "https") {
if ($configManager->getConfig("docServVerifyPeerOff") === true) {
if ($config_manager->ssl_verify_peer_mode_enabled()) {
$opts['ssl'] = ['verify_peer' => false, 'verify_peer_name' => false];
}
}
@ -890,7 +826,10 @@ function tryGetDefaultByType($createExt, $user)
{
$sample = isset($_GET["sample"]) && $_GET["sample"];
$demoName = ($sample ? "sample." : "new.") . $createExt;
$demoPath = "assets" . DIRECTORY_SEPARATOR . ($sample ? "sample" : "new") . DIRECTORY_SEPARATOR;
$demoPath =
"assets" . DIRECTORY_SEPARATOR .
"document-templates" . DIRECTORY_SEPARATOR .
($sample ? "sample" : "new") . DIRECTORY_SEPARATOR;
$demoFilename = GetCorrectName($demoName);
if (!@copy(dirname(__FILE__) . DIRECTORY_SEPARATOR . $demoPath . $demoName, getStoragePath($demoFilename))) {
@ -914,9 +853,8 @@ function tryGetDefaultByType($createExt, $user)
function getCallbackUrl($fileName)
{
return serverPath(true) . '/'
. "webeditor-ajax.php"
. "?type=track"
. "&fileName=" . urlencode($fileName)
. "track"
. "?fileName=" . urlencode($fileName)
. "&userAddress=" . getClientIp();
}
@ -933,10 +871,9 @@ function getCreateUrl($fileName, $uid, $type)
{
$ext = trim(getInternalExtension($fileName), '.');
return serverPath(false) . '/'
. "doceditor.php"
. "editor"
. "?fileExt=" . $ext
. "&user=" . $uid
. "&type=" . $type;
. "&user=" . $uid;
}
/**
@ -953,9 +890,8 @@ function getHistoryDownloadUrl($fileName, $version, $file, $isServer = true)
{
$userAddress = $isServer ? "&userAddress=" . getClientIp() : "";
return serverPath($isServer) . '/'
. "webeditor-ajax.php"
. "?type=history"
. "&fileName=" . urlencode($fileName)
. "history"
. "?fileName=" . urlencode($fileName)
. "&ver=" . $version
. "&file=" . urlencode($file)
. $userAddress;
@ -973,9 +909,8 @@ function getDownloadUrl($fileName, $isServer = true)
{
$userAddress = $isServer ? "&userAddress=" . getClientIp() : "";
return serverPath($isServer) . '/'
. "webeditor-ajax.php"
. "?type=download"
. "&fileName=" . urlencode($fileName)
. "download"
. "?fileName=" . urlencode($fileName)
. $userAddress;
}
@ -992,8 +927,6 @@ function getDownloadUrl($fileName, $isServer = true)
*/
function getHistory($filename, $filetype, $docKey, $fileuri, $isEnableDirectUrl)
{
$configManager = new ConfigManager();
$storagePath = $configManager->getConfig("storagePath");
$histDir = getHistoryDir(getStoragePath($filename)); // get the path to the file history
if (getFileVersion($histDir) > 0) { // check if the file was modified (the file version is greater than 0)
@ -1034,13 +967,11 @@ function getHistory($filename, $filetype, $docKey, $fileuri, $isEnableDirectUrl)
$directUrl = $i == $curVer ? fileUri($filename, false) :
getHistoryDownloadUrl($filename, $i, "prev.".$fileExe, false);
$prevFileUrl = $i == $curVer ? $fileuri : getHistoryDownloadUrl($filename, $i, "prev.".$fileExe);
if (realpath($storagePath) === $storagePath) {
$prevFileUrl = $i == $curVer ? getDownloadUrl($filename) :
getHistoryDownloadUrl($filename, $i, "prev.".$fileExe);
if ($isEnableDirectUrl) {
$directUrl = $i == $curVer ? getDownloadUrl($filename, false) :
getHistoryDownloadUrl($filename, $i, "prev.".$fileExe, false);
}
$prevFileUrl = $i == $curVer ? getDownloadUrl($filename) :
getHistoryDownloadUrl($filename, $i, "prev.".$fileExe);
if ($isEnableDirectUrl) {
$directUrl = $i == $curVer ? getDownloadUrl($filename, false) :
getHistoryDownloadUrl($filename, $i, "prev.".$fileExe, false);
}
$dataObj["url"] = $prevFileUrl; // write file url to the data object

View File

@ -20,20 +20,13 @@ namespace OnlineEditorsExamplePhp\Helpers;
final class ConfigManager
{
private mixed $config;
private mixed $configFormats;
public function __construct()
{
$this->config = json_decode($this->getConfigurationJson());
$this->configFormats = json_decode($this->getConfigurationFromatsJson());
}
private function getConfigurationJson(): bool|string
{
return file_exists("./config.json") ? file_get_contents("./config.json") : false;
}
private function getConfigurationFromatsJson(): bool|string
{
return file_exists("./assets/document-formats/onlyoffice-docs-formats.json")
@ -41,18 +34,6 @@ final class ConfigManager
: false;
}
/**
* @param string|null $configName
* @return mixed
*/
public function getConfig(string $configName = null): mixed
{
if ($configName) {
return $this->config->$configName ?? "";
}
return $this->config;
}
public function getSuppotredFormats(): mixed
{
return $this->configFormats;

View File

@ -18,6 +18,10 @@ namespace OnlineEditorsExamplePhp\Helpers;
* limitations under the License.
*/
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use OnlineEditorsExamplePhp\Configuration\ConfigurationManager;
final class JwtManager
{
/**
@ -27,8 +31,8 @@ final class JwtManager
*/
public function isJwtEnabled(): bool
{
$configManager = new ConfigManager();
return !empty($configManager->getConfig("docServJwtSecret"));
$config_manager = new ConfigurationManager();
return !empty($config_manager->jwt_secret());
}
/**
@ -38,8 +42,8 @@ final class JwtManager
*/
public function tokenUseForRequest(): bool
{
$configManager = new ConfigManager();
return $configManager->getConfig("docServJwtUseForRequest") ?: false;
$config_manager = new ConfigurationManager();
return $config_manager->jwt_use_for_request() ?: false;
}
/**
@ -51,8 +55,8 @@ final class JwtManager
*/
public function jwtEncode($payload)
{
$configManager = new ConfigManager();
return \Firebase\JWT\JWT::encode($payload, $configManager->getConfig("docServJwtSecret"));
$config_manager = new ConfigurationManager();
return JWT::encode($payload, $config_manager->jwt_secret(), 'HS256');
}
/**
@ -64,12 +68,11 @@ final class JwtManager
*/
public function jwtDecode($token)
{
$configManager = new ConfigManager();
$config_manager = new ConfigurationManager();
try {
$payload = \Firebase\JWT\JWT::decode(
$payload = JWT::decode(
$token,
$configManager->getConfig("docServJwtSecret"),
["HS256"]
new Key($config_manager->jwt_secret(), 'HS256')
);
} catch (\UnexpectedValueException $e) {
$payload = "";

View File

@ -17,10 +17,140 @@
namespace OnlineEditorsExamplePhp;
require_once __DIR__ . '/vendor/autoload.php';
require_once __DIR__ . '/ajax.php';
require_once __DIR__ . '/functions.php';
require_once __DIR__ . '/trackmanager.php';
use OnlineEditorsExamplePhp\Common\HTTPStatus;
use OnlineEditorsExamplePhp\Common\URL;
use OnlineEditorsExamplePhp\Configuration\ConfigurationManager;
use OnlineEditorsExamplePhp\Views\DocEditorView;
use OnlineEditorsExamplePhp\Views\IndexView;
require_once dirname(__FILE__) . '/functions.php';
require_once dirname(__FILE__) . '/vendor/autoload.php';
function configure() {
$config_manager = new ConfigurationManager();
if ($config_manager->ssl_verify_peer_mode_enabled()) {
// Ignore self-signed certificate.
stream_context_set_default([
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false
]
]);
}
}
$indexView = new IndexView($_REQUEST);
$indexView->render();
function routers() {
// TODO: delete fallback.
// In theory, the content type of the response should be declared inside the
// router function. However, this statement isn't true for all routers, and
// it's also not true for all branches in all routers. Therefore, we are
// setting the default content type for all routers here.
header('Content-Type: application/json; charset=utf-8');
header('Cache-Control: no-cache, must-revalidate, max-age=0');
header('Expires: Wed, 11 Jan 1984 05:00:00 GMT');
header('Pragma: no-cache');
@header_remove('Last-Modified');
header('X-Content-Type-Options: nosniff');
header('X-Robots-Tag: noindex');
$url = new URL($_SERVER['REQUEST_URI']);
sendlog($url->string(), 'webedior-ajax.log');
$path = $url->path();
if (!$path || $path === '/') {
header('Content-Type: text/html; charset=utf-8');
$view = new IndexView($_REQUEST);
$view->render();
return;
}
if (str_starts_with($path, '/editor')) {
header('Content-Type: text/html; charset=utf-8');
$view = new DocEditorView($_REQUEST);
$view->render();
return;
}
if (str_starts_with($path, '/assets')) {
$response = assets();
$response['status'] = 'success';
echo json_encode($response);
return;
}
if (str_starts_with($path, '/convert')) {
$response = convert();
$response['status'] = 'success';
echo json_encode($response);
return;
}
if (str_starts_with($path, '/csv')) {
$response = csv();
$response['status'] = 'success';
echo json_encode($response);
return;
}
if (str_starts_with($path, '/delete')) {
$response = delete();
$response['status'] = isset($response['error']) ? 'error' : 'success';
echo json_encode($response);
return;
}
if (str_starts_with($path, '/download')) {
$response = download();
$response['status'] = 'success';
echo json_encode($response);
return;
}
if (str_starts_with($path, '/files')) {
$response = files();
echo json_encode($response);
return;
}
if (str_starts_with($path, '/history')) {
$response = historyDownload();
$response['status'] = 'success';
echo json_encode($response);
return;
}
if (str_starts_with($path, '/reference')) {
$response = reference();
$response['status'] = 'success';
echo json_encode($response);
return;
}
if (str_starts_with($path, '/rename')) {
$response = renamefile();
$content = json_encode($response);
echo $content;
return;
}
if (str_starts_with($path, '/restore')) {
$response = restore();
echo json_encode($response);
return;
}
if (str_starts_with($path, '/saveas')) {
$response = saveas();
$response['status'] = 'success';
echo json_encode($response);
return;
}
if (str_starts_with($path, '/track')) {
$response = track();
$response['status'] = 'success';
echo json_encode($response);
return;
}
if (str_starts_with($path, '/upload')) {
$response = upload();
$response['status'] = isset($response['error']) ? 'error' : 'success';
echo json_encode($response);
return;
}
http_response_code(HTTPStatus::not_found->value);
}
configure();
routers();

View File

@ -125,7 +125,7 @@ if (typeof jQuery != "undefined") {
jq("#filePass").val("");
timer = setTimeout(function () {
var requestAddress = "webeditor-ajax.php?type=convert&user=" + user;
var requestAddress = "convert?user=" + user;
jq.ajax({
async: true,
@ -247,7 +247,7 @@ if (typeof jQuery != "undefined") {
jq(document).on("click", "#beginEdit:not(.disable)", function () {
var fileId = encodeURIComponent(jq('#hiddenFileName').val());
var url = "doceditor.php?fileID=" + fileId + "&user=" + user + "&directUrl=" + directUrl;
var url = "editor?fileID=" + fileId + "&user=" + user + "&directUrl=" + directUrl;
window.open(url, "_blank");
jq('#hiddenFileName').val("");
jq.unblockUI();
@ -256,7 +256,7 @@ if (typeof jQuery != "undefined") {
jq(document).on("click", "#beginView:not(.disable)", function () {
var fileId = encodeURIComponent(jq('#hiddenFileName').val());
var url = "doceditor.php?action=view&fileID=" + fileId + "&user=" + user + "&directUrl=" + directUrl;
var url = "editor?action=view&fileID=" + fileId + "&user=" + user + "&directUrl=" + directUrl;
window.open(url, "_blank");
jq('#hiddenFileName').val("");
jq.unblockUI();
@ -265,7 +265,7 @@ if (typeof jQuery != "undefined") {
jq(document).on("click", "#beginEmbedded:not(.disable)", function () {
var fileId = encodeURIComponent(jq('#hiddenFileName').val());
var url = "doceditor.php?type=embedded&fileID=" + fileId + "&user=" + user + "&directUrl=" + directUrl;
var url = "editor?type=embedded&fileID=" + fileId + "&user=" + user + "&directUrl=" + directUrl;
jq("#mainProgress").addClass("embedded");
jq("#beginEmbedded").addClass("disable");
@ -297,7 +297,7 @@ if (typeof jQuery != "undefined") {
jq(document).on("click", ".delete-file", function () {
var fileName = jq(this).attr("data");
var requestAddress = "webeditor-ajax.php?type=delete&fileName=" + fileName;
var requestAddress = "delete?fileName=" + fileName;
jq.ajax({
async: true,

View File

@ -1,7 +0,0 @@
<?php
namespace Firebase\JWT;
class BeforeValidException extends \UnexpectedValueException
{
}

View File

@ -1,7 +0,0 @@
<?php
namespace Firebase\JWT;
class ExpiredException extends \UnexpectedValueException
{
}

View File

@ -1,370 +0,0 @@
<?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);
}
}

View File

@ -1,30 +0,0 @@
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.

View File

@ -1,7 +0,0 @@
<?php
namespace Firebase\JWT;
class SignatureInvalidException extends \UnexpectedValueException
{
}

View File

@ -31,3 +31,7 @@ License File: jwt.license
PHP_CodeSniffer - PHP_CodeSniffer is a set of two PHP scripts; the main phpcs script that tokenizes PHP, JavaScript and CSS files to detect violations of a defined coding standard, and a second phpcbf script to automatically correct coding standard violations. PHP_CodeSniffer is an essential development tool that ensures your code remains clean and consistent. (https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt)
License: BSD-3-Clause
License File: PHP_CodeSniffer.license
PHPUnit - The PHP Unit Testing framework. (https://github.com/sebastianbergmann/phpunit/blob/main/LICENSE)
License: BSD 3-Clause
License File: phpunit.license

View File

@ -1,27 +1,29 @@
Copyright (c) 2004, Jamis Buck (jamis@jamisbuck.org)
BSD 3-Clause License
Copyright (c) 2001-2023, Sebastian Bergmann
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.
1. 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.
2. 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.
* The names of its contributors may not be used to endorse or promote
products derived from this software without specific prior written
permission.
3. Neither the name of the copyright holder nor the names of its
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
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 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.
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,13 @@
[global]
daemonize = no
[www]
user = www-data
group = www-data
listen = ${ADDRESS}:${PORT}
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
clear_env = no

View File

@ -0,0 +1,13 @@
<?xml version="1.0"?>
<ruleset name="Custom Standard">
<description>A custom coding standard</description>
<rule ref="PSR2"/>
<rule ref="Generic.Files.LineLength">
<properties>
<property name="lineLimit" value="120"/>
<property name="absoluteLineLimit" value="0"/>
</properties>
</rule>
</ruleset>

View File

@ -1,14 +0,0 @@
<?xml version="1.0"?>
<ruleset name="CustomRuleset">
<description>This standard changes the line length</description>
<!-- Insert all sniff from PSR2 standard -->
<rule ref="PSR2"/>
<rule ref="Generic.Files.LineLength">
<properties>
<property name="lineLimit" value="120"/>
<property name="absoluteLineLimit" value="0"/>
</properties>
</rule>
</ruleset>

View File

@ -103,7 +103,7 @@
event.data.directUrl = !!config.document.directUrl;
let xhr = new XMLHttpRequest();
xhr.open("POST", "webeditor-ajax.php?type=reference");
xhr.open("POST", "reference");
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(JSON.stringify(event.data));
xhr.onload = function () {
@ -161,7 +161,7 @@
url: url
};
let xhr = new XMLHttpRequest();
xhr.open("POST", "webeditor-ajax.php?type=saveas");
xhr.open("POST", "saveas");
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify(data));
xhr.onload = function () {
@ -181,7 +181,7 @@
};
let xhr = new XMLHttpRequest();
xhr.open("POST", "webeditor-ajax.php?type=rename");
xhr.open("POST", "rename");
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify(data));
xhr.onload = function () {
@ -189,6 +189,27 @@
}
};
function onRequestRestore(event) {
const query = new URLSearchParams(window.location.search)
const config = {config}
const payload = {
fileName: query.get('fileID'),
version: event.data.version,
userId: query.get('user') || config.editorConfig.user.id
}
const request = new XMLHttpRequest()
request.open("PUT", 'restore')
request.send(JSON.stringify(payload))
request.onload = function () {
if (request.status != 200) {
response = JSON.parse(request.response)
innerAlert(response.error)
return
}
document.location.reload();
}
}
var сonnectEditor = function () {
{fileNotFoundAlert}
@ -208,6 +229,7 @@
'onRequestCompareFile': onRequestCompareFile,
'onRequestMailMergeRecipients': onRequestMailMergeRecipients,
'onRequestReferenceData': onRequestReferenceData,
'onRequestRestore': onRequestRestore
};
{history}

View File

@ -35,19 +35,19 @@
<ul class="try-editor-list clearFix">
<li>
<a class="try-editor word reload-page" target="_blank"
href="doceditor.php?fileExt=docx&user={user}">Document</a>
href="editor?fileExt=docx&user={user}">Document</a>
</li>
<li>
<a class="try-editor cell reload-page" target="_blank"
href="doceditor.php?fileExt=xlsx&user={user}">Spreadsheet</a>
href="editor?fileExt=xlsx&user={user}">Spreadsheet</a>
</li>
<li>
<a class="try-editor slide reload-page" target="_blank"
href="doceditor.php?fileExt=pptx&user={user}">Presentation</a>
href="editor?fileExt=pptx&user={user}">Presentation</a>
</li>
<li>
<a class="try-editor form reload-page" target="_blank"
href="doceditor.php?fileExt=docxf&user={user}">Form template</a>
href="editor?fileExt=docxf&user={user}">Form template</a>
</li>
</ul>
<label class="side-option">
@ -58,7 +58,7 @@
<div class="upload-panel clearFix">
<a class="file-upload">Upload file
<input type="file" id="fileupload" name="files"
data-url="webeditor-ajax.php?type=upload&user={user}" />
data-url="upload?user={user}" />
</a>
</div>

View File

@ -18,6 +18,7 @@
namespace OnlineEditorsExamplePhp;
use Exception;
use OnlineEditorsExamplePhp\Configuration\ConfigurationManager;
use OnlineEditorsExamplePhp\Helpers\ConfigManager;
use OnlineEditorsExamplePhp\Helpers\JwtManager;
@ -28,6 +29,8 @@ use OnlineEditorsExamplePhp\Helpers\JwtManager;
*/
function readBody()
{
$config_manager = new ConfigurationManager();
$result["error"] = 0;
$configManager = new ConfigManager();
$jwtManager = new JwtManager();
@ -53,8 +56,7 @@ function readBody()
$inHeader = false;
$data = "";
$jwtHeader = $configManager->getConfig("docServJwtHeader") ==
"" ? "Authorization" : $configManager->getConfig("docServJwtHeader");
$jwtHeader = $config_manager->jwt_header();
if (!empty($data["token"])) { // if the document token is in the data
$data = $jwtManager->jwtDecode($data["token"]); // decode it
@ -290,10 +292,10 @@ function processForceSave($data, $fileName, $userAddress)
*/
function commandRequest($method, $key, $meta = null)
{
$configManager = new ConfigManager();
$config_manager = new ConfigurationManager();
$jwtManager = new JwtManager();
$documentCommandUrl = $configManager->getConfig("docServSiteUrl").
$configManager->getConfig("docServCommandUrl");
$documentCommandUrl = $config_manager->document_server_command_url()->string();
$arr = [
"c" => $method,
@ -305,8 +307,7 @@ function commandRequest($method, $key, $meta = null)
}
$headerToken = "";
$jwtHeader = $configManager->getConfig("docServJwtHeader") == "" ? "Authorization" :
$configManager->getConfig("docServJwtHeader");
$jwtHeader = $config_manager->jwt_header();
// check if a secret key to generate token exists or not
if ($jwtManager->isJwtEnabled() && $jwtManager->tokenUseForRequest()) {
@ -326,7 +327,7 @@ function commandRequest($method, $key, $meta = null)
]];
if (mb_substr($documentCommandUrl, 0, mb_strlen("https")) === "https") {
if ($configManager->getConfig("docServVerifyPeerOff") === true) {
if ($config_manager->ssl_verify_peer_mode_enabled()) {
$opts['ssl'] = ['verify_peer' => false, 'verify_peer_name' => false];
}
}

View File

@ -17,6 +17,7 @@
namespace OnlineEditorsExamplePhp\Views;
use OnlineEditorsExamplePhp\Configuration\ConfigurationManager;
use OnlineEditorsExamplePhp\Helpers\ConfigManager;
use OnlineEditorsExamplePhp\Helpers\ExampleUsers;
use OnlineEditorsExamplePhp\Helpers\JwtManager;
@ -39,6 +40,9 @@ final class DocEditorView extends View
public function __construct($request, $tempName = "docEditor")
{
parent::__construct($tempName);
$config_manager = new ConfigurationManager();
$externalUrl = $request["fileUrl"] ?? "";
$confgManager = new ConfigManager();
$jwtManager = new JwtManager();
@ -59,15 +63,12 @@ final class DocEditorView extends View
$filename = tryGetDefaultByType($createExt, $user);
// create the demo file url
$new_url = "doceditor.php?fileID=" . $filename . "&user=" . $request["user"];
$new_url = "editor?fileID=" . $filename . "&user=" . $request["user"];
header('Location: ' . $new_url, true);
exit;
}
$fileuri = fileUri($filename, true);
$fileuriUser = realpath($confgManager->getConfig("storagePath")) ===
$confgManager->getConfig("storagePath") ?
getDownloadUrl($filename) . "&dmode=emb" : fileUri($filename);
$directUrl = getDownloadUrl($filename, false);
$docKey = getDocEditorKey($filename);
$filetype = mb_strtolower(pathinfo($filename, PATHINFO_EXTENSION));
@ -200,21 +201,21 @@ final class DocEditorView extends View
// a document for comparing
$dataCompareFile = $isEnableDirectUrl ? [
"fileType" => "docx",
"url" => serverPath(true) . "/webeditor-ajax.php?type=assets&name=sample.docx",
"directUrl" => serverPath(false) . "/webeditor-ajax.php?type=assets&name=sample.docx",
"url" => serverPath(true) . "/assets?name=sample.docx",
"directUrl" => serverPath(false) . "/assets?name=sample.docx",
] : [
"fileType" => "docx",
"url" => serverPath(true) . "/webeditor-ajax.php?type=assets&name=sample.docx",
"url" => serverPath(true) . "/assets?name=sample.docx",
];
// recipients data for mail merging
$dataMailMergeRecipients = $isEnableDirectUrl ? [
"fileType" => "csv",
"url" => serverPath(true) . "/webeditor-ajax.php?type=csv",
"directUrl" => serverPath(false) . "/webeditor-ajax.php?type=csv",
"url" => serverPath(true) . "/csv",
"directUrl" => serverPath(false) . "/csv",
] : [
"fileType" => "csv",
"url" => serverPath(true) . "/webeditor-ajax.php?type=csv",
"url" => serverPath(true) . "/csv",
];
// users data for mentions
@ -266,7 +267,7 @@ final class DocEditorView extends View
}
$this->tagsValues = [
"docType" => getDocumentType($filename),
"apiUrl" => $confgManager->getConfig("docServSiteUrl").$confgManager->getConfig("docServApiUrl"),
"apiUrl" => $config_manager->document_server_api_url()->string(),
"dataInsertImage" => mb_strimwidth(
json_encode($dataInsertImage),
1,

View File

@ -50,40 +50,40 @@ class IndexStoredListView extends View
)
).']">';
$layout .= ' <td class="contentCells"><a class="stored-edit '.
$storeFile->documentType.'" href="doceditor.php?fileID='.
$storeFile->documentType.'" href="editor?fileID='.
urlencode($storeFile->name).
'&user='.$user.
$directUrlArg .'" target="_blank">'.'<span>'.$storeFile->name.'</span></a></td>';
if ($storeFile->canEdit) {
$layout .= ' <td class="contentCells contentCells-icon"> <a href="doceditor.php?fileID='.
$layout .= ' <td class="contentCells contentCells-icon"> <a href="editor?fileID='.
urlencode($storeFile->name).'&user=' . htmlentities($user).$directUrlArg.
'&action=edit&type=desktop" target="_blank">'.
'<img src="css/images/desktop.svg" alt="Open in editor for full size screens"'.
' title="Open in editor for full size screens"/></a></td>'.
' <td class="contentCells contentCells-icon"> <a href="doceditor.php?fileID='.
' <td class="contentCells contentCells-icon"> <a href="editor?fileID='.
urlencode($storeFile->name).'&user=' . htmlentities($user).$directUrlArg.
'&action=edit&type=mobile" target="_blank">'.
'<img src="css/images/mobile.svg" alt="Open in editor for mobile devices"'.
' title="Open in editor for mobile devices" /></a></td>'.
' <td class="contentCells contentCells-icon"> <a href="doceditor.php?fileID='.
' <td class="contentCells contentCells-icon"> <a href="editor?fileID='.
urlencode($storeFile->name).'&user='.htmlentities($user).$directUrlArg.
'&action=comment&type=desktop" target="_blank">'.
' <img src="css/images/comment.svg" alt="Open in editor for comment"'.
' title="Open in editor for comment" /></a></td>';
if ($storeFile->documentType == "word") {
$layout .= '<td class="contentCells contentCells-icon"> <a href="doceditor.php?fileID='.
$layout .= '<td class="contentCells contentCells-icon"> <a href="editor?fileID='.
urlencode($storeFile->name).'&user='.htmlentities($user).$directUrlArg.
'&action=review&type=desktop" target="_blank">'.
' <img src="css/images/review.svg" alt="Open in editor for review"'.
' title="Open in editor for review" /></a></td>'.
' <td class="contentCells contentCells-icon "> <a href="doceditor.php?fileID='.
' <td class="contentCells contentCells-icon "> <a href="editor?fileID='.
urlencode($storeFile->name).'&user='.htmlentities($user).$directUrlArg.
'&action=blockcontent&type=desktop" target="_blank">'.
' <img src="css/images/block-content.svg"'.
' alt="Open in editor without content control modification"'.
' title="Open in editor without content control modification"</a></td>';
} elseif ($storeFile->documentType == "cell") {
$layout .= '<td class="contentCells contentCells-icon"> <a href="doceditor.php?fileID='.
$layout .= '<td class="contentCells contentCells-icon"> <a href="editor?fileID='.
urlencode($storeFile->name).'&user='.htmlentities($user).$directUrlArg.
'&action=filter&type=desktop" target="_blank">'.
' <img src="css/images/filter.svg" alt="Open in editor without access to change the filter"'.
@ -95,7 +95,7 @@ class IndexStoredListView extends View
if ($storeFile->isFillFormDoc) {
$layout.= ' <td class="contentCells contentCells-shift contentCells-icon'.
' firstContentCellShift">'.
' <a href="doceditor.php?fileID='.urlencode($storeFile->name).
' <a href="editor?fileID='.urlencode($storeFile->name).
'&user='.htmlentities($user).$directUrlArg.
'&action=fillForms&type=desktop" target="_blank">'.
' <img src="css/images/fill-forms.svg" alt="Open in editor for filling in forms"'.
@ -105,7 +105,7 @@ class IndexStoredListView extends View
'firstContentCellShift"></td>';
}
} elseif ($storeFile->isFillFormDoc) {
$layout .= '<td class="contentCells contentCells-icon"> <a href="doceditor.php?fileID='.
$layout .= '<td class="contentCells contentCells-icon"> <a href="editor?fileID='.
urlencode($storeFile->name).'&user='.htmlentities($user).$directUrlArg.
'&action=fillForms&type=desktop" target="_blank">'.
' <img src="css/images/mobile-fill-forms.svg" alt="Open in editor for filling in forms'.
@ -114,7 +114,7 @@ class IndexStoredListView extends View
'<td class="contentCells contentCells-icon"></td>'.
'<td class="contentCells contentCells-icon"></td>'.
'<td class="contentCells contentCells-shift contentCells-icon firstContentCellShift">'.
'<a href="doceditor.php?fileID='.urlencode($storeFile->name).'&user='.htmlentities($user).
'<a href="editor?fileID='.urlencode($storeFile->name).'&user='.htmlentities($user).
$directUrlArg.'&action=fillForms&type=desktop" target="_blank">'.
'<img src="css/images/fill-forms.svg" alt="Open in editor for filling in forms"'.
' title="Open in editor for filling in forms"/></a></td>';
@ -123,22 +123,22 @@ class IndexStoredListView extends View
'contentCellsEmpty" colspan="6"></td>';
}
$layout .= '<td class="contentCells contentCells-icon firstContentCellViewers">'.
' <a href="doceditor.php?fileID='.urlencode($storeFile->name).'&user='.htmlentities($user).
' <a href="editor?fileID='.urlencode($storeFile->name).'&user='.htmlentities($user).
$directUrlArg.'&action=view&type=desktop" target="_blank">'.
' <img src="css/images/desktop.svg" alt="Open in viewer for full size screens"'.
' title="Open in viewer for full size screens" /></a></td>'.
' <td class="contentCells contentCells-icon"> <a href="doceditor.php?fileID='.
' <td class="contentCells contentCells-icon"> <a href="editor?fileID='.
urlencode($storeFile->name).'&user='.htmlentities($user).$directUrlArg.
'&action=view&type=mobile" target="_blank">'.
' <img src="css/images/mobile.svg" alt="Open in viewer for mobile devices"'.
' title="Open in viewer for mobile devices" /></a></td>'.
' <td class="contentCells contentCells-icon contentCells-shift">'.
' <a href="doceditor.php?fileID='.urlencode($storeFile->name).'&user='.htmlentities($user).
' <a href="editor?fileID='.urlencode($storeFile->name).'&user='.htmlentities($user).
$directUrlArg.'&action=embedded&type=embedded" target="_blank">'.
' <img src="css/images/embeded.svg" alt="Open in embedded mode"'.
' title="Open in embedded mode" /></a>'.
' <td class="contentCells contentCells-icon contentCells-shift downloadContentCellShift">'.
'<a href="webeditor-ajax.php?type=download&fileName='.urlencode($storeFile->name).'">'.
'<a href="download?fileName='.urlencode($storeFile->name).'">'.
' <img class="icon-download" src="css/images/download.svg" alt="Download" title="Download"'.
' /></a></td>'.
'<td class="contentCells contentCells-icon contentCells-shift">'.

View File

@ -17,6 +17,7 @@
namespace OnlineEditorsExamplePhp\Views;
use OnlineEditorsExamplePhp\Configuration\ConfigurationManager;
use OnlineEditorsExamplePhp\Helpers\ConfigManager;
use OnlineEditorsExamplePhp\Helpers\ExampleUsers;
use function OnlineEditorsExamplePhp\getStoredFiles;
@ -61,8 +62,8 @@ final class IndexView extends View
private function getLanguageListOptionsLayout()
{
$layout = "";
$configManager = new ConfigManager();
foreach ($configManager->getConfig("languages") as $key => $language) {
$config_manager = new ConfigurationManager();
foreach ($config_manager->languages() as $key => $language) {
$layout .= '<option value="'.$key.'">'.$language.'</option>'.PHP_EOL;
}
return $layout;
@ -100,16 +101,12 @@ final class IndexView extends View
private function getPreloaderUrl()
{
$configManager = new ConfigManager();
return $configManager->getConfig("docServSiteUrl").
$configManager->getConfig("docServPreloaderUrl");
$config_manager = new ConfigurationManager();
return $config_manager->document_server_preloader_url()->string();
}
private function getEditButton()
{
$configManager = new ConfigManager();
return $configManager->getConfig("mode") != "view" ?
'<div id="beginEdit" class="button orange disable">Edit</div>' : "";
return '<div id="beginEdit" class="button orange disable">Edit</div>';
}
}

View File

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.web>
<customErrors mode="Off"/>
</system.web>
<system.webServer>
<defaultDocument enabled="true" />
<httpErrors errorMode="Detailed"/>
<urlCompression doDynamicCompression="false"/>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
</customHeaders>
</httpProtocol>
</system.webServer>
</configuration>

View File

@ -1,120 +0,0 @@
<?php
/**
* (c) Copyright Ascensio System SIA 2023
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace OnlineEditorsExamplePhp;
use OnlineEditorsExamplePhp\Helpers\ConfigManager;
/**
* WebEditor AJAX Process Execution.
*/
require_once dirname(__FILE__) . '/ajax.php';
require_once dirname(__FILE__) . '/functions.php';
require_once dirname(__FILE__) . '/trackmanager.php';
require_once dirname(__FILE__) . '/vendor/autoload.php';
$configManager = new ConfigManager();
// define tracker status
$_trackerStatus = [
0 => 'NotFound',
1 => 'Editing',
2 => 'MustSave',
3 => 'Corrupted',
4 => 'Closed',
6 => 'MustForceSave',
7 => 'CorruptedForceSave',
];
// ignore self-signed certificate
if ($configManager->getConfig("docServVerifyPeerOff") === true) {
stream_context_set_default([
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false,
],
]);
}
// check if type value exists
if (isset($_GET["type"]) && !empty($_GET["type"])) {
@header('Content-Type: application/json; charset==utf-8');
@header('X-Robots-Tag: noindex');
@header('X-Content-Type-Options: nosniff');
// set headers that prevent caching in all the browsers
nocacheHeaders();
// write the request result to the log file
sendlog(serialize($_GET), "webedior-ajax.log");
$type = $_GET["type"];
// switch case for type value
switch ($type) {
case "upload":
$response_array = upload();
$response_array['status'] = isset($response_array['error']) ? 'error' : 'success';
die(json_encode($response_array));
case "download":
$response_array = download();
$response_array['status'] = 'success';
die(json_encode($response_array));
case "history":
$response_array = historyDownload();
$response_array['status'] = 'success';
die(json_encode($response_array));
case "convert":
$response_array = convert();
$response_array['status'] = 'success';
die(json_encode($response_array));
case "track":
$response_array = track();
$response_array['status'] = 'success';
die(json_encode($response_array));
case "delete":
$response_array = delete();
$response_array['status'] = 'success';
die(json_encode($response_array));
case "assets":
$response_array = assets();
$response_array['status'] = 'success';
die(json_encode($response_array));
case "csv":
$response_array = csv();
$response_array['status'] = 'success';
die(json_encode($response_array));
case "reference":
$response_array = reference();
$response_array['status'] = 'success';
die(json_encode($response_array));
case "files":
$response_array = files();
die(json_encode($response_array));
case "saveas":
$response_array = saveas();
$response_array['status'] = 'success';
die(json_encode($response_array));
case "rename":
$response_array = renamefile();
die(json_encode($response_array));
default:
$response_array['status'] = 'error';
$response_array['error'] = '404 Method not found';
die(json_encode($response_array));
}
}

View File

@ -0,0 +1,3 @@
*.egg-info
build
storage

View File

@ -0,0 +1 @@
3.11

View File

@ -1,9 +1,17 @@
ONLYOFFICE Applications example uses code from the following 3rd party projects:
django-stubs - PEP-484 stubs for Django. (https://github.com/typeddjango/django-stubs/blob/master/LICENSE.md)
License: MIT
License File: django-stubs.license
Django - Django is a high-level Python web framework that encourages rapid development and clean, pragmatic design. Thanks for checking it out. (https://github.com/django/django/blob/main/LICENSE)
License: BSD-3-Clause
License File: Django.license
flake8 - flake8 is a python tool that glues together pycodestyle, pyflakes, mccabe, and third-party plugins to check the style and quality of some python code. (https://github.com/PyCQA/flake8/blob/main/LICENSE)
License: MIT
License File: flake8.license
jQuery.BlockUI - The jQuery BlockUI Plugin lets you simulate synchronous behavior when using AJAX, without locking the browser. (https://github.com/malsup/blockui/)
License: MIT, GPL
License File: jQuery.BlockUI.license
@ -28,6 +36,10 @@ jQuery.UI - jQuery UI is an open source library of interface components —
License: MIT
License File: jQuery.UI.license
mypy - Optional static typing for Python. (https://github.com/python/mypy/blob/master/LICENSE)
License: MIT
License File: mypy.license
PyJWT - A Python implementation of RFC 7519. (https://github.com/jpadilla/pyjwt/blob/master/LICENSE)
License: MIT
License File: PyJWT.license
@ -39,3 +51,7 @@ License File: python-magic.license
requests - Requests allows you to send HTTP/1.1 requests extremely easily. Theres no need to manually add query strings to your URLs, or to form-encode your PUT & POST data — but nowadays, just use the json method! (https://github.com/psf/requests/blob/main/LICENSE)
License: Apache 2.0
License File: requests.license
typeshed - Collection of library stubs for Python, with static types. (https://github.com/python/typeshed/blob/main/LICENSE)
License: Apache 2.0
License File: typeshed.license

View File

@ -0,0 +1,19 @@
FROM python:3.11.4-alpine3.18 AS example-base
WORKDIR /srv
COPY . .
RUN \
apk update && \
apk add --no-cache \
libmagic \
make
FROM example-base AS example-dev
RUN make dev
CMD ["make", "server-dev"]
FROM example-base AS example-prod
RUN make prod
CMD ["make", "server-prod"]
FROM nginx:1.23.4-alpine3.17 AS proxy
COPY proxy/nginx.conf /etc/nginx/nginx.conf

View File

@ -0,0 +1,55 @@
.DEFAULT_GOAL := help
.PHONY: help
help: # Show help message for each of the Makefile recipes.
@grep -E "^[a-z-]+: #" $(MAKEFILE_LIST) | \
awk 'BEGIN {FS = ": # "}; {printf "%s: %s\n", $$1, $$2}'
.PHONY: dev
dev: # Install development dependencies.
@pip install --editable .[development]
.PHONY: prod
prod: # Install production dependencies.
@pip install .
.PHONY: server-dev
server-dev: # Start the development server on localhost at $PORT (default: 8000).
@python manage.py runserver
.PHONY: server-prod
server-prod: \
export DEBUG := false
server-prod: # Start the production server on 0.0.0.0 at $PORT (default: 8000).
@python manage.py runserver
.PHONY: compose-dev
compose-dev: # Up containers in a development environment.
@docker-compose \
--file compose-base.yml \
--file compose-dev.yml \
build
@docker-compose \
--file compose-base.yml \
--file compose-dev.yml \
up --detach
.PHONY: compose-prod
compose-prod: # Up containers in a production environment.
@docker-compose \
--file compose-base.yml \
--file compose-prod.yml \
build
@docker-compose \
--file compose-base.yml \
--file compose-prod.yml \
up --detach
.PHONY: lint
lint: # Lint the source code for style and check for types.
@flake8
@mypy .
.PHONY: test
test: # Recursively run the tests.
@python -m unittest ./src/**/*_tests.py

View File

@ -0,0 +1,33 @@
version: "3.8"
services:
# document-server:
# container_name: document-server
# image: onlyoffice/documentserver:7.3.3.50
# expose:
# - "80"
# environment:
# - JWT_SECRET=your-256-bit-secret
example:
container_name: example
build:
context: .
expose:
- "80"
environment:
- ADDRESS=0.0.0.0
- DOCUMENT_SERVER_PRIVATE_URL=http://proxy:8080
- DOCUMENT_SERVER_PUBLIC_URL=http://localhost:8080
- EXAMPLE_URL=http://proxy
- JWT_SECRET=your-256-bit-secret
- PORT=80
proxy:
container_name: proxy
build:
context: .
target: proxy
ports:
- "80:80"
- "8080:8080"

View File

@ -0,0 +1,6 @@
version: "3.8"
services:
example:
build:
target: example-dev

View File

@ -0,0 +1,6 @@
version: "3.8"
services:
example:
build:
target: example-prod

View File

@ -1,108 +0,0 @@
import os
VERSION = '1.6.0'
FILE_SIZE_MAX = 5242880
STORAGE_PATH = 'app_data'
DOC_SERV_FILLFORMS = [".docx", ".oform"]
DOC_SERV_VIEWED = [".djvu", ".oxps", ".pdf", ".xps"] # file extensions that can be viewed
DOC_SERV_EDITED = [ # file extensions that can be edited
".csv", ".docm", ".docx", ".docxf", ".dotm", ".dotx",
".epub", ".fb2", ".html", ".odp", ".ods", ".odt", ".otp",
".ots", ".ott", ".potm", ".potx", ".ppsm", ".ppsx", ".pptm",
".pptx", ".rtf", ".txt", ".xlsm", ".xlsx", ".xltm", ".xltx"
]
DOC_SERV_CONVERT = [ # file extensions that can be converted
".doc", ".dot", ".dps", ".dpt", ".epub", ".et", ".ett", ".fb2",
".fodp", ".fods", ".fodt", ".htm", ".html", ".mht", ".mhtml",
".odp", ".ods", ".odt", ".otp", ".ots", ".ott", ".pot", ".pps",
".ppt", ".rtf", ".stw", ".sxc", ".sxi", ".sxw", ".wps", ".wpt",
".xls", ".xlsb", ".xlt", ".xml"
]
DOC_SERV_TIMEOUT = 120000
DOC_SERV_SITE_URL = 'http://documentserver/'
DOC_SERV_CONVERTER_URL = 'ConvertService.ashx'
DOC_SERV_API_URL = 'web-apps/apps/api/documents/api.js'
DOC_SERV_PRELOADER_URL = 'web-apps/apps/api/documents/cache-scripts.html'
DOC_SERV_COMMAND_URL='coauthoring/CommandService.ashx'
EXAMPLE_DOMAIN = None
DOC_SERV_JWT_SECRET = '' # the secret key for generating token
DOC_SERV_JWT_HEADER = 'Authorization'
DOC_SERV_JWT_USE_FOR_REQUEST = True
DOC_SERV_VERIFY_PEER = False
EXT_SPREADSHEET = [
".xls", ".xlsx", ".xlsm", ".xlsb",
".xlt", ".xltx", ".xltm",
".ods", ".fods", ".ots", ".csv"
]
EXT_PRESENTATION = [
".pps", ".ppsx", ".ppsm",
".ppt", ".pptx", ".pptm",
".pot", ".potx", ".potm",
".odp", ".fodp", ".otp"
]
EXT_DOCUMENT = [
".doc", ".docx", ".docm",
".dot", ".dotx", ".dotm",
".odt", ".fodt", ".ott", ".rtf", ".txt",
".html", ".htm", ".mht", ".xml",
".pdf", ".djvu", ".fb2", ".epub", ".xps", ".oxps", ".oform"
]
LANGUAGES = {
'en': 'English',
'hy': 'Armenian',
'az': 'Azerbaijani',
'eu': 'Basque',
'be': 'Belarusian',
'bg': 'Bulgarian',
'ca': 'Catalan',
'zh': 'Chinese (Simplified)',
'zh-TW': 'Chinese (Traditional)',
'cs': 'Czech',
'da': 'Danish',
'nl': 'Dutch',
'fi': 'Finnish',
'fr': 'French',
'gl': 'Galego',
'de': 'German',
'el': 'Greek',
'hu': 'Hungarian',
'id': 'Indonesian',
'it': 'Italian',
'ja': 'Japanese',
'ko': 'Korean',
'lo': 'Lao',
'lv': 'Latvian',
'ms': 'Malay (Malaysia)',
'no': 'Norwegian',
'pl': 'Polish',
'pt': 'Portuguese (Brazil)',
'pt-PT': 'Portuguese (Portugal)',
'ro': 'Romanian',
'ru': 'Russian',
'si': 'Sinhala (Sri Lanka)',
'sk': 'Slovak',
'sl': 'Slovenian',
'es': 'Spanish',
'sv': 'Swedish',
'tr': 'Turkish',
'uk': 'Ukrainian',
'vi': 'Vietnamese',
'aa-AA': 'Test Language'
}
if os.environ.get("EXAMPLE_DOMAIN"): # generates a link for example domain
EXAMPLE_DOMAIN = os.environ.get("EXAMPLE_DOMAIN")
if os.environ.get("DOC_SERV"): # generates links for document server
DOC_SERV_SITE_URL = os.environ.get("DOC_SERV")

View File

@ -1,9 +1,17 @@
ONLYOFFICE Applications example uses code from the following 3rd party projects:
django-stubs - PEP-484 stubs for Django. (https://github.com/typeddjango/django-stubs/blob/master/LICENSE.md)
License: MIT
License File: django-stubs.license
Django - Django is a high-level Python web framework that encourages rapid development and clean, pragmatic design. Thanks for checking it out. (https://github.com/django/django/blob/main/LICENSE)
License: BSD-3-Clause
License File: Django.license
flake8 - flake8 is a python tool that glues together pycodestyle, pyflakes, mccabe, and third-party plugins to check the style and quality of some python code. (https://github.com/PyCQA/flake8/blob/main/LICENSE)
License: MIT
License File: flake8.license
jQuery.BlockUI - The jQuery BlockUI Plugin lets you simulate synchronous behavior when using AJAX, without locking the browser. (https://github.com/malsup/blockui/)
License: MIT, GPL
License File: jQuery.BlockUI.license
@ -28,6 +36,10 @@ jQuery.UI - jQuery UI is an open source library of interface components —
License: MIT
License File: jQuery.UI.license
mypy - Optional static typing for Python. (https://github.com/python/mypy/blob/master/LICENSE)
License: MIT
License File: mypy.license
PyJWT - A Python implementation of RFC 7519. (https://github.com/jpadilla/pyjwt/blob/master/LICENSE)
License: MIT
License File: PyJWT.license
@ -39,3 +51,7 @@ License File: python-magic.license
requests - Requests allows you to send HTTP/1.1 requests extremely easily. Theres no need to manually add query strings to your URLs, or to form-encode your PUT & POST data — but nowadays, just use the json method! (https://github.com/psf/requests/blob/main/LICENSE)
License: Apache 2.0
License File: requests.license
typeshed - Collection of library stubs for Python, with static types. (https://github.com/python/typeshed/blob/main/LICENSE)
License: Apache 2.0
License File: typeshed.license

View File

@ -0,0 +1,8 @@
Copyright (c) Maxim Kurnikov.
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,22 @@
== Flake8 License (MIT) ==
Copyright (C) 2011-2013 Tarek Ziade <tarek@ziade.org>
Copyright (C) 2012-2016 Ian Cordasco <graffatcolmingov@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,229 @@
Mypy (and mypyc) are licensed under the terms of the MIT license, reproduced below.
= = = = =
The MIT License
Copyright (c) 2012-2022 Jukka Lehtosalo and contributors
Copyright (c) 2015-2022 Dropbox, Inc.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
= = = = =
Portions of mypy and mypyc are licensed under different licenses.
The files
mypyc/lib-rt/pythonsupport.h, mypyc/lib-rt/getargs.c and
mypyc/lib-rt/getargsfast.c are licensed under the PSF 2 License, reproduced
below.
= = = = =
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
--------------------------------------------
1. This LICENSE AGREEMENT is between the Python Software Foundation
("PSF"), and the Individual or Organization ("Licensee") accessing and
otherwise using this software ("Python") in source or binary form and
its associated documentation.
2. Subject to the terms and conditions of this License Agreement, PSF hereby
grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
analyze, test, perform and/or display publicly, prepare derivative works,
distribute, and otherwise use Python alone or in any derivative version,
provided, however, that PSF's License Agreement and PSF's notice of copyright,
i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
2011, 2012 Python Software Foundation; All Rights Reserved" are retained in Python
alone or in any derivative version prepared by Licensee.
3. In the event Licensee prepares a derivative work that is based on
or incorporates Python or any part thereof, and wants to make
the derivative work available to others as provided herein, then
Licensee hereby agrees to include in any such work a brief summary of
the changes made to Python.
4. PSF is making Python available to Licensee on an "AS IS"
basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
INFRINGE ANY THIRD PARTY RIGHTS.
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
6. This License Agreement will automatically terminate upon a material
breach of its terms and conditions.
7. Nothing in this License Agreement shall be deemed to create any
relationship of agency, partnership, or joint venture between PSF and
Licensee. This License Agreement does not grant permission to use PSF
trademarks or trade name in a trademark sense to endorse or promote
products or services of Licensee, or any third party.
8. By copying, installing or otherwise using Python, Licensee
agrees to be bound by the terms and conditions of this License
Agreement.
BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
-------------------------------------------
BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
Individual or Organization ("Licensee") accessing and otherwise using
this software in source or binary form and its associated
documentation ("the Software").
2. Subject to the terms and conditions of this BeOpen Python License
Agreement, BeOpen hereby grants Licensee a non-exclusive,
royalty-free, world-wide license to reproduce, analyze, test, perform
and/or display publicly, prepare derivative works, distribute, and
otherwise use the Software alone or in any derivative version,
provided, however, that the BeOpen Python License is retained in the
Software, alone or in any derivative version prepared by Licensee.
3. BeOpen is making the Software available to Licensee on an "AS IS"
basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
INFRINGE ANY THIRD PARTY RIGHTS.
4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
5. This License Agreement will automatically terminate upon a material
breach of its terms and conditions.
6. This License Agreement shall be governed by and interpreted in all
respects by the law of the State of California, excluding conflict of
law provisions. Nothing in this License Agreement shall be deemed to
create any relationship of agency, partnership, or joint venture
between BeOpen and Licensee. This License Agreement does not grant
permission to use BeOpen trademarks or trade names in a trademark
sense to endorse or promote products or services of Licensee, or any
third party. As an exception, the "BeOpen Python" logos available at
http://www.pythonlabs.com/logos.html may be used according to the
permissions granted on that web page.
7. By copying, installing or otherwise using the software, Licensee
agrees to be bound by the terms and conditions of this License
Agreement.
CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
---------------------------------------
1. This LICENSE AGREEMENT is between the Corporation for National
Research Initiatives, having an office at 1895 Preston White Drive,
Reston, VA 20191 ("CNRI"), and the Individual or Organization
("Licensee") accessing and otherwise using Python 1.6.1 software in
source or binary form and its associated documentation.
2. Subject to the terms and conditions of this License Agreement, CNRI
hereby grants Licensee a nonexclusive, royalty-free, world-wide
license to reproduce, analyze, test, perform and/or display publicly,
prepare derivative works, distribute, and otherwise use Python 1.6.1
alone or in any derivative version, provided, however, that CNRI's
License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
1995-2001 Corporation for National Research Initiatives; All Rights
Reserved" are retained in Python 1.6.1 alone or in any derivative
version prepared by Licensee. Alternately, in lieu of CNRI's License
Agreement, Licensee may substitute the following text (omitting the
quotes): "Python 1.6.1 is made available subject to the terms and
conditions in CNRI's License Agreement. This Agreement together with
Python 1.6.1 may be located on the Internet using the following
unique, persistent identifier (known as a handle): 1895.22/1013. This
Agreement may also be obtained from a proxy server on the Internet
using the following URL: http://hdl.handle.net/1895.22/1013".
3. In the event Licensee prepares a derivative work that is based on
or incorporates Python 1.6.1 or any part thereof, and wants to make
the derivative work available to others as provided herein, then
Licensee hereby agrees to include in any such work a brief summary of
the changes made to Python 1.6.1.
4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
INFRINGE ANY THIRD PARTY RIGHTS.
5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
6. This License Agreement will automatically terminate upon a material
breach of its terms and conditions.
7. This License Agreement shall be governed by the federal
intellectual property law of the United States, including without
limitation the federal copyright law, and, to the extent such
U.S. federal law does not apply, by the law of the Commonwealth of
Virginia, excluding Virginia's conflict of law provisions.
Notwithstanding the foregoing, with regard to derivative works based
on Python 1.6.1 that incorporate non-separable material that was
previously distributed under the GNU General Public License (GPL), the
law of the Commonwealth of Virginia shall govern this License
Agreement only as to issues arising under or with respect to
Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this
License Agreement shall be deemed to create any relationship of
agency, partnership, or joint venture between CNRI and Licensee. This
License Agreement does not grant permission to use CNRI trademarks or
trade name in a trademark sense to endorse or promote products or
services of Licensee, or any third party.
8. By clicking on the "ACCEPT" button where indicated, or by copying,
installing or otherwise using Python 1.6.1, Licensee agrees to be
bound by the terms and conditions of this License Agreement.
ACCEPT
CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
--------------------------------------------------
Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
The Netherlands. All rights reserved.
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the name of Stichting Mathematisch
Centrum or CWI not be used in advertising or publicity pertaining to
distribution of the software without specific, written prior
permission.
STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@ -0,0 +1,237 @@
The "typeshed" project is licensed under the terms of the Apache license, as
reproduced below.
= = = = =
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.
= = = = =
Parts of typeshed are licensed under different licenses (like the MIT
license), reproduced below.
= = = = =
The MIT License
Copyright (c) 2015 Jukka Lehtosalo and contributors
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
= = = = =

View File

@ -1,21 +1,92 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
from os import environ
from pathlib import Path
from sys import argv
from uuid import uuid1
from django import setup
from django.conf import settings
from django.core.management import execute_from_command_line
from django.core.management.commands.runserver import Command as RunServer
from django.conf.urls.static import static
from django.urls import path
from src.common import string
from src.views import actions, index
def debug():
env = environ.get('DEBUG')
return string.boolean(env, True)
def main():
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'src.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
def address():
env = environ.get('ADDRESS')
if env is not None:
return env
if settings.DEBUG:
return RunServer.default_addr
return '0.0.0.0'
def port():
env = environ.get('PORT')
if env:
return int(env)
return RunServer.default_port
def configuration():
file = Path(__file__)
base_dir = file.parent
static_root = base_dir.joinpath('static')
static_url = f'{static_root.name}/'
return {
'ALLOWED_HOSTS': [
'*'
],
'BASE_DIR': f'{base_dir}',
'DEBUG': debug(),
'MIDDLEWARE': [
'src.utils.historyManager.CorsHeaderMiddleware'
],
'ROOT_URLCONF': __name__,
'SECRET_KEY': uuid1(),
'STATIC_ROOT': f'{static_root}',
'STATIC_URL': static_url,
'TEMPLATES': [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
'templates'
]
}
]
}
def routers():
main = [
path('', index.default),
path('convert', actions.convert),
path('create', actions.createNew),
path('csv', actions.csv),
path('download', actions.download),
path('downloadhistory', actions.downloadhistory),
path('edit', actions.edit),
path('files', actions.files),
path('formats-convertible', actions.formats_convertible),
path('reference', actions.reference),
path('remove', actions.remove),
path('rename', actions.rename),
path('restore', actions.restore),
path('saveas', actions.saveAs),
path('track', actions.track),
path('upload', actions.upload)
]
main += static(
settings.STATIC_URL,
document_root=settings.STATIC_ROOT
)
return main
settings.configure(**configuration())
urlpatterns = routers()
RunServer.default_addr = address()
RunServer.default_port = port()
setup()
if __name__ == '__main__':
main()
execute_from_command_line(argv)

View File

@ -0,0 +1,38 @@
worker_processes auto;
events {
worker_connections 512;
}
http {
include /etc/nginx/mime.types;
server {
listen 80;
server_name localhost;
location / {
proxy_http_version 1.1;
proxy_pass http://example;
}
}
# server {
# listen 8080;
# server_name localhost;
# location / {
# client_max_body_size 100m;
# proxy_http_version 1.1;
# proxy_pass http://document-server;
# proxy_redirect off;
# proxy_set_header Connection "upgrade";
# proxy_set_header Host $http_host;
# proxy_set_header Upgrade $http_upgrade;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Forwarded-Host $http_x_forwarded_host;
# proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
# proxy_set_header X-Real-IP $remote_addr;
# }
# }
}

View File

@ -0,0 +1,38 @@
[build-system]
requires = [
"setuptools>=67.6.0"
]
[project]
name = "online-editor-example"
version = "1.6.0"
requires-python = ">=3.11"
dependencies = [
"django>=3.1.3",
"django-stubs>=4.2.3",
"pyjwt>=2.6.0",
"python-magic>=0.4.27",
"requests>=2.25.0"
]
[project.license]
text = "Apache-2.0"
[[project.authors]]
name = "ONLYOFFICE"
email = "support@onlyoffice.com"
[project.optional-dependencies]
development = [
"flake8>=6.0.0",
"mypy>=1.4.1",
"types-requests>=2.31.0"
]
[tool.mypy]
plugins = [
"mypy_django_plugin.main"
]
[tool.django-stubs]
django_settings_module = "manage"

View File

@ -0,0 +1,35 @@
#
# (c) Copyright Ascensio System SIA 2023
#
# 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.
#
'''
The Codable module provides the ability to decode a string JSON into a class
instance and encode it back. It also provides the ability to remap JSON keys and
work with nested Codable instances.
```python
from dataclasses import dataclass
from src.codable import Codable, CodingKey
@dataclass
class Fruit(Codable):
class CodingKeys(CodingKey):
native_for_python: 'foreignForPython'
native_for_python: str
```
'''
from .codable import Codable, CodingKey

View File

@ -0,0 +1,189 @@
#
# (c) Copyright Ascensio System SIA 2023
#
# 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.
#
from __future__ import annotations
from copy import deepcopy
from enum import StrEnum
from json import JSONDecoder, JSONEncoder
from typing import Any, Optional, Self, Type, get_args, get_origin, get_type_hints
class Monkey():
key: str
def __init__(self, key: str = '_slugs'):
self.key = key
def patch(self, obj: dict[str, Any]) -> dict[str, Any]:
def inner(slug: list[str], value: Any):
if isinstance(value, dict):
value[self.key] = slug
for child_slug, child_value in value.items():
inner(slug + [child_slug], child_value)
return
if isinstance(value, list):
for child_value in value:
inner(slug, child_value)
copied = deepcopy(obj)
inner([], copied)
return copied
def slugs(self, obj: dict[str, Any]) -> list[str]:
return obj[self.key]
def clean(self, obj: dict[str, Any]) -> dict[str, Any]:
copied = deepcopy(obj)
del copied[self.key]
return copied
class CodingKey(StrEnum):
@classmethod
def keywords(cls, obj: dict[str, Any]) -> dict[str, Any]:
words = {}
for pair in list(cls):
# Errors are false positives.
native = pair.name # type: ignore
foreign = pair.value # type: ignore
value = obj.get(foreign)
words[native] = value
return words
class Codable():
__decoder = JSONDecoder()
__encoder = JSONEncoder()
__monkey = Monkey()
class CodingKeys(CodingKey):
pass
@classmethod
def decode(cls, content: str) -> Self:
decoded = cls.__decoder.decode(content)
patched = cls.__monkey.patch(decoded)
encoded = cls.__encoder.encode(patched)
decoder = Decoder(
monkey=cls.__monkey,
cls=cls
)
return decoder.decode(encoded)
def encode(self) -> str:
cls = type(self)
encoder = Encoder(
decoder=self.__decoder,
cls=cls
)
return encoder.encode(self)
class Decoder(JSONDecoder):
monkey: Monkey
cls: Type[Codable]
def __init__(
self,
monkey: Monkey,
cls: Type[Codable],
**kwargs
):
self.monkey = monkey
self.cls = cls
kwargs['object_hook'] = self.__object_hook
super().__init__(**kwargs)
def __object_hook(self, obj):
cls = self.cls
for foreign in self.monkey.slugs(obj):
native = cls.CodingKeys(foreign).name
if native is None:
return self.monkey.clean(obj)
types = get_type_hints(cls)
cls = self.__find_codable(types[native])
if cls is None:
return self.monkey.clean(obj)
cleaned = self.monkey.clean(obj)
return self.__init_codable(cls, cleaned)
def __find_codable(self, cls: Type) -> Optional[Type[Codable]]:
if issubclass(cls, Codable):
return cls
if get_origin(cls) is list:
item = get_args(cls)[0]
return self.__find_codable(item)
return None
def __init_codable(self, cls: Type[Codable], obj: dict[str, Any]) -> Codable:
keywords = cls.CodingKeys.keywords(obj)
return cls(**keywords)
class Encoder(JSONEncoder):
decoder: JSONDecoder
cls: Type[Codable]
def __init__(
self,
decoder: JSONDecoder,
cls: Type[Codable],
indent: int = 2,
**kwargs
):
self.decoder = decoder
self.cls = cls
kwargs['indent'] = indent
super().__init__(**kwargs)
def default(self, o):
obj = {}
for pair in list(self.cls.CodingKeys):
native = pair.name
foreign = pair.value
if not hasattr(o, native):
continue
value = getattr(o, native)
obj[foreign] = self.__prepare_value(value)
return obj
def __prepare_value(self, value: Any) -> Any:
if isinstance(value, Codable):
return self.__prepare_codable(value)
if isinstance(value, list):
return self.__prepare_list(value)
return value
def __prepare_codable(self, value: Codable) -> Any:
content = value.encode()
return self.decoder.decode(content)
def __prepare_list(self, value: list[Any]) -> list[Any]:
mapped = map(self.__prepare_value, value)
return list(mapped)

View File

@ -0,0 +1,154 @@
#
# (c) Copyright Ascensio System SIA 2023
#
# 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.
#
from __future__ import annotations
from dataclasses import dataclass
from textwrap import dedent
from typing import Optional
from unittest import TestCase
from . import Codable, CodingKey
@dataclass
class Fruit(Codable):
class CodingKeys(CodingKey):
name = 'fruit_name'
weight = 'fruitWeight'
texture = 'fruit_texture'
vitamins = 'fruitVitamins'
organic = 'fruit_organic'
name: str
weight: int
texture: Optional[str]
vitamins: list[str]
organic: bool
class CodablePlainTests(TestCase):
json = (
dedent(
'''
{
"fruit_name": "kiwi",
"fruitWeight": 100,
"fruit_texture": null,
"fruitVitamins": [
"Vitamin C",
"Vitamin K"
],
"fruit_organic": true
}
'''
)
.strip()
)
def test_decodes(self):
fruit = Fruit.decode(self.json)
self.assertEqual(fruit.name, 'kiwi')
self.assertEqual(fruit.weight, 100)
self.assertIsNone(fruit.texture)
self.assertEqual(fruit.vitamins, ['Vitamin C', 'Vitamin K'])
self.assertTrue(fruit.organic)
def test_encodes(self):
fruit = Fruit(
name='kiwi',
weight=100,
texture=None,
vitamins=['Vitamin C', 'Vitamin K'],
organic=True
)
content = fruit.encode()
self.assertEqual(content, self.json)
@dataclass
class Smoothie(Codable):
class CodingKeys(CodingKey):
recipe = 'recipe'
recipe: Recipe
@dataclass
class Recipe(Codable):
class CodingKeys(CodingKey):
ingredients = 'ingredients'
ingredients: list[Ingredient]
@dataclass
class Ingredient(Codable):
class CodingKeys(CodingKey):
name = 'name'
name: str
class CodableNestedTests(TestCase):
json = (
dedent(
'''
{
"recipe": {
"ingredients": [
{
"name": "kiwi"
}
]
}
}
'''
)
.strip()
)
def test_decodes(self):
smoothie = Smoothie.decode(self.json)
self.assertEqual(smoothie.recipe.ingredients[0].name, 'kiwi')
def test_encodes(self):
ingredient = Ingredient(name='kiwi')
recipe = Recipe(ingredients=[ingredient])
smoothie = Smoothie(recipe=recipe)
content = smoothie.encode()
self.assertEqual(content, self.json)
@dataclass
class Vegetable(Codable):
class CodingKeys(CodingKey):
name = 'name'
name: Optional[str]
class CodableMissedTests(TestCase):
source_json = '{}'
distribute_json = (
dedent(
'''
{
"name": null
}
'''
)
.strip()
)
def test_decodes(self):
vegetable = Vegetable.decode(self.source_json)
self.assertIsNone(vegetable.name)
def test_encodes(self):
vegetable = Vegetable(name=None)
content = vegetable.encode()
self.assertEqual(content, self.distribute_json)

View File

@ -0,0 +1,23 @@
#
# (c) Copyright Ascensio System SIA 2023
#
# 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.
#
'''
The common module is a collection of small, generic functions that are
independent of business logic.
'''
from . import http
from . import string

View File

@ -0,0 +1,49 @@
#
# (c) Copyright Ascensio System SIA 2023
#
# 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.
#
# TODO: add types for parameters.
# https://github.com/python/typing/discussions/946r
from http import HTTPStatus, HTTPMethod
from django.http import HttpRequest, HttpResponse
def GET():
return method(HTTPMethod.GET)
def POST():
return method(HTTPMethod.POST)
def PUT():
return method(HTTPMethod.PUT)
def method(meth: HTTPMethod):
def wrapper(func):
def inner(request: HttpRequest, *args, **kwargs):
if request.method is None:
return HttpResponse(
status=HTTPStatus.METHOD_NOT_ALLOWED
)
if request.method.upper() != meth.name:
return HttpResponse(
status=HTTPStatus.METHOD_NOT_ALLOWED
)
return func(request, *args, **kwargs)
return inner
return wrapper

View File

@ -0,0 +1,41 @@
#
# (c) Copyright Ascensio System SIA 2023
#
# 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.
#
from http import HTTPMethod, HTTPStatus
from unittest import TestCase
from django.conf import settings
from django.http import HttpRequest, HttpResponse
from . import http
# Under the hood, HttpRequest uses a settings object.
settings.configure()
@http.GET()
def endpoint(_: HttpRequest) -> HttpResponse:
return HttpResponse()
class HTTPMethodTests(TestCase):
def test_returns_a_response_from_the_endpoint(self):
request = HttpRequest()
request.method = HTTPMethod.GET
response = endpoint(request)
self.assertEqual(response.status_code, HTTPStatus.OK)
def test_returns_an_error_response(self):
request = HttpRequest()
request.method = HTTPMethod.POST
response = endpoint(request)
self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED)

View File

@ -0,0 +1,40 @@
#
# (c) Copyright Ascensio System SIA 2023
#
# 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.
#
from typing import Optional
def boolean(string: Optional[str], default: bool = False) -> bool:
'''
Converts a string that represents a boolean value to its corresponding
boolean value. It supports case-insensitive `true`, `t`, `yes`, `y`, and `1`
for the positive value, and `false`, `f`, `no`, `n`, and `0` for the
negative value. If the string doesn't match any of these values, returns the
default value.
'''
if string is None:
return default
lower = string.lower()
positive = lower in ["true", "t", "yes", "y", "1"]
if positive:
return True
negative = lower in ["false", "f", "no", "n", "0"]
if negative:
return False
return default

View File

@ -0,0 +1,56 @@
#
# (c) Copyright Ascensio System SIA 2023
#
# 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.
#
from unittest import TestCase
from .string import boolean
class BooleanDefaultTests(TestCase):
def test_converts_to_the_default_value(self):
value = boolean("unknown")
self.assertFalse(value)
def test_converts_to_the_negative_value_by_default(self):
value = boolean("unknown", False)
self.assertFalse(value)
def test_converts_to_the_positive_value_by_default(self):
value = boolean("unknown", True)
self.assertTrue(value)
class BooleanOptionalTests(TestCase):
def test_converts_to_the_default_value(self):
value = boolean(None)
self.assertFalse(value)
def test_converts_to_the_negative_value_by_default(self):
value = boolean(None, False)
self.assertFalse(value)
def test_converts_to_the_positive_value_by_default(self):
value = boolean(None, True)
self.assertTrue(value)
class BooleanNegativeTests(TestCase):
def test_converts_a_negative_string_to_the_negative_value(self):
for string in ["false", "f", "no", "n", "0"]:
value = boolean(string)
self.assertFalse(value)
class BooleanPositiveTests(TestCase):
def test_converts_a_positive_string_to_the_positive_value(self):
for string in ["true", "t", "yes", "y", "1"]:
value = boolean(string)
self.assertTrue(value)

View File

@ -0,0 +1,17 @@
#
# (c) Copyright Ascensio System SIA 2023
#
# 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.
#
from .configuration import *

View File

@ -0,0 +1,162 @@
#
# (c) Copyright Ascensio System SIA 2023
#
# 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.
#
from os import environ
from pathlib import Path
from typing import Optional
from urllib.parse import ParseResult, urlparse, urljoin
from src.common import string
class ConfigurationManager:
version = '1.6.0'
def example_url(self) -> Optional[ParseResult]:
url = environ.get('EXAMPLE_URL')
if not url:
return None
return urlparse(url)
def document_server_public_url(self) -> ParseResult:
url = (
environ.get('DOCUMENT_SERVER_PUBLIC_URL') or
'http://document-server'
)
return urlparse(url)
def document_server_private_url(self) -> ParseResult:
url = environ.get('DOCUMENT_SERVER_PRIVATE_URL')
if not url:
return self.document_server_public_url()
return urlparse(url)
def document_server_api_url(self) -> ParseResult:
server_url = self.document_server_public_url()
base_url = server_url.geturl()
path = (
environ.get('DOCUMENT_SERVER_API_PATH') or
'/web-apps/apps/api/documents/api.js'
)
url = urljoin(base_url, path)
return urlparse(url)
def document_server_preloader_url(self) -> ParseResult:
server_url = self.document_server_public_url()
base_url = server_url.geturl()
path = (
environ.get('DOCUMENT_SERVER_PRELOADER_PATH') or
'/web-apps/apps/api/documents/cache-scripts.html'
)
url = urljoin(base_url, path)
return urlparse(url)
def document_server_command_url(self) -> ParseResult:
server_url = self.document_server_private_url()
base_url = server_url.geturl()
path = (
environ.get('DOCUMENT_SERVER_COMMAND_PATH') or
'/coauthoring/CommandService.ashx'
)
url = urljoin(base_url, path)
return urlparse(url)
def document_server_converter_url(self) -> ParseResult:
server_url = self.document_server_private_url()
base_url = server_url.geturl()
path = (
environ.get('DOCUMENT_SERVER_CONVERTER_PATH') or
'/ConvertService.ashx'
)
url = urljoin(base_url, path)
return urlparse(url)
def jwt_secret(self) -> str:
return environ.get('JWT_SECRET') or ''
def jwt_header(self) -> str:
return environ.get('JWT_HEADER') or 'Authorization'
def jwt_use_for_request(self) -> bool:
use = environ.get('JWT_USE_FOR_REQUEST')
return string.boolean(use, True)
def ssl_verify_peer_mode_enabled(self) -> bool:
enabled = environ.get('SSL_VERIFY_PEER_MODE_ENABLED')
return string.boolean(enabled, False)
def storage_path(self) -> Path:
storage_path = environ.get('STORAGE_PATH') or 'storage'
storage_directory = Path(storage_path)
if storage_directory.is_absolute():
return storage_directory
file = Path(__file__)
directory = file.parent.joinpath('../..', storage_directory)
return directory.resolve()
def maximum_file_size(self) -> int:
size = environ.get('MAXIMUM_FILE_SIZE')
if size:
return int(size)
return 5 * 1024 * 1024
def conversion_timeout(self) -> int:
timeout = environ.get('CONVERSION_TIMEOUT')
if timeout:
return int(timeout)
return 120 * 1000
def languages(self) -> dict[str, str]:
return {
'en': 'English',
'hy': 'Armenian',
'az': 'Azerbaijani',
'eu': 'Basque',
'be': 'Belarusian',
'bg': 'Bulgarian',
'ca': 'Catalan',
'zh': 'Chinese (Simplified)',
'zh-TW': 'Chinese (Traditional)',
'cs': 'Czech',
'da': 'Danish',
'nl': 'Dutch',
'fi': 'Finnish',
'fr': 'French',
'gl': 'Galego',
'de': 'German',
'el': 'Greek',
'hu': 'Hungarian',
'id': 'Indonesian',
'it': 'Italian',
'ja': 'Japanese',
'ko': 'Korean',
'lo': 'Lao',
'lv': 'Latvian',
'ms': 'Malay (Malaysia)',
'no': 'Norwegian',
'pl': 'Polish',
'pt': 'Portuguese (Brazil)',
'pt-PT': 'Portuguese (Portugal)',
'ro': 'Romanian',
'ru': 'Russian',
'si': 'Sinhala (Sri Lanka)',
'sk': 'Slovak',
'sl': 'Slovenian',
'es': 'Spanish',
'sv': 'Swedish',
'tr': 'Turkish',
'uk': 'Ukrainian',
'vi': 'Vietnamese',
'aa-AA': 'Test Language'
}

View File

@ -0,0 +1,296 @@
#
# (c) Copyright Ascensio System SIA 2023
#
# 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.
#
from os import environ
from unittest import TestCase
from unittest.mock import patch
from urllib.parse import urlparse
from . import ConfigurationManager
class ConfigurationManagerTests(TestCase):
def test_corresponds_the_latest_version(self):
config_manager = ConfigurationManager()
self.assertEqual(config_manager.version, '1.6.0')
class ConfigurationManagerExampleURLTests(TestCase):
def test_assigns_a_default_value(self):
config_manager = ConfigurationManager()
url = config_manager.example_url()
self.assertIsNone(url)
@patch.dict(environ, {
'EXAMPLE_URL': 'http://localhost'
})
def test_assigns_a_value_from_the_environment(self):
config_manager = ConfigurationManager()
url = config_manager.example_url()
self.assertEqual(url.geturl(), 'http://localhost')
class ConfigurationManagerDocumentServerPublicURLTests(TestCase):
def test_assigns_a_default_value(self):
config_manager = ConfigurationManager()
url = config_manager.document_server_public_url()
self.assertEqual(url.geturl(), 'http://document-server')
@patch.dict(environ, {
'DOCUMENT_SERVER_PUBLIC_URL': 'http://localhost'
})
def test_assigns_a_value_from_the_environment(self):
config_manager = ConfigurationManager()
url = config_manager.document_server_public_url()
self.assertEqual(url.geturl(), 'http://localhost')
class ConfigurationManagerDocumentServerPrivateURLTests(TestCase):
def test_assigns_a_default_value(self):
config_manager = ConfigurationManager()
url = config_manager.document_server_private_url()
self.assertEqual(url.geturl(), 'http://document-server')
@patch.dict(environ, {
'DOCUMENT_SERVER_PRIVATE_URL': 'http://localhost'
})
def test_assigns_a_value_from_the_environment(self):
config_manager = ConfigurationManager()
url = config_manager.document_server_private_url()
self.assertEqual(url.geturl(), 'http://localhost')
class ConfigurationManagerDocumentServerAPIURLTests(TestCase):
@patch.object(
ConfigurationManager,
'document_server_public_url',
return_value=urlparse('http://localhost')
)
def test_assigns_a_default_value(self, _):
config_manager = ConfigurationManager()
url = config_manager.document_server_api_url()
self.assertEqual(
url.geturl(),
'http://localhost/web-apps/apps/api/documents/api.js'
)
@patch.object(
ConfigurationManager,
'document_server_public_url',
return_value=urlparse('http://localhost')
)
@patch.dict(environ, {
'DOCUMENT_SERVER_API_PATH': '/api'
})
def test_assigns_a_value_from_the_environment(self, _):
config_manager = ConfigurationManager()
url = config_manager.document_server_api_url()
self.assertEqual(
url.geturl(),
'http://localhost/api'
)
class ConfigurationManagerDocumentServerPreloaderURLTests(TestCase):
@patch.object(
ConfigurationManager,
'document_server_public_url',
return_value=urlparse('http://localhost')
)
def test_assigns_a_default_value(self, _):
config_manager = ConfigurationManager()
url = config_manager.document_server_preloader_url()
self.assertEqual(
url.geturl(),
'http://localhost/web-apps/apps/api/documents/cache-scripts.html'
)
@patch.object(
ConfigurationManager,
'document_server_public_url',
return_value=urlparse('http://localhost')
)
@patch.dict(environ, {
'DOCUMENT_SERVER_PRELOADER_PATH': '/preloader'
})
def test_assigns_a_value_from_the_environment(self, _):
config_manager = ConfigurationManager()
url = config_manager.document_server_preloader_url()
self.assertEqual(
url.geturl(),
'http://localhost/preloader'
)
class ConfigurationManagerDocumentServerCommandURLTests(TestCase):
@patch.object(
ConfigurationManager,
'document_server_private_url',
return_value=urlparse('http://localhost')
)
def test_assigns_a_default_value(self, _):
config_manager = ConfigurationManager()
url = config_manager.document_server_command_url()
self.assertEqual(
url.geturl(),
'http://localhost/coauthoring/CommandService.ashx'
)
@patch.object(
ConfigurationManager,
'document_server_private_url',
return_value=urlparse('http://localhost')
)
@patch.dict(environ, {
'DOCUMENT_SERVER_COMMAND_PATH': '/command'
})
def test_assigns_a_value_from_the_environment(self, _):
config_manager = ConfigurationManager()
url = config_manager.document_server_command_url()
self.assertEqual(
url.geturl(),
'http://localhost/command'
)
class ConfigurationManagerDocumentServerConverterURLTests(TestCase):
@patch.object(
ConfigurationManager,
'document_server_private_url',
return_value=urlparse('http://localhost')
)
def test_assigns_a_default_value(self, _):
config_manager = ConfigurationManager()
url = config_manager.document_server_converter_url()
self.assertEqual(
url.geturl(),
'http://localhost/ConvertService.ashx'
)
@patch.object(
ConfigurationManager,
'document_server_private_url',
return_value=urlparse('http://localhost')
)
@patch.dict(environ, {
'DOCUMENT_SERVER_CONVERTER_PATH': '/converter'
})
def test_assigns_a_value_from_the_environment(self, _):
config_manager = ConfigurationManager()
url = config_manager.document_server_converter_url()
self.assertEqual(
url.geturl(),
'http://localhost/converter'
)
class ConfigurationManagerJWTSecretTests(TestCase):
def test_assigns_a_default_value(self):
config_manager = ConfigurationManager()
secret = config_manager.jwt_secret()
self.assertEqual(secret, '')
@patch.dict(environ, {
'JWT_SECRET': 'your-256-bit-secret'
})
def test_assigns_a_value_from_the_environment(self):
config_manager = ConfigurationManager()
secret = config_manager.jwt_secret()
self.assertEqual(secret, 'your-256-bit-secret')
class ConfigurationManagerJWTHeaderTests(TestCase):
def test_assigns_a_default_value(self):
config_manager = ConfigurationManager()
header = config_manager.jwt_header()
self.assertEqual(header, 'Authorization')
@patch.dict(environ, {
'JWT_HEADER': 'Proxy-Authorization'
})
def test_assigns_a_value_from_the_environment(self):
config_manager = ConfigurationManager()
header = config_manager.jwt_header()
self.assertEqual(header, 'Proxy-Authorization')
class ConfigurationManagerJWTUseForRequest(TestCase):
def test_assigns_a_default_value(self):
config_manager = ConfigurationManager()
use = config_manager.jwt_use_for_request()
self.assertTrue(use)
@patch.dict(environ, {
'JWT_USE_FOR_REQUEST': 'false'
})
def test_assigns_a_value_from_the_environment(self):
config_manager = ConfigurationManager()
use = config_manager.jwt_use_for_request()
self.assertFalse(use)
class ConfigurationManagerSSLTests(TestCase):
def test_assigns_a_default_value(self):
config_manager = ConfigurationManager()
enabled = config_manager.ssl_verify_peer_mode_enabled()
self.assertFalse(enabled)
@patch.dict(environ, {
'SSL_VERIFY_PEER_MODE_ENABLED': 'true'
})
def test_assigns_a_value_from_the_environment(self):
config_manager = ConfigurationManager()
enabled = config_manager.ssl_verify_peer_mode_enabled()
self.assertTrue(enabled)
class ConfigurationManagerStoragePathTests(TestCase):
def test_assigns_a_default_value(self):
config_manager = ConfigurationManager()
path = config_manager.storage_path()
self.assertTrue(path.is_absolute())
self.assertEqual(path.name, 'storage')
@patch.dict(environ, {
'STORAGE_PATH': 'directory'
})
def test_assigns_a_relative_path_from_the_environment(self):
config_manager = ConfigurationManager()
path = config_manager.storage_path()
self.assertTrue(path.is_absolute())
self.assertEqual(path.name, 'directory')
@patch.dict(environ, {
'STORAGE_PATH': '/directory'
})
def test_assigns_an_absolute_path_from_the_environment(self):
config_manager = ConfigurationManager()
path = config_manager.storage_path()
self.assertEqual(f'{path}', '/directory')
class ConfigurationManagerMaximumFileSizeTests(TestCase):
def test_assigns_a_default_value(self):
config_manager = ConfigurationManager()
size = config_manager.maximum_file_size()
self.assertEqual(size, 5_242_880)
@patch.dict(environ, {
'MAXIMUM_FILE_SIZE': '10'
})
def test_assigns_a_value_from_the_environment(self):
config_manager = ConfigurationManager()
size = config_manager.maximum_file_size()
self.assertEqual(size, 10)
class ConfigurationManagerConversionTimeoutTests(TestCase):
def test_assigns_a_default_value(self):
config_manager = ConfigurationManager()
timeout = config_manager.conversion_timeout()
self.assertEqual(timeout, 120_000)
@patch.dict(environ, {
'CONVERSION_TIMEOUT': '10'
})
def test_assigns_a_value_from_the_environment(self):
config_manager = ConfigurationManager()
timeout = config_manager.conversion_timeout()
self.assertEqual(timeout, 10)

View File

@ -0,0 +1,17 @@
#
# (c) Copyright Ascensio System SIA 2023
#
# 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.
#
from .format import *

View File

@ -0,0 +1,177 @@
#
# (c) Copyright Ascensio System SIA 2023
#
# 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.
#
from dataclasses import dataclass
from json import dumps, loads
from pathlib import Path
from src.codable import Codable, CodingKey
@dataclass
class Format(Codable):
class CodingKeys(CodingKey):
name = 'name'
type = 'type'
actions = 'actions'
convert = 'convert'
mime = 'mime'
name: str
type: str
actions: list[str]
convert: list[str]
mime: list[str]
def extension(self) -> str:
return f'.{self.name}'
class FormatManager():
def spreadsheet_extensions(self) -> list[str]:
formats = self.spreadsheets()
mapped = map(lambda format: format.extension(), formats)
return list(mapped)
def spreadsheets(self) -> list[Format]:
formats = self.all()
mapped = filter(lambda format: format.type == 'cell', formats)
return list(mapped)
def presentation_extensions(self) -> list[str]:
formats = self.presentations()
mapped = map(lambda format: format.extension(), formats)
return list(mapped)
def presentations(self) -> list[Format]:
formats = self.all()
mapped = filter(lambda format: format.type == 'slide', formats)
return list(mapped)
def document_extensions(self) -> list[str]:
formats = self.documents()
mapped = map(lambda format: format.extension(), formats)
return list(mapped)
def documents(self) -> list[Format]:
formats = self.all()
mapped = filter(lambda format: format.type == 'word', formats)
return list(mapped)
def fillable_extensions(self) -> list[str]:
formats = self.fillable()
mapped = map(lambda format: format.extension(), formats)
return list(mapped)
def fillable(self) -> list[Format]:
formats = self.all()
mapped = filter(lambda format: 'fill' in format.actions, formats)
return list(mapped)
def viewable_extensions(self) -> list[str]:
formats = self.viewable()
mapped = map(lambda format: format.extension(), formats)
return list(mapped)
def viewable(self) -> list[Format]:
formats = self.all()
mapped = filter(lambda format: 'view' in format.actions, formats)
return list(mapped)
def editable_extensions(self) -> list[str]:
formats = self.editable()
mapped = map(lambda format: format.extension(), formats)
return list(mapped)
def editable(self) -> list[Format]:
formats = self.all()
mapped = filter(
lambda format: (
'edit' in format.actions or
'lossy-edit' in format.actions
),
formats
)
return list(mapped)
def convertible_to_names(self, extension) -> list[str]:
formats = self.convertible_to(extension)
mapped = map(lambda format: format.name, formats)
return list(mapped)
def convertible_to(self, extension) -> list[Format]:
formats = self.all()
filtered: list[Format] = []
names: list[str] = []
for format in formats:
if format.extension() == extension:
names = format.convert
break
for name in names:
for format in formats:
if format.name == name:
filtered.append(format)
break
return list(filtered)
def convertible_extensions(self) -> list[str]:
formats = self.convertible()
mapped = map(lambda format: format.extension(), formats)
return list(mapped)
def convertible(self) -> list[Format]:
formats = self.all()
filtered = filter(
lambda format: (
format.type == 'cell' and 'xlsx' in format.convert or
format.type == 'slide' and 'pptx' in format.convert or
format.type == 'word' and 'docx' in format.convert
),
formats
)
return list(filtered)
def all_extensions(self) -> list[str]:
formats = self.all()
mapped = map(lambda format: format.extension(), formats)
return list(mapped)
def all(self) -> list[Format]:
path = self.__file()
formats: list[Format] = []
with open(path, 'r', encoding='utf-8') as file:
content = file.read()
array = loads(content)
for obj in array:
raw = dumps(obj)
decoded = Format.decode(raw)
formats.append(decoded)
return formats
def __file(self) -> Path:
directory = self.__directory()
return directory.joinpath('onlyoffice-docs-formats.json')
def __directory(self) -> Path:
current_file = Path(__file__)
directory = current_file.joinpath(
'..',
'..',
'..',
'assets',
'document-formats'
)
return directory.resolve()

Some files were not shown because too many files have changed in this diff Show More