Compare commits

...

143 Commits

Author SHA1 Message Date
4ee110c2d5 Merge remote-tracking branch 'remotes/origin/develop' into release/v7.2.0
# Conflicts:
#	web/documentserver-example/nodejs/views/config.ejs
#	web/documentserver-example/ruby/app/models/document_helper.rb
2022-09-15 17:17:24 +03:00
ad1d93dd20 Merge pull request #301 from ONLYOFFICE/bugfix/file-request
added request status check for file
2022-09-15 14:43:19 +03:00
7805f7dc91 added request status check for file 2022-09-01 09:34:30 +03:00
51ec27d67c nodejs: removed trailing comma 2022-08-25 15:47:10 +03:00
9e94716df0 nodejs: reorder config items 2022-08-16 10:15:39 +03:00
988a8339b1 ruby: ERB::Util.url instead URI::encode 2022-08-15 11:10:02 +03:00
a482635e3f Merge branch 'feature/directUrl' into release/v7.2.0 2022-08-03 12:20:57 +03:00
d2c679f6b1 nodejs: option to send directUrl 2022-08-03 12:20:39 +03:00
1da452d857 Merge branch 'feature/directUrl' into release/v7.2.0 2022-08-03 12:20:18 +03:00
d9b8d2b7e9 directUrl to changelog 2022-08-03 12:14:16 +03:00
24aee6d194 Merge branch 'release/v7.2.0' into feature/directUrl
# Conflicts:
#	web/documentserver-example/csharp-mvc/Helpers/DocManagerHelper.cs
#	web/documentserver-example/csharp/DocEditor.aspx.cs
2022-08-03 12:13:12 +03:00
bd108a9c77 fix changelog 2022-08-03 12:11:01 +03:00
e737e8ed48 Merge remote-tracking branch 'remotes/origin/develop' into release/v7.2.0 2022-08-03 12:10:20 +03:00
b425d15f0a Merge branch 'feature/tooltip' into develop 2022-08-03 12:09:47 +03:00
0f8dec1e6e nodejs: revert tooltip for language (e1753a154d) 2022-08-03 12:08:10 +03:00
193b31fe75 nodejs: id to info block 2022-08-03 11:59:58 +03:00
583403cf78 update empty files (Fix Bug 58202) 2022-08-02 16:50:01 +03:00
641e45e24f 1.3.0 2022-08-02 15:35:18 +03:00
53c30fbb7d Merge branch 'develop' into release/v7.2.0
# Conflicts:
#	CHANGELOG.md
#	web/documentserver-example/csharp-mvc/Helpers/Users.cs
#	web/documentserver-example/csharp/Users.cs
#	web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/ExampleData.java
#	web/documentserver-example/java/src/main/java/helpers/Users.java
#	web/documentserver-example/nodejs/app.js
#	web/documentserver-example/nodejs/helpers/users.js
#	web/documentserver-example/php/users.php
#	web/documentserver-example/python/src/utils/users.py
#	web/documentserver-example/ruby/app/models/users.rb
2022-08-02 15:04:09 +03:00
87a1755729 Merge pull request #300 from ONLYOFFICE/feature/ruby-bundle-update
ruby: bundle update
2022-08-02 14:32:40 +03:00
752a2a2364 Merge pull request #298 from ONLYOFFICE/fix/notification-incorrect-file-size
Fix/notification incorrect file size
2022-08-02 14:32:40 +03:00
d1affa2911 Merge pull request #297 from ONLYOFFICE/feature/refactoring-jwt-manager
Feature/refactoring jwt manager
# Conflicts:
#	CHANGELOG.md
2022-08-02 14:32:31 +03:00
0ef2a4f3fd Merge pull request #292 from ONLYOFFICE/Build(deps)/bot-recommendations
changes - ruby - raise gem version
2022-08-02 14:25:18 +03:00
675d3f318a Merge pull request #294 from ONLYOFFICE/dependabot/nuget/web/documentserver-example/csharp-mvc/Newtonsoft.Json-13.0.1
build(deps): bump Newtonsoft.Json from 12.0.3 to 13.0.1 in /web/documentserver-example/csharp-mvc
2022-08-02 14:25:18 +03:00
05512ffa23 Merge pull request #288 from ONLYOFFICE/feature/localhost-for-client
Localhost for client urls
2022-08-02 14:25:17 +03:00
2eeed5d26d Fix #287 Fix #285 Fix #284 Fix #280
Build(deps)/bot recommendations
2022-08-02 14:24:11 +03:00
9e3dbd5eb5 Merge pull request #290 from ONLYOFFICE/fix/php-convert-manager
fix - php - add substrings to string
2022-08-02 14:21:09 +03:00
efbab7c113 nodejs: fix embed link for client request 2022-08-02 14:20:48 +03:00
b8f0f6447c nodejs: remove getlocalFileUri 2022-08-02 14:20:48 +03:00
8779a90433 nodejs: fix history url for server request 2022-08-02 14:20:48 +03:00
b3162ee410 Merge pull request #278 from ONLYOFFICE/dependabot/maven/web/documentserver-example/java-spring/com.fasterxml.jackson.core-jackson-databind-2.12.6.1
build(deps): bump jackson-databind from 2.12.2 to 2.12.6.1 in /web/documentserver-example/java-spring
2022-08-02 14:18:23 +03:00
42edd5e208 Merge pull request #276 from ONLYOFFICE/dependabot/bundler/web/documentserver-example/ruby/nokogiri-1.13.4
build(deps): bump nokogiri from 1.12.5 to 1.13.4 in /web/documentserver-example/ruby
2022-08-02 14:18:23 +03:00
e041baeef7 Merge branch 'feature/extension-on-rename' into develop 2022-08-02 14:18:22 +03:00
5bf682a007 nodejs: package lock 2022-08-02 14:18:22 +03:00
6dacf49faa ruby: fix (23e616aade) 2022-08-02 14:18:22 +03:00
63c9c4242e Merge pull request #291 from ONLYOFFICE/feature/chat
Feature/chat
# Conflicts:
#	CHANGELOG.md
2022-08-02 14:18:21 +03:00
b5e345ed50 Merge branch 'feature/chat' into develop 2022-08-02 14:18:21 +03:00
33f8436d42 changelog 2022-08-02 14:18:20 +03:00
c5217242ab Merge pull request #300 from ONLYOFFICE/feature/ruby-bundle-update
ruby: bundle update
2022-08-01 14:34:39 +03:00
c910d5193b Merge remote-tracking branch 'remotes/origin/dependabot/bundler/web/documentserver-example/ruby/rails-html-sanitizer-1.4.3' into feature/ruby-bundle-update
# Conflicts:
#	web/documentserver-example/ruby/Gemfile.lock
2022-08-01 14:34:05 +03:00
58c27f8f2f ruby: bundle update 2022-08-01 12:56:57 +03:00
1ec831789b Merge pull request #298 from ONLYOFFICE/fix/notification-incorrect-file-size
Fix/notification incorrect file size
2022-07-29 17:37:36 +03:00
0510fd1354 [fix] - node.js - don't send file size limit to incoming form
Revert "nodejs: upload big file"
This reverts commit fa4c3e3258.
2022-07-29 17:31:22 +03:00
25238b5614 Merge pull request #293 from ONLYOFFICE/feature/liveview
Feature/liveview
2022-07-29 17:23:48 +03:00
901bb61883 Merge remote-tracking branch 'remotes/origin/develop' into feature/liveview
# Conflicts:
#	CHANGELOG.md
2022-07-29 17:21:54 +03:00
7f3e6c6364 Merge pull request #291 from ONLYOFFICE/feature/chat
Feature/chat
2022-07-29 17:18:24 +03:00
b3512ea0ce Merge remote-tracking branch 'remotes/origin/develop' into feature/chat
# Conflicts:
#	CHANGELOG.md
2022-07-29 17:16:54 +03:00
642a1bd19a Merge pull request #297 from ONLYOFFICE/feature/refactoring-jwt-manager
Feature/refactoring jwt manager
2022-07-29 17:13:08 +03:00
ac471588de jwt to changelog 2022-07-29 17:12:20 +03:00
17674ef5bc ruby: licenses 2022-07-29 17:12:12 +03:00
dc2ee2a997 php: jwt 2022-07-29 17:12:02 +03:00
c358fa814a csharp-mvc: jwt 2022-07-29 17:11:05 +03:00
143a73e083 csharp: jwt 2022-07-29 17:10:37 +03:00
c266592434 Merge pull request #292 from ONLYOFFICE/Build(deps)/bot-recommendations
changes - ruby - raise gem version
2022-07-29 11:55:36 +03:00
19ed666d56 Merge pull request #294 from ONLYOFFICE/dependabot/nuget/web/documentserver-example/csharp-mvc/Newtonsoft.Json-13.0.1
build(deps): bump Newtonsoft.Json from 12.0.3 to 13.0.1 in /web/documentserver-example/csharp-mvc
2022-07-29 11:53:18 +03:00
84b9202af0 Merge pull request #288 from ONLYOFFICE/feature/localhost-for-client
Localhost for client urls
2022-07-29 11:51:09 +03:00
8fb698bb75 Basque (Spain), Armenian and Malay (Malaysia) lang 2022-07-29 11:38:25 +03:00
00f46ef080 build(deps): bump rails-html-sanitizer
Bumps [rails-html-sanitizer](https://github.com/rails/rails-html-sanitizer) from 1.4.2 to 1.4.3.
- [Release notes](https://github.com/rails/rails-html-sanitizer/releases)
- [Changelog](https://github.com/rails/rails-html-sanitizer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rails/rails-html-sanitizer/compare/v1.4.2...v1.4.3)

---
updated-dependencies:
- dependency-name: rails-html-sanitizer
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-06 12:11:57 +00:00
5c194da95d [add] - java - directUrl 2022-07-04 19:46:34 +03:00
0d5cebe28e [add] - java spring - directUrl 2022-07-04 19:45:37 +03:00
dacf72cae2 [add] - csharp mvc - directUrl 2022-07-01 19:29:52 +03:00
208b8926be [add] - csharp - directUrl 2022-07-01 19:28:55 +03:00
55c4853f04 [add] - php - directUrl 2022-07-01 17:30:18 +03:00
ccae7c6a06 [add] - python - directUrl 2022-06-30 20:29:04 +03:00
dc84dd3b29 [add] - ruby - fix 2022-06-30 19:53:44 +03:00
65d3671b09 [add] - ruby - directUrl 2022-06-30 19:15:15 +03:00
1fb5ef8bfd build(deps): bump Newtonsoft.Json
Bumps [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json) from 12.0.3 to 13.0.1.
- [Release notes](https://github.com/JamesNK/Newtonsoft.Json/releases)
- [Commits](https://github.com/JamesNK/Newtonsoft.Json/compare/12.0.3...13.0.1)

---
updated-dependencies:
- dependency-name: Newtonsoft.Json
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-23 00:47:07 +00:00
b630c3802b fix - ruby - coEditing from permissions to editorConfig 2022-06-16 16:56:18 +03:00
ad3993b532 fix - ruby - coEditing from permissions to editorConfig 2022-06-16 16:52:51 +03:00
35ee61e011 fix - python - checking mode view insert edit 2022-06-16 16:48:01 +03:00
ee59ab3dfd fix - python - coEditing from permissions to editorConfig 2022-06-16 16:43:48 +03:00
500f076e38 fix - csharp - coEditing from permissions to editorConfig 2022-06-16 16:36:51 +03:00
e578f29d9d fix - csharp mvc - coEditing from permissions to editorConfig 2022-06-16 16:35:57 +03:00
9de9bc90a4 fix - java - checking mode view insert edit 2022-06-16 16:32:36 +03:00
7a9ebdbbe5 fix - java - coEditing from permissions to editorConfig 2022-06-16 16:26:03 +03:00
887089bcad add - java spring - anonymous without liveview 2022-06-16 16:11:29 +03:00
680e53823a add - java - anonymous without liveview 2022-06-15 21:08:18 +03:00
33e1489cd6 add - charp mvc - anonymous without liveview 2022-06-15 20:41:32 +03:00
8c90737bf6 add - charp - anonymous without liveview 2022-06-15 20:38:16 +03:00
e5f1bc8ed6 add - php - anonymous without liveview 2022-06-15 20:23:52 +03:00
985b179475 add - python - anonymous without liveview 2022-06-15 20:03:07 +03:00
775d15132f add - ruby - json transform 2022-06-15 19:39:00 +03:00
4a4298ca10 add - ruby - anonymous without liveview 2022-06-15 19:30:36 +03:00
cdbd264ed5 Merge branch 'feature/chat' into feature/liveview
# Conflicts:
#	CHANGELOG.md
2022-06-15 19:21:30 +03:00
e61f6cae89 changes - ruby - raise gem version 2022-06-15 19:10:58 +03:00
22039078ef Merge remote-tracking branch 'remotes/origin/develop' into feature/chat 2022-06-15 18:38:12 +03:00
62793a42ab add - java spring - anonymous without permissions.chat 2022-06-15 18:09:14 +03:00
f49f96a2c2 add - java - anonymous without permissions.chat 2022-06-14 20:16:06 +03:00
2031d65adb add - csharp mvc - anonymous without permissions.chat 2022-06-14 19:53:12 +03:00
7f54659d3b add - csharp - anonymous without permissions.chat 2022-06-14 19:50:44 +03:00
a3bc13bb6e add - php - anonymous without permissions.chat 2022-06-14 19:45:46 +03:00
29ea16c124 add - python - anonymous without permissions.chat 2022-06-14 19:37:42 +03:00
6d4c8b926e add - ruby - anonymous without permissions.chat 2022-06-14 19:25:53 +03:00
22d2031426 fix - csharp - replace with regex 2022-06-14 18:10:18 +03:00
fcc51e8154 fix - csharp-mvc - check if '/' exist + replace with regex 2022-06-14 18:08:46 +03:00
8cd85ad63c Fix #287 Fix #285 Fix #284 Fix #280
Build(deps)/bot recommendations
2022-06-14 17:20:47 +03:00
1d51688884 Merge pull request #290 from ONLYOFFICE/fix/php-convert-manager
fix - php - add substrings to string
2022-06-14 17:05:02 +03:00
2c63a85b00 fix - php - add substrings to string 2022-06-14 16:55:12 +03:00
d1545c4db4 csharp-mvc-fix folder display for localhost 2022-06-10 18:37:17 +03:00
bd8e4d6ec7 csharp-fix goback 2022-06-10 18:31:22 +03:00
e1c815b574 Fix folder display for localhost 2022-06-10 18:29:44 +03:00
4042d2bb8a Merge remote-tracking branch 'remotes/origin/dependabot/maven/web/documentserver-example/java-spring/com.google.code.gson-gson-2.8.9' into build(deps)/bot-recommendations 2022-06-10 16:14:14 +03:00
8c9d2d80bd Merge remote-tracking branch 'remotes/origin/dependabot/maven/web/documentserver-example/java/com.google.code.gson-gson-2.8.9' into build(deps)/bot-recommendations 2022-06-10 16:13:53 +03:00
3a9259d713 Merge remote-tracking branch 'remotes/origin/dependabot/bundler/web/documentserver-example/ruby/rack-2.2.3.1' into build(deps)/bot-recommendations
# Conflicts:
#	web/documentserver-example/ruby/Gemfile.lock
2022-06-10 16:13:02 +03:00
39129a537e Merge remote-tracking branch 'remotes/origin/dependabot/bundler/web/documentserver-example/ruby/nokogiri-1.13.6' into build(deps)/bot-recommendations
# Conflicts:
#	web/documentserver-example/ruby/Gemfile.lock
2022-06-10 16:07:32 +03:00
dfaf552bf8 ruby-fix - use client URL in goBack 2022-06-09 19:30:43 +03:00
cc8b6f3716 nodejs: directUrl 2022-06-09 13:23:43 +03:00
13726b3c2f nodejs: fix embed link for client request 2022-06-09 13:18:06 +03:00
8bcc15e320 nodejs: remove getlocalFileUri 2022-06-09 13:07:02 +03:00
8fca045dd7 nodejs: fix history url for server request 2022-06-09 12:15:04 +03:00
a018cc5787 Merge remote-tracking branch 'remotes/origin/master' into develop 2022-06-03 11:09:35 +03:00
054236ef8a build(deps): bump rack in /web/documentserver-example/ruby
Bumps [rack](https://github.com/rack/rack) from 2.2.3 to 2.2.3.1.
- [Release notes](https://github.com/rack/rack/releases)
- [Changelog](https://github.com/rack/rack/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rack/rack/compare/2.2.3...2.2.3.1)

---
updated-dependencies:
- dependency-name: rack
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-27 16:45:08 +00:00
e662c765e6 build(deps): bump gson in /web/documentserver-example/java-spring
Bumps [gson](https://github.com/google/gson) from 2.8.5 to 2.8.9.
- [Release notes](https://github.com/google/gson/releases)
- [Changelog](https://github.com/google/gson/blob/master/CHANGELOG.md)
- [Commits](https://github.com/google/gson/compare/gson-parent-2.8.5...gson-parent-2.8.9)

---
updated-dependencies:
- dependency-name: com.google.code.gson:gson
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-20 22:07:08 +00:00
058bb0d2e2 build(deps): bump gson in /web/documentserver-example/java
Bumps [gson](https://github.com/google/gson) from 2.8.5 to 2.8.9.
- [Release notes](https://github.com/google/gson/releases)
- [Changelog](https://github.com/google/gson/blob/master/CHANGELOG.md)
- [Commits](https://github.com/google/gson/compare/gson-parent-2.8.5...gson-parent-2.8.9)

---
updated-dependencies:
- dependency-name: com.google.code.gson:gson
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-20 20:58:39 +00:00
ac78d1fc08 build(deps): bump nokogiri in /web/documentserver-example/ruby
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.13.3 to 1.13.6.
- [Release notes](https://github.com/sparklemotion/nokogiri/releases)
- [Changelog](https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.13.3...v1.13.6)

---
updated-dependencies:
- dependency-name: nokogiri
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-19 03:44:52 +00:00
070dac6878 Merge remote-tracking branch 'remotes/origin/master' into develop 2022-05-12 13:22:41 +03:00
cfa0fb1b74 Merge pull request #278 from ONLYOFFICE/dependabot/maven/web/documentserver-example/java-spring/com.fasterxml.jackson.core-jackson-databind-2.12.6.1
build(deps): bump jackson-databind from 2.12.2 to 2.12.6.1 in /web/documentserver-example/java-spring
2022-04-25 16:57:39 +03:00
1d624e6831 Merge pull request #276 from ONLYOFFICE/dependabot/bundler/web/documentserver-example/ruby/nokogiri-1.13.4
build(deps): bump nokogiri from 1.12.5 to 1.13.4 in /web/documentserver-example/ruby
2022-04-25 16:56:55 +03:00
fe83e98b37 Merge branch 'feature/extension-on-rename' into develop 2022-04-25 14:27:32 +03:00
f9ea7f7b50 nodejs: package lock 2022-04-25 14:27:28 +03:00
328ea46693 format 2022-04-25 14:20:41 +03:00
41ec2742a7 csharp-mvc: with ext 2022-04-25 14:06:27 +03:00
76fc55c2d4 csharp: with ext 2022-04-25 14:04:01 +03:00
e10f3962b6 java: with ext 2022-04-25 13:28:35 +03:00
b9859ef5ca java-spring: with ext 2022-04-25 12:53:28 +03:00
1cb2b586b1 ruby: with ext 2022-04-25 11:45:21 +03:00
e5d31eebbf nodejs: fix 2022-04-25 11:43:52 +03:00
1ac40c539f python: with ext 2022-04-25 11:13:09 +03:00
371366befa nodejs: with ext 2022-04-25 10:41:59 +03:00
0bee4817e7 php: with ext 2022-04-25 09:38:12 +03:00
ad941e0ba4 ruby: fix (23e616aade) 2022-04-20 10:19:34 +03:00
93c69e51ed changelog: offline viewer for anonymous 2022-04-18 15:37:45 +03:00
8b8b7d3938 Merge branch 'feature/liveview' into develop 2022-04-18 15:35:36 +03:00
f8f01a033e nodejs: anonymous without liveview 2022-04-18 15:34:18 +03:00
f497f10913 changelog: anonymous without chat 2022-04-18 15:26:44 +03:00
8b2478ac71 Merge branch 'feature/chat' into develop 2022-04-18 15:25:42 +03:00
391c347419 nodejs: anonymous without permissions.chat 2022-04-18 15:25:04 +03:00
5a08009b7b changelog: zh-TW, pt-PT 2022-04-18 15:24:31 +03:00
4e5b65f673 changelog 2022-04-18 15:23:43 +03:00
13e3d7673b Merge pull request #275 from ONLYOFFICE/feature/new-langs-7.2
added langs
2022-04-18 15:20:53 +03:00
c0a46866ce build(deps): bump nokogiri in /web/documentserver-example/ruby
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.12.5 to 1.13.4.
- [Release notes](https://github.com/sparklemotion/nokogiri/releases)
- [Changelog](https://github.com/sparklemotion/nokogiri/blob/v1.13.4/CHANGELOG.md)
- [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.12.5...v1.13.4)

---
updated-dependencies:
- dependency-name: nokogiri
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-18 11:18:07 +00:00
e7b76729a4 build(deps): bump jackson-databind
Bumps [jackson-databind](https://github.com/FasterXML/jackson) from 2.12.2 to 2.12.6.1.
- [Release notes](https://github.com/FasterXML/jackson/releases)
- [Commits](https://github.com/FasterXML/jackson/commits)

---
updated-dependencies:
- dependency-name: com.fasterxml.jackson.core:jackson-databind
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-18 11:17:36 +00:00
ce2e7f97d6 added langs 2022-04-11 15:28:55 +03:00
106 changed files with 3318 additions and 688 deletions

34
CHANGELOG.md Normal file
View File

@ -0,0 +1,34 @@
# Change Log
- nodejs: option to send directUrl
- opening file on client by directUrl
- offline viewer for anonymous
- added hy, eu, zh-TW, ms, pt-PT
## 1.3.0
- update empty files
- anonymous without chat
- changed jwt implementation in csharp, csharp-mvc, php, ruby
## 1.2.0
- ruby v3.0
- set filetype in setHistoryData
- read filetype from input request
- creating file on WOPI
- upload on WOPI page
- fix xss
- set userInfoGroups
- check JWT on downloading history
- upload dialog on mobile
- anonymous without id
- renaming from editor
- new skin languages
- ignore certificate
## 1.1.0
- creating docxf
- opening docxf, oform
## 1.0.0
- added java spring

View File

@ -24,11 +24,15 @@ jQuery.UI - jQuery UI is an open source library of interface components —
License: MIT
License File: jQuery.UI.license
JWT - JWT (JSON Web Token) Implementation for .NET (Public Domain) (https://github.com/jwt-dotnet/jwt/)
License: MIT
License File: JWT.license
Microsoft.Web.Infrastructure - This package contains the Microsoft.Web.Infrastructure assembly that lets you dynamically register HTTP modules at run time. (https://www.microsoft.com/web/webpi/eula/aspnetmvc3update-eula.htm)
License: MS-EULA License
License File: Microsoft.Web.Infrastructure.license
Newtonsoft.Json - Json.NET is a popular high-performance JSON framework for .NET (https://licenses.nuget.org/MIT)
Newtonsoft.Json - Json.NET is a popular high-performance JSON framework for .NET (https://github.com/JamesNK/Newtonsoft.Json)
License: MIT
License File: Newtonsoft.Json.license

View File

@ -242,7 +242,8 @@ namespace OnlineEditorsExampleMVC.Helpers
{
var uri = new UriBuilder(GetServerUrl(forDocumentServer))
{
Path = HttpRuntime.AppDomainAppVirtualPath + "/"
Path = HttpRuntime.AppDomainAppVirtualPath
+ (HttpRuntime.AppDomainAppVirtualPath.EndsWith("/") ? "" : "/")
+ CurUserHostAddress() + "/"
+ fileName,
Query = ""
@ -292,7 +293,7 @@ namespace OnlineEditorsExampleMVC.Helpers
+ "webeditor.ashx",
Query = "type=track"
+ "&fileName=" + HttpUtility.UrlEncode(fileName)
+ "&userAddress=" + HttpUtility.UrlEncode(HttpContext.Current.Request.UserHostAddress)
+ "&userAddress=" + HttpUtility.UrlEncode(CurUserHostAddress(HttpContext.Current.Request.UserHostAddress))
};
return callbackUrl.ToString();
}
@ -312,9 +313,10 @@ namespace OnlineEditorsExampleMVC.Helpers
}
// create the public history url
public static string GetHistoryDownloadUrl(string filename, string version, string file)
public static string GetHistoryDownloadUrl(string filename, string version, string file, Boolean isServer = true)
{
var downloadUrl = new UriBuilder(GetServerUrl(true))
var userAddress = "&userAddress=" + HttpUtility.UrlEncode(CurUserHostAddress(HttpContext.Current.Request.UserHostAddress));
var downloadUrl = new UriBuilder(GetServerUrl(isServer))
{
Path =
HttpRuntime.AppDomainAppVirtualPath
@ -322,7 +324,7 @@ namespace OnlineEditorsExampleMVC.Helpers
+ "webeditor.ashx",
Query = "type=downloadhistory"
+ "&fileName=" + HttpUtility.UrlEncode(filename)
+ "&userAddress=" + HttpUtility.UrlEncode(HttpContext.Current.Request.UserHostAddress)
+ userAddress
+ "&ver=" + version
+ "&file="+ file
};
@ -330,9 +332,10 @@ namespace OnlineEditorsExampleMVC.Helpers
}
// get url to download a file
public static string GetDownloadUrl(string fileName)
public static string GetDownloadUrl(string fileName, Boolean isServer = true)
{
var downloadUrl = new UriBuilder(GetServerUrl(true))
var userAddress = isServer ? "&userAddress=" + HttpUtility.UrlEncode(HttpContext.Current.Request.UserHostAddress) : "";
var downloadUrl = new UriBuilder(GetServerUrl(isServer))
{
Path =
HttpRuntime.AppDomainAppVirtualPath
@ -340,7 +343,8 @@ namespace OnlineEditorsExampleMVC.Helpers
+ "webeditor.ashx",
Query = "type=download"
+ "&fileName=" + HttpUtility.UrlEncode(fileName)
+ "&userAddress=" + HttpUtility.UrlEncode(HttpContext.Current.Request.UserHostAddress)
+ "&userAddress=" + HttpUtility.UrlEncode(CurUserHostAddress(HttpContext.Current.Request.UserHostAddress))
+ userAddress
};
return downloadUrl.ToString();
}

View File

@ -16,12 +16,12 @@
*
*/
using System;
using JWT;
using JWT.Algorithms;
using JWT.Builder;
using JWT.Serializers;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Web.Configuration;
using System.Web.Script.Serialization;
namespace OnlineEditorsExampleMVC.Helpers
{
@ -30,31 +30,19 @@ namespace OnlineEditorsExampleMVC.Helpers
private static readonly string Secret;
public static readonly bool Enabled;
private static readonly JavaScriptSerializer Serializer;
static JwtManager()
{
Secret = WebConfigurationManager.AppSettings["files.docservice.secret"] ?? ""; // get token secret from the config parameters
Enabled = !string.IsNullOrEmpty(Secret); // check if the token is enabled
Serializer = new JavaScriptSerializer(); // define java script serializer
}
// encode a payload object into a token using a secret key
public static string Encode(IDictionary<string, object> payload)
{
// define the hashing algorithm and the token type
var header = new Dictionary<string, object>
{
{ "alg", "HS256" },
{ "typ", "JWT" }
};
// three parts of token
var encHeader = Base64UrlEncode(Serializer.Serialize(header)); // header
var encPayload = Base64UrlEncode(Serializer.Serialize(payload)); // payload
var hashSum = Base64UrlEncode(CalculateHash(encHeader, encPayload)); // signature
return string.Format("{0}.{1}.{2}", encHeader, encPayload, hashSum);
var encoder = new JwtEncoder(new HMACSHA256Algorithm(),
new JsonNetSerializer(),
new JwtBase64UrlEncoder());
return encoder.Encode(payload, Secret);
}
// decode a token into a payload object using a secret key
@ -62,52 +50,11 @@ namespace OnlineEditorsExampleMVC.Helpers
{
if (!Enabled || string.IsNullOrEmpty(token)) return "";
var split = token.Split('.');
if (split.Length != 3) return "";
var hashSum = Base64UrlEncode(CalculateHash(split[0], split[1])); // get signature
if (hashSum != split[2]) return ""; // and check if it is equal to the signature from the token
return Base64UrlDecode(split[1]); // decode payload
}
// generate a hash code based on a key using the HMAC method
private static byte[] CalculateHash(string encHeader, string encPayload)
{
using (var hasher = new HMACSHA256(Encoding.UTF8.GetBytes(Secret)))
{
var bytes = Encoding.UTF8.GetBytes(string.Format("{0}.{1}", encHeader, encPayload));
return hasher.ComputeHash(bytes);
}
}
// encode a string into the base64 value
private static string Base64UrlEncode(string str)
{
return Base64UrlEncode(Encoding.UTF8.GetBytes(str));
}
// encode bytes into the base64 value
private static string Base64UrlEncode(byte[] bytes)
{
return Convert.ToBase64String(bytes)
.TrimEnd('=').Replace('+', '-').Replace('/', '_');
}
// decode a base64 value into the string
private static string Base64UrlDecode(string payload)
{
var b64 = payload.Replace('_', '/').Replace('-', '+');
switch (b64.Length%4)
{
case 2:
b64 += "==";
break;
case 3:
b64 += "=";
break;
}
var bytes = Convert.FromBase64String(b64);
return Encoding.UTF8.GetString(bytes);
return JwtBuilder.Create()
.WithAlgorithm(new HMACSHA256Algorithm())
.WithSecret(Secret)
.MustVerifySignature()
.Decode(token);
}
}
}

View File

@ -67,7 +67,9 @@ namespace OnlineEditorsExampleMVC.Helpers
"Can't mention others in comments",
"Can't create new files from the editor",
"Cant see anyones information",
"Can't rename files from the editor"
"Can't rename files from the editor",
"Can't view chat",
"View file without collaboration",
};
private static List<User> users = new List<User>() {

View File

@ -97,6 +97,7 @@ namespace OnlineEditorsExampleMVC.Models
var actionLink = request.GetOrDefault("actionLink", null); // get the action link (comment or bookmark) if it exists
var actionData = string.IsNullOrEmpty(actionLink) ? null : jss.DeserializeObject(actionLink); // get action data for the action link
var directUrl = DocManagerHelper.GetDownloadUrl(FileName, false);
var createUrl = DocManagerHelper.GetCreateUrl(FileUtility.GetFileType(FileName));
var templatesImageUrl = DocManagerHelper.GetTemplateImageUrl(FileUtility.GetFileType(FileName)); // image url for templates
var templates = new List<Dictionary<string, string>>
@ -125,6 +126,7 @@ namespace OnlineEditorsExampleMVC.Models
{
{ "title", FileName },
{ "url", DownloadUrl },
{ "directUrl", directUrl },
{ "fileType", ext.Trim('.') },
{ "key", Key },
{
@ -148,6 +150,7 @@ namespace OnlineEditorsExampleMVC.Models
{ "modifyFilter", editorsMode != "filter" },
{ "modifyContentControl", editorsMode != "blockcontent" },
{ "review", canEdit && (editorsMode == "edit" || editorsMode == "review") },
{ "chat", !user.id.Equals("uid-0") },
{ "reviewGroups", user.reviewGroups },
{ "commentGroups", user.commentGroups },
{ "userInfoGroups", user.userInfoGroups }
@ -162,6 +165,11 @@ namespace OnlineEditorsExampleMVC.Models
{ "mode", mode },
{ "lang", request.Cookies.GetOrDefault("ulang", "en") },
{ "callbackUrl", CallbackUrl }, // absolute URL to the document storage service
{ "coEditing", editorsMode == "view" && user.id.Equals("uid-0") ?
new Dictionary<string, object>{
{"mode", "strict"},
{"change", false}
} : null },
{ "createUrl", !user.id.Equals("uid-0") ? createUrl : null },
{ "templates", user.templates ? templates : null },
{
@ -177,9 +185,9 @@ namespace OnlineEditorsExampleMVC.Models
// the parameters for the embedded document type
"embedded", new Dictionary<string, object>
{
{ "saveUrl", FileUriUser }, // the absolute URL that will allow the document to be saved onto the user personal computer
{ "embedUrl", FileUriUser }, // the absolute URL to the document serving as a source file for the document embedded into the web page
{ "shareUrl", FileUriUser }, // the absolute URL that will allow other users to share this document
{ "saveUrl", directUrl }, // the absolute URL that will allow the document to be saved onto the user personal computer
{ "embedUrl", directUrl }, // the absolute URL to the document serving as a source file for the document embedded into the web page
{ "shareUrl", directUrl }, // the absolute URL that will allow other users to share this document
{ "toolbarDocked", "top" } // the place for the embedded viewer toolbar (top or bottom)
}
},
@ -195,7 +203,7 @@ namespace OnlineEditorsExampleMVC.Models
{
"goback", new Dictionary<string, object> // settings for the Open file location menu button and upper right corner button
{
{ "url", url.Action("Index", "Home") } // the absolute URL to the website address which will be opened when clicking the Open file location menu button
{ "url", DocManagerHelper.GetServerUrl(false) } // the absolute URL to the website address which will be opened when clicking the Open file location menu button
}
}
}
@ -262,17 +270,23 @@ namespace OnlineEditorsExampleMVC.Models
dataObj.Add("key", key);
// write file url to the data object
string prevFileUrl;
string directPrevFileUrl;
if (Path.IsPathRooted(storagePath) && !string.IsNullOrEmpty(storagePath))
{
prevFileUrl = i == currentVersion ? DocManagerHelper.GetHistoryDownloadUrl(FileName, i.ToString(), "prev" + ext)
: DocManagerHelper.GetDownloadUrl(Directory.GetFiles(verDir, "prev.*")[0].Replace(storagePath + "\\", ""));
directPrevFileUrl = i == currentVersion ? DocManagerHelper.GetHistoryDownloadUrl(FileName, i.ToString(), "prev" + ext, false)
: DocManagerHelper.GetDownloadUrl(Directory.GetFiles(verDir, "prev.*")[0].Replace(storagePath + "\\", ""), false);
}
else {
prevFileUrl = i == currentVersion ? FileUri
: DocManagerHelper.GetHistoryDownloadUrl(FileName, i.ToString(), "prev" + ext);
prevFileUrl = i == currentVersion ? FileUri
: DocManagerHelper.GetHistoryDownloadUrl(FileName, i.ToString(), "prev" + ext);
directPrevFileUrl = i == currentVersion ? DocManagerHelper.GetHistoryDownloadUrl(FileName, i.ToString(), "prev" + ext, false)
: DocManagerHelper.GetDownloadUrl(Directory.GetFiles(verDir, "prev.*")[0].Replace(storagePath + "\\", ""), false);
}
dataObj.Add("url", prevFileUrl);
dataObj.Add("directUrl", directPrevFileUrl);
dataObj.Add("version", i);
if (i > 1) // check if the version number is greater than 1 (the file was modified)
{
@ -294,6 +308,7 @@ namespace OnlineEditorsExampleMVC.Models
{ "fileType", prev["fileType"] },
{ "key", prev["key"] }, // write key and url information about previous file version
{ "url", prev["url"] },
{ "directUrl", prev["directUrl"] },
});
// write the path to the diff.zip archive with differences in this file version
var changesUrl = DocManagerHelper.GetHistoryDownloadUrl(FileName, (i - 1).ToString(), "diff.zip");
@ -332,11 +347,20 @@ namespace OnlineEditorsExampleMVC.Models
Query = "type=assets&fileName=" + HttpUtility.UrlEncode("sample.docx")
};
var directCompareFileUrl = new UriBuilder(DocManagerHelper.GetServerUrl(false))
{
Path = HttpRuntime.AppDomainAppVirtualPath
+ (HttpRuntime.AppDomainAppVirtualPath.EndsWith("/") ? "" : "/")
+ "webeditor.ashx",
Query = "type=assets&fileName=" + HttpUtility.UrlEncode("sample.docx")
};
// create an object with the information about the compared file
var dataCompareFile = new Dictionary<string, object>
{
{ "fileType", "docx" },
{ "url", compareFileUrl.ToString() }
{ "url", compareFileUrl.ToString() },
{ "directUrl", directCompareFileUrl.ToString()}
};
if (JwtManager.Enabled) // if the secret key to generate token exists
@ -361,11 +385,19 @@ namespace OnlineEditorsExampleMVC.Models
+ "Content\\images\\logo.png"
};
var directMailMergeUrl = new UriBuilder(DocManagerHelper.GetServerUrl(false))
{
Path = HttpRuntime.AppDomainAppVirtualPath
+ (HttpRuntime.AppDomainAppVirtualPath.EndsWith("/") ? "" : "/")
+ "Content\\images\\logo.png"
};
// create a logo config
var logoConfig = new Dictionary<string, object>
{
{ "fileType", "png"},
{ "url", mailMergeUrl.ToString()}
{ "url", mailMergeUrl.ToString()},
{ "directUrl", directMailMergeUrl.ToString()}
};
if (JwtManager.Enabled) // if the secret key to generate token exists
@ -392,11 +424,21 @@ namespace OnlineEditorsExampleMVC.Models
Query = "type=csv"
};
var directMailMergeUrl = new UriBuilder(DocManagerHelper.GetServerUrl(false))
{
Path =
HttpRuntime.AppDomainAppVirtualPath
+ (HttpRuntime.AppDomainAppVirtualPath.EndsWith("/") ? "" : "/")
+ "webeditor.ashx",
Query = "type=csv"
};
// create a mail merge config
var mailMergeConfig = new Dictionary<string, object>
{
{ "fileType", "csv" },
{ "url", mailMergeUrl.ToString()}
{ "url", mailMergeUrl.ToString()},
{ "directUrl", directMailMergeUrl.ToString()}
};
if (JwtManager.Enabled) // if the secret key to generate token exists

View File

@ -43,6 +43,9 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="JWT, Version=9.0.0.0, Culture=neutral, PublicKeyToken=6f98bca0f40f2ecf, processorArchitecture=MSIL">
<HintPath>packages\JWT.9.0.3\lib\net46\JWT.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations" />

View File

@ -166,6 +166,7 @@
var data = {
newfilename: newfilename,
dockey: config.document.key,
ext: config.document.fileType
};
let xhr = new XMLHttpRequest();

View File

@ -163,7 +163,7 @@
var isFillFormDoc = DocManagerHelper.FillFormExts.Contains(ext);
%>
<tr class="tableRow" title="<%= storedFile.Name %> [<%= DocManagerHelper.GetFileVersion(storedFile.Name, HttpContext.Current.Request.UserHostAddress) %>]">
<tr class="tableRow" title="<%= storedFile.Name %> [<%= DocManagerHelper.GetFileVersion(storedFile.Name, HttpContext.Current.Request.UserHostAddress.Replace(':', '_')) %>]">
<td class="contentCells">
<a class="stored-edit <%= docType %>" href="<%= Url.Action("Editor", "Home", new { fileName = storedFile.Name }) %>" target="_blank">
<span><%= storedFile.Name %></span>

View File

@ -575,7 +575,15 @@ namespace OnlineEditorsExampleMVC
var jss = new JavaScriptSerializer();
var body = jss.Deserialize<Dictionary<string, object>>(fileData);
var newFileName = (string) body["newfilename"];
var docKey = (string) body["dockey"];
var docKey = (string) body["dockey"];
var origExt = '.' + (string) body["ext"];
var curExt = Path.GetExtension(newFileName).ToLower();
if (string.Compare(origExt, curExt, true) != 0)
{
newFileName += origExt;
}
var meta = new Dictionary<string, object>() {
{ "title", newFileName }
};

View File

@ -24,11 +24,15 @@ jQuery.UI - jQuery UI is an open source library of interface components —
License: MIT
License File: jQuery.UI.license
JWT - JWT (JSON Web Token) Implementation for .NET (Public Domain) (https://github.com/jwt-dotnet/jwt/)
License: MIT
License File: JWT.license
Microsoft.Web.Infrastructure - This package contains the Microsoft.Web.Infrastructure assembly that lets you dynamically register HTTP modules at run time. (https://www.microsoft.com/web/webpi/eula/aspnetmvc3update-eula.htm)
License: MS-EULA License
License File: Microsoft.Web.Infrastructure.license
Newtonsoft.Json - Json.NET is a popular high-performance JSON framework for .NET (https://licenses.nuget.org/MIT)
Newtonsoft.Json - Json.NET is a popular high-performance JSON framework for .NET (https://github.com/JamesNK/Newtonsoft.Json)
License: MIT
License File: Newtonsoft.Json.license

View File

@ -0,0 +1,21 @@
# Public Domain
Written by John Sheehan (http://john-sheehan.com)
This work is public domain.
The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law.
For more information, please visit: http://creativecommons.org/publicdomain/zero/1.0/
# MIT
Copyright (c) 2019 Jwt.Net Maintainers 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.
For more information, please visit: https://opensource.org/licenses/MIT

View File

@ -2,6 +2,7 @@
<packages>
<package id="Antlr" version="3.5.0.2" targetFramework="net45" />
<package id="EntityFramework" version="6.4.4" targetFramework="net45" />
<package id="JWT" version="9.0.3" targetFramework="net48" />
<package id="Microsoft.AspNet.Mvc" version="5.2.7" targetFramework="net45" />
<package id="Microsoft.AspNet.Razor" version="3.2.7" targetFramework="net45" />
<package id="Microsoft.AspNet.Web.Optimization" version="1.1.3" targetFramework="net45" />
@ -11,6 +12,6 @@
<package id="Microsoft.AspNet.WebPages" version="3.2.7" targetFramework="net45" />
<package id="Microsoft.CSharp" version="4.7.0" targetFramework="net45" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net45" />
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net45" />
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net45" />
<package id="WebGrease" version="1.6.0" targetFramework="net45" />
</packages>

View File

@ -1,7 +1,7 @@
<?xml version="1.0"?>
<appSettings>
<clear />
<add key="version" value="1.2.0"/>
<add key="version" value="1.3.0"/>
<add key="filesize-max" value="52428800"/>
<add key="storage-path" value=""/>
@ -16,7 +16,7 @@
<add key="files.docservice.verify-peer-off" value="true"/>
<add key="files.docservice.languages" value="en:English|az:Azerbaijani|be:Belarusian|bg:Bulgarian|ca:Catalan|zh:Chinese|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|lv:Latvian|lo:Lao|nb:Norwegian|pl:Polish|pt:Portuguese|ro:Romanian|ru:Russian|sk:Slovak|sl:Slovenian|es:Spanish|sv:Swedish|tr:Turkish|uk:Ukrainian|vi:Vietnamese"/>
<add key="files.docservice.languages" value="en:English|hy:Armenian|az:Azerbaijani|eu:Basque|be:Belarusian|bg:Bulgarian|ca:Catalan|zh:Chinese (People's Republic of China)|zh-TW:Chinese (Traditional, Taiwan)|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|lv:Latvian|lo:Lao|ms:Malay (Malaysia)|nb:Norwegian|pl:Polish|pt:Portuguese (Brazil)|pt-PT:Portuguese (Portugal)|ro:Romanian|ru:Russian|sk:Slovak|sl:Slovenian|es:Spanish|sv:Swedish|tr:Turkish|uk:Ukrainian|vi:Vietnamese"/>
<add key="files.docservice.url.site" value="http://documentserver/"/>

View File

@ -19,3 +19,11 @@ License File: jQuery.iframe-transport.license
jQuery.UI - jQuery UI is an open source library of interface components — interactions, full-featured widgets, and animation effects — based on the stellar jQuery javascript library . Each component is built according to jQuery's event-driven architecture (find something, manipulate it) and is themeable, making it easy for developers of any skill level to integrate and extend into their own code. (https://jquery.org/license/)
License: MIT
License File: jQuery.UI.license
JWT - JWT (JSON Web Token) Implementation for .NET (Public Domain) (https://github.com/jwt-dotnet/jwt/)
License: MIT
License File: JWT.license
Newtonsoft.Json - Json.NET is a popular high-performance JSON framework for .NET (https://github.com/JamesNK/Newtonsoft.Json)
License: MIT
License File: Newtonsoft.Json.license

View File

@ -165,7 +165,7 @@
var isFillFormDoc = FillFormsExts.Contains(ext);
%>
<tr class="tableRow" title="<%= storedFile.Name %> [<%= GetFileVersion(storedFile.Name, HttpContext.Current.Request.UserHostAddress) %>]">
<tr class="tableRow" title="<%= storedFile.Name %> [<%= GetFileVersion(storedFile.Name, HttpContext.Current.Request.UserHostAddress.Replace(':','_')) %>]">
<td class="contentCells">
<a class="stored-edit <%= docType %>" href="<%= editUrl %>" target="_blank">
<span><%= storedFile.Name %></span>

View File

@ -485,7 +485,7 @@ namespace OnlineEditorsExample
+ (HttpRuntime.AppDomainAppVirtualPath.EndsWith("/") ? "" : "/")
+ "webeditor.ashx";
fileUrl.Query = "type=download&fileName=" + HttpUtility.UrlEncode(_fileName)
+ "&userAddress=" + HttpUtility.UrlEncode(HttpContext.Current.Request.UserHostAddress);
+ "&userAddress=" + HttpUtility.UrlEncode(CurUserHostAddress(HttpContext.Current.Request.UserHostAddress));
// get the url to the converted file
string newFileUri;

View File

@ -178,6 +178,7 @@
var data = {
newfilename: newfilename,
dockey: config.document.key,
ext: config.document.fileType
};
let xhr = new XMLHttpRequest();

View File

@ -81,7 +81,7 @@ namespace OnlineEditorsExample
+ "webeditor.ashx";
callbackUrl.Query = "type=track"
+ "&fileName=" + HttpUtility.UrlEncode(FileName)
+ "&userAddress=" + HttpUtility.UrlEncode(HttpContext.Current.Request.UserHostAddress);
+ "&userAddress=" + HttpUtility.UrlEncode(_Default.CurUserHostAddress(HttpContext.Current.Request.UserHostAddress));
return callbackUrl.ToString();
}
}
@ -100,16 +100,17 @@ namespace OnlineEditorsExample
}
// get url to download a file
public static string getDownloadUrl(string fileName)
public static string getDownloadUrl(string fileName, Boolean isServer = true)
{
var downloadUrl = new UriBuilder(_Default.GetServerUrl(true));
var userAddress = isServer ? "&userAddress=" + HttpUtility.UrlEncode(_Default.CurUserHostAddress(HttpContext.Current.Request.UserHostAddress)) : "";
var downloadUrl = new UriBuilder(_Default.GetServerUrl(isServer));
downloadUrl.Path =
HttpRuntime.AppDomainAppVirtualPath
+ (HttpRuntime.AppDomainAppVirtualPath.EndsWith("/") ? "" : "/")
+ "webeditor.ashx";
downloadUrl.Query = "type=download"
+ "&fileName=" + HttpUtility.UrlEncode(fileName)
+ "&userAddress=" + HttpUtility.UrlEncode(HttpContext.Current.Request.UserHostAddress);
+ userAddress;
return downloadUrl.ToString();
}
@ -165,6 +166,7 @@ namespace OnlineEditorsExample
var actionLink = Request.GetOrDefault("actionLink", null); // get the action link (comment or bookmark) if it exists
var actionData = string.IsNullOrEmpty(actionLink) ? null : jss.DeserializeObject(actionLink); // get action data for the action link
var directUrl = getDownloadUrl(FileName, false);
var createUrl = getCreateUrl(DocumentType, editorsType);
var templatesImageUrl = GetTemplateImageUrl(ext); // image url for templates
var templates = new List<Dictionary<string, string>>
@ -193,6 +195,7 @@ namespace OnlineEditorsExample
{
{ "title", FileName },
{ "url", getDownloadUrl(FileName) },
{ "directUrl", directUrl },
{ "fileType", ext.Trim('.') },
{ "key", Key },
{
@ -216,6 +219,7 @@ namespace OnlineEditorsExample
{ "modifyFilter", editorsMode != "filter" },
{ "modifyContentControl", editorsMode != "blockcontent" },
{ "review", canEdit && (editorsMode == "edit" || editorsMode == "review") },
{ "chat", !user.id.Equals("uid-0") },
{ "reviewGroups", user.reviewGroups },
{ "commentGroups", user.commentGroups },
{ "userInfoGroups", user.userInfoGroups }
@ -230,6 +234,11 @@ namespace OnlineEditorsExample
{ "mode", mode },
{ "lang", Request.Cookies.GetOrDefault("ulang", "en") },
{ "callbackUrl", CallbackUrl }, // absolute URL to the document storage service
{ "coEditing", editorsMode == "view" && user.id.Equals("uid-0") ?
new Dictionary<string, object>{
{"mode", "strict"},
{"change", false}
} : null },
{ "createUrl", !user.id.Equals("uid-0") ? createUrl : null },
{ "templates", user.templates ? templates : null },
{
@ -245,9 +254,9 @@ namespace OnlineEditorsExample
// the parameters for the embedded document type
"embedded", new Dictionary<string, object>
{
{ "saveUrl", FileUriUser }, // the absolute URL that will allow the document to be saved onto the user personal computer
{ "embedUrl", FileUriUser }, // the absolute URL to the document serving as a source file for the document embedded into the web page
{ "shareUrl", FileUriUser }, // the absolute URL that will allow other users to share this document
{ "saveUrl", directUrl }, // the absolute URL that will allow the document to be saved onto the user personal computer
{ "embedUrl", directUrl }, // the absolute URL to the document serving as a source file for the document embedded into the web page
{ "shareUrl", directUrl }, // the absolute URL that will allow other users to share this document
{ "toolbarDocked", "top" } // the place for the embedded viewer toolbar (top or bottom)
}
},
@ -359,13 +368,16 @@ namespace OnlineEditorsExample
dataObj.Add("fileType", ext.Replace(".", ""));
dataObj.Add("key", key);
// write file url to the data object
var directPrevFileUrl = i == currentVersion ? _Default.FileUri(FileName, false) : MakePublicHistoryUrl(FileName, i.ToString(), "prev" + ext, false);
var prevFileUrl = i == currentVersion ? FileUri : MakePublicHistoryUrl(FileName, i.ToString(), "prev" + ext);
if (Path.IsPathRooted(storagePath))
{
prevFileUrl = i == currentVersion ? getDownloadUrl(FileName) : getDownloadUrl(Directory.GetFiles(verDir, "prev.*")[0].Replace(storagePath + "\\", ""));
directPrevFileUrl = i == currentVersion ? getDownloadUrl(FileName, false) : getDownloadUrl(Directory.GetFiles(verDir, "prev.*")[0].Replace(storagePath + "\\", ""), false);
}
dataObj.Add("url", prevFileUrl); // write file url to the data object
dataObj.Add("directUrl", directPrevFileUrl); // write direct url to the data object
dataObj.Add("version", i);
if (i > 1) // check if the version number is greater than 1 (the file was modified)
{
@ -387,6 +399,7 @@ namespace OnlineEditorsExample
{ "fileType", prev["fileType"] },
{ "key", prev["key"] }, // write key and url information about previous file version
{ "url", prev["url"] },
{ "directUrl", prev["directUrl"] },
});
// write the path to the diff.zip archive with differences in this file version
var changesUrl = MakePublicHistoryUrl(FileName, (i - 1).ToString(), "diff.zip");
@ -420,11 +433,17 @@ namespace OnlineEditorsExample
+ (HttpRuntime.AppDomainAppVirtualPath.EndsWith("/") ? "" : "/")
+ "App_Themes\\images\\logo.png";
var DirectImageUrl = new UriBuilder(_Default.GetServerUrl(false));
DirectImageUrl.Path = HttpRuntime.AppDomainAppVirtualPath
+ (HttpRuntime.AppDomainAppVirtualPath.EndsWith("/") ? "" : "/")
+ "App_Themes\\images\\logo.png";
// create a logo config
Dictionary<string, object> logoConfig = new Dictionary<string, object>
{
{ "fileType", "png"},
{ "url", InsertImageUrl.ToString()}
{ "url", InsertImageUrl.ToString()},
{ "directUrl", DirectImageUrl.ToString()}
};
if (JwtManager.Enabled) // if the secret key to generate token exists
@ -446,11 +465,18 @@ namespace OnlineEditorsExample
+ "webeditor.ashx";
compareFileUrl.Query = "type=assets&fileName=" + HttpUtility.UrlEncode("sample.docx");
var DirectFileUrl = new UriBuilder(_Default.GetServerUrl(false));
DirectFileUrl.Path = HttpRuntime.AppDomainAppVirtualPath
+ (HttpRuntime.AppDomainAppVirtualPath.EndsWith("/") ? "" : "/")
+ "webeditor.ashx";
DirectFileUrl.Query = "type=assets&fileName=" + HttpUtility.UrlEncode("sample.docx");
// create an object with the information about the compared file
Dictionary<string, object> dataCompareFile = new Dictionary<string, object>
{
{ "fileType", "docx" },
{ "url", compareFileUrl.ToString() }
{ "url", compareFileUrl.ToString() },
{ "directUrl", DirectFileUrl.ToString() }
};
if (JwtManager.Enabled) // if the secret key to generate token exists
@ -473,11 +499,19 @@ namespace OnlineEditorsExample
+ "webeditor.ashx";
mailmergeUrl.Query = "type=csv";
var DirectMailMergeUrl = new UriBuilder(_Default.GetServerUrl(false));
DirectMailMergeUrl.Path =
HttpRuntime.AppDomainAppVirtualPath
+ (HttpRuntime.AppDomainAppVirtualPath.EndsWith("/") ? "" : "/")
+ "webeditor.ashx";
DirectMailMergeUrl.Query = "type=csv";
// create a mail merge config
Dictionary<string, object> mailMergeConfig = new Dictionary<string, object>
{
{ "fileType", "csv" },
{ "url", mailmergeUrl.ToString() }
{ "url", mailmergeUrl.ToString() },
{ "directUrl", DirectMailMergeUrl.ToString() }
};
if (JwtManager.Enabled) // if the secret key to generate token exists
@ -521,15 +555,16 @@ namespace OnlineEditorsExample
// create the public history url
private string MakePublicHistoryUrl(string filename, string version, string file)
private string MakePublicHistoryUrl(string filename, string version, string file, Boolean isServer = true)
{
var fileUrl = new UriBuilder(_Default.GetServerUrl(true));
var userAddress = isServer ? "&userAddress=" + HttpUtility.UrlEncode(_Default.CurUserHostAddress(HttpContext.Current.Request.UserHostAddress)) : "";
var fileUrl = new UriBuilder(_Default.GetServerUrl(isServer));
fileUrl.Path = HttpRuntime.AppDomainAppVirtualPath
+ (HttpRuntime.AppDomainAppVirtualPath.EndsWith("/") ? "" : "/")
+ "webeditor.ashx";
fileUrl.Query = "type=downloadhistory&fileName=" + HttpUtility.UrlEncode(filename)
+ "&ver=" + version + "&file=" + file
+ "&userAddress=" + HttpUtility.UrlEncode(HttpContext.Current.Request.UserHostAddress);
+ userAddress;
return fileUrl.ToString();
}

View File

@ -16,12 +16,12 @@
*
*/
using System;
using JWT;
using JWT.Algorithms;
using JWT.Builder;
using JWT.Serializers;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Web.Configuration;
using System.Web.Script.Serialization;
namespace OnlineEditorsExample
{
@ -30,31 +30,19 @@ namespace OnlineEditorsExample
private static readonly string Secret;
public static readonly bool Enabled;
private static readonly JavaScriptSerializer Serializer;
static JwtManager()
{
Secret = WebConfigurationManager.AppSettings["files.docservice.secret"] ?? ""; // get token secret from the config parameters
Enabled = !string.IsNullOrEmpty(Secret); // check if the token is enabled
Serializer = new JavaScriptSerializer(); // define java script serializer
}
// encode a payload object into a token using a secret key
public static string Encode(IDictionary<string, object> payload)
{
// define the hashing algorithm and the token type
var header = new Dictionary<string, object>
{
{ "alg", "HS256" },
{ "typ", "JWT" }
};
// three parts of token
var encHeader = Base64UrlEncode(Serializer.Serialize(header)); // header
var encPayload = Base64UrlEncode(Serializer.Serialize(payload)); // payload
var hashSum = Base64UrlEncode(CalculateHash(encHeader, encPayload)); // signature
return string.Format("{0}.{1}.{2}", encHeader, encPayload, hashSum);
var encoder = new JwtEncoder(new HMACSHA256Algorithm(),
new JsonNetSerializer(),
new JwtBase64UrlEncoder());
return encoder.Encode(payload, Secret);
}
// decode a token into a payload object using a secret key
@ -62,52 +50,11 @@ namespace OnlineEditorsExample
{
if (!Enabled || string.IsNullOrEmpty(token)) return "";
var split = token.Split('.');
if (split.Length != 3) return "";
var hashSum = Base64UrlEncode(CalculateHash(split[0], split[1])); // get signature
if (hashSum != split[2]) return ""; // and check if it is equal to the signature from the token
return Base64UrlDecode(split[1]); // decode payload
}
// generate a hash code based on a key using the HMAC method
private static byte[] CalculateHash(string encHeader, string encPayload)
{
using (var hasher = new HMACSHA256(Encoding.UTF8.GetBytes(Secret)))
{
var bytes = Encoding.UTF8.GetBytes(string.Format("{0}.{1}", encHeader, encPayload));
return hasher.ComputeHash(bytes);
}
}
// encode a string into the base64 value
private static string Base64UrlEncode(string str)
{
return Base64UrlEncode(Encoding.UTF8.GetBytes(str));
}
// encode bytes into the base64 value
private static string Base64UrlEncode(byte[] bytes)
{
return Convert.ToBase64String(bytes)
.TrimEnd('=').Replace('+', '-').Replace('/', '_');
}
// decode a base64 value into the string
private static string Base64UrlDecode(string payload)
{
var b64 = payload.Replace('_', '/').Replace('-', '+');
switch (b64.Length%4)
{
case 2:
b64 += "==";
break;
case 3:
b64 += "=";
break;
}
var bytes = Convert.FromBase64String(b64);
return Encoding.UTF8.GetString(bytes);
return JwtBuilder.Create()
.WithAlgorithm(new HMACSHA256Algorithm())
.WithSecret(Secret)
.MustVerifySignature()
.Decode(token);
}
}
}

View File

@ -44,10 +44,16 @@
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="JWT, Version=9.0.0.0, Culture=neutral, PublicKeyToken=6f98bca0f40f2ecf, processorArchitecture=MSIL">
<HintPath>packages\JWT.9.0.3\lib\net46\JWT.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Web.Infrastructure">
<HintPath>packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Web.Extensions" />
<Reference Include="System.Web" />

View File

@ -66,7 +66,9 @@ namespace OnlineEditorsExample
"Can't mention others in comments",
"Can't create new files from the editor",
"Cant see anyones information",
"Can't rename files from the editor"
"Can't rename files from the editor",
"Can't view chat",
"View file without collaboration",
};
private static List<User> users = new List<User>() {

View File

@ -207,7 +207,7 @@ namespace OnlineEditorsExample
try
{
var fileName = Path.GetFileName(context.Request["fileName"]);
var path = _Default.StoragePath(fileName, HttpUtility.UrlEncode(HttpContext.Current.Request.UserHostAddress));
var path = _Default.StoragePath(fileName, HttpUtility.UrlEncode(_Default.CurUserHostAddress(HttpContext.Current.Request.UserHostAddress)));
var histDir = _Default.HistoryDir(path);
if (File.Exists(path)) File.Delete(path); // delete file
@ -394,6 +394,15 @@ namespace OnlineEditorsExample
var body = jss.Deserialize<Dictionary<string, object>>(fileData);
var newFileName = (string) body["newfilename"];
var docKey = (string) body["dockey"];
var origExt = '.' + (string) body["ext"];
var curExt = Path.GetExtension(newFileName).ToLower();
if (string.Compare(origExt, curExt, true) != 0)
{
newFileName += origExt;
}
var meta = new Dictionary<string, object>() {
{ "title", newFileName }
};

View File

@ -19,3 +19,11 @@ License File: jQuery.iframe-transport.license
jQuery.UI - jQuery UI is an open source library of interface components — interactions, full-featured widgets, and animation effects — based on the stellar jQuery javascript library . Each component is built according to jQuery's event-driven architecture (find something, manipulate it) and is themeable, making it easy for developers of any skill level to integrate and extend into their own code. (https://jquery.org/license/)
License: MIT
License File: jQuery.UI.license
JWT - JWT (JSON Web Token) Implementation for .NET (Public Domain) (https://github.com/jwt-dotnet/jwt/)
License: MIT
License File: JWT.license
Newtonsoft.Json - Json.NET is a popular high-performance JSON framework for .NET (https://github.com/JamesNK/Newtonsoft.Json)
License: MIT
License File: Newtonsoft.Json.license

View File

@ -0,0 +1,21 @@
# Public Domain
Written by John Sheehan (http://john-sheehan.com)
This work is public domain.
The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law.
For more information, please visit: http://creativecommons.org/publicdomain/zero/1.0/
# MIT
Copyright (c) 2019 Jwt.Net Maintainers 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.
For more information, please visit: https://opensource.org/licenses/MIT

View File

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2007 James Newton-King
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,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="JWT" version="9.0.3" targetFramework="net48" />
<package id="Microsoft.AspNet.Razor" version="3.2.7" targetFramework="net45" />
<package id="Microsoft.AspNet.WebPages" version="3.2.7" targetFramework="net45" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net45" />
<package id="Newtonsoft.Json" version="10.0.3" targetFramework="net48" />
</packages>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
<appSettings>
<clear />
<add key="version" value="1.2.0"/>
<add key="version" value="1.3.0"/>
<add key="filesize-max" value="52428800"/>
<add key="storage-path" value=""/>
@ -15,7 +15,7 @@
<add key="files.docservice.header" value="Authorization" />
<add key="files.docservice.verify-peer-off" value="true"/>
<add key="files.docservice.languages" value="en:English|az:Azerbaijani|be:Belarusian|bg:Bulgarian|ca:Catalan|zh:Chinese|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|lv:Latvian|lo:Lao|nb:Norwegian|pl:Polish|pt:Portuguese|ro:Romanian|ru:Russian|sk:Slovak|sl:Slovenian|es:Spanish|sv:Swedish|tr:Turkish|uk:Ukrainian|vi:Vietnamese"/>
<add key="files.docservice.languages" value="en:English|hy:Armenian|az:Azerbaijani|eu:Basque|be:Belarusian|bg:Bulgarian|ca:Catalan|zh:Chinese (People's Republic of China)|zh-TW:Chinese (Traditional, Taiwan)|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|lv:Latvian|lo:Lao|ms:Malay (Malaysia)|nb:Norwegian|pl:Polish|pt:Portuguese (Brazil)|pt-PT:Portuguese (Portugal)|ro:Romanian|ru:Russian|sk:Slovak|sl:Slovenian|es:Spanish|sv:Swedish|tr:Turkish|uk:Ukrainian|vi:Vietnamese"/>
<add key="files.docservice.url.site" value="http://documentserver/"/>

4
web/documentserver-example/java-spring/pom.xml Executable file → Normal file
View File

@ -48,7 +48,7 @@
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
<version>2.8.9</version>
</dependency>
<dependency>
<groupId>com.inversoft</groupId>
@ -64,7 +64,7 @@
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.2</version>
<version>2.12.6.1</version>
</dependency>
<dependency>
<groupId>org.modelmapper</groupId>

View File

@ -42,7 +42,9 @@ public class ExampleData {
"Can't mention others in comments",
"Can't create new files from the editor",
"Cant see anyones information",
"Can't rename files from the editor"
"Can't rename files from the editor",
"Can't view chat",
"View file without collaboration"
);
List<String> description_user_1 = List.of( // the description for user 1
"File author by default",
@ -51,7 +53,8 @@ public class ExampleData {
"He can do everything with the comments",
"The file favorite state is undefined",
"Can create a file from a template with data from the editor",
"Can see the information about all users"
"Can see the information about all users",
"Can view chat"
);
List<String> description_user_2 = List.of( // the description for user 2
"He belongs to Group2",
@ -59,7 +62,8 @@ public class ExampleData {
"He can view every comment, edit his comments and the comments left by the users who don't belong to any of the groups and remove only his comments",
"This file is favorite",
"Can create a file from an editor",
"Can see the information about users from Group2 and users who dont belong to any group"
"Can see the information about users from Group2 and users who dont belong to any group",
"Can view chat"
);
List<String> description_user_3 = List.of( // the description for user 3
"He belongs to Group3",
@ -70,7 +74,8 @@ public class ExampleData {
"He cant download the file",
"He cant print the file",
"Can create a file from an editor",
"Can see the information about Group2 users"
"Can see the information about Group2 users",
"Can view chat"
);
userService.createUser("John Smith", "smith@example.com", // create user 1 with the specified parameters
description_user_1, "", List.of(FilterState.NULL.toString()),
@ -78,15 +83,18 @@ public class ExampleData {
List.of(FilterState.NULL.toString()),
List.of(FilterState.NULL.toString()),
List.of(FilterState.NULL.toString()),
null);
null, true);
userService.createUser("Mark Pottato", "pottato@example.com", // create user 2 with the specified parameters
description_user_2, "group-2", List.of("","group-2"), List.of(FilterState.NULL.toString()),
List.of("group-2", ""), List.of("group-2"), List.of("group-2", ""), true);
List.of("group-2", ""), List.of("group-2"), List.of("group-2", ""), true,
true);
userService.createUser("Hamish Mitchell", "mitchell@example.com", // create user 3 with the specified parameters
description_user_3, "group-3", List.of("group-2"), List.of("group-2", "group-3"),
List.of("group-2"), new ArrayList<>(), List.of("group-2"), false);
List.of("group-2"), new ArrayList<>(), List.of("group-2"), false,
true);
userService.createUser("Anonymous",null, // create user 0 with the specified parameters
description_user_0,"", List.of(FilterState.NULL.toString()), List.of(FilterState.NULL.toString()),
List.of(FilterState.NULL.toString()), List.of(FilterState.NULL.toString()), new ArrayList<>(),null);
List.of(FilterState.NULL.toString()), List.of(FilterState.NULL.toString()), new ArrayList<>(),null,
false);
}
}

View File

@ -138,6 +138,7 @@ public class EditorController {
Map<String, Object> dataInsertImage = new HashMap<>();
dataInsertImage.put("fileType", "png");
dataInsertImage.put("url", storagePathBuilder.getServerUrl(true) + "/css/img/logo.png");
dataInsertImage.put("directUrl", storagePathBuilder.getServerUrl(false) + "/css/img/logo.png");
// check if the document token is enabled
if(jwtManager.tokenEnabled()){
@ -152,6 +153,7 @@ public class EditorController {
Map<String, Object> dataCompareFile = new HashMap<>();
dataCompareFile.put("fileType", "docx");
dataCompareFile.put("url", storagePathBuilder.getServerUrl(true) + "/assets?name=sample.docx");
dataCompareFile.put("directUrl", storagePathBuilder.getServerUrl(false) + "/assets?name=sample.docx");
// check if the document token is enabled
if(jwtManager.tokenEnabled()){
@ -166,6 +168,7 @@ public class EditorController {
Map<String, Object> dataMailMergeRecipients = new HashMap<>(); // get recipients data for mail merging
dataMailMergeRecipients.put("fileType", "csv");
dataMailMergeRecipients.put("url", storagePathBuilder.getServerUrl(true) + "/csv");
dataMailMergeRecipients.put("directUrl", storagePathBuilder.getServerUrl(false) + "/csv");
// check if the document token is enabled
if(jwtManager.tokenEnabled()){

View File

@ -164,7 +164,7 @@ public class FileController {
public String convert(@RequestBody Converter body, // convert a file
@CookieValue("uid") String uid, @CookieValue("ulang") String lang){
String fileName = body.getFileName(); // get file name
String fileUri = documentManager.getDownloadUrl(fileName); // get URL for downloading a file with the specified name
String fileUri = documentManager.getDownloadUrl(fileName, true); // get URL for downloading a file with the specified name
String filePass = body.getFilePass() != null ? body.getFilePass() : null; // get file password if it exists
String fileExt = fileUtility.getFileExtension(fileName); // get file extension
DocumentType type = fileUtility.getDocumentType(fileName); // get document type (word, cell or slide)
@ -371,6 +371,16 @@ public class FileController {
public String rename(@RequestBody JSONObject body) {
String newfilename = (String) body.get("newfilename");
String dockey = (String) body.get("dockey");
String origExt = "." + (String) body.get("ext");
String curExt = newfilename;
if(newfilename.indexOf(".") != -1) {
curExt = (String) fileUtility.getFileExtension(newfilename);
}
if(origExt.compareTo(curExt) != 0) {
newfilename += origExt;
}
HashMap<String, String> meta = new HashMap<>();
meta.put("title", newfilename);

View File

@ -118,10 +118,10 @@ public class DefaultDocumentManager implements DocumentManager {
String hostAddress = storagePathBuilder.getStorageLocation(); // get the storage directory
String filePathDownload = !fileName.contains(InetAddress.getLocalHost().getHostAddress()) ? fileName
: fileName.substring(fileName.indexOf(InetAddress.getLocalHost().getHostAddress()) + InetAddress.getLocalHost().getHostAddress().length() + 1);
String userAddress = forDocumentServer ? "&userAddress" + URLEncoder.encode(hostAddress, java.nio.charset.StandardCharsets.UTF_8.toString()) : "";
String filePath = serverPath + "/downloadhistory?fileName=" + URLEncoder.encode(filePathDownload, java.nio.charset.StandardCharsets.UTF_8.toString())
+ "&ver=" + version + "&file="+file
+ "&userAddress" + URLEncoder.encode(hostAddress, java.nio.charset.StandardCharsets.UTF_8.toString());
+ userAddress;
return filePath;
}
catch (UnsupportedEncodingException | UnknownHostException e)
@ -149,15 +149,15 @@ public class DefaultDocumentManager implements DocumentManager {
}
// get URL to download a file
public String getDownloadUrl(String fileName) {
String serverPath = storagePathBuilder.getServerUrl(true);
public String getDownloadUrl(String fileName, Boolean isServer) {
String serverPath = storagePathBuilder.getServerUrl(isServer);
String storageAddress = storagePathBuilder.getStorageLocation();
try
{
String userAddress = isServer ? "&userAddress=" + URLEncoder.encode(storageAddress, java.nio.charset.StandardCharsets.UTF_8.toString()) : "";
String query = downloadUrl+"?fileName="
+ URLEncoder.encode(fileName, java.nio.charset.StandardCharsets.UTF_8.toString())
+ "&userAddress="
+ URLEncoder.encode(storageAddress, java.nio.charset.StandardCharsets.UTF_8.toString());
+ userAddress;
return serverPath + query;
}

View File

@ -27,7 +27,7 @@ public interface DocumentManager {
String getFileUri(String fileName, Boolean forDocumentServer); // get file URL
String getHistoryFileUrl(String fileName, Integer version, String file, Boolean forDocumentServer); // get file URL
String getCallback(String fileName); // get the callback URL
String getDownloadUrl(String fileName); // get URL to download a file
String getDownloadUrl(String fileName, Boolean forDocumentServer); // get URL to download a file
ArrayList<Map<String, Object>> getFilesInfo(); // get file information
ArrayList<Map<String, Object>> getFilesInfo(String fileId); // get file information by its ID
String versionDir(String path, Integer version, boolean historyPath); // get the path to the file version by the history path and file version

View File

@ -91,6 +91,8 @@ public class DefaultHistoryManager implements HistoryManager {
dataObj.put("key", key);
dataObj.put("url", i == curVer ? document.getUrl() :
documentManager.getHistoryFileUrl(document.getTitle(), i, "prev" + fileUtility.getFileExtension(document.getTitle()), true));
dataObj.put("directUrl", i == curVer ? document.getDirectUrl() :
documentManager.getHistoryFileUrl(document.getTitle(), i, "prev" + fileUtility.getFileExtension(document.getTitle()), false));
dataObj.put("version", i);
if (i > 1) { //check if the version number is greater than 1
@ -109,6 +111,7 @@ public class DefaultHistoryManager implements HistoryManager {
prevInfo.put("fileType", prev.get("fileType"));
prevInfo.put("key", prev.get("key")); // write key and URL information about previous file version
prevInfo.put("url", prev.get("url"));
prevInfo.put("directUrl", prev.get("directUrl"));
dataObj.put("previous", prevInfo); // write information about previous file version to the data object
// write the path to the diff.zip archive with differences in this file version
Integer verdiff = i - 1;

View File

@ -25,6 +25,7 @@ public enum Action {
embedded,
filter,
comment,
chat,
fillForms,
blockcontent
}

View File

@ -39,4 +39,5 @@ public class Document { // the parameters pertaining to the document (title, ur
private String urlUser; // the absolute URL that will allow the document to be saved onto the user personal computer
private String title; // the desired file name for the viewed or edited document which will also be used as file name when the document is downloaded
private String url; // the absolute URL where the source viewed or edited document is stored
private String directUrl;
}

View File

@ -38,6 +38,7 @@ import java.util.List;
public class EditorConfig { // the parameters pertaining to the editor interface: opening mode (viewer or editor), interface language, additional buttons, etc.
private HashMap<String, Object> actionLink = null; // the data which contains the information about the action in the document that will be scrolled to
private String callbackUrl; // the absolute URL to the document storage service
private HashMap<String, Object> coEditing = null;
private String createUrl; // the absolute URL of the document where it will be created and available after creation
@Autowired
private Customization customization; // the parameters which allow to customize the editor interface so that it looked like your other products (if there are any) and change the presence or absence of the additional buttons, links, change logos and editor owner details

View File

@ -43,6 +43,7 @@ public class Permission extends AbstractModel { // the permission for the docum
private Boolean modifyFilter = true; // if the filter can applied globally (true) affecting all the other users, or locally (false)
private Boolean modifyContentControl = true; // if the content control settings can be changed
private Boolean review = true; // if the document can be reviewed or not
private Boolean chat = true; // if a chat can be used
@JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = SerializerFilter.class)
private List<String> reviewGroups; // the groups whose changes the user can accept/reject
@Autowired

View File

@ -38,6 +38,7 @@ public class Permission extends AbstractEntity {
private Boolean modifyFilter = true;
private Boolean modifyContentControl = true;
private Boolean review = true;
private Boolean chat = true;
private Boolean templates=true;
@ManyToMany
private List<Group> reviewGroups;

View File

@ -37,7 +37,8 @@ public class PermissionServices {
List<Group> commentViewGroups,
List<Group> commentEditGroups,
List<Group> commentRemoveGroups,
List<Group> userInfoGroups){
List<Group> userInfoGroups,
Boolean chat){
Permission permission = new Permission();
permission.setReviewGroups(reviewGroups); // define the groups whose changes the user can accept/reject
@ -45,6 +46,7 @@ public class PermissionServices {
permission.setCommentsEditGroups(commentEditGroups); // defines the groups whose comments the user can edit
permission.setCommentsRemoveGroups(commentRemoveGroups); // defines the groups whose comments the user can remove
permission.setUserInfoGroups(userInfoGroups);
permission.setChat(chat);
permissionRepository.save(permission); // save new permissions

View File

@ -56,7 +56,9 @@ public class UserServices {
List<String> reviewGroups,
List<String> viewGroups,
List<String> editGroups,
List<String> removeGroups, List<String> userInfoGroups, Boolean favoriteDoc){
List<String> removeGroups,
List<String> userInfoGroups, Boolean favoriteDoc,
Boolean chat){
User newUser = new User();
newUser.setName(name); // set the user name
newUser.setEmail(email); // set the user email
@ -71,7 +73,7 @@ public class UserServices {
List<Group> usInfoGroups = groupServices.createGroups(userInfoGroups);
Permission permission = permissionService
.createPermission(groupsReview, commentGroupsView, commentGroupsEdit, commentGroupsRemove, usInfoGroups); // specify permissions for the current user
.createPermission(groupsReview, commentGroupsView, commentGroupsEdit, commentGroupsRemove, usInfoGroups, chat); // specify permissions for the current user
newUser.setPermissions(permission);
userRepository.save(newUser); // save a new user

View File

@ -53,8 +53,9 @@ public class DefaultDocumentConfigurer implements DocumentConfigurer<DefaultDocu
Permission permission = wrapper.getPermission(); // get the permission parameter from the document wrapper
document.setTitle(fileName); // set the title to the document config
document.setUrl(documentManager.getDownloadUrl(fileName)); // set the URL to download a file to the document config
document.setUrl(documentManager.getDownloadUrl(fileName, true)); // set the URL to download a file to the document config
document.setUrlUser(documentManager.getFileUri(fileName, false)); // set the file URL to the document config
document.setDirectUrl(documentManager.getDownloadUrl(fileName, false));
document.setFileType(fileUtility.getFileExtension(fileName).replace(".","")); // set the file type to the document config
document.getInfo().setFavorite(wrapper.getFavorite()); // set the favorite parameter to the document config

View File

@ -82,6 +82,10 @@ public class DefaultEditorConfigConfigurer implements EditorConfigConfigurer<Def
config.setLang(wrapper.getLang()); // set the language to the editorConfig
Boolean canEdit = wrapper.getCanEdit(); // check if the file of the specified type can be edited or not
Action action = wrapper.getAction(); // get the action parameter from the editorConfig wrapper
config.setCoEditing(action.equals(Action.view) && userIsAnon ? new HashMap<String, Object>() {{
put("mode", "strict");
put("change", false);
}} : null);
defaultCustomizationConfigurer.configure(config.getCustomization(), DefaultCustomizationWrapper.builder() // define the customization configurer
.action(action)

View File

@ -37,7 +37,7 @@ public class DefaultEmbeddedConfigurer implements EmbeddedConfigurer<DefaultEmbe
public void configure(Embedded embedded, DefaultEmbeddedWrapper wrapper){ // define the embedded configurer
if(wrapper.getType().equals(Type.embedded)) { // check if the type from the embedded wrapper is embedded
String url = documentManager.getFileUri(wrapper.getFileName(), false); // get file URL of the specified file
String url = documentManager.getDownloadUrl(wrapper.getFileName(), false); // get file URL of the specified file
embedded.setEmbedUrl(url); // set the embedURL parameter to the embedded config (the absolute URL to the document serving as a source file for the document embedded into the web page)
embedded.setSaveUrl(url); // set the saveURL parameter to the embedded config (the absolute URL that will allow the document to be saved onto the user personal computer)
embedded.setShareUrl(url); // set the shareURL parameter to the embedded config (the absolute URL that will allow other users to share this document)

View File

@ -1,4 +1,4 @@
server.version=1.2.0
server.version=1.3.0
server.address=
server.port=4000
@ -27,7 +27,7 @@ files.docservice.header=Authorization
files.docservice.verify-peer-off=true
files.docservice.languages=en:English|az:Azerbaijani|be:Belarusian|bg:Bulgarian|ca:Catalan|zh:Chinese|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|lv:Latvian|lo:Lao|nb:Norwegian|pl:Polish|pt:Portuguese|ro:Romanian|ru:Russian|sk:Slovak|sl:Slovenian|es:Spanish|sv:Swedish|tr:Turkish|uk:Ukrainian|vi:Vietnamese
files.docservice.languages=en:English|hy:Armenian|az:Azerbaijani|eu:Basque|be:Belarusian|bg:Bulgarian|ca:Catalan|zh:Chinese (People's Republic of China)|zh-TW:Chinese (Traditional, Taiwan)|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|lv:Latvian|lo:Lao|ms:Malay (Malaysia)|nb:Norwegian|pl:Polish|pt:Portuguese (Brazil)|pt-PT:Portuguese (Portugal)|ro:Romanian|ru:Russian|sk:Slovak|sl:Slovenian|es:Spanish|sv:Swedish|tr:Turkish|uk:Ukrainian|vi:Vietnamese
spring.datasource.url=jdbc:h2:mem:usersdb
spring.datasource.driverClassName=org.h2.Driver

View File

@ -149,6 +149,7 @@
var data = {
newfilename: newfilename,
dockey: config.document.key,
ext: config.document.fileType
};
let xhr = new XMLHttpRequest();
xhr.open("POST", "rename");

View File

@ -29,7 +29,7 @@
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
<version>2.8.9</version>
</dependency>
<dependency>
<groupId>com.inversoft</groupId>

View File

@ -78,16 +78,19 @@ public class EditorServlet extends HttpServlet
Map<String, Object> dataInsertImage = new HashMap<>();
dataInsertImage.put("fileType", "png");
dataInsertImage.put("url", DocumentManager.GetServerUrl(true) + "/css/img/logo.png");
dataInsertImage.put("directUrl", DocumentManager.GetServerUrl(false) + "/css/img/logo.png");
// a document that will be compared with the current document
Map<String, Object> dataCompareFile = new HashMap<>();
dataCompareFile.put("fileType", "docx");
dataCompareFile.put("url", DocumentManager.GetServerUrl(true) + "/IndexServlet?type=assets&name=sample.docx");
dataCompareFile.put("directUrl", DocumentManager.GetServerUrl(false) + "/IndexServlet?type=assets&name=sample.docx");
// recipients data for mail merging
Map<String, Object> dataMailMergeRecipients = new HashMap<>();
dataMailMergeRecipients.put("fileType", "csv");
dataMailMergeRecipients.put("url", DocumentManager.GetServerUrl(true) + "/IndexServlet?type=csv");
dataMailMergeRecipients.put("directUrl", DocumentManager.GetServerUrl(false) + "/IndexServlet?type=csv");
// users data for mentions
List<Map<String, Object>> usersForMentions = Users.getUsersForMentions(user.id);

View File

@ -234,7 +234,7 @@ public class IndexServlet extends HttpServlet
String fileName = FileUtility.GetFileName((String) body.get("filename"));
String lang = cm.getCookie("ulang");
String filePass = body.get("filePass") != null ? (String) body.get("filePass") : null;
String fileUri = DocumentManager.GetDownloadUrl(fileName);
String fileUri = DocumentManager.GetDownloadUrl(fileName, true);
String fileExt = FileUtility.GetFileExtension(fileName);
FileType fileType = FileUtility.GetFileType(fileName);
String internalFileExt = DocumentManager.GetInternalExtension(fileType);
@ -573,6 +573,17 @@ public class IndexServlet extends HttpServlet
String newfilename = (String) body.get("newfilename");
String dockey = (String) body.get("dockey");
String origExt = "." + (String) body.get("ext");
String curExt = newfilename;
if(newfilename.indexOf(".") != -1) {
curExt = (String) FileUtility.GetFileExtension(newfilename);
}
if(origExt.compareTo(curExt) != 0) {
newfilename += origExt;
}
HashMap<String, String> meta = new HashMap<>();
meta.put("title", newfilename);

View File

@ -51,7 +51,8 @@ public class FileModel
// set the document parameters
document = new Document();
document.title = fileName;
document.url = DocumentManager.GetDownloadUrl(fileName); // get file url
document.url = DocumentManager.GetDownloadUrl(fileName, true); // get file url
document.directUrl = DocumentManager.GetDownloadUrl(fileName, false); // get direct url
document.fileType = FileUtility.GetFileExtension(fileName).replace(".", ""); // get file extension from the file name
// generate document key
document.key = ServiceConverter.GenerateRevisionId(DocumentManager.CurUserHostAddress(null) + "/" + fileName + "/" + Long.toString(new File(DocumentManager.StoragePath(fileName, null)).lastModified()));
@ -77,6 +78,12 @@ public class FileModel
// set the editor config parameters
editorConfig = new EditorConfig(actionData);
editorConfig.callbackUrl = DocumentManager.GetCallback(fileName); // get callback url
editorConfig.coEditing = mode.equals("view") && user.id.equals("uid-0") ?
new HashMap<String, Object>() {{
put("mode", "strict");
put("change", false);
}} : null;
if (lang != null) editorConfig.lang = lang; // write language parameter to the config
@ -121,7 +128,7 @@ public class FileModel
public void InitDesktop(String fileName)
{
editorConfig.InitDesktop(DocumentManager.GetDownloadUrl(fileName) + "&dmode=emb");
editorConfig.InitDesktop(DocumentManager.GetDownloadUrl(fileName, false) + "&dmode=emb");
}
// generate document token
@ -177,7 +184,8 @@ public class FileModel
dataObj.put("fileType", FileUtility.GetFileExtension(document.title).substring(1));
dataObj.put("key", key);
dataObj.put("url", i == curVer ? document.url : DocumentManager.GetDownloadHistoryUrl(document.title, i, "prev" + FileUtility.GetFileExtension(document.title)));
dataObj.put("url", i == curVer ? document.url : DocumentManager.GetDownloadHistoryUrl(document.title, i, "prev" + FileUtility.GetFileExtension(document.title), true));
dataObj.put("directUrl", i == curVer ? document.url : DocumentManager.GetDownloadHistoryUrl(document.title, i, "prev" + FileUtility.GetFileExtension(document.title), false));
dataObj.put("version", i);
if (i > 1) { //check if the version number is greater than 1
@ -199,7 +207,7 @@ public class FileModel
dataObj.put("previous", prevInfo); // write information about previous file version to the data object
// write the path to the diff.zip archive with differences in this file version
Integer verdiff = i - 1;
String changesUrl = DocumentManager.GetDownloadHistoryUrl(document.title, verdiff, "diff.zip");
String changesUrl = DocumentManager.GetDownloadHistoryUrl(document.title, verdiff, "diff.zip", true);
dataObj.put("changesUrl", changesUrl);
}
@ -247,6 +255,7 @@ public class FileModel
{
public String title;
public String url;
public String directUrl;
public String fileType;
public String key;
public Info info;
@ -265,9 +274,11 @@ public class FileModel
public Boolean modifyFilter;
public Boolean modifyContentControl;
public Boolean review;
public Boolean chat;
public List<String> reviewGroups;
public CommentGroups commentGroups;
public List<String> userInfoGroups;
//public Gson gson = new Gson();
// defines what can be done with a document
public Permissions(String mode, String type, Boolean canEdit, User user)
@ -281,6 +292,7 @@ public class FileModel
modifyFilter = !mode.equals("filter");
modifyContentControl = !mode.equals("blockcontent");
review = canEdit && (mode.equals("edit") || mode.equals("review"));
chat = !user.id.equals("uid-0");
reviewGroups = user.reviewGroups;
commentGroups = user.commentGroups;
userInfoGroups = user.userInfoGroups;
@ -305,6 +317,7 @@ public class FileModel
public HashMap<String, Object> actionLink = null;
public String mode = "edit";
public String callbackUrl;
public HashMap<String, Object> coEditing = null;
public String lang = "en";
public String createUrl;
public List<Map<String, String>> templates;

View File

@ -354,7 +354,7 @@ public class DocumentManager
String filePath = serverPath + "/" + storagePath + "/" + hostAddress + "/" + URLEncoder.encode(fileName, java.nio.charset.StandardCharsets.UTF_8.toString()).replace("+", "%20");
if (f.isAbsolute() && f.isFile()) {
filePath = GetDownloadUrl(fileName);
filePath = GetDownloadUrl(fileName, true);
if (!Files.isWritable(f.toPath())) {
throw new SecurityException("No write permission to path: " + f.toPath());
}
@ -450,12 +450,13 @@ public class DocumentManager
}
// get url to download a file
public static String GetDownloadUrl(String fileName) {
String serverPath = GetServerUrl(true);
public static String GetDownloadUrl(String fileName, Boolean forDocumentServer) {
String serverPath = GetServerUrl(forDocumentServer);
String hostAddress = CurUserHostAddress(null);
try
{
String query = "?type=download&fileName=" + URLEncoder.encode(fileName, java.nio.charset.StandardCharsets.UTF_8.toString()) + "&userAddress=" + URLEncoder.encode(hostAddress, java.nio.charset.StandardCharsets.UTF_8.toString());
String userAddress = forDocumentServer ? "&userAddress=" + URLEncoder.encode(hostAddress, java.nio.charset.StandardCharsets.UTF_8.toString()) : "";
String query = "?type=download&fileName=" + URLEncoder.encode(fileName, java.nio.charset.StandardCharsets.UTF_8.toString()) + userAddress;
return serverPath + "/IndexServlet" + query;
}
@ -466,12 +467,13 @@ public class DocumentManager
}
// get url to download a file to History prev.*
public static String GetDownloadHistoryUrl(String fileName, Integer version, String file) {
String serverPath = GetServerUrl(true);
public static String GetDownloadHistoryUrl(String fileName, Integer version, String file, Boolean forDocumentServer) {
String serverPath = GetServerUrl(forDocumentServer);
String hostAddress = CurUserHostAddress(null);
try
{
String query = "?type=downloadhistory&fileName=" + URLEncoder.encode(fileName, java.nio.charset.StandardCharsets.UTF_8.toString()) + "&userAddress=" + URLEncoder.encode(hostAddress, java.nio.charset.StandardCharsets.UTF_8.toString());
String userAddress = forDocumentServer ? "&userAddress=" + URLEncoder.encode(hostAddress, java.nio.charset.StandardCharsets.UTF_8.toString()) : "";
String query = "?type=downloadhistory&fileName=" + URLEncoder.encode(fileName, java.nio.charset.StandardCharsets.UTF_8.toString()) + userAddress;
query = query + "&ver=" + version + "&file=" + URLEncoder.encode(file, java.nio.charset.StandardCharsets.UTF_8.toString());
return serverPath + "/IndexServlet" + query;

View File

@ -65,6 +65,8 @@ public class Users {
add("Can't create new files from the editor");
add("Cant see anyones information");
add("Can't rename files from the editor");
add("Can't view chat");
add("View file without collaboration");
}};
private static List<User> users = new ArrayList<User>() {{

View File

@ -1,4 +1,4 @@
version=1.2.0
version=1.3.0
filesize-max=5242880
storage-folder=app_data
@ -16,7 +16,7 @@ files.docservice.url.api=web-apps/apps/api/documents/api.js
files.docservice.url.preloader=web-apps/apps/api/documents/cache-scripts.html
files.docservice.url.example=
files.docservice.languages=en:English|az:Azerbaijani|be:Belarusian|bg:Bulgarian|ca:Catalan|zh:Chinese|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|lv:Latvian|lo:Lao|nb:Norwegian|pl:Polish|pt:Portuguese|ro:Romanian|ru:Russian|sk:Slovak|sl:Slovenian|es:Spanish|sv:Swedish|tr:Turkish|uk:Ukrainian|vi:Vietnamese
files.docservice.languages=en:English|hy:Armenian|az:Azerbaijani|eu:Basque|be:Belarusian|bg:Bulgarian|ca:Catalan|zh:Chinese (People's Republic of China)|zh-TW:Chinese (Traditional, Taiwan)|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|lv:Latvian|lo:Lao|ms:Malay (Malaysia)|nb:Norwegian|pl:Polish|pt:Portuguese (Brazil)|pt-PT:Portuguese (Portugal)|ro:Romanian|ru:Russian|sk:Slovak|sl:Slovenian|es:Spanish|sv:Swedish|tr:Turkish|uk:Ukrainian|vi:Vietnamese
files.docservice.secret=
files.docservice.header=Authorization

View File

@ -150,6 +150,7 @@
var data = {
newfilename: newfilename,
dockey: config.document.key,
ext: config.document.fileType
};
let xhr = new XMLHttpRequest();
xhr.open("POST", "IndexServlet?type=rename");

View File

@ -44,6 +44,7 @@ const cfgSignatureAuthorizationHeaderPrefix = configServer.get('token.authorizat
const cfgSignatureSecretExpiresIn = configServer.get('token.expiresIn');
const cfgSignatureSecret = configServer.get('token.secret');
const urllib = require("urllib");
const { emitWarning } = require("process");
const verifyPeerOff = configServer.get('verify_peer_off');
if(verifyPeerOff) {
@ -128,9 +129,8 @@ app.get("/download", function(req, res) { // define a handler for downloading f
var fileName = fileUtility.getFileName(req.query.fileName);
var userAddress = req.query.useraddress;
var isEmbedded = req.query.dmode;
if ((cfgSignatureEnable && cfgSignatureUseForRequest) && isEmbedded == null ) {
if ((cfgSignatureEnable && cfgSignatureUseForRequest)) {
var authorization = req.get(cfgSignatureAuthorizationHeader);
if (authorization && authorization.startsWith(cfgSignatureAuthorizationHeaderPrefix)) {
var token = authorization.substring(cfgSignatureAuthorizationHeaderPrefix.length);
@ -212,7 +212,6 @@ app.post("/upload", function (req, res) { // define a handler for uploading fil
const form = new formidable.IncomingForm(); // create a new incoming form
form.uploadDir = uploadDirTmp; // and write there all the necessary parameters
form.keepExtensions = true;
form.maxFileSize = configServer.get("maxFileSize");
form.parse(req, function (err, fields, files) { // parse this form
if (err) { // if an error occurs
@ -325,7 +324,7 @@ app.post("/convert", function (req, res) { // define a handler for converting f
var fileName = fileUtility.getFileName(req.body.filename);
var filePass = req.body.filePass ? req.body.filePass : null;
var lang = req.body.lang ? req.body.lang : null;
var fileUri = req.docManager.getFileUri(fileName);
var fileUri = req.docManager.getDownloadUrl(fileName, true);
var fileExt = fileUtility.getFileExtension(fileName);
var fileType = fileUtility.getFileType(fileName);
var internalFileExt = req.docManager.getInternalExtension(fileType);
@ -349,7 +348,7 @@ app.post("/convert", function (req, res) { // define a handler for converting f
response.end();
};
var callback = function (err, data) {
var callback = async function (err, res) {
if (err) { // if an error occurs
if (err.name === "ConnectionTimeoutError" || err.name === "ResponseTimeoutError") { // check what type of error it is
writeResult(fileName, 0, null); // despite the timeout errors, write the file to the result object
@ -360,7 +359,7 @@ app.post("/convert", function (req, res) { // define a handler for converting f
}
try {
var responseUri = documentService.getResponseUri(data.toString());
var responseUri = documentService.getResponseUri(res.toString());
var result = responseUri.key;
var newFileUri = responseUri.value; // get the callback url
@ -371,10 +370,11 @@ app.post("/convert", function (req, res) { // define a handler for converting f
var correctName = req.docManager.getCorrectName(fileUtility.getFileName(fileName, true) + internalFileExt); // get the file name with a new extension
urllib.request(newFileUri, {method: "GET"},function(err, data) {
fileSystem.writeFileSync(req.docManager.storagePath(correctName), data); // write a file with a new extension, but with the content from the origin file
});
const {status, data} = await urllib.request(newFileUri, {method: "GET"});
if (status != 200) throw new Error("Conversion service returned status: " + status);
fileSystem.writeFileSync(req.docManager.storagePath(correctName), data); // write a file with a new extension, but with the content from the origin file
fileSystem.unlinkSync(req.docManager.storagePath(fileName)); // remove file with the origin extension
var userAddress = req.docManager.curUserHostAddress();
@ -469,7 +469,7 @@ app.get("/csv", function (req, res) { // define a handler for downloading csv f
filestream.pipe(res); // send file information to the response by streams
})
app.post("/track", function (req, res) { // define a handler for tracking file changes
app.post("/track", async function (req, res) { // define a handler for tracking file changes
req.docManager = new docManager(req, res);
@ -478,11 +478,15 @@ app.post("/track", function (req, res) { // define a handler for tracking file
var version = 0;
// track file changes
var processTrack = function (response, body, fileName, userAddress) {
var processTrack = async function (response, body, fileName, userAddress) {
// callback file saving process
var callbackProcessSave = function (downloadUri, body, fileName, userAddress, newFileName) {
var callbackProcessSave = async function (downloadUri, body, fileName, userAddress, newFileName) {
try {
const {status, data} = await urllib.request(downloadUri, {method: "GET"});
if (status != 200) throw new Error("Document editing service returned status: " + status);
var storagePath = req.docManager.storagePath(newFileName, userAddress);
var historyPath = req.docManager.historyPath(newFileName, userAddress); // get the path to the history data
@ -499,9 +503,12 @@ app.post("/track", function (req, res) { // define a handler for tracking file
var downloadZip = body.changesurl;
if (downloadZip) {
var path_changes = req.docManager.diffPath(newFileName, userAddress, version); // get the path to the file with document versions differences
urllib.request(downloadZip, {method: "GET"},function(err, data) {
const {status, data} = await urllib.request(downloadZip, {method: "GET"});
if (status == 200) {
fileSystem.writeFileSync(path_changes, data); // write the document version differences to the archive
});
} else {
emitWarning("Document editing service returned status: " + status);
}
}
var changeshistory = body.changeshistory || JSON.stringify(body.history);
@ -516,9 +523,7 @@ app.post("/track", function (req, res) { // define a handler for tracking file
var path_prev = path.join(versionPath, "prev" + fileUtility.getFileExtension(fileName)); // get the path to the previous file version
fileSystem.renameSync(req.docManager.storagePath(fileName, userAddress), path_prev); // and write it to the current path
urllib.request(downloadUri, {method: "GET"},function(err, data) {
fileSystem.writeFileSync(storagePath, data);
});
fileSystem.writeFileSync(storagePath, data);
var forcesavePath = req.docManager.forcesavePath(newFileName, userAddress, false); // get the path to the forcesaved file
if (forcesavePath != "") { // if this path is empty
@ -526,6 +531,7 @@ app.post("/track", function (req, res) { // define a handler for tracking file
}
} catch (ex) {
console.log(ex);
response.write("{\"error\":1}");
response.end();
return;
@ -536,7 +542,7 @@ app.post("/track", function (req, res) { // define a handler for tracking file
}
// file saving process
var processSave = function (downloadUri, body, fileName, userAddress) {
var processSave = async function (downloadUri, body, fileName, userAddress) {
if (!downloadUri) {
response.write("{\"error\":1}");
@ -557,18 +563,18 @@ app.post("/track", function (req, res) { // define a handler for tracking file
var key = documentService.generateRevisionId(downloadUri);
newFileName = req.docManager.getCorrectName(fileUtility.getFileName(fileName, true) + downloadExt, userAddress); // get the correct file name if it already exists
try {
documentService.getConvertedUriSync(downloadUri, downloadExt, curExt, key, function (err, data) {
documentService.getConvertedUriSync(downloadUri, downloadExt, curExt, key, async function (err, data) {
if (err) {
callbackProcessSave(downloadUri, body, fileName, userAddress, newFileName);
await callbackProcessSave(downloadUri, body, fileName, userAddress, newFileName);
return;
}
try {
var res = documentService.getResponseUri(data);
callbackProcessSave(res.value, body, fileName, userAddress, fileName);
await callbackProcessSave(res.value, body, fileName, userAddress, fileName);
return;
} catch (ex) {
console.log(ex);
callbackProcessSave(downloadUri, body, fileName, userAddress, newFileName);
await callbackProcessSave(downloadUri, body, fileName, userAddress, newFileName);
return;
}
});
@ -577,12 +583,16 @@ app.post("/track", function (req, res) { // define a handler for tracking file
console.log(ex);
}
}
callbackProcessSave(downloadUri, body, fileName, userAddress, newFileName);
await callbackProcessSave(downloadUri, body, fileName, userAddress, newFileName);
};
// callback file force saving process
var callbackProcessForceSave = function (downloadUri, body, fileName, userAddress, newFileName = false){
var callbackProcessForceSave = async function (downloadUri, body, fileName, userAddress, newFileName = false){
try {
const {status, data} = await urllib.request(downloadUri, {method: "GET"});
if (status != 200) throw new Error("Document editing service returned status: " + status);
var downloadExt = "." + body.fileType;
/// TODO [Delete in version 7.0 or higher]
@ -610,9 +620,7 @@ app.post("/track", function (req, res) { // define a handler for tracking file
}
}
urllib.request(downloadUri, {method: "GET"},function(err, data) {
fileSystem.writeFileSync(forcesavePath, data);
});
fileSystem.writeFileSync(forcesavePath, data);
if (isSubmitForm) {
var uid =body.actions[0].userid
@ -629,7 +637,7 @@ app.post("/track", function (req, res) { // define a handler for tracking file
}
// file force saving process
var processForceSave = function (downloadUri, body, fileName, userAddress) {
var processForceSave = async function (downloadUri, body, fileName, userAddress) {
if (!downloadUri) {
response.write("{\"error\":1}");
@ -647,18 +655,18 @@ app.post("/track", function (req, res) { // define a handler for tracking file
if (downloadExt != curExt) {
var key = documentService.generateRevisionId(downloadUri);
try {
documentService.getConvertedUriSync(downloadUri, downloadExt, curExt, key, function (err, data) {
documentService.getConvertedUriSync(downloadUri, downloadExt, curExt, key, async function (err, data) {
if (err) {
callbackProcessForceSave(downloadUri, body, fileName, userAddress, true);
await callbackProcessForceSave(downloadUri, body, fileName, userAddress, true);
return;
}
try {
var res = documentService.getResponseUri(data);
callbackProcessForceSave(res.value, body, fileName, userAddress, false);
await callbackProcessForceSave(res.value, body, fileName, userAddress, false);
return;
} catch (ex) {
console.log(ex);
callbackProcessForceSave(downloadUri, body, fileName, userAddress, true);
await callbackProcessForceSave(downloadUri, body, fileName, userAddress, true);
return;
}
});
@ -667,7 +675,7 @@ app.post("/track", function (req, res) { // define a handler for tracking file
console.log(ex);
}
}
callbackProcessForceSave (downloadUri, body, fileName, userAddress, false);
await callbackProcessForceSave (downloadUri, body, fileName, userAddress, false);
};
if (body.status == 1) { // editing
@ -683,10 +691,10 @@ app.post("/track", function (req, res) { // define a handler for tracking file
}
}
} else if (body.status == 2 || body.status == 3) { // MustSave, Corrupted
processSave(body.url, body, fileName, userAddress); // save file
await processSave(body.url, body, fileName, userAddress); // save file
return;
} else if (body.status == 6 || body.status == 7) { // MustForceSave, CorruptedForceSave
processForceSave(body.url, body, fileName, userAddress); // force save file
await processForceSave(body.url, body, fileName, userAddress); // force save file
return;
}
@ -695,14 +703,14 @@ app.post("/track", function (req, res) { // define a handler for tracking file
};
// read request body
var readbody = function (request, response, fileName, userAddress) {
var readbody = async function (request, response, fileName, userAddress) {
var content = "";
request.on('data', function (data) { // get data from the request
request.on('data', async function (data) { // get data from the request
content += data;
});
request.on('end', function () {
request.on('end', async function () {
var body = JSON.parse(content);
processTrack(response, body, fileName, userAddress); // and track file changes
await processTrack(response, body, fileName, userAddress); // and track file changes
});
};
@ -734,14 +742,14 @@ app.post("/track", function (req, res) { // define a handler for tracking file
res.end();
return;
}
processTrack(res, body, fileName, userAddress);
await processTrack(res, body, fileName, userAddress);
return;
}
if (req.body.hasOwnProperty("status")) { // if the request body has status parameter
processTrack(res, req.body, fileName, userAddress); // track file changes
await processTrack(res, req.body, fileName, userAddress); // track file changes
} else {
readbody(req, res, fileName, userAddress); // otherwise, read request body first
await readbody(req, res, fileName, userAddress); // otherwise, read request body first
}
});
@ -756,6 +764,7 @@ app.get("/editor", function (req, res) { // define a handler for editing docume
var historyData = [];
var lang = req.docManager.getLang();
var user = users.getUser(req.query.userid);
var userDirectUrl = req.query.directUrl == "true";
var userid = user.id;
var name = user.name;
@ -807,8 +816,8 @@ app.get("/editor", function (req, res) { // define a handler for editing docume
};
}
var key = req.docManager.getKey(fileName);
var url = req.docManager.getDownloadUrl(fileName);
var urlUser = path.isAbsolute(storageFolder) ? req.docManager.getDownloadUrl(fileName) + "&dmode=emb" : req.docManager.getlocalFileUri(fileName, 0, false);
var url = req.docManager.getDownloadUrl(fileName, true);
var directUrl = req.docManager.getDownloadUrl(fileName);
var mode = req.query.mode || "edit"; // mode: view/edit/review/comment/fillForms/embedded
var type = req.query.type || ""; // type: embedded/mobile/desktop
@ -852,7 +861,8 @@ app.get("/editor", function (req, res) { // define a handler for editing docume
fileType: fileExt.slice(1),
version: i,
key: keyVersion,
url: i == countVersion ? url : (`${req.docManager.getServerUrl(false)}/history?fileName=${encodeURIComponent(fileName)}&file=prev${fileExt}&ver=${i}&useraddress=${userAddress}`),
url: i == countVersion ? url : (`${req.docManager.getServerUrl(true)}/history?fileName=${encodeURIComponent(fileName)}&file=prev${fileExt}&ver=${i}&useraddress=${userAddress}`),
directUrl: !userDirectUrl ? null : i == countVersion ? directUrl : (`${req.docManager.getServerUrl(false)}/history?fileName=${encodeURIComponent(fileName)}&file=prev${fileExt}&ver=${i}`),
};
if (i > 1 && req.docManager.existsSync(req.docManager.diffPath(fileName, userAddress, i-1))) { // check if the path to the file with document versions differences exists
@ -860,8 +870,9 @@ app.get("/editor", function (req, res) { // define a handler for editing docume
fileType: historyData[i-2].fileType,
key: historyData[i-2].key,
url: historyData[i-2].url,
directUrl: !userDirectUrl ? null : historyData[i-2].directUrl,
};
let changesUrl = `${req.docManager.getServerUrl(false)}/history?fileName=${encodeURIComponent(fileName)}&file=diff.zip&ver=${i-1}&useraddress=${userAddress}`;
let changesUrl = `${req.docManager.getServerUrl(true)}/history?fileName=${encodeURIComponent(fileName)}&file=diff.zip&ver=${i-1}&useraddress=${userAddress}`;
historyD.changesUrl = changesUrl; // get the path to the diff.zip file and write it to the history object
}
@ -877,7 +888,8 @@ app.get("/editor", function (req, res) { // define a handler for editing docume
historyData.push({
version: countVersion,
key: key,
url: url
url: url,
directUrl: !userDirectUrl ? null : directUrl,
});
}
@ -894,7 +906,8 @@ app.get("/editor", function (req, res) { // define a handler for editing docume
name: fileName,
ext: fileUtility.getFileExtension(fileName, true),
uri: url,
uriUser: urlUser,
directUrl: !userDirectUrl ? null : directUrl,
uriUser: directUrl,
version: countVersion,
created: new Date().toDateString(),
favorite: user.favorite != null ? user.favorite : "null"
@ -909,6 +922,8 @@ app.get("/editor", function (req, res) { // define a handler for editing docume
templates: user.templates ? templates : null,
isEdit: canEdit && (mode == "edit" || mode == "view" || mode == "filter" || mode == "blockcontent"),
review: canEdit && (mode == "edit" || mode == "review"),
chat: userid != "uid-0",
coEditing: mode == "view" && userid == "uid-0" ? {mode: "strict", change: false} : null,
comment: mode != "view" && mode != "fillForms" && mode != "embedded" && mode != "blockcontent",
fillForms: mode != "view" && mode != "comment" && mode != "embedded" && mode != "blockcontent",
modifyFilter: mode != "filter",
@ -936,15 +951,18 @@ app.get("/editor", function (req, res) { // define a handler for editing docume
historyData: historyData,
dataInsertImage: {
fileType: "png",
url: req.docManager.getServerUrl(true) + "/images/logo.png"
url: req.docManager.getServerUrl(true) + "/images/logo.png",
directUrl: !userDirectUrl ? null : req.docManager.getServerUrl() + "/images/logo.png",
},
dataCompareFile: {
fileType: "docx",
url: req.docManager.getServerUrl(true) + "/assets/sample/sample.docx"
url: req.docManager.getServerUrl(true) + "/assets/sample/sample.docx",
directUrl: !userDirectUrl ? null : req.docManager.getServerUrl() + "/assets/sample/sample.docx",
},
dataMailMergeRecipients: {
fileType: "csv",
url: req.docManager.getServerUrl(true) + "/csv"
url: req.docManager.getServerUrl(true) + "/csv",
directUrl: !userDirectUrl ? null : req.docManager.getServerUrl() + "/csv",
},
usersForMentions: user.id != "uid-0" ? users.getUsersForMentions(user.id) : null,
};
@ -976,6 +994,12 @@ app.get("/editor", function (req, res) { // define a handler for editing docume
app.post("/rename", function (req, res) { //define a handler for renaming file
var newfilename = req.body.newfilename;
var origExt = req.body.ext;
var curExt = fileUtility.getFileExtension(newfilename, true);
if (curExt !== origExt) {
newfilename += '.' + origExt;
}
var dockey = req.body.dockey;
var meta = {title: newfilename};

View File

@ -1,5 +1,5 @@
{
"version": "1.2.0",
"version": "1.3.0",
"log": {
"appenders": [
{
@ -48,11 +48,14 @@
"verify_peer_off": true,
"languages": {
"en": "English",
"hy": "Armenian",
"az": "Azerbaijani",
"eu": "Basque",
"be": "Belarusian",
"bg": "Bulgarian",
"ca": "Catalan",
"zh": "Chinese",
"zh" : "Chinese (People's Republic of China)",
"zh-TW" : "Chinese (Traditional, Taiwan)",
"cs": "Czech",
"da": "Danish",
"nl": "Dutch",
@ -68,9 +71,11 @@
"ko": "Korean",
"lv": "Latvian",
"lo": "Lao",
"ms": "Malay (Malaysia)",
"nb": "Norwegian",
"pl": "Polish",
"pt": "Portuguese",
"pt" : "Portuguese (Brazil)",
"pt-PT" : "Portuguese (Portugal)",
"ro": "Romanian",
"ru": "Russian",
"sk": "Slovak",

View File

@ -66,6 +66,9 @@ docManager.prototype.getCustomParams = function () {
const lang = this.req.query.lang; // language
params += (lang ? "&lang=" + this.getLang() : "");
const directUrl = this.req.query.directUrl; // directUrl
params += (directUrl ? "&directUrl=" + (directUrl == "true") : "");
const fileName = this.req.query.fileName; // file name
params += (fileName ? "&fileName=" + fileName : "");
@ -163,22 +166,6 @@ docManager.prototype.getFileData = function (fileName, userAddress) {
return ((fileSystem.readFileSync(history)).toString()).split(",");
};
// get url to the original file
docManager.prototype.getFileUri = function (fileName) {
return this.getlocalFileUri(fileName, 0, true);
};
// get local file url
docManager.prototype.getlocalFileUri = function (fileName, version, forDocumentServer) {
const serverPath = this.getServerUrl(forDocumentServer);
const hostAddress = this.curUserHostAddress();
let url = serverPath + configServer.get("storagePath") + "/" + hostAddress + "/" + encodeURIComponent(fileName); // get full url address to the file
if (!version) {
return url;
}
return url + "-history/" + version; // return history path to the specified file version
};
// get server url
docManager.prototype.getServerUrl = function (forDocumentServer) {
return (forDocumentServer && !!configServer.get("exampleUrl")) ? configServer.get("exampleUrl") : this.getServerPath();
@ -218,10 +205,13 @@ docManager.prototype.getCreateUrl = function (docType, userid, type, lang) {
}
// get url to download a file
docManager.prototype.getDownloadUrl = function (fileName) {
const server = this.getServerUrl(true);
const hostAddress = this.curUserHostAddress();
const handler = "/download?fileName=" + encodeURIComponent(fileName) + "&useraddress=" + encodeURIComponent(hostAddress);
docManager.prototype.getDownloadUrl = function (fileName, forDocumentServer) {
const server = this.getServerUrl(forDocumentServer);
var handler = "/download?fileName=" + encodeURIComponent(fileName);
if (forDocumentServer) {
const hostAddress = this.curUserHostAddress();
handler += "&useraddress=" + encodeURIComponent(hostAddress);
}
return server + handler;
};
@ -387,7 +377,7 @@ docManager.prototype.getTemplateImageUrl = function (fileType) {
// get document key
docManager.prototype.getKey = function (fileName, userAddress) {
userAddress = userAddress || this.curUserHostAddress();
let key = userAddress + this.getlocalFileUri(fileName); // get document key by adding local file url to the current user host address
let key = userAddress + fileName; // get document key by adding local file url to the current user host address
let historyPath = this.historyPath(fileName, userAddress); // get the path to the file history
if (historyPath != ""){ // if the path to the file history exists

View File

@ -60,6 +60,8 @@ var descr_user_0 = [
"Can't create new files from the editor",
"Cant see anyones information",
"Can't rename files from the editor",
"Can't view chat",
"View file without collaboration",
//"Cant submit forms"
];

File diff suppressed because it is too large Load Diff

View File

@ -18,12 +18,14 @@
var language;
var userid;
var directUrl;
if (typeof jQuery != "undefined") {
jq = jQuery.noConflict();
userid = getUrlVars()["userid"];
language = getUrlVars()["lang"];
directUrl = getUrlVars()["directUrl"] == "true";
mustReload = false;
@ -34,19 +36,29 @@ if (typeof jQuery != "undefined") {
jq("#language").change(function() {
window.location = "?lang=" + jq(this).val() + "&userid=" + userid;
window.location = "?lang=" + jq(this).val() + "&userid=" + userid + "&directUrl=" + directUrl;
});
if ("" != userid && undefined != userid)
jq("#user").val(userid);
else
userid = jq("#user").val();
jq("#user").change(function() {
window.location = "?lang=" + language + "&userid=" + jq(this).val();
window.location = "?lang=" + language + "&userid=" + jq(this).val() + "&directUrl=" + directUrl;
});
if (directUrl)
jq("#directUrl").prop("checked", directUrl);
else
directUrl = jq("#directUrl").prop("checked");
jq("#directUrl").change(function() {
window.location = "?lang=" + language + "&userid=" + userid + "&directUrl=" + jq(this).prop("checked");
});
jq(function () {
jq('#fileupload').fileupload({
dataType: 'json',
@ -228,7 +240,7 @@ if (typeof jQuery != "undefined") {
if (UrlEditor == "wopi-action"){
var url = UrlEditor + "/" + fileId + "?action=edit";
}else{
var url = UrlEditor + "?fileName=" + fileId + "&lang=" + language + "&userid=" + userid;
var url = UrlEditor + "?fileName=" + fileId + "&lang=" + language + "&userid=" + userid + "&directUrl=" + directUrl;
}
window.open(url, "_blank");
jq('#hiddenFileName').val("");
@ -241,7 +253,7 @@ if (typeof jQuery != "undefined") {
if (UrlEditor == "wopi-action"){
var url = UrlEditor + "/" + fileId + "?action=view";
}else{
var url = UrlEditor + "?mode=view&fileName=" + fileId + "&lang=" + language + "&userid=" + userid;
var url = UrlEditor + "?mode=view&fileName=" + fileId + "&lang=" + language + "&userid=" + userid + "&directUrl=" + directUrl;
}
window.open(url, "_blank");
jq('#hiddenFileName').val("");
@ -251,7 +263,7 @@ if (typeof jQuery != "undefined") {
jq(document).on("click", "#beginEmbedded:not(.disable)", function () {
var fileId = encodeURIComponent(jq('#hiddenFileName').val());
var url = UrlEditor + "?type=embedded&fileName=" + fileId + "&lang=" + language + "&userid=" + userid;
var url = UrlEditor + "?type=embedded&fileName=" + fileId + "&lang=" + language + "&userid=" + userid + "&directUrl=" + directUrl;
jq("#mainProgress").addClass("embedded");
jq("#beginEmbedded").addClass("disable");
@ -328,12 +340,12 @@ if (typeof jQuery != "undefined") {
if (hideTooltipTimeout != null) {
clearTimeout(hideTooltipTimeout);
}
jq(".info").on("touchend", function () {
jq("#info").on("touchend", function () {
showUserTooltip(true);
});
}
} else {
jq(".info").mouseover(function (event) {
jq("#info").mouseover(function (event) {
if (fileList.length > 0) {
if (hideTooltipTimeout != null) {
clearTimeout(hideTooltipTimeout);
@ -357,6 +369,20 @@ if (typeof jQuery != "undefined") {
}, 500);
});
}
jq(".info-tooltip").mouseover(function (event) {
var target = event.target;
var id = target.dataset.id ? target.dataset.id : target.id;
var tooltip = target.dataset.tooltip;
jq("<div class='tooltip'>" + tooltip + "<div class='arrow'></div></div>").appendTo("body");
var top = jq("#" + id).offset().top + jq("#" + id).outerHeight() / 2 - jq("div.tooltip").outerHeight() / 2;
var left = jq("#" + id).offset().left + jq("#" + id).outerWidth() + 20;
jq("div.tooltip").css({"top": top, "left": left});
}).mouseout(function () {
jq("div.tooltip").remove();
});
}
function getUrlVars() {

View File

@ -165,7 +165,7 @@ label .checkbox {
background-image: url("../images/file_docxf.svg");
}
.create-sample {
.side-option {
color: #666666;
line-height: 24px;
}
@ -620,6 +620,31 @@ footer table tr td:first-child {
margin: -2px 5px;
}
.tooltip {
background: #FFFFFF;
border-radius: 5px;
box-shadow: 0px 7px 25px rgba(85, 85, 85, 0.15);
color: #666666;
line-height: 160%;
max-width: 455px;
padding: 14px;
position: absolute;
}
.tooltip ul {
margin: 0;
}
.arrow {
border-top: 8px solid transparent;
border-bottom: 8px solid transparent;
border-right: 8px solid #FFFFFF;
left: -4px;
position: absolute;
top: 50%;
transform: translate(-50%, -50%);
}
.user-block-table {
height: 100%;
padding-top: 14px;

View File

@ -1,51 +1,36 @@
"width": "100%",
"height": "100%",
"type": "<%- editor.type %>",
"documentType": "<%- editor.documentType %>",
"token": "<%- editor.token %>",
"document": {
"title": "<%- file.name %>",
"url": "<%- file.uri %>",
"document": {
"directUrl": "<%- file.directUrl %>",
"fileType": "<%- file.ext %>",
"key": "<%- editor.key %>",
"info": {
"owner": "Me",
"uploaded": "<%- file.created %>",
"favorite": <%- file.favorite %>
},
"key": "<%- editor.key %>",
"permissions": {
"chat": <%- editor.chat %>,
"comment": <%- editor.comment %>,
"copy": <%- editor.copy %>,
"download": <%- editor.download %>,
"edit": <%- editor.isEdit %>,
"print": <%- editor.print %>,
"fillForms": <%- editor.fillForms %>,
"modifyFilter": <%- editor.modifyFilter %>,
"modifyContentControl": <%- editor.modifyContentControl %>,
"modifyFilter": <%- editor.modifyFilter %>,
"print": <%- editor.print %>,
"review": <%- editor.review %>,
"reviewGroups": <%- editor.reviewGroups %>,
"commentGroups": <%- editor.commentGroups %>,
"userInfoGroups": <%- editor.userInfoGroups %>
}
},
"title": "<%- file.name %>",
"url": "<%- file.uri %>"
},
"documentType": "<%- editor.documentType %>",
"editorConfig": {
"actionLink": <%- editor.actionData %>,
"mode": "<%- editor.mode %>",
"lang": "<%- editor.lang %>",
"callbackUrl": "<%- editor.callbackUrl %>",
"coEditing": <%- JSON.stringify(editor.coEditing) %>,
"createUrl": <%- JSON.stringify(editor.createUrl) %>,
"templates": <%- JSON.stringify(editor.templates) %>,
"user": {
"group": "<%- editor.userGroup %>",
"id": "<%- editor.userid %>",
"name": "<%- editor.name %>"
},
"embedded": {
"saveUrl": "<%- file.uriUser %>",
"embedUrl": "<%- file.uriUser %>",
"shareUrl": "<%- file.uriUser %>",
"toolbarDocked": "top"
},
"customization": {
"about": true,
"comments": true,
@ -56,6 +41,24 @@
},
"submitForm": <%- editor.submitForm %>
},
"embedded": {
"embedUrl": "<%- file.uriUser %>",
"saveUrl": "<%- file.uriUser %>",
"shareUrl": "<%- file.uriUser %>",
"toolbarDocked": "top"
},
"fileChoiceUrl": "<%- editor.fileChoiceUrl %>",
"plugins": <%- editor.plugins %>
}
"lang": "<%- editor.lang %>",
"mode": "<%- editor.mode %>",
"plugins": <%- editor.plugins %>,
"templates": <%- JSON.stringify(editor.templates) %>,
"user": {
"group": "<%- editor.userGroup %>",
"id": "<%- editor.userid %>",
"name": "<%- editor.name %>"
}
},
"height": "100%",
"token": "<%- editor.token %>",
"type": "<%- editor.type %>",
"width": "100%"

View File

@ -175,6 +175,7 @@
var data = {
newfilename: newfilename,
dockey: config.document.key,
ext: config.document.fileType
};
let xhr = new XMLHttpRequest();
xhr.open("POST", "rename");
@ -185,20 +186,21 @@
}
};
config = {<%- include("config") %>,
events: {
"onAppReady": onAppReady,
"onDocumentStateChange": onDocumentStateChange,
"onRequestEditRights": onRequestEditRights,
"onError": onError,
"onOutdatedVersion": onOutdatedVersion,
"onMakeActionLink": onMakeActionLink,
"onMetaChange": onMetaChange,
"onRequestInsertImage": onRequestInsertImage,
"onRequestCompareFile": onRequestCompareFile,
"onRequestMailMergeRecipients": onRequestMailMergeRecipients,
}
};
config = {
<%- include("config") %>
};
config.events = {
"onAppReady": onAppReady,
"onDocumentStateChange": onDocumentStateChange,
"onRequestEditRights": onRequestEditRights,
"onError": onError,
"onOutdatedVersion": onOutdatedVersion,
"onMakeActionLink": onMakeActionLink,
"onMetaChange": onMetaChange,
"onRequestInsertImage": onRequestInsertImage,
"onRequestCompareFile": onRequestCompareFile,
"onRequestMailMergeRecipients": onRequestMailMergeRecipients,
};
if (<%- JSON.stringify(editor.userid) %> != null) {
config.events.onRequestHistory = onRequestHistory;

View File

@ -60,7 +60,7 @@
<a class="try-editor form reload-page" target="_blank" href="editor?fileExt=docxf<%= params %>" title="Create new form template">Form template</a>
</li>
</ul>
<label class="create-sample">
<label class="side-option">
<input id="createSample" type="checkbox" class="checkbox" />With sample content
</label>
</div>
@ -76,7 +76,7 @@
<tr>
<td valign="middle">
<span class="select-user">Username</span>
<img class="info" src="images/info.svg" />
<img id="info" class="info" src="images/info.svg" />
<select class="select-user" id="user">
<% users.forEach(user => { %>
<option value="<%= user.id %>"><%= user.name == null ? "Anonymous" : user.name %></option>
@ -86,7 +86,8 @@
</tr>
<tr>
<td valign="middle">
<span class="select-user">Language editors interface</span>
<span class="select-user">Language</span>
<img class="info info-tooltip" data-id="language" data-tooltip="Choose the language for ONLYOFFICE editors interface" src="images/info.svg" />
<select class="select-user" id="language">
<% Object.keys(languages).forEach(key => { %>
<option value="<%= key %>"><%= languages[key] %></option>
@ -94,6 +95,14 @@
</select>
</td>
</tr>
<tr>
<td valign="middle">
<label class="side-option">
<input id="directUrl" type="checkbox" class="checkbox" />Try opening on client
<img id="directUrlInfo" class="info info-tooltip" data-id="directUrlInfo" data-tooltip="Some files can be opened in the user's browser without connecting to the document server." src="images/info.svg" />
</label>
</td>
</tr>
</tbody>
</table>

View File

@ -77,7 +77,7 @@
<tr>
<td valign="middle">
<span class="select-user">Username</span>
<img class="info" data-id="user" src="images/info.svg" />
<img id="info" class="info" data-id="user" src="images/info.svg" />
<select class="select-user" id="user">
<% users.forEach(user => { %>
<option value="<%= user.id %>"><%= user.name == null ? "Anonymous" : user.name %></option>
@ -87,7 +87,8 @@
</tr>
<tr>
<td valign="middle">
<span class="select-user">Language editors interface</span>
<span class="select-user">Language</span>
<img class="info info-tooltip" data-id="language" data-tooltip="Choose the language for ONLYOFFICE editors interface" src="images/info.svg" />
<select class="select-user" id="language">
<% Object.keys(languages).forEach(key => { %>
<option value="<%= key %>"><%= languages[key] %></option>

View File

@ -19,3 +19,7 @@ License File: jQuery.iframe-transport.license
jQuery.UI - jQuery UI is an open source library of interface components — interactions, full-featured widgets, and animation effects — based on the stellar jQuery javascript library . Each component is built according to jQuery's event-driven architecture (find something, manipulate it) and is themeable, making it easy for developers of any skill level to integrate and extend into their own code. (https://jquery.org/license/)
License: MIT
License File: jQuery.UI.license
JWT - JSON Web Token implementation (https://github.com/firebase/php-jwt/blob/master/LICENSE)
License: BSD
License File: jwt.license

View File

@ -1,6 +1,6 @@
<?php
$GLOBALS['version'] = "1.2.0";
$GLOBALS['version'] = "1.3.0";
$GLOBALS['FILE_SIZE_MAX'] = 5242880;
$GLOBALS['STORAGE_PATH'] = "";
@ -48,11 +48,14 @@ $GLOBALS['ExtsDocument'] = array(".doc", ".docx", ".docm",
$GLOBALS['LANGUAGES'] = array(
'en' => 'English',
'hy' => 'Armenian',
'az' => 'Azerbaijani',
'eu' => 'Basque',
'be' => 'Belarusian',
'bg' => 'Bulgarian',
'ca' => 'Catalan',
'zh' => 'Chinese',
'zh' => 'Chinese (People\'s Republic of China)',
'zh-TW' => 'Chinese (Traditional, Taiwan)',
'cs' => 'Czech',
'da' => 'Danish',
'nl' => 'Dutch',
@ -68,9 +71,11 @@ $GLOBALS['LANGUAGES'] = array(
'ko' => 'Korean',
'lv' => 'Latvian',
'lo' => 'Lao',
'ms' => 'Malay (Malaysia)',
'nb' => 'Norwegian',
'pl' => 'Polish',
'pt' => 'Portuguese',
'pt' => 'Portuguese (Brazil)',
'pt-PT' => 'Portuguese (Portugal)',
'ro' => 'Romanian',
'ru' => 'Russian',
'sk' => 'Slovak',

View File

@ -53,6 +53,7 @@
$fileuri = FileUri($filename, true);
$fileuriUser = realpath($GLOBALS['STORAGE_PATH']) === $GLOBALS['STORAGE_PATH'] ? getDownloadUrl($filename) . "&dmode=emb" : FileUri($filename);
$directUrl = getDownloadUrl($filename, FALSE);
$docKey = getDocEditorKey($filename);
$filetype = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
@ -89,6 +90,7 @@
"document" => [
"title" => $filename,
"url" => getDownloadUrl($filename),
"directUrl" => $directUrl,
"fileType" => $filetype,
"key" => $docKey,
"info" => [
@ -106,6 +108,7 @@
"modifyFilter" => $editorsMode != "filter",
"modifyContentControl" => $editorsMode != "blockcontent",
"review" => $canEdit && ($editorsMode == "edit" || $editorsMode == "review"),
"chat" => $user->id != "uid-0",
"reviewGroups" => $user->reviewGroups,
"commentGroups" => $user->commentGroups,
"userInfoGroups" => $user->userInfoGroups
@ -116,6 +119,10 @@
"mode" => $mode,
"lang" => empty($_COOKIE["ulang"]) ? "en" : $_COOKIE["ulang"],
"callbackUrl" => getCallbackUrl($filename), // absolute URL to the document storage service
"coEditing" => $editorsMode == "view" && $user->id == "uid-0" ? [
"mode" => "strict",
"change" => false
] : null,
"createUrl" => $user->id != "uid-0" ? $createUrl : null,
"templates" => $user->templates ? $templates : null,
"user" => [ // the user currently viewing or editing the document
@ -124,9 +131,9 @@
"group" => $user->group
],
"embedded" => [ // the parameters for the embedded document type
"saveUrl" => $fileuriUser, // the absolute URL that will allow the document to be saved onto the user personal computer
"embedUrl" => $fileuriUser, // the absolute URL to the document serving as a source file for the document embedded into the web page
"shareUrl" => $fileuriUser, // the absolute URL that will allow other users to share this document
"saveUrl" => $directUrl, // the absolute URL that will allow the document to be saved onto the user personal computer
"embedUrl" => $directUrl, // the absolute URL to the document serving as a source file for the document embedded into the web page
"shareUrl" => $directUrl, // the absolute URL that will allow other users to share this document
"toolbarDocked" => "top", // the place for the embedded viewer toolbar (top or bottom)
],
"customization" => [ // the parameters for the editor interface
@ -145,19 +152,22 @@
// an image for inserting
$dataInsertImage = [
"fileType" => "png",
"url" => serverPath(true) . "/css/images/logo.png"
"url" => serverPath(true) . "/css/images/logo.png",
"directUrl" => serverPath(false) . "/css/images/logo.png"
];
// a document for comparing
$dataCompareFile = [
"fileType" => "docx",
"url" => serverPath(true) . "/webeditor-ajax.php?type=assets&name=sample.docx"
"url" => serverPath(true) . "/webeditor-ajax.php?type=assets&name=sample.docx",
"directUrl" => serverPath(false) . "/webeditor-ajax.php?type=assets&name=sample.docx"
];
// recipients data for mail merging
$dataMailMergeRecipients = [
"fileType" =>"csv",
"url" => serverPath(true) . "/webeditor-ajax.php?type=csv"
"url" => serverPath(true) . "/webeditor-ajax.php?type=csv",
"directUrl" => serverPath(false) . "/webeditor-ajax.php?type=csv"
];
// users data for mentions
@ -208,23 +218,25 @@
. "&type=" . $type;
}
function getHistoryDownloadUrl($fileName, $version, $file) {
return serverPath(TRUE) . '/'
function getHistoryDownloadUrl($fileName, $version, $file, $isServer = TRUE) {
$userAddress = $isServer ? "&userAddress=" . getClientIp() : "";
return serverPath($isServer) . '/'
. "webeditor-ajax.php"
. "?type=history"
. "&fileName=" . urlencode($fileName)
. "&ver=" . $version
. "&file=" . urlencode($file)
. "&userAddress=" . getClientIp();
. $userAddress;
}
// get url to download a file
function getDownloadUrl($fileName) {
return serverPath(TRUE) . '/'
function getDownloadUrl($fileName, $isServer = TRUE) {
$userAddress = $isServer ? "&userAddress=" . getClientIp() : "";
return serverPath($isServer) . '/'
. "webeditor-ajax.php"
. "?type=download"
. "&fileName=" . urlencode($fileName)
. "&userAddress=" . getClientIp();
. $userAddress;
}
// get document history
@ -266,12 +278,15 @@
$dataObj["fileType"] = $fileExe;
$dataObj["key"] = $key;
$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);
$prevFileUrl = $i == $curVer ? getDownloadUrl($filename) : getHistoryDownloadUrl($filename, $i, "prev.".$fileExe);
$directUrl = $i == $curVer ? getDownloadUrl($filename, FALSE) : getHistoryDownloadUrl($filename, $i, "prev.".$fileExe, FALSE);
}
$dataObj["url"] = $prevFileUrl; // write file url to the data object
$dataObj["directUrl"] = $directUrl; // write direct url to the data object
$dataObj["version"] = $i;
if ($i > 1) { // check if the version number is greater than 1 (the document was modified)
@ -287,7 +302,8 @@
$dataObj["previous"] = [ // write information about previous file version to the data object
"fileType" => $prev["fileType"],
"key" => $prev["key"],
"url" => $prev["url"]
"url" => $prev["url"],
"directUrl" => $prev["directUrl"]
];
// write the path to the diff.zip archive with differences in this file version
@ -474,6 +490,7 @@
var data = {
newfilename: newfilename,
dockey: config.document.key,
ext: config.document.fileType
};
let xhr = new XMLHttpRequest();

View File

@ -17,6 +17,10 @@
*
*/
require_once( dirname(__FILE__) . '/lib/jwt/BeforeValidException.php' );
require_once( dirname(__FILE__) . '/lib/jwt/ExpiredException.php' );
require_once( dirname(__FILE__) . '/lib/jwt/SignatureInvalidException.php' );
require_once( dirname(__FILE__) . '/lib/jwt/JWT.php' );
require_once( dirname(__FILE__) . '/config.php' );
// check if a secret key to generate token exists or not
@ -26,51 +30,17 @@ function isJwtEnabled() {
// encode a payload object into a token using a secret key
function jwtEncode($payload) {
$header = [
"alg" => "HS256", // the hashing algorithm
"typ" => "JWT" // the token type
];
// three parts of token
$encHeader = base64UrlEncode(json_encode($header)); // header
$encPayload = base64UrlEncode(json_encode($payload)); // payload
$hash = base64UrlEncode(calculateHash($encHeader, $encPayload)); // signature
return "$encHeader.$encPayload.$hash";
return \Firebase\JWT\JWT::encode($payload, $GLOBALS["DOC_SERV_JWT_SECRET"]);
}
// decode a token into a payload object using a secret key
function jwtDecode($token) {
if (!isJwtEnabled()) return "";
$split = explode(".", $token);
if (count($split) != 3) return "";
$hash = base64UrlEncode(calculateHash($split[0], $split[1]));
if (strcmp($hash, $split[2]) != 0) return "";
return base64UrlDecode($split[1]);
}
// generate a hash code based on a key using the HMAC method
function calculateHash($encHeader, $encPayload) {
return hash_hmac("sha256", "$encHeader.$encPayload", $GLOBALS['DOC_SERV_JWT_SECRET'], true);
}
// encode a string into the base64 value
function base64UrlEncode($str) {
return str_replace("/", "_", str_replace("+", "-", trim(base64_encode($str), "=")));
}
// decode a base64 value into the string
function base64UrlDecode($payload) {
$b64 = str_replace("_", "/", str_replace("-", "+", $payload));
switch (strlen($b64) % 4) {
case 2:
$b64 = $b64 . "=="; break;
case 3:
$b64 = $b64 . "="; break;
try {
$payload = \Firebase\JWT\JWT::decode($token, $GLOBALS["DOC_SERV_JWT_SECRET"], array("HS256"));
} catch (\UnexpectedValueException $e) {
$payload = "";
}
return base64_decode($b64);
}
return $payload;
}
?>

View File

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

View File

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

View File

@ -0,0 +1,370 @@
<?php
namespace Firebase\JWT;
use \DomainException;
use \InvalidArgumentException;
use \UnexpectedValueException;
use \DateTime;
/**
* JSON Web Token implementation, based on this spec:
* http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-06
*
* PHP version 5
*
* @category Authentication
* @package Authentication_JWT
* @author Neuman Vong <neuman@twilio.com>
* @author Anant Narayanan <anant@php.net>
* @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
* @link https://github.com/firebase/php-jwt
*/
class JWT
{
/**
* When checking nbf, iat or expiration times,
* we want to provide some extra leeway time to
* account for clock skew.
*/
public static $leeway = 0;
/**
* Allow the current timestamp to be specified.
* Useful for fixing a value within unit testing.
*
* Will default to PHP time() value if null.
*/
public static $timestamp = null;
public static $supported_algs = array(
'HS256' => array('hash_hmac', 'SHA256'),
'HS512' => array('hash_hmac', 'SHA512'),
'HS384' => array('hash_hmac', 'SHA384'),
'RS256' => array('openssl', 'SHA256'),
);
/**
* Decodes a JWT string into a PHP object.
*
* @param string $jwt The JWT
* @param string|array $key The key, or map of keys.
* If the algorithm used is asymmetric, this is the public key
* @param array $allowed_algs List of supported verification algorithms
* Supported algorithms are 'HS256', 'HS384', 'HS512' and 'RS256'
*
* @return object The JWT's payload as a PHP object
*
* @throws UnexpectedValueException Provided JWT was invalid
* @throws SignatureInvalidException Provided JWT was invalid because the signature verification failed
* @throws BeforeValidException Provided JWT is trying to be used before it's eligible as defined by 'nbf'
* @throws BeforeValidException Provided JWT is trying to be used before it's been created as defined by 'iat'
* @throws ExpiredException Provided JWT has since expired, as defined by the 'exp' claim
*
* @uses jsonDecode
* @uses urlsafeB64Decode
*/
public static function decode($jwt, $key, $allowed_algs = array())
{
$timestamp = is_null(static::$timestamp) ? time() : static::$timestamp;
if (empty($key)) {
throw new InvalidArgumentException('Key may not be empty');
}
if (!is_array($allowed_algs)) {
throw new InvalidArgumentException('Algorithm not allowed');
}
$tks = explode('.', $jwt);
if (count($tks) != 3) {
throw new UnexpectedValueException('Wrong number of segments');
}
list($headb64, $bodyb64, $cryptob64) = $tks;
if (null === ($header = static::jsonDecode(static::urlsafeB64Decode($headb64)))) {
throw new UnexpectedValueException('Invalid header encoding');
}
if (null === $payload = static::jsonDecode(static::urlsafeB64Decode($bodyb64))) {
throw new UnexpectedValueException('Invalid claims encoding');
}
$sig = static::urlsafeB64Decode($cryptob64);
if (empty($header->alg)) {
throw new UnexpectedValueException('Empty algorithm');
}
if (empty(static::$supported_algs[$header->alg])) {
throw new UnexpectedValueException('Algorithm not supported');
}
if (!in_array($header->alg, $allowed_algs)) {
throw new UnexpectedValueException('Algorithm not allowed');
}
if (is_array($key) || $key instanceof \ArrayAccess) {
if (isset($header->kid)) {
$key = $key[$header->kid];
} else {
throw new UnexpectedValueException('"kid" empty, unable to lookup correct key');
}
}
// Check the signature
if (!static::verify("$headb64.$bodyb64", $sig, $key, $header->alg)) {
throw new SignatureInvalidException('Signature verification failed');
}
// Check if the nbf if it is defined. This is the time that the
// token can actually be used. If it's not yet that time, abort.
if (isset($payload->nbf) && $payload->nbf > ($timestamp + static::$leeway)) {
throw new BeforeValidException(
'Cannot handle token prior to ' . date(DateTime::ISO8601, $payload->nbf)
);
}
// Check that this token has been created before 'now'. This prevents
// using tokens that have been created for later use (and haven't
// correctly used the nbf claim).
if (isset($payload->iat) && $payload->iat > ($timestamp + static::$leeway)) {
throw new BeforeValidException(
'Cannot handle token prior to ' . date(DateTime::ISO8601, $payload->iat)
);
}
// Check if this token has expired.
if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) {
throw new ExpiredException('Expired token');
}
return $payload;
}
/**
* Converts and signs a PHP object or array into a JWT string.
*
* @param object|array $payload PHP object or array
* @param string $key The secret key.
* If the algorithm used is asymmetric, this is the private key
* @param string $alg The signing algorithm.
* Supported algorithms are 'HS256', 'HS384', 'HS512' and 'RS256'
* @param mixed $keyId
* @param array $head An array with header elements to attach
*
* @return string A signed JWT
*
* @uses jsonEncode
* @uses urlsafeB64Encode
*/
public static function encode($payload, $key, $alg = 'HS256', $keyId = null, $head = null)
{
$header = array('typ' => 'JWT', 'alg' => $alg);
if ($keyId !== null) {
$header['kid'] = $keyId;
}
if ( isset($head) && is_array($head) ) {
$header = array_merge($head, $header);
}
$segments = array();
$segments[] = static::urlsafeB64Encode(static::jsonEncode($header));
$segments[] = static::urlsafeB64Encode(static::jsonEncode($payload));
$signing_input = implode('.', $segments);
$signature = static::sign($signing_input, $key, $alg);
$segments[] = static::urlsafeB64Encode($signature);
return implode('.', $segments);
}
/**
* Sign a string with a given key and algorithm.
*
* @param string $msg The message to sign
* @param string|resource $key The secret key
* @param string $alg The signing algorithm.
* Supported algorithms are 'HS256', 'HS384', 'HS512' and 'RS256'
*
* @return string An encrypted message
*
* @throws DomainException Unsupported algorithm was specified
*/
public static function sign($msg, $key, $alg = 'HS256')
{
if (empty(static::$supported_algs[$alg])) {
throw new DomainException('Algorithm not supported');
}
list($function, $algorithm) = static::$supported_algs[$alg];
switch($function) {
case 'hash_hmac':
return hash_hmac($algorithm, $msg, $key, true);
case 'openssl':
$signature = '';
$success = openssl_sign($msg, $signature, $key, $algorithm);
if (!$success) {
throw new DomainException("OpenSSL unable to sign data");
} else {
return $signature;
}
}
}
/**
* Verify a signature with the message, key and method. Not all methods
* are symmetric, so we must have a separate verify and sign method.
*
* @param string $msg The original message (header and body)
* @param string $signature The original signature
* @param string|resource $key For HS*, a string key works. for RS*, must be a resource of an openssl public key
* @param string $alg The algorithm
*
* @return bool
*
* @throws DomainException Invalid Algorithm or OpenSSL failure
*/
private static function verify($msg, $signature, $key, $alg)
{
if (empty(static::$supported_algs[$alg])) {
throw new DomainException('Algorithm not supported');
}
list($function, $algorithm) = static::$supported_algs[$alg];
switch($function) {
case 'openssl':
$success = openssl_verify($msg, $signature, $key, $algorithm);
if (!$success) {
throw new DomainException("OpenSSL unable to verify data: " . openssl_error_string());
} else {
return $signature;
}
case 'hash_hmac':
default:
$hash = hash_hmac($algorithm, $msg, $key, true);
if (function_exists('hash_equals')) {
return hash_equals($signature, $hash);
}
$len = min(static::safeStrlen($signature), static::safeStrlen($hash));
$status = 0;
for ($i = 0; $i < $len; $i++) {
$status |= (ord($signature[$i]) ^ ord($hash[$i]));
}
$status |= (static::safeStrlen($signature) ^ static::safeStrlen($hash));
return ($status === 0);
}
}
/**
* Decode a JSON string into a PHP object.
*
* @param string $input JSON string
*
* @return object Object representation of JSON string
*
* @throws DomainException Provided string was invalid JSON
*/
public static function jsonDecode($input)
{
if (version_compare(PHP_VERSION, '5.4.0', '>=') && !(defined('JSON_C_VERSION') && PHP_INT_SIZE > 4)) {
/** In PHP >=5.4.0, json_decode() accepts an options parameter, that allows you
* to specify that large ints (like Steam Transaction IDs) should be treated as
* strings, rather than the PHP default behaviour of converting them to floats.
*/
$obj = json_decode($input, false, 512, JSON_BIGINT_AS_STRING);
} else {
/** Not all servers will support that, however, so for older versions we must
* manually detect large ints in the JSON string and quote them (thus converting
*them to strings) before decoding, hence the preg_replace() call.
*/
$max_int_length = strlen((string) PHP_INT_MAX) - 1;
$json_without_bigints = preg_replace('/:\s*(-?\d{'.$max_int_length.',})/', ': "$1"', $input);
$obj = json_decode($json_without_bigints);
}
if (function_exists('json_last_error') && $errno = json_last_error()) {
static::handleJsonError($errno);
} elseif ($obj === null && $input !== 'null') {
throw new DomainException('Null result with non-null input');
}
return $obj;
}
/**
* Encode a PHP object into a JSON string.
*
* @param object|array $input A PHP object or array
*
* @return string JSON representation of the PHP object or array
*
* @throws DomainException Provided object could not be encoded to valid JSON
*/
public static function jsonEncode($input)
{
$json = json_encode($input);
if (function_exists('json_last_error') && $errno = json_last_error()) {
static::handleJsonError($errno);
} elseif ($json === 'null' && $input !== null) {
throw new DomainException('Null result with non-null input');
}
return $json;
}
/**
* Decode a string with URL-safe Base64.
*
* @param string $input A Base64 encoded string
*
* @return string A decoded string
*/
public static function urlsafeB64Decode($input)
{
$remainder = strlen($input) % 4;
if ($remainder) {
$padlen = 4 - $remainder;
$input .= str_repeat('=', $padlen);
}
return base64_decode(strtr($input, '-_', '+/'));
}
/**
* Encode a string with URL-safe Base64.
*
* @param string $input The string you want encoded
*
* @return string The base64 encode of what you passed in
*/
public static function urlsafeB64Encode($input)
{
return str_replace('=', '', strtr(base64_encode($input), '+/', '-_'));
}
/**
* Helper method to create a JSON error.
*
* @param int $errno An error number from json_last_error()
*
* @return void
*/
private static function handleJsonError($errno)
{
$messages = array(
JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON'
);
throw new DomainException(
isset($messages[$errno])
? $messages[$errno]
: 'Unknown JSON error: ' . $errno
);
}
/**
* Get the number of bytes in cryptographic strings.
*
* @param string
*
* @return int
*/
private static function safeStrlen($str)
{
if (function_exists('mb_strlen')) {
return mb_strlen($str, '8bit');
}
return strlen($str);
}
}

View File

@ -0,0 +1,30 @@
Copyright (c) 2011, Neuman Vong
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Neuman Vong nor the names of other
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

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

View File

@ -19,3 +19,7 @@ License File: jQuery.iframe-transport.license
jQuery.UI - jQuery UI is an open source library of interface components — interactions, full-featured widgets, and animation effects — based on the stellar jQuery javascript library . Each component is built according to jQuery's event-driven architecture (find something, manipulate it) and is themeable, making it easy for developers of any skill level to integrate and extend into their own code. (https://jquery.org/license/)
License: MIT
License File: jQuery.UI.license
JWT - JSON Web Token implementation (https://github.com/firebase/php-jwt/blob/master/LICENSE)
License: BSD
License File: jwt.license

View File

@ -0,0 +1,30 @@
Copyright (c) 2011, Neuman Vong
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Neuman Vong nor the names of other
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -32,7 +32,7 @@ function readBody() {
return $result;
}
$data = json_decode($body_stream, TRUE); // json_decode - PHP 5 >= 5.2.0
$data = json_decode($body_stream, false);
// check if the response is correct
if ($data === NULL) {
@ -47,27 +47,29 @@ function readBody() {
sendlog(" jwt enabled, checking tokens", "webedior-ajax.log");
$inHeader = false;
$token = "";
$data = "";
$jwtHeader = $GLOBALS['DOC_SERV_JWT_HEADER'] == "" ? "Authorization" : $GLOBALS['DOC_SERV_JWT_HEADER'];
if (!empty($data["token"])) { // if the document token is in the data
$token = jwtDecode($data["token"]); // decode it
$data = jwtDecode($data["token"]); // decode it
sendlog(" jwt in body", "webedior-ajax.log");
} elseif (!empty(apache_request_headers()[$jwtHeader])) { // if the Authorization header exists
$token = jwtDecode(substr(apache_request_headers()[$jwtHeader], strlen("Bearer "))); // decode its part after Authorization prefix
$data = jwtDecode(substr(apache_request_headers()[$jwtHeader], strlen("Bearer "))); // decode its part after Authorization prefix
$inHeader = true;
sendlog(" jwt in header", "webedior-ajax.log");
} else { // otherwise, an error occurs
sendlog(" jwt token wasn't found in body or headers", "webedior-ajax.log");
$result["error"] = "Expected JWT";
return $result;
}
if (empty($token)) { // invalid signature error
if ($data === "") { // invalid signature error
sendlog(" token was found but signature is invalid", "webedior-ajax.log");
$result["error"] = "Invalid JWT signature";
return $result;
}
$data = json_decode($token, true);
if ($inHeader) $data = $data["payload"];
if ($inHeader) $data = $data->payload;
}
return $data;
@ -75,14 +77,14 @@ function readBody() {
// file saving process
function processSave($data, $fileName, $userAddress) {
$downloadUri = $data["url"];
$downloadUri = $data->url;
if ($downloadUri === null) {
$result["error"] = 1;
return $result;
}
$curExt = strtolower('.' . pathinfo($fileName, PATHINFO_EXTENSION)); // get current file extension
$downloadExt = strtolower('.' . $data["filetype"]); // get the extension of the downloaded file
$downloadExt = strtolower('.' . $data->filetype); // get the extension of the downloaded file
// TODO [Delete in version 7.0 or higher]
if (!$downloadExt) $downloadExt = strtolower('.' . pathinfo($downloadUri, PATHINFO_EXTENSION)); // Support for versions below 7.0
@ -123,18 +125,18 @@ function processSave($data, $fileName, $userAddress) {
rename(getStoragePath($fileName, $userAddress), $verDir . DIRECTORY_SEPARATOR . "prev" . $curExt); // get the path to the previous file version and rename the storage path with it
file_put_contents($storagePath, $new_data, LOCK_EX); // save file to the storage directory
if ($changesData = file_get_contents($data["changesurl"])) {
if ($changesData = file_get_contents($data->changesurl)) {
file_put_contents($verDir . DIRECTORY_SEPARATOR . "diff.zip", $changesData, LOCK_EX); // save file changes to the diff.zip archive
}
$histData = empty($data["changeshistory"]) ? null : $data["changeshistory"];
$histData = empty($data->changeshistory) ? null : $data->changeshistory;
if (empty($histData)) {
$histData = json_encode($data["history"], JSON_PRETTY_PRINT);
$histData = json_encode($data->history, JSON_PRETTY_PRINT);
}
if (!empty($histData)) {
file_put_contents($verDir . DIRECTORY_SEPARATOR . "changes.json", $histData, LOCK_EX); // write the history changes to the changes.json file
}
file_put_contents($verDir . DIRECTORY_SEPARATOR . "key.txt", $data["key"], LOCK_EX); // write the key value to the key.txt file
file_put_contents($verDir . DIRECTORY_SEPARATOR . "key.txt", $data->key, LOCK_EX); // write the key value to the key.txt file
$forcesavePath = getForcesavePath($newFileName, $userAddress, false); // get the path to the forcesaved file version
if ($forcesavePath != "") { // if the forcesaved file version exists
@ -151,14 +153,14 @@ function processSave($data, $fileName, $userAddress) {
// file force saving process
function processForceSave($data, $fileName, $userAddress) {
$downloadUri = $data["url"];
$downloadUri = $data->url;
if ($downloadUri === null) {
$result["error"] = 1;
return $result;
}
$curExt = strtolower('.' . pathinfo($fileName, PATHINFO_EXTENSION)); // get current file extension
$downloadExt = strtolower('.' . $data["filetype"]); // get the extension of the downloaded file
$downloadExt = strtolower('.' . $data->filetype); // get the extension of the downloaded file
// TODO [Delete in version 7.0 or higher]
if (!$downloadExt) $downloadExt = strtolower('.' . pathinfo($downloadUri, PATHINFO_EXTENSION)); // Support for versions below 7.0
@ -190,7 +192,7 @@ function processForceSave($data, $fileName, $userAddress) {
if (!(($new_data = file_get_contents($downloadUri)) === FALSE)) {
$baseNameWithoutExt = substr($fileName, 0, strlen($fileName) - strlen($curExt));
$isSubmitForm = $data["forcesavetype"] == 3; // SubmitForm
$isSubmitForm = $data->forcesavetype == 3; // SubmitForm
if ($isSubmitForm) {
if ($newFileName){
@ -213,7 +215,7 @@ function processForceSave($data, $fileName, $userAddress) {
file_put_contents($forcesavePath, $new_data, LOCK_EX);
if ($isSubmitForm) {
$uid = $data["actions"][0]["userid"]; // get the user id
$uid = $data->actions[0]->userid; // get the user id
createMeta($fileName, $uid, "Filling Form", $userAddress); // create meta data for the forcesaved file
}

View File

@ -74,7 +74,9 @@ $descr_user_0 = [
"Can't mention others in comments",
"Can't create new files from the editor",
"Cant see anyones information",
"Can't rename files from the editor"
"Can't rename files from the editor",
"Can't view chat",
"View file without collaboration",
];
$users = [

View File

@ -215,22 +215,24 @@ function track() {
// get the body of the post request and check if it is correct
$data = readBody();
if (!empty($data["error"])){
if (!empty($data->error)){
return $data;
}
global $_trackerStatus;
$status = $_trackerStatus[$data["status"]]; // get status from the request body
$status = $_trackerStatus[$data->status]; // get status from the request body
$userAddress = $_GET["userAddress"];
$fileName = basename($_GET["fileName"]);
sendlog(" CommandRequest status: " . $data->status, "webedior-ajax.log");
switch ($status) {
case "Editing": // status == 1
if ($data["actions"] && $data["actions"][0]["type"] == 0) { // finished edit
$user = $data["actions"][0]["userid"]; // the user who finished editing
if (array_search($user, $data["users"]) === FALSE) {
$commandRequest = commandRequest("forcesave", $data["key"]); // create a command request with the forcasave method
if ($data->actions && $data->actions[0]->type == 0) { // finished edit
$user = $data->actions[0]->userid; // the user who finished editing
if (array_search($user, $data->users) === FALSE) {
$commandRequest = commandRequest("forcesave", $data->key); // create a command request with the forcasave method
sendlog(" CommandRequest forcesave: " . serialize($commandRequest), "webedior-ajax.log");
}
}
@ -259,7 +261,7 @@ function convert() {
$internalExtension = trim(getInternalExtension($fileName),'.');
// check if the file with such an extension can be converted
if (in_array("." + $extension, $GLOBALS['DOC_SERV_CONVERT']) && $internalExtension != "") {
if (in_array("." . $extension, $GLOBALS['DOC_SERV_CONVERT']) && $internalExtension != "") {
$fileUri = $post["fileUri"];
if ($fileUri == NULL || $fileUri == "") {
@ -469,6 +471,13 @@ function delTree($dir) {
function renamefile() {
$post = json_decode(file_get_contents('php://input'), true);
$newfilename = $post["newfilename"];
$curExt = strtolower(array_pop(explode('.', $newfilename)));
$origExt = $post["ext"];
if($origExt !== $curExt){
$newfilename .= '.' . $origExt;
}
$dockey = $post["dockey"];
$meta = ["title" => $newfilename];

View File

@ -1,6 +1,6 @@
import os
VERSION = '1.2.0'
VERSION = '1.3.0'
FILE_SIZE_MAX = 5242880
STORAGE_PATH = 'app_data'
@ -55,11 +55,14 @@ EXT_DOCUMENT = [
LANGUAGES = {
'en': 'English',
'hy': 'Armenian',
'az': 'Azerbaijani',
'eu': 'Basque',
'be': 'Belarusian',
'bg': 'Bulgarian',
'ca': 'Catalan',
'zh': 'Chinese',
'zh' : 'Chinese (People\'s Republic of China)',
'zh-TW' : 'Chinese (Traditional, Taiwan)',
'cs': 'Czech',
'da': 'Danish',
'nl': 'Dutch',
@ -75,9 +78,11 @@ LANGUAGES = {
'ko': 'Korean',
'lv': 'Latvian',
'lo': 'Lao',
'ms': 'Malay (Malaysia)',
'nb': 'Norwegian',
'pl': 'Polish',
'pt': 'Portuguese',
'pt' : 'Portuguese (Brazil)',
'pt-PT' : 'Portuguese (Portugal)',
'ro': 'Romanian',
'ru': 'Russian',
'sk': 'Slovak',

View File

@ -118,10 +118,10 @@ def getCreateUrl(fileType, req):
return f'{host}/create?fileType={fileType}'
# get url to download a file
def getDownloadUrl(filename, req):
host = getServerUrl(True, req)
curAdr = req.META['REMOTE_ADDR']
return f'{host}/download?fileName={filename}&userAddress={curAdr}'
def getDownloadUrl(filename, req, isServerUrl = True):
host = getServerUrl(isServerUrl, req)
curAdr = f'&userAddress={req.META["REMOTE_ADDR"]}' if isServerUrl else ""
return f'{host}/download?fileName={filename}{curAdr}'
# get root folder for the current file
def getRootFolder(req):

View File

@ -135,10 +135,10 @@ def readFile(path):
return stream.read()
# get the url to the history file version with a given extension
def getPublicHistUri(filename, ver, file, req):
host = docManager.getServerUrl(True, req)
curAdr = req.META['REMOTE_ADDR']
return f'{host}/downloadhistory?fileName={filename}&ver={ver}&file={file}&userAddress={curAdr}'
def getPublicHistUri(filename, ver, file, req, isServerUrl=True):
host = docManager.getServerUrl(isServerUrl, req)
curAdr = f'&userAddress={req.META["REMOTE_ADDR"]}' if isServerUrl else ''
return f'{host}/downloadhistory?fileName={filename}&ver={ver}&file={file}{curAdr}'
# get the meta data of the file
def getMeta(storagePath):
@ -184,6 +184,7 @@ def getHistoryObject(storagePath, filename, docKey, docUrl, req):
}
dataObj['url'] = docUrl if i == version else getPublicHistUri(filename, i, "prev" + fileUtils.getFileExt(filename), req) # write file url to the data object
dataObj['directUrl'] = docManager.getDownloadUrl(filename, req, False) if i == version else getPublicHistUri(filename, i, "prev" + fileUtils.getFileExt(filename), req, False) # write file direct url to the data object
if i > 1: # check if the version number is greater than 1 (the file was modified)
changes = json.loads(readFile(getChangesHistoryPath(prevVerDir))) # get the path to the changes.json file
@ -198,7 +199,8 @@ def getHistoryObject(storagePath, filename, docKey, docUrl, req):
prevInfo = { # write key and url information about previous file version
'fileType': prev['fileType'],
'key': prev['key'],
'url': prev['url']
'url': prev['url'],
'directUrl': prev['directUrl']
}
dataObj['previous'] = prevInfo # write information about previous file version to the data object
dataObj['changesUrl'] = getPublicHistUri(filename, i - 1, "diff.zip", req) # write the path to the diff.zip archive with differences in this file version

View File

@ -80,7 +80,9 @@ descr_user_0 = [
"Can't mention others in comments",
"Can't create new files from the editor",
"Cant see anyones information",
"Can't rename files from the editor"
"Can't rename files from the editor",
"Can't view chat",
"View file without collaboration",
]
USERS = [

View File

@ -153,6 +153,12 @@ def rename(request):
body = json.loads(request.body)
newfilename = body['newfilename']
origExt = '.' + body['ext']
curExt = fileUtils.getFileExt(newfilename)
if (origExt != curExt):
newfilename += origExt
dockey = body['dockey']
meta = {'title': newfilename}
@ -170,6 +176,7 @@ def edit(request):
fileUri = docManager.getFileUri(filename, True, request)
fileUriUser = docManager.getDownloadUrl(filename, request) + "&dmode=emb" if os.path.isabs(config.STORAGE_PATH) else docManager.getFileUri(filename, False, request)
directUrl = docManager.getDownloadUrl(filename, request, False)
docKey = docManager.generateFileKey(filename, request)
fileType = fileUtils.getFileType(filename)
user = users.getUserFromReq(request) # get user
@ -227,6 +234,7 @@ def edit(request):
'document': {
'title': filename,
'url': docManager.getDownloadUrl(filename, request),
'directUrl': directUrl,
'fileType': ext[1:],
'key': docKey,
'info': infObj,
@ -240,6 +248,7 @@ def edit(request):
'modifyFilter': edMode != 'filter',
'modifyContentControl': edMode != "blockcontent",
'review': canEdit & ((edMode == 'edit') | (edMode == 'review')),
'chat': user.id !='uid-0',
'reviewGroups': user.reviewGroups,
'commentGroups': user.commentGroups,
'userInfoGroups': user.userInfoGroups
@ -250,6 +259,11 @@ def edit(request):
'mode': mode,
'lang': lang,
'callbackUrl': docManager.getCallbackUrl(filename, request), # absolute URL to the document storage service
'coEditing': {
"mode": "strict",
"change": False
}
if edMode == 'view' and user.id =='uid-0' else None,
'createUrl' : createUrl if user.id !='uid-0' else None,
'templates' : templates if user.templates else None,
'user': { # the user currently viewing or editing the document
@ -258,9 +272,9 @@ def edit(request):
'group': user.group
},
'embedded': { # the parameters for the embedded document type
'saveUrl': fileUriUser, # the absolute URL that will allow the document to be saved onto the user personal computer
'embedUrl': fileUriUser, # the absolute URL to the document serving as a source file for the document embedded into the web page
'shareUrl': fileUriUser, # the absolute URL that will allow other users to share this document
'saveUrl': directUrl, # the absolute URL that will allow the document to be saved onto the user personal computer
'embedUrl': directUrl, # the absolute URL to the document serving as a source file for the document embedded into the web page
'shareUrl': directUrl, # the absolute URL that will allow other users to share this document
'toolbarDocked': 'top' # the place for the embedded viewer toolbar (top or bottom)
},
'customization': { # the parameters for the editor interface
@ -279,19 +293,22 @@ def edit(request):
# an image which will be inserted into the document
dataInsertImage = {
'fileType': 'png',
'url': docManager.getServerUrl(True, request) + 'static/images/logo.png'
'url': docManager.getServerUrl(True, request) + 'static/images/logo.png',
'directUrl': docManager.getServerUrl(False, request) + 'static/images/logo.png'
}
# a document which will be compared with the current document
dataCompareFile = {
'fileType': 'docx',
'url': docManager.getServerUrl(True, request) + 'static/sample.docx'
'url': docManager.getServerUrl(True, request) + 'static/sample.docx',
'directUrl': docManager.getServerUrl(False, request) + 'static/sample.docx'
}
# recipient data for mail merging
dataMailMergeRecipients = {
'fileType': 'csv',
'url': docManager.getServerUrl(True, request) + 'csv'
'url': docManager.getServerUrl(True, request) + 'csv',
'directUrl': docManager.getServerUrl(False, request) + 'csv'
}
# users data for mentions

View File

@ -165,6 +165,7 @@
var data = {
newfilename: newfilename,
dockey: config.document.key,
ext: config.document.fileType
};
let xhr = new XMLHttpRequest();
xhr.open("POST", "rename");

View File

@ -47,4 +47,7 @@ gem 'uuid'
gem 'rack-cors'
gem 'webrick'
gem 'webrick'
# A ruby implementation of the RFC 7519 OAuth JSON Web Token (JWT) standard.
gem 'jwt', '~> 2.4.1'

View File

@ -78,24 +78,26 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.12.2)
concurrent-ruby (1.1.9)
concurrent-ruby (1.1.10)
crass (1.0.6)
debug_inspector (1.1.0)
erubi (1.10.0)
execjs (2.8.1)
ffi (1.15.5)
ffi (1.15.5-x64-mingw32)
globalid (1.0.0)
activesupport (>= 5.0)
i18n (1.9.1)
i18n (1.12.0)
concurrent-ruby (~> 1.0)
jbuilder (2.9.1)
activesupport (>= 4.2.0)
jquery-rails (4.4.0)
jquery-rails (4.5.0)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
json (2.3.0)
loofah (2.13.0)
json (1.8.6)
jwt (2.4.1)
loofah (2.18.0)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
macaddr (1.7.2)
@ -105,21 +107,18 @@ GEM
marcel (1.0.2)
method_source (1.0.0)
mini_mime (1.1.2)
minitest (5.15.0)
minitest (5.16.2)
nio4r (2.5.8)
nokogiri (1.13.3)
mini_portile2 (~> 2.8.0)
nokogiri (1.13.8-x64-mingw32)
racc (~> 1.4)
nokogiri (1.13.3-x64-mingw32)
racc (~> 1.4)
nokogiri (1.13.3-x86_64-linux)
nokogiri (1.13.8-x86_64-linux)
racc (~> 1.4)
racc (1.6.0)
rack (2.2.3)
rack (2.2.4)
rack-cors (1.1.1)
rack (>= 2.0.0)
rack-test (1.1.0)
rack (>= 1.0, < 3)
rack-test (2.0.2)
rack (>= 1.3)
rails (6.1.4.1)
actioncable (= 6.1.4.1)
actionmailbox (= 6.1.4.1)
@ -138,7 +137,7 @@ GEM
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
rails-html-sanitizer (1.4.2)
rails-html-sanitizer (1.4.3)
loofah (~> 2.3)
railties (6.1.4.1)
actionpack (= 6.1.4.1)
@ -147,9 +146,11 @@ GEM
rake (>= 0.13)
thor (~> 1.0)
rake (13.0.6)
rdoc (6.3.1)
rdoc (4.3.0)
sass-rails (6.0.0)
sassc-rails (~> 2.1, >= 2.1.1)
sassc (2.4.0)
ffi (~> 1.9)
sassc (2.4.0-x64-mingw32)
ffi (~> 1.9)
sassc-rails (2.1.2)
@ -161,7 +162,7 @@ GEM
sdoc (0.4.2)
json (~> 1.7, >= 1.7.7)
rdoc (~> 4.0)
sprockets (4.0.2)
sprockets (4.1.1)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.4.2)
@ -171,13 +172,13 @@ GEM
sqlite3 (1.4.2)
systemu (2.6.5)
thor (1.2.1)
tilt (2.0.10)
tilt (2.0.11)
turbolinks (5.2.1)
turbolinks-source (~> 5.2)
turbolinks-source (5.2.0)
tzinfo (2.0.4)
tzinfo (2.0.5)
concurrent-ruby (~> 1.0)
tzinfo-data (1.2021.5)
tzinfo-data (1.2022.1)
tzinfo (>= 1.0.0)
uglifier (4.2.0)
execjs (>= 0.3.0, < 3)
@ -192,16 +193,18 @@ GEM
websocket-driver (0.7.5)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
zeitwerk (2.5.4)
zeitwerk (2.6.0)
PLATFORMS
x64-mingw32
x86_64-linux
DEPENDENCIES
byebug
coffee-rails (~> 5.0.0)
jbuilder (~> 2.9.1)
jquery-rails
jwt (~> 2.4.1)
mimemagic!
rack-cors
rails (= 6.1.4.1)
@ -216,4 +219,4 @@ DEPENDENCIES
webrick
BUNDLED WITH
2.1.4
2.2.22

View File

@ -351,6 +351,13 @@ class HomeController < ApplicationController
body = JSON.parse(request.body.read)
dockey = body["dockey"]
newfilename = body["newfilename"]
orig_ext = '.' + body["ext"]
cur_ext = File.extname(newfilename).downcase
if orig_ext != cur_ext
newfilename += orig_ext
end
meta = {
:title => newfilename
}

View File

@ -230,15 +230,16 @@ class DocumentHelper
# get file url
def get_file_uri(file_name, for_document_server)
uri = get_server_url(for_document_server) + '/' + Rails.configuration.storagePath + '/' + cur_user_host_address(nil) + '/' + URI::encode(file_name)
uri = get_server_url(for_document_server) + '/' + Rails.configuration.storagePath + '/' + cur_user_host_address(nil) + '/' + ERB::Util.url_encode(file_name)
return uri
end
# get history path url
def get_historypath_uri(file_name,version,file)
def get_historypath_uri(file_name,version,file,is_serverUrl=true)
# for redirection to my link
uri = get_server_url(true) + '/downloadhistory/?fileName=' + URI::encode(file_name) + '&ver='+ version.to_s + '&file='+ URI::encode(file) + '&userAddress=' + cur_user_host_address(nil)
user_host = is_serverUrl ? '&userAddress=' + cur_user_host_address(nil) : ""
uri = get_server_url(is_serverUrl) + '/downloadhistory/?fileName=' + ERB::Util.url_encode(file_name) + '&ver='+ version.to_s + '&file='+ ERB::Util.url_encode(file) + user_host
return uri
end
@ -254,7 +255,7 @@ class DocumentHelper
# get callback url
def get_callback(file_name)
get_server_url(true) + '/track?fileName=' + URI::encode(file_name) + '&userAddress=' + cur_user_host_address(nil)
get_server_url(true) + '/track?fileName=' + ERB::Util.url_encode(file_name) + '&userAddress=' + cur_user_host_address(nil)
end
@ -266,9 +267,10 @@ class DocumentHelper
end
# get url to download a file
def get_download_url(file_name)
def get_download_url(file_name, is_serverUrl=true)
get_server_url(true) + '/download?fileName=' + URI::encode(file_name) + '&userAddress=' + cur_user_host_address(nil)
user_host = is_serverUrl ? '&userAddress=' + cur_user_host_address(nil) : ""
get_server_url(is_serverUrl) + '/download?fileName=' + ERB::Util.url_encode(file_name) + user_host
end

View File

@ -71,8 +71,8 @@ class FileModel
end
# get url to download a file
def download_url
DocumentHelper.get_download_url(@file_name)
def download_url(is_serverUrl=true)
DocumentHelper.get_download_url(@file_name, is_serverUrl)
end
# get current user host address
@ -110,6 +110,7 @@ class FileModel
:document => {
:title => @file_name,
:url => download_url,
:directUrl => download_url(false),
:fileType => file_ext.delete("."),
:key => key,
:info => {
@ -127,6 +128,7 @@ class FileModel
:modifyFilter => !editorsmode.eql?("filter"),
:modifyContentControl => !editorsmode.eql?("blockcontent"),
:review => canEdit && (editorsmode.eql?("edit") || editorsmode.eql?("review")),
:chat => !@user.id.eql?("uid-0"),
:reviewGroups => @user.reviewGroups,
:commentGroups => @user.commentGroups,
:userInfoGroups => @user.userInfoGroups
@ -137,6 +139,10 @@ class FileModel
:mode => mode,
:lang => @lang ? @lang : "en",
:callbackUrl => callback_url, # absolute URL to the document storage service
:coEditing => editorsmode.eql?("view") && @user.id.eql?("uid-0") ? {
:mode => "strict",
:change => false
} : nil,
:createUrl => !@user.id.eql?("uid-0") ? create_url : nil,
:templates => @user.templates ? templates : nil,
:user => { # the user currently viewing or editing the document
@ -145,9 +151,9 @@ class FileModel
:group => @user.group
},
:embedded => { # the parameters for the embedded document type
:saveUrl => file_uri_user, # the absolute URL that will allow the document to be saved onto the user personal computer
:embedUrl => file_uri_user, # the absolute URL to the document serving as a source file for the document embedded into the web page
:shareUrl => file_uri_user, # the absolute URL that will allow other users to share this document
:saveUrl => download_url(false), # the absolute URL that will allow the document to be saved onto the user personal computer
:embedUrl => download_url(false), # the absolute URL to the document serving as a source file for the document embedded into the web page
:shareUrl => download_url(false), # the absolute URL that will allow other users to share this document
:toolbarDocked => "top" # the place for the embedded viewer toolbar (top or bottom)
},
:customization => { # the parameters for the editor interface
@ -157,7 +163,7 @@ class FileModel
:forcesave => false, # adding the request for the forced file saving to the callback handler
:submitForm => submitForm, # the Submit form button state
:goback => {
:url => DocumentHelper.get_server_url(true)
:url => DocumentHelper.get_server_url(false)
},
}
}
@ -218,6 +224,7 @@ class FileModel
dataObj["fileType"] = file_ext[1..file_ext.length]
dataObj["key"] = cur_key
dataObj["url"] = i == cur_ver ? doc_uri : DocumentHelper.get_historypath_uri(file_name, i, "prev#{file_ext}")
dataObj["directUrl"] = i == cur_ver ? download_url(false) : DocumentHelper.get_historypath_uri(file_name, i, "prev#{file_ext}", false)
dataObj["version"] = i
if (i > 1) # check if the version number is greater than 1
@ -239,7 +246,8 @@ class FileModel
dataObj["previous"] = { # write key and url information about previous file version
:fileType => prev["fileType"],
:key => prev["key"],
:url => prev["url"]
:url => prev["url"],
:directUrl => prev["directUrl"]
}
# write the path to the diff.zip archive with differences in this file version
@ -271,7 +279,8 @@ class FileModel
def get_insert_image
insert_image = {
:fileType => "png", # image file type
:url => DocumentHelper.get_server_url(true) + "/assets/logo.png" # server url to the image
:url => DocumentHelper.get_server_url(true) + "/assets/logo.png", # server url to the image
:directUrl => DocumentHelper.get_server_url(false) + "/assets/logo.png" # direct url to the image
}
if JwtHelper.is_enabled # check if a secret key to generate token exists or not
@ -285,7 +294,8 @@ class FileModel
def get_compare_file
compare_file = {
:fileType => "docx", # file type
:url => DocumentHelper.get_server_url(true) + "/assets/sample/sample.docx" # server url to the compared file
:url => DocumentHelper.get_server_url(true) + "/assets/sample/sample.docx", # server url to the compared file
:directUrl => DocumentHelper.get_server_url(false) + "/assets/sample/sample.docx" # direct url to the compared file
}
if JwtHelper.is_enabled # check if a secret key to generate token exists or not
@ -299,7 +309,8 @@ class FileModel
def dataMailMergeRecipients
dataMailMergeRecipients = {
:fileType => "csv", # file type
:url => DocumentHelper.get_server_url(true) + "/csv" # server url to the mail merge recipients file
:url => DocumentHelper.get_server_url(true) + "/csv", # server url to the mail merge recipients file
:directUrl => DocumentHelper.get_server_url(false) + "/csv" # direct url to the mail merge recipients file
}
if JwtHelper.is_enabled # check if a secret key to generate token exists or not

View File

@ -14,6 +14,8 @@
# limitations under the License.
#
require 'jwt'
class JwtHelper
@jwt_secret = Rails.configuration.jwtSecret
@ -26,37 +28,19 @@ class JwtHelper
# encode a payload object into a token using a secret key
def encode(payload)
header = { :alg => "HS256", :typ => "JWT" } # define the hashing algorithm and the token type
# three parts of token
enc_header = Base64.urlsafe_encode64(header.to_json).remove("=") # header
enc_payload = Base64.urlsafe_encode64(payload.to_json).remove("=") # payload
hash = Base64.urlsafe_encode64(calc_hash(enc_header, enc_payload)).remove("=") # signature
return "#{enc_header}.#{enc_payload}.#{hash}"
return JWT.encode payload, @jwt_secret, 'HS256' # define the hashing algorithm and get token
end
# decode a token into a payload object using a secret key
def decode(token)
if !is_enabled
begin
decoded = JWT.decode token, @jwt_secret, true, { algorithm: 'HS256' }
rescue
return ""
end
split = token.split(".")
hash = Base64.urlsafe_encode64(calc_hash(split[0], split[1])).remove("=")
if !hash.eql?(split[2])
return ""
end
return Base64.urlsafe_decode64(split[1])
end
private
# generate a hash code based on a key using the HMAC method
def calc_hash(header, payload)
return OpenSSL::HMAC.digest("SHA256", @jwt_secret, "#{header}.#{payload}")
# decoded = Array [ {"data"=>"test"}, # payload
# {"alg"=>"HS256"} # header ]
return decoded[0].to_json # get json payload
end
end
end

View File

@ -73,7 +73,9 @@ class Users
"Can't mention others in comments",
"Can't create new files from the editor",
"Cant see anyones information",
"Can't rename files from the editor"
"Can't rename files from the editor",
"Can't view chat",
"View file without collaboration"
];
@@users = [

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