Merge pull request 'release/v1.10.0 from release/v1.10.0 into master

Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/document-server-integration/pulls/9
This commit is contained in:
Sergey Linnik
2024-10-11 09:27:13 +00:00
355 changed files with 13812 additions and 8004 deletions

View File

@ -30,7 +30,7 @@ jobs:
- name: Install Dependencies - name: Install Dependencies
run: | run: |
bundle update bundle install
- name: Rubocop - name: Rubocop
run: | run: |

4
.gitmodules vendored
View File

@ -48,10 +48,6 @@
path = web/documentserver-example/java/src/main/resources/assets/document-formats path = web/documentserver-example/java/src/main/resources/assets/document-formats
url = https://github.com/ONLYOFFICE/document-formats url = https://github.com/ONLYOFFICE/document-formats
branch = master branch = master
[submodule "web/documentserver-example/java-spring/src/main/resources/assets/document-formats"]
path = web/documentserver-example/java-spring/src/main/resources/assets/document-formats
url = https://github.com/ONLYOFFICE/document-formats
branch = master
[submodule "web/documentserver-example/csharp/assets/document-templates"] [submodule "web/documentserver-example/csharp/assets/document-templates"]
path = web/documentserver-example/csharp/assets/document-templates path = web/documentserver-example/csharp/assets/document-templates
url = https://github.com/ONLYOFFICE/document-templates url = https://github.com/ONLYOFFICE/document-templates

View File

@ -1,6 +1,5 @@
Document Server integration example uses code from the following 3rd party projects. Document Server integration example uses code from the following 3rd party projects.
web/documentserver-example/csharp web/documentserver-example/csharp
jQuery.BlockUI - The jQuery BlockUI Plugin lets you simulate synchronous behavior when using AJAX, without locking the browser. (https://github.com/malsup/blockui/) jQuery.BlockUI - The jQuery BlockUI Plugin lets you simulate synchronous behavior when using AJAX, without locking the browser. (https://github.com/malsup/blockui/)
@ -132,6 +131,10 @@ Jackson Databind - General-purpose data-binding functionality and tree-model for
License: Apache 2.0 License: Apache 2.0
License File: jackson-databind.license License File: jackson-databind.license
Jackson Dataformat Properties - Support for reading and writing content of "Java Properties" style configuration files as if there was implied nesting structure (by default using dots as separators). (https://github.com/FasterXML/jackson-dataformats-text/blob/master/LICENSE)
License: Apache 2.0
License File: jackson-dataformat-properties.license
jQuery.BlockUI - The jQuery BlockUI Plugin lets you simulate synchronous behavior when using AJAX, without locking the browser. (https://github.com/malsup/blockui/) jQuery.BlockUI - The jQuery BlockUI Plugin lets you simulate synchronous behavior when using AJAX, without locking the browser. (https://github.com/malsup/blockui/)
License: MIT, GPL License: MIT, GPL
License File: jQuery.BlockUI.license License File: jQuery.BlockUI.license
@ -156,6 +159,10 @@ jQuery.UI - jQuery UI is an open source library of interface components —
License: MIT License: MIT
License File: jQuery.UI.license License File: jQuery.UI.license
JSON - JSON is a light-weight, language independent, data interchange format. (https://github.com/stleary/JSON-java/blob/master/LICENSE)
License Public Domain
License File: json.license
JSON.simple - JSON.simple is a simple Java toolkit for JSON. You can use JSON.simple to encode or decode JSON text. (https://github.com/fangyidong/json-simple/blob/master/LICENSE.txt) JSON.simple - JSON.simple is a simple Java toolkit for JSON. You can use JSON.simple to encode or decode JSON text. (https://github.com/fangyidong/json-simple/blob/master/LICENSE.txt)
License: Apache 2.0 License: Apache 2.0
License File: JSON.simple.license License File: JSON.simple.license
@ -168,14 +175,14 @@ ModelMapper - ModelMapper is an intelligent object mapping library that automa
License: Apache 2.0 License: Apache 2.0
License File modelmapper.license License File modelmapper.license
Prime JWT - is intended to be fast and easy to use. Prime JWT has a single external dependency on Jackson. (https://github.com/ws-apps/prime-jwt/blob/master/LICENSE)
License: Apache 2.0
License File: prime-jwt.license
Spring Boot - Helps create Spring-powered, production-grade applications and services. Has external dependencies on Spring Framework. (https://github.com/spring-projects/spring-boot/blob/main/LICENSE.txt) Spring Boot - Helps create Spring-powered, production-grade applications and services. Has external dependencies on Spring Framework. (https://github.com/spring-projects/spring-boot/blob/main/LICENSE.txt)
License: Apache 2.0 License: Apache 2.0
License File: spring-boot.license License File: spring-boot.license
Spring Boot Web - Starter for building web, including RESTful, applications using Spring MVC. Uses Tomcat as the default embedded container. (https://github.com/spring-projects/spring-boot/blob/main/LICENSE.txt)
License: Apache 2.0
License File: spring-boot.license
Spring Boot Devtools - Provides fast application restarts, LiveReload, and configurations for enhanced development experience. (https://github.com/spring-projects/spring-boot/blob/main/LICENSE.txt) Spring Boot Devtools - Provides fast application restarts, LiveReload, and configurations for enhanced development experience. (https://github.com/spring-projects/spring-boot/blob/main/LICENSE.txt)
License: Apache 2.0 License: Apache 2.0
License File: spring-boot.license License File: spring-boot.license
@ -271,6 +278,9 @@ urllib - Request HTTP URLs in a complex world — basic and digest authen
License: MIT License: MIT
License File: urllib.license License File: urllib.license
utf7 - Encodes and decodes JavaScript (Unicode/UCS-2) strings to UTF-7 ASCII strings. (https://github.com/kkaefer/utf7/blob/master/LICENSE)
License: MIT
License File: utf7.license
web/documentserver-example/php web/documentserver-example/php

View File

@ -1,5 +1,18 @@
# Change Log # Change Log
## 1.10.0
- nodejs: converting function on index page
- java-spring: using java docs-integration-sdk
- tabs menu
- creating and editing pdf instead docxf
- filling by default
- forgotten files
- delete all files
- save as for pdf
- handling conversion -9 error
- change inserted image
- different goback for users
## 1.9.0 ## 1.9.0
- nodejs: filling by default - nodejs: filling by default
- nodejs: docxf, oform as pdf documentType - nodejs: docxf, oform as pdf documentType

View File

@ -1,11 +1,11 @@
## Integration examples ## Integration examples
Test examples are simple document management systems that can be built into your application for testing. These test examples are simple document management systems that can be built into your application for testing.
Do NOT use these integration examples on your own server without proper code modifications! Do NOT use these integration examples on your own server without proper code modifications!
In case you enabled any of the test examples, disable it before going for production. In case you enabled any of the test examples, disable it before going for production.
These examples show the way to integrate [ONLYOFFICE Docs][2] into your own website or application using one of the programming languages. These examples show the way to integrate [ONLYOFFICE Docs][2] into your own website or application using one of the programming languages.
The package contains examples written in .Net (C# MVC), .Net (C#), Java, Node.js, PHP and Ruby. The package contains examples written in .Net (C# MVC), .Net (C#), Java, Java Spring, Node.js, PHP, Python and Ruby.
You should change `http://documentserver` to your server address in these files: You should change `http://documentserver` to your server address in these files:
* [.Net (C# MVC)](https://github.com/ONLYOFFICE/document-server-integration/tree/master/web/documentserver-example/csharp-mvc) - `web/documentserver-example/csharp-mvc/web.appsettings.config` * [.Net (C# MVC)](https://github.com/ONLYOFFICE/document-server-integration/tree/master/web/documentserver-example/csharp-mvc) - `web/documentserver-example/csharp-mvc/web.appsettings.config`
@ -13,9 +13,9 @@ You should change `http://documentserver` to your server address in these files:
* [Java](https://github.com/ONLYOFFICE/document-server-integration/tree/master/web/documentserver-example/java) - `web/documentserver-example/java/src/main/resources/settings.properties` * [Java](https://github.com/ONLYOFFICE/document-server-integration/tree/master/web/documentserver-example/java) - `web/documentserver-example/java/src/main/resources/settings.properties`
* [Java Spring](https://github.com/ONLYOFFICE/document-server-integration/tree/master/web/documentserver-example/java-spring) - `web/documentserver-example/java-spring/src/main/resources/application.properties` * [Java Spring](https://github.com/ONLYOFFICE/document-server-integration/tree/master/web/documentserver-example/java-spring) - `web/documentserver-example/java-spring/src/main/resources/application.properties`
* [Node.js](https://github.com/ONLYOFFICE/document-server-integration/tree/master/web/documentserver-example/nodejs) - `web/documentserver-example/nodejs/config/default.json` * [Node.js](https://github.com/ONLYOFFICE/document-server-integration/tree/master/web/documentserver-example/nodejs) - `web/documentserver-example/nodejs/config/default.json`
* [PHP](https://github.com/ONLYOFFICE/document-server-integration/tree/master/web/documentserver-example/php) - `web/documentserver-example/php/config.json` * [PHP](https://github.com/ONLYOFFICE/document-server-integration/tree/master/web/documentserver-example/php) - `web/documentserver-example/php/src/configuration/ConfigurationManager.php`
* [Python](https://github.com/ONLYOFFICE/document-server-integration/tree/master/web/documentserver-example/python) - `web/documentserver-example/python/config.py` * [Python](https://github.com/ONLYOFFICE/document-server-integration/tree/master/web/documentserver-example/python) - `web/documentserver-example/python/src/configuration/configuration.py`
* [Ruby](https://github.com/ONLYOFFICE/document-server-integration/tree/master/web/documentserver-example/ruby) - `web/documentserver-example/ruby/config/application.rb` * [Ruby](https://github.com/ONLYOFFICE/document-server-integration/tree/master/web/documentserver-example/ruby) - `web/documentserver-example/ruby/app/configuration/configuration.rb`
More information on how to use these examples can be found here: [http://api.onlyoffice.com/editors/demopreview](http://api.onlyoffice.com/editors/demopreview "http://api.onlyoffice.com/editors/demopreview") More information on how to use these examples can be found here: [http://api.onlyoffice.com/editors/demopreview](http://api.onlyoffice.com/editors/demopreview "http://api.onlyoffice.com/editors/demopreview")

View File

@ -38,9 +38,15 @@ namespace OnlineEditorsExampleMVC
// create the main script bundle // create the main script bundle
bundles.Add(new ScriptBundle("~/bundles/scripts").Include( bundles.Add(new ScriptBundle("~/bundles/scripts").Include(
"~/Scripts/formats.js",
"~/Scripts/jscript.js" "~/Scripts/jscript.js"
)); ));
// create the forgotten page script bundle
bundles.Add(new ScriptBundle("~/bundles/forgotten").Include(
"~/Scripts/forgotten.js"
));
// create a style bundle // create a style bundle
bundles.Add(new StyleBundle("~/Content/css").Include( bundles.Add(new StyleBundle("~/Content/css").Include(
"~/Content/stylesheet.css", "~/Content/stylesheet.css",
@ -52,6 +58,11 @@ namespace OnlineEditorsExampleMVC
bundles.Add(new StyleBundle("~/Content/editor").Include( bundles.Add(new StyleBundle("~/Content/editor").Include(
"~/Content/editor.css" "~/Content/editor.css"
)); ));
// create the forgotten page style bundle
bundles.Add(new StyleBundle("~/Content/forgotten").Include(
"~/Content/forgotten.css"
));
} }
} }
} }

View File

@ -0,0 +1,161 @@
.center {
width: auto;
}
.left-panel {
width: 256px;
}
.main-panel {
width: 832px;
margin: 0 32px;
padding: 48px 0;
left: 0;
}
.tableRow {
display: flex;
width: 100%;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #E2E2E2;
}
.tableRow td:first-child {
width: 70%;
flex-grow: 0;
max-width: none;
}
.tableHeader td:first-child {
text-align: left;
}
.tableHeader td:last-child, .tableRow td:last-child {
width: 10%;
text-align: center;
padding: 0 !important;
}
.tableHeader {
width: 100%;
}
.stored-edit {
display: block;
padding-top: 0;
max-width: none;
}
menu.links {
width: 100%;
}
.scroll-table-body table {
table-layout: fixed;
}
.stored-edit span {
font-size: 12px;
line-height: normal;
position: static;
}
.scroll-table-body {
overflow-y: auto;
}
.stored-list {
height: calc(100% - 58px);
}
header {
min-width:auto;
}
header a {
display: block;
margin: 0 auto;
width: 1152px;
}
@media (max-width: 1279px) and (min-width: 1024px) {
.left-panel {
width: 208px;
}
.main-panel {
width: 688px;
}
header a {
width: 928px;
}
header img {
margin-left: 16px;
}
}
@media (max-width: 1023px) and (min-width: 593px) {
.center {
max-width: 768px;
width: calc(100% - 80px);
}
.table-main {
width: 100%;
}
.left-panel {
width: 208px;
}
.main-panel {
width: calc(100% - 32px);
}
.tableHeader td:last-child, .tableRow td:last-child {
width: 20%;
}
header a {
width: 768px;
}
header img {
margin-left: 40px;
}
}
@media (max-width: 592px) and (min-width: 320px) {
.center, .table-main {
width: 100%;
}
.left-panel {
display: none;
}
.main-panel {
width: 100%;
margin: 0;
padding: 28px 16px;
}
.tableHeader td:last-child, .tableRow td:last-child {
width: 25%;
}
header a {
width: auto;
}
.scroll-table-body {
top: 40px;
}
.tableRow {
padding: 8px 0;
}
}

View File

@ -1 +0,0 @@
<svg width="30" height="40" viewBox="0 0 30 40" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M0 3c0-1.65685 1.34315-3 3-3h20l7 7v30c0 1.6569-1.3431 3-3 3H3c-1.65685 0-3-1.3431-3-3V3z" fill="#27ABA3"/><path d="M23 0l7 7h-4c-1.6569 0-3-1.34315-3-3V0z" fill="#008078"/><path fill-rule="evenodd" clip-rule="evenodd" d="M24 14H6v7h18v-7zm-.8182 5.9978H6.81818V15H23.1818v4.9978zM24 23H6v7h18v-7zm-.8182 5.9978H6.81818V24H23.1818v4.9978z" fill="#fff"/></svg>

Before

Width:  |  Height:  |  Size: 463 B

View File

@ -0,0 +1,5 @@
<svg width="30" height="40" viewBox="0 0 30 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 3C0 1.34315 1.34315 0 3 0H23L30 7V37C30 38.6569 28.6569 40 27 40H3C1.34315 40 0 38.6569 0 37V3Z" fill="#9E1919"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.0062 15V16.0065H24V15H17.0062ZM17.0062 19V20.0163H24V19H17.0062ZM24 23H17.0062V24.0261H24V23ZM6 28V27.0293H24V28H6ZM7 16H14V23H7V16ZM6 15H7H14H15V16V23V24H14H7H6V23V16V15Z" fill="white"/>
<path d="M23 0L30 7H26C24.3431 7 23 5.65685 23 4V0Z" fill="black" fill-opacity="0.25"/>
</svg>

After

Width:  |  Height:  |  Size: 558 B

View File

@ -0,0 +1,3 @@
<svg width="20" height="17" viewBox="0 0 20 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 17V11H12V17H17V9H20L10 0L0 9H3V17H8Z" fill="#444444"/>
</svg>

After

Width:  |  Height:  |  Size: 173 B

View File

@ -0,0 +1 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M2 1h8l4 4v10H2V1z" fill="#fff"/><path fill-rule="evenodd" clip-rule="evenodd" d="M14 5l-4-4H2v14h12V5zm-4-5l5 5v11H1V0h9z" fill="#BFBFBF"/><path fill="#9E1919" d="M3 10h10v4H3z"/><path d="M7 7V2H3v5h4zM8 3V2h2v1H8zM8 5V4h5v1H8zM13 6H8v1h5V6zM13 8H3v1h10V8z" fill="#BFBFBF"/><path opacity=".3" d="M9 1h1v3h4l1 1H9V1z" fill="#333"/></svg>

After

Width:  |  Height:  |  Size: 441 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,5 @@
<svg width="24" height="23" viewBox="0 0 24 23" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5332 22.2243L0.632544 17.5921C-0.210848 17.1877 -0.210848 16.5627 0.632544 16.1951L4.07945 14.5775L10.4966 17.5921C11.34 17.9965 12.6967 17.9965 13.5034 17.5921L19.9206 14.5775L23.3675 16.1951C24.2108 16.5995 24.2108 17.2245 23.3675 17.5921L13.4668 22.2243C12.6967 22.592 11.34 22.592 10.5332 22.2243Z" fill="#FF6F3D"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5011 16.4922L0.630617 11.8546C-0.210206 11.4497 -0.210206 10.824 0.630617 10.456L3.99391 8.87329L10.5011 11.9282C11.342 12.3331 12.6946 12.3331 13.4989 11.9282L20.0061 8.87329L23.3694 10.456C24.2102 10.8608 24.2102 11.4865 23.3694 11.8546L13.4989 16.4922C12.658 16.897 11.3054 16.897 10.5011 16.4922Z" fill="#95C038"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5011 10.8195L0.630617 6.24863C-0.210206 5.84959 -0.210206 5.23289 0.630617 4.87013L10.5011 0.299281C11.342 -0.0997605 12.6946 -0.0997605 13.4989 0.299281L23.3694 4.87013C24.2102 5.26917 24.2102 5.88587 23.3694 6.24863L13.4989 10.8195C12.658 11.1822 11.3054 11.1822 10.5011 10.8195Z" fill="#5DC0E8"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,5 @@
<svg width="20" height="14" viewBox="0 0 20 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="20" height="2" rx="1" fill="white"/>
<rect y="6" width="20" height="2" rx="1" fill="white"/>
<rect y="12" width="20" height="2" rx="1" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 278 B

View File

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 17C13 17.5523 12.5523 18 12 18C11.4477 18 11 17.5523 11 17V13H7C6.44772 13 6 12.5523 6 12C6 11.4477 6.44772 11 7 11H11V7C11 6.44772 11.4477 6 12 6C12.5523 6 13 6.44772 13 7V11H17C17.5523 11 18 11.4477 18 12C18 12.5523 17.5523 13 17 13H13V17Z" fill="#444444"/>
</svg>

After

Width:  |  Height:  |  Size: 416 B

View File

@ -79,7 +79,8 @@
margin-left: 0; margin-left: 0;
} }
.tableRow { .tableRow,
menu.links {
width: 90%; width: 90%;
} }
@ -119,7 +120,8 @@
.contentCells-icon{ .contentCells-icon{
width: 5%; width: 5%;
} }
.tableRow { .tableRow,
menu.links {
width: 55%; width: 55%;
} }
@ -175,7 +177,8 @@
} }
@media (max-width: 715px) { @media (max-width: 715px) {
.tableRow { .tableRow,
menu.links {
width: 45%; width: 45%;
} }
} }
@ -263,7 +266,8 @@
padding-left: 0; padding-left: 0;
} }
.tableRow { .tableRow,
menu.links {
width: 75%; width: 75%;
} }
@ -293,7 +297,8 @@
width: 580px; width: 580px;
} }
.tableRow { .tableRow,
menu.links {
width: 95%; width: 95%;
} }
@ -407,7 +412,8 @@
padding: 16px 0 6px; padding: 16px 0 6px;
} }
.tableRow { .tableRow,
menu.links {
width: 40%; width: 40%;
} }
@ -502,6 +508,17 @@
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
} }
.buttonsMobile.indent {
margin-bottom: 0;
flex-wrap: nowrap;
}
.button.file-type:hover,
.button.file-type {
height: 28px;
width: 100px;
margin-bottom: 10px !important;
font-size: 9px;
}
.button.gray{ .button.gray{
margin: 0; margin: 0;
} }
@ -527,7 +544,8 @@
} }
@media (max-width: 510px) and (min-width: 470px) { @media (max-width: 510px) and (min-width: 470px) {
.tableRow { .tableRow,
menu.links {
width: 35%; width: 35%;
} }
@ -553,7 +571,8 @@
} }
@media (max-width: 470px) and (min-width: 420px) { @media (max-width: 470px) and (min-width: 420px) {
.tableRow { .tableRow,
menu.links {
width: 30%; width: 30%;
} }
.tableRow td:first-child{ .tableRow td:first-child{
@ -588,7 +607,8 @@
} }
@media (max-width: 420px) and (min-width: 320px) { @media (max-width: 420px) and (min-width: 320px) {
.tableRow { .tableRow,
menu.links {
width: 25%; width: 25%;
} }
@ -638,7 +658,8 @@
} }
} }
@media (max-width: 769px) and (min-width: 715px){ @media (max-width: 769px) and (min-width: 715px){
.tableRow{ .tableRow,
menu.links {
width: 50%; width: 50%;
} }
} }
@ -687,3 +708,216 @@
max-width: none; max-width: none;
} }
} }
@media (max-width: 592px) and (min-width: 320px) {
body.menu-open {
overflow: hidden;
}
header {
min-width: auto;
height: fit-content;
}
header a {
display: block;
}
header img {
margin: 0;
}
header, footer {
position: -webkit-sticky; /* Safari */
position: sticky;
top: 0;
z-index: 100;
}
.center {
width: 100%;
margin: 0;
}
.left-panel {
background-color: rgba(186, 186, 186, 0.6);
display: none;
flex-direction: row;
align-items: start;
max-width: none;
width: 100%;
margin: 0;
position: fixed;
left: 0;
height: calc(100% - 124px);
z-index:99;
}
.left-panel.active {
display: flex;
}
.help-block {
height: 100%;
margin: 0;
background-color: #F5F5F5;
width: 248px;
padding-left: 16px;
padding-top: 33px;
padding-bottom: 33px;
padding-right: 40px;
box-sizing: border-box;
overflow-y: auto;
overflow-x: hidden;
}
.table-main {
width: 100%;
}
.mobile-close-btn {
display: block;
width: 48px;
height: 48px;
background-color: #E2E2E2;
border-radius: 2px;
border-color: #E2E2E2;
color: #808080;
cursor: pointer;
outline: inherit;
border: none;
}
.main-panel {
width: 100%;
left: 0;
padding: 28px 16px;
}
#portal-info {
width: 100%;
max-width: fit-content;
}
menu.links {
width: 100%;
margin-top: 0;
padding: 0;
}
span.portal-name {
font-size: 16px;
}
span.portal-descr:first-child {
font-size: 13px;
}
span.portal-descr {
font-size: 12px;
}
.user-descr {
width: 100%;
max-width: none;
min-width: auto;
border-bottom: 1px solid #E5E5E5;
padding: 12px 0;
margin: 0;
cursor: pointer;
}
.user-descr ul {
display: none;
}
.user-descr ul.active {
display: block;
}
.user-descr b {
font-size: 13px;
display: flex;
align-items: center;
column-gap: 8px;
margin: 0;
}
.user-descr b::before {
content: url("images/plus.svg");
display: inline-block;
width: 24px;
height: 24px;
}
.storedHeader {
width: 100%;
}
.storedHeaderClearAll {
padding-right: 0;
}
.scroll-table-body {
top: 36px;
}
.scroll-table-body tr:first-child {
padding-top: 0;
}
.tableRow {
border-bottom: 1px solid #e5e5e5;
padding: 16px 0;
width: 100%;
}
.tableRow td:first-child {
width: 100%;
}
.stored-edit span {
font-size: 14px;
}
.header-list {
font-size: 16px;
}
.firstContentCellViewers {
border-bottom: none !important;
}
.firstContentCellViewers ~ td {
border-bottom: none !important;
}
.downloadContentCellShift:after {
display: none;
}
.main-nav {
display: none;
}
.responsive-nav {
height: 44px;
display: flex;
flex-direction: row;
margin: 0;
align-items: center;
column-gap: 16px;
padding: 10px 16px;
width: 100%;
box-sizing: border-box;
list-style: none;
}
.main {
height: calc(100% - 124px);
}
.user-block-table {
height: auto;
}
}

View File

@ -65,6 +65,14 @@ header img {
margin: 10px 0 22px 32px; margin: 10px 0 22px 32px;
} }
.responsive-nav {
display: none;
}
.mobile-close-btn {
display: none;
}
.center { .center {
position:relative; position:relative;
margin: 0 auto 0; margin: 0 auto 0;
@ -164,7 +172,7 @@ label .checkbox {
} }
.try-editor.form { .try-editor.form {
background-image: url("images/file_docxf.svg"); background-image: url("images/file_pdf.svg");
} }
.side-option { .side-option {
@ -230,6 +238,33 @@ label .checkbox {
color: #FF6F3D; color: #FF6F3D;
} }
.button.file-type {
font-size: 11px;
color: #FFFFFF;
padding: 8px 8px;
margin-right: 10px;
}
.button.file-type.disable {
cursor: default;
}
.button.file-type.pale {
opacity: 30%;
}
.button.file-type.document {
background: #446995;
}
.button.file-type.spreadsheet {
background: #40865C;
}
.button.file-type.presentation {
background: #AA5252;
}
.upload-panel { .upload-panel {
float: left; float: left;
padding: 24px 0; padding: 24px 0;
@ -271,6 +306,43 @@ label .checkbox {
border-bottom: 1px solid #D0D5DA; border-bottom: 1px solid #D0D5DA;
} }
.links {
display: flex;
padding: 0;
column-gap: 30px;
align-items: center;
list-style: none;
border-bottom: 1px solid #E2E2E2;
margin: 0;
margin-bottom: 24px;
}
.links li {
padding: 4px;
border-bottom: 2px solid transparent;
margin-bottom: -1px;
}
.links li.active {
border-bottom: 2px solid #FF6F3D;
}
.links li.active a {
color: #FF6F3D;
}
.links li.active a img {
filter: invert(55%) sepia(67%) saturate(2727%) hue-rotate(335deg) brightness(104%) contrast(101%);
}
.links a {
display: inline-block;
padding: 2px 0;
line-height: 20px;
font-size: 13px;
text-decoration: none;
}
#mainProgress { #mainProgress {
color: #333333; color: #333333;
display: none; display: none;
@ -464,6 +536,11 @@ footer table tr td:first-child {
background-image: url("images/icon_pptx.svg"); background-image: url("images/icon_pptx.svg");
} }
.stored-edit.pdf,
.uploadFileName.pdf {
background-image: url("images/icon_pdf.svg");
}
.stored-edit span { .stored-edit span {
font-size: 12px; font-size: 12px;
line-height: 12px; line-height: 12px;
@ -592,6 +669,29 @@ footer table tr td:first-child {
width: 4%; width: 4%;
} }
.storedHeader {
display: flex;
width: 100%;
justify-content: space-between;
align-items: center;
}
.storedHeaderClearAll {
padding-right: 52px;
}
.clear-all {
display: inline-block;
width: 100px;
padding: 2px;
outline: 1px solid #E5E5E5;
text-align: center;
cursor:pointer;
text-transform: uppercase;
background-color: #F5F5F5;
color: #666666;
}
.select-user { .select-user {
color: #444444; color: #444444;
font-family: Open Sans; font-family: Open Sans;
@ -741,6 +841,16 @@ html {
margin-left: 25px; margin-left: 25px;
} }
.buttonsMobile.indent{
padding-left: 35px;
margin-top: 10px;
margin-bottom: 10px;
}
.invisible {
display: none;
}
.tooltip { .tooltip {
background: #FFFFFF; background: #FFFFFF;
border-radius: 5px; border-radius: 5px;

View File

@ -17,10 +17,13 @@
*/ */
using System; using System;
using System.Collections.Generic;
using System.Collections;
using System.IO; using System.IO;
using System.Web.Mvc; using System.Web.Mvc;
using OnlineEditorsExampleMVC.Helpers; using OnlineEditorsExampleMVC.Helpers;
using OnlineEditorsExampleMVC.Models; using OnlineEditorsExampleMVC.Models;
using System.Web.Configuration;
namespace OnlineEditorsExampleMVC.Controllers namespace OnlineEditorsExampleMVC.Controllers
{ {
@ -31,6 +34,46 @@ namespace OnlineEditorsExampleMVC.Controllers
return View(); return View();
} }
public ActionResult Forgotten()
{
if (!bool.Parse(WebConfigurationManager.AppSettings["enable-forgotten"]))
{
ViewData["Message"] = "The forgotten page is disabled";
return View("~/Views/Shared/Error.aspx");
}
var files = new List<Dictionary<string, string>>();
try
{
var response = TrackManager.commandRequest("getForgottenList", null);
ArrayList keys = (ArrayList)response["keys"];
// fetch all the forgotten files from the document server
foreach (string key in keys)
{
var file = new Dictionary<string, string>();
var fileResult = TrackManager.commandRequest("getForgotten", key);
file.Add("key", fileResult["key"].ToString());
file.Add("url", fileResult["url"].ToString());
file.Add(
"type",
FileUtility.GetFileType(fileResult["url"].ToString())
.ToString()
.ToLower()
);
files.Add(file);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
return View("Forgotten", new ForgottenFilesModel(files));
}
// viewing file in the editor // viewing file in the editor
public ActionResult Editor(string fileName, string editorsMode, string editorsType, string directUrl) public ActionResult Editor(string fileName, string editorsMode, string editorsType, string directUrl)
{ {
@ -52,7 +95,7 @@ namespace OnlineEditorsExampleMVC.Controllers
var id = Request.Cookies.GetOrDefault("uid", null); var id = Request.Cookies.GetOrDefault("uid", null);
var user = Users.getUser(id); var user = Users.getUser(id);
DocManagerHelper.CreateMeta(fileName, user.id, user.name); // create meta information for the sample document DocManagerHelper.CreateMeta(fileName, user.id, user.name); // create meta information for the sample document
Response.Redirect(Url.Action("Editor", "Home", new { fileName = fileName })); Response.Redirect(Url.Action("Editor", "Home", new { fileName = fileName, editorsMode="edit" }));
return null; return null;
} }
} }

View File

@ -229,6 +229,10 @@ namespace OnlineEditorsExampleMVC.Helpers
switch (errorCode) switch (errorCode)
{ {
case -9:
// public const int c_nErrorConversionOutputFormatError = -9;
errorMessage = String.Format(errorMessageTemplate, "Error conversion output format");
break;
case -8: case -8:
// public const int c_nErrorFileVKey = -8; // public const int c_nErrorFileVKey = -8;
errorMessage = String.Format(errorMessageTemplate, "Error document VKey"); errorMessage = String.Format(errorMessageTemplate, "Error document VKey");

View File

@ -282,7 +282,7 @@ namespace OnlineEditorsExampleMVC.Helpers
} }
// create a command request // create a command request
public static void commandRequest(string method, string key, object meta = null) public static Dictionary<string, object> commandRequest(string method, string key, object meta = null)
{ {
DocManagerHelper.VerifySSL(); DocManagerHelper.VerifySSL();
@ -345,6 +345,7 @@ namespace OnlineEditorsExampleMVC.Helpers
{ {
throw new Exception(dataResponse); throw new Exception(dataResponse);
} }
return responseObj;
} }
// save file // save file

View File

@ -59,7 +59,9 @@ namespace OnlineEditorsExampleMVC.Helpers
"Cant print the file", "Cant print the file",
"Can create new files from the editor", "Can create new files from the editor",
"Can see the information about Group2 users", "Can see the information about Group2 users",
"Cant submit forms" "Cant submit forms",
"Can't close history",
"Can't restore the file version"
}; };
static List<string> descr_user_0 = new List<string>() static List<string> descr_user_0 = new List<string>()
@ -92,7 +94,8 @@ namespace OnlineEditorsExampleMVC.Helpers
new List<string>(), new List<string>(),
descr_user_1, descr_user_1,
true, true,
true true,
new Goback(null, false)
), ),
new User( new User(
"uid-2", "uid-2",
@ -111,7 +114,8 @@ namespace OnlineEditorsExampleMVC.Helpers
new List<string>(), new List<string>(),
descr_user_2, descr_user_2,
false, false,
true true,
new Goback("Go to Documents", null)
), ),
new User( new User(
"uid-3", "uid-3",
@ -130,7 +134,8 @@ namespace OnlineEditorsExampleMVC.Helpers
new List<string>() { "copy", "download", "print" }, new List<string>() { "copy", "download", "print" },
descr_user_3, descr_user_3,
false, false,
false false,
null
), ),
new User( new User(
"uid-0", "uid-0",
@ -144,7 +149,8 @@ namespace OnlineEditorsExampleMVC.Helpers
new List<string>() { "protect" }, new List<string>() { "protect" },
descr_user_0, descr_user_0,
false, false,
false false,
null
) )
}; };
@ -235,7 +241,9 @@ namespace OnlineEditorsExampleMVC.Helpers
public List<string> userInfoGroups; public List<string> userInfoGroups;
public bool avatar; public bool avatar;
public User(string id, string name, string email, string group, List<string> reviewGroups, Dictionary<string, object> commentGroups, List<string> userInfoGroups, bool? favorite, List<string> deniedPermissions, List<string> descriptions, bool templates, bool avatar) public Goback goback;
public User(string id, string name, string email, string group, List<string> reviewGroups, Dictionary<string, object> commentGroups, List<string> userInfoGroups, bool? favorite, List<string> deniedPermissions, List<string> descriptions, bool templates, bool avatar, Goback goback)
{ {
this.id = id; this.id = id;
this.name = name; this.name = name;
@ -249,6 +257,21 @@ namespace OnlineEditorsExampleMVC.Helpers
this.templates = templates; this.templates = templates;
this.userInfoGroups = userInfoGroups; this.userInfoGroups = userInfoGroups;
this.avatar = avatar; this.avatar = avatar;
this.goback = goback;
}
}
public class Goback
{
public string text;
public bool? blank;
public Goback(){}
public Goback(string text, bool? blank)
{
this.text = text;
this.blank = blank;
} }
} }
} }

View File

@ -77,18 +77,19 @@ namespace OnlineEditorsExampleMVC.Models
var jss = new JavaScriptSerializer(); var jss = new JavaScriptSerializer();
var ext = Path.GetExtension(FileName).ToLower(); // get file extension var ext = Path.GetExtension(FileName).ToLower(); // get file extension
var editorsMode = Mode ?? "edit"; // get editor mode var canFill = DocManagerHelper.FillFormExts.Contains(ext);
var editorsMode = Mode ?? (canFill ? "fillForms" : "edit"); // get editor mode
var canEdit = DocManagerHelper.EditedExts.Contains(ext); // check if the file with such an extension can be edited var canEdit = DocManagerHelper.EditedExts.Contains(ext); // check if the file with such an extension can be edited
var id = request.Cookies.GetOrDefault("uid", null); var id = request.Cookies.GetOrDefault("uid", null);
var user = Users.getUser(id); // get the user var user = Users.getUser(id); // get the user
if ((!canEdit && editorsMode.Equals("edit") || editorsMode.Equals("fillForms")) && DocManagerHelper.FillFormExts.Contains(ext)) { if ((!canEdit && editorsMode.Equals("edit") || editorsMode.Equals("fillForms")) && canFill) {
editorsMode = "fillForms"; editorsMode = "fillForms";
canEdit = true; canEdit = true;
} }
var submitForm = editorsMode.Equals("fillForms") && id.Equals("uid-1"); // check if the Submit form button is displayed or not var submitForm = (editorsMode.Equals("fillForms") || editorsMode.Equals("embedded")) && user.id.Equals("uid-1"); // check if the Submit form button is displayed or not
var mode = canEdit && editorsMode != "view" ? "edit" : "view"; // set the mode parameter: change it to view if the document can't be edited var mode = canEdit && editorsMode != "view" ? "edit" : "view"; // set the mode parameter: change it to view if the document can't be edited
// favorite icon state // favorite icon state
@ -214,10 +215,12 @@ namespace OnlineEditorsExampleMVC.Models
{ "forcesave", false }, // adds the request for the forced file saving to the callback handler { "forcesave", false }, // adds the request for the forced file saving to the callback handler
{ "submitForm", submitForm }, // if the Submit form button is displayed or not { "submitForm", submitForm }, // if the Submit form button is displayed or not
{ {
"goback", new Dictionary<string, object> // settings for the Open file location menu button and upper right corner button "goback", user.goback != null ? new Dictionary<string, object> // settings for the Open file location menu button and upper right corner button
{ {
{ "url", DocManagerHelper.GetServerUrl(false) } // 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
} { "text", user.goback.text },
{ "blank", user.goback.blank }
} : new Dictionary<string, object>{}
} }
} }
} }
@ -289,20 +292,20 @@ namespace OnlineEditorsExampleMVC.Models
{ {
Path = HttpRuntime.AppDomainAppVirtualPath Path = HttpRuntime.AppDomainAppVirtualPath
+ (HttpRuntime.AppDomainAppVirtualPath.EndsWith("/") ? "" : "/") + (HttpRuntime.AppDomainAppVirtualPath.EndsWith("/") ? "" : "/")
+ "Content\\images\\logo.png" + "Content\\images\\logo.svg"
}; };
var directMailMergeUrl = new UriBuilder(DocManagerHelper.GetServerUrl(false)) var directMailMergeUrl = new UriBuilder(DocManagerHelper.GetServerUrl(false))
{ {
Path = HttpRuntime.AppDomainAppVirtualPath Path = HttpRuntime.AppDomainAppVirtualPath
+ (HttpRuntime.AppDomainAppVirtualPath.EndsWith("/") ? "" : "/") + (HttpRuntime.AppDomainAppVirtualPath.EndsWith("/") ? "" : "/")
+ "Content\\images\\logo.png" + "Content\\images\\logo.svg"
}; };
// create a logo config // create a logo config
var logoConfig = new Dictionary<string, object> var logoConfig = new Dictionary<string, object>
{ {
{ "fileType", "png"}, { "fileType", "svg"},
{ "url", mailMergeUrl.ToString()} { "url", mailMergeUrl.ToString()}
}; };

View File

@ -33,7 +33,8 @@ namespace OnlineEditorsExampleMVC.Models
{ {
Word, Word,
Cell, Cell,
Slide Slide,
Pdf
} }
// get file type // get file type
@ -41,6 +42,7 @@ namespace OnlineEditorsExampleMVC.Models
{ {
var ext = Path.GetExtension(fileName).ToLower(); var ext = Path.GetExtension(fileName).ToLower();
if (FormatManager.PdfExtensions().Contains(ext)) return FileType.Pdf; // pdf type for document extensions
if (FormatManager.DocumentExtensions().Contains(ext)) return FileType.Word; // word type for document extensions if (FormatManager.DocumentExtensions().Contains(ext)) return FileType.Word; // word type for document extensions
if (FormatManager.SpreadsheetExtensions().Contains(ext)) return FileType.Cell; // cell type for spreadsheet extensions if (FormatManager.SpreadsheetExtensions().Contains(ext)) return FileType.Cell; // cell type for spreadsheet extensions
if (FormatManager.PresentationExtensions().Contains(ext)) return FileType.Slide; // slide type for presentation extensions if (FormatManager.PresentationExtensions().Contains(ext)) return FileType.Slide; // slide type for presentation extensions
@ -185,6 +187,20 @@ namespace OnlineEditorsExampleMVC.Models
.ToList(); .ToList();
} }
public static List<string> PdfExtensions()
{
return Pdfs()
.Select(format => format.Extension())
.ToList();
}
public static List<Format> Pdfs()
{
return All()
.Where(format => format.Type == FileType.Pdf)
.ToList();
}
public static List<string> AllExtensions() public static List<string> AllExtensions()
{ {
return All() return All()

View File

@ -1,4 +1,4 @@
/** /**
* *
* (c) Copyright Ascensio System SIA 2024 * (c) Copyright Ascensio System SIA 2024
* *
@ -16,16 +16,15 @@
* *
*/ */
package com.onlyoffice.integration.dto; using System.Collections.Generic;
import lombok.AllArgsConstructor; namespace OnlineEditorsExampleMVC.Models
import lombok.Data; {
import lombok.NoArgsConstructor; // create file model
public class ForgottenFilesModel
{
public List<Dictionary<string, string>> files { get; set; }
@Data public ForgottenFilesModel(List<Dictionary<string, string>> files) { this.files = files; }
@AllArgsConstructor }
@NoArgsConstructor }
public class ChangesHistory {
private String created;
private ChangesUser user;
}

View File

@ -116,6 +116,7 @@
<Compile Include="Helpers\Utils.cs" /> <Compile Include="Helpers\Utils.cs" />
<Compile Include="Models\FileModel.cs" /> <Compile Include="Models\FileModel.cs" />
<Compile Include="Models\FileUtility.cs" /> <Compile Include="Models\FileUtility.cs" />
<Compile Include="Models\ForgottenFilesModel.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="WebEditor.ashx.cs"> <Compile Include="WebEditor.ashx.cs">
<DependentUpon>WebEditor.ashx</DependentUpon> <DependentUpon>WebEditor.ashx</DependentUpon>
@ -123,6 +124,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Content\editor.css" /> <Content Include="Content\editor.css" />
<Content Include="Content\forgotten.css" />
<Content Include="Content\images\block-content.svg" /> <Content Include="Content\images\block-content.svg" />
<Content Include="Content\images\cell.ico" /> <Content Include="Content\images\cell.ico" />
<Content Include="Content\images\close.svg" /> <Content Include="Content\images\close.svg" />
@ -140,18 +142,20 @@
<Content Include="Content\images\file_xlsx.svg" /> <Content Include="Content\images\file_xlsx.svg" />
<Content Include="Content\images\fill-forms.svg" /> <Content Include="Content\images\fill-forms.svg" />
<Content Include="Content\images\filter.svg" /> <Content Include="Content\images\filter.svg" />
<Content Include="Content\images\home.svg" />
<Content Include="Content\images\icon_docx.svg" /> <Content Include="Content\images\icon_docx.svg" />
<Content Include="Content\images\icon_pptx.svg" /> <Content Include="Content\images\icon_pptx.svg" />
<Content Include="Content\images\icon_xlsx.svg" /> <Content Include="Content\images\icon_xlsx.svg" />
<Content Include="Content\images\info.svg" /> <Content Include="Content\images\info.svg" />
<Content Include="Content\images\loader16.gif" /> <Content Include="Content\images\loader16.gif" />
<Content Include="Content\images\logo.png" />
<Content Include="Content\images\logo.svg" /> <Content Include="Content\images\logo.svg" />
<Content Include="Content\images\mobile-fill-forms.svg" /> <Content Include="Content\images\mobile-fill-forms.svg" />
<Content Include="Content\images\mobile.svg" /> <Content Include="Content\images\mobile.svg" />
<Content Include="Content\images\notdone.svg" /> <Content Include="Content\images\notdone.svg" />
<Content Include="Content\images\review.svg" /> <Content Include="Content\images\review.svg" />
<Content Include="Content\images\slide.ico" /> <Content Include="Content\images\slide.ico" />
<Content Include="Content\images\uid-1.png" />
<Content Include="Content\images\uid-2.png" />
<Content Include="Content\images\word.ico" /> <Content Include="Content\images\word.ico" />
<Content Include="Content\jquery-ui.css" /> <Content Include="Content\jquery-ui.css" />
<Content Include="Content\media.css" /> <Content Include="Content\media.css" />
@ -159,6 +163,10 @@
<Content Include="favicon.ico" /> <Content Include="favicon.ico" />
<Content Include="Global.asax" /> <Content Include="Global.asax" />
<Content Include="LICENSE" /> <Content Include="LICENSE" />
<Content Include="Scripts\forgotten.js" />
<Content Include="Scripts\formats.js" />
<Content Include="Scripts\jquery-3.6.4.js" />
<Content Include="Scripts\jquery-migrate-3.4.1.js" />
<Content Include="Scripts\jquery-ui.js" /> <Content Include="Scripts\jquery-ui.js" />
<Content Include="Scripts\jquery.blockUI.js" /> <Content Include="Scripts\jquery.blockUI.js" />
<Content Include="Scripts\jquery.dropdownToggle.js" /> <Content Include="Scripts\jquery.dropdownToggle.js" />
@ -166,17 +174,13 @@
<Content Include="Scripts\jquery.iframe-transport.js" /> <Content Include="Scripts\jquery.iframe-transport.js" />
<Content Include="Scripts\jscript.js" /> <Content Include="Scripts\jscript.js" />
<Content Include="Views\Home\Editor.aspx" /> <Content Include="Views\Home\Editor.aspx" />
<Content Include="Views\Home\Forgotten.aspx" />
<Content Include="Views\Home\Index.aspx" /> <Content Include="Views\Home\Index.aspx" />
<Content Include="Web.config"> <Content Include="Web.config">
<SubType>Designer</SubType> <SubType>Designer</SubType>
</Content> </Content>
<Content Include="Views\Web.config" /> <Content Include="Views\Web.config" />
<Content Include="Views\Shared\Error.aspx" /> <Content Include="Views\Shared\Error.aspx" />
<Content Include="licenses\EntityFramework.license" />
<Content Include="licenses\Microsoft.Web.Infrastructure.license" />
<Content Include="licenses\Newtonsoft.Json.license" />
<Content Include="licenses\jquery.license" />
<Content Include="licenses\WebGrease.license" />
<Content Include="web.appsettings.config" /> <Content Include="web.appsettings.config" />
<Content Include="WebEditor.ashx" /> <Content Include="WebEditor.ashx" />
</ItemGroup> </ItemGroup>

View File

@ -0,0 +1,19 @@
function deleteFile(event) {
let filename = event.currentTarget.getAttribute("data");
filename = encodeURIComponent(filename);
let url = `webeditor.ashx?type=removeforgotten&filename=${filename}`;
fetch(url, {
headers: {
"Content-Type": "application/json",
}
}).then(result => {
if(result.status == 204) {
document.location.reload(true);
}
});
}
document.querySelectorAll('.delete-file').forEach(el => {
el.addEventListener('click', deleteFile);
});

View File

@ -0,0 +1,66 @@
/**
*
* (c) Copyright Ascensio System SIA 2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
class Format {
constructor(name, type, actions, convert, mime) {
this.name = name;
this.type = type;
this.actions = actions;
this.convert = convert;
this.mime = mime;
}
isAutoConvertible() {
return this.actions.includes('auto-convert');
}
isEditable() {
return this.actions.includes('edit') || this.actions.includes('lossy-edit');
}
isFillable() {
return this.actions.includes('fill');
}
}
class FormatManager {
formats = [];
constructor(formats) {
if(Array.isArray(formats)) this.formats = formats;
}
findByExtension(extension) {
return this.formats.find(format => format.name == extension);
}
isAutoConvertible(extension) {
let format = this.findByExtension(extension);
return format !== undefined && format.isAutoConvertible();
}
isEditable(extension) {
let format = this.findByExtension(extension);
return format !== undefined && format.isEditable();
}
isFillable(extension) {
let format = this.findByExtension(extension);
return format !== undefined && format.isFillable();
}
}

View File

@ -17,6 +17,27 @@
*/ */
var directUrl; var directUrl;
var formatManager;
window.onload = function () {
fetch("webeditor.ashx?type=formats")
.then((response) => response.json())
.then((data) => {
if (data.formats) {
let formats = [];
data.formats.forEach(format => {
formats.push(new Format(
format.Name,
format.Type,
format.Actions,
format.Convert,
format.Mime
));
});
formatManager = new FormatManager(formats);
}
})
}
if (typeof jQuery != "undefined") { if (typeof jQuery != "undefined") {
jq = jQuery.noConflict(); jq = jQuery.noConflict();
@ -87,8 +108,8 @@ if (typeof jQuery != "undefined") {
}); });
var timer = null; var timer = null;
var checkConvert = function (filePass) { var checkConvert = function (filePass, fileType) {
filePass = filePass ? filePass : null; filePass = filePass ? filePass : null;
if (timer != null) { if (timer != null) {
clearTimeout(timer); clearTimeout(timer);
} }
@ -100,10 +121,10 @@ if (typeof jQuery != "undefined") {
jq("#filePass").val(""); jq("#filePass").val("");
var fileName = jq("#hiddenFileName").val(); var fileName = jq("#hiddenFileName").val();
var posExt = fileName.lastIndexOf('.'); var posExt = fileName.lastIndexOf('.') + 1;
posExt = 0 <= posExt ? fileName.substring(posExt).trim().toLowerCase() : ''; posExt = 0 <= posExt ? fileName.substring(posExt).trim().toLowerCase() : '';
if (ConverExtList.indexOf(posExt) == -1) { if (!formatManager.isAutoConvertible(posExt)) {
jq("#step2").addClass("done").removeClass("current"); jq("#step2").addClass("done").removeClass("current");
loadScripts(); loadScripts();
return; return;
@ -116,7 +137,7 @@ if (typeof jQuery != "undefined") {
contentType: "text/xml", contentType: "text/xml",
type: "post", type: "post",
dataType: "json", dataType: "json",
data: JSON.stringify({ filename: fileName, filePass: filePass }), data: JSON.stringify({ filename: fileName, filePass: filePass, fileExt: fileType }),
url: UrlConverter, url: UrlConverter,
complete: function (data) { complete: function (data) {
var responseText = data.responseText; var responseText = data.responseText;
@ -132,6 +153,12 @@ if (typeof jQuery != "undefined") {
} }
return; return;
} else { } else {
if (response.error.includes("Error conversion output format")) {
jq("#select-file-type").removeClass("invisible");
jq("#step2").removeClass("current");
jq("#hiddenFileName").attr("placeholder", filePass);
return;
}
jq(".current").removeClass("current"); jq(".current").removeClass("current");
jq(".step:not(.done)").addClass("error"); jq(".step:not(.done)").addClass("error");
jq("#mainProgress .error-message").show().find("span").text(response.error); jq("#mainProgress .error-message").show().find("span").text(response.error);
@ -143,7 +170,7 @@ if (typeof jQuery != "undefined") {
jq("#hiddenFileName").val(response.filename); jq("#hiddenFileName").val(response.filename);
if (response.step && response.step < 100) { if (response.step && response.step < 100) {
checkConvert(filePass); checkConvert(filePass, fileType);
} else { } else {
jq("#step2").addClass("done").removeClass("current"); jq("#step2").addClass("done").removeClass("current");
loadScripts(); loadScripts();
@ -178,10 +205,10 @@ if (typeof jQuery != "undefined") {
jq("#beginView, #beginEmbedded").removeClass("disable"); jq("#beginView, #beginEmbedded").removeClass("disable");
var fileName = jq("#hiddenFileName").val(); var fileName = jq("#hiddenFileName").val();
var posExt = fileName.lastIndexOf('.'); var posExt = fileName.lastIndexOf('.') + 1;
posExt = 0 <= posExt ? fileName.substring(posExt).trim().toLowerCase() : ''; posExt = 0 <= posExt ? fileName.substring(posExt).trim().toLowerCase() : '';
if (EditedExtList.indexOf(posExt) != -1 || FillExtList.indexOf(posExt) != -1) { if (formatManager.isEditable(posExt) || formatManager.isFillable(posExt)) {
jq("#beginEdit").removeClass("disable"); jq("#beginEdit").removeClass("disable");
} }
}; };
@ -213,6 +240,15 @@ if (typeof jQuery != "undefined") {
}); });
}; };
jq(document).on("click", ".file-type:not(.disable)", function () {
const currentElement = jq(this);
var fileType = currentElement.attr("data");
var filePass = jq("#hiddenFileName").attr("placeholder");
jq('.file-type').addClass(["disable", "pale"]);
currentElement.removeClass("pale");
checkConvert(filePass, fileType);
});
jq(document).on("click", "#enterPass", function () { jq(document).on("click", "#enterPass", function () {
var filePass = jq("#filePass").val(); var filePass = jq("#filePass").val();
if (filePass) { if (filePass) {
@ -294,6 +330,23 @@ if (typeof jQuery != "undefined") {
}); });
}); });
jq(document).on("click", ".clear-all", function () {
if (confirm("Delete all the files?")) {
var requestAddress = "webeditor.ashx"
+ "?type=remove";
jq.ajax({
async: true,
contentType: "text/xml",
url: requestAddress,
complete: function (data) {
if (JSON.parse(data.responseText).success) {
window.location.reload(true);
}
}
});
}
});
function showUserTooltip (isMobile) { function showUserTooltip (isMobile) {
if ( jq("div#portal-info").is(":hidden") ) { if ( jq("div#portal-info").is(":hidden") ) {
jq("div#portal-info").show(); jq("div#portal-info").show();
@ -368,4 +421,27 @@ if (typeof jQuery != "undefined") {
}).mouseout(function () { }).mouseout(function () {
jq("div.tooltip").remove(); jq("div.tooltip").remove();
}); });
} }
function toggleSidePanel(event) {
event.preventDefault();
let sidePanel = document.querySelector(".left-panel");
let body = document.querySelector("body");
if (sidePanel.classList.contains("active")) {
sidePanel.classList.remove("active");
body.classList.remove("menu-open");
} else {
sidePanel.classList.add("active")
body.classList.add("menu-open");
}
}
function toggleUserDescr(event) {
let list = event.currentTarget.querySelector("ul");
let cursor = window.getComputedStyle(event.currentTarget).getPropertyValue("cursor");
if (cursor === "pointer") {
if (list.classList.contains("active")) list.classList.remove("active");
else list.classList.add("active");
}
}

View File

@ -108,7 +108,7 @@
// the meta information of the document is changed via the meta command // the meta information of the document is changed via the meta command
var onMetaChange = function (event) { var onMetaChange = function (event) {
if (event.data.favorite) { if (event.data.favorite !== undefined) {
var favorite = !!event.data.favorite; var favorite = !!event.data.favorite;
var title = document.title.replace(/^\☆/g, ""); var title = document.title.replace(/^\☆/g, "");
document.title = (favorite ? "☆" : "") + title; document.title = (favorite ? "☆" : "") + title;
@ -259,6 +259,48 @@
} }
} }
<% string usersForMentions; %>
<% Model.GetUsersMentions(Request, out usersForMentions); %>
<% string usersInfo; %>
<% Model.GetUsersInfo(Request, out usersInfo); %>
<% string usersForProtect; %>
<% Model.GetUsersProtect(Request, out usersForProtect); %>
var onRequestUsers = function (event) {
if (event && event.data){
var c = event.data.c;
}
switch (c) {
case "info":
users = [];
var allUsers = <%= usersInfo %>;
for (var i = 0; i < event.data.id.length; i++) {
for (var j = 0; j < allUsers.length; j++) {
if (allUsers[j].id == event.data.id[i]) {
users.push(allUsers[j]);
break;
}
}
}
break;
case "protect":
var users = <%= usersForProtect %>;
break;
default:
users = <%= usersForMentions %>;
}
docEditor.setUsers({
"c": c,
"users": users,
});
};
var onRequestSendNotify = function (event) {
event.data.actionLink = replaceActionLink(location.href, JSON.stringify(event.data.actionLink));
var data = JSON.stringify(event.data);
innerAlert("onRequestSendNotify: " + data);
};
config = <%= Model.GetDocConfig(Request, Url) %>; config = <%= Model.GetDocConfig(Request, Url) %>;
config.width = "100%"; config.width = "100%";
@ -276,63 +318,28 @@
"onRequestSelectSpreadsheet": onRequestSelectSpreadsheet, "onRequestSelectSpreadsheet": onRequestSelectSpreadsheet,
}; };
<% string usersForMentions; %>
<% Model.GetUsersMentions(Request, out usersForMentions); %>
<% string usersInfo; %>
<% Model.GetUsersInfo(Request, out usersInfo); %>
<% string usersForProtect; %>
<% Model.GetUsersProtect(Request, out usersForProtect); %>
if (config.editorConfig.user.id) { if (config.editorConfig.user.id) {
// the user is trying to show the document version history // the user is trying to show the document version history
config.events['onRequestHistory'] = onRequestHistory; config.events['onRequestHistory'] = onRequestHistory;
// the user is trying to click the specific document version in the document version history // the user is trying to click the specific document version in the document version history
config.events['onRequestHistoryData'] = onRequestHistoryData; config.events['onRequestHistoryData'] = onRequestHistoryData;
// the user is trying to go back to the document from viewing the document version history // the user is trying to go back to the document from viewing the document version history
config.events['onRequestHistoryClose'] = function () { if (config.editorConfig.user.id !== "uid-3") {
document.location.reload(); config.events['onRequestHistoryClose'] = function () {
}; document.location.reload();
config.events['onRequestRestore'] = onRequestRestore; };
config.events['onRequestRestore'] = onRequestRestore;
}
// add mentions for not anonymous users // add mentions for not anonymous users
<% if (!string.IsNullOrEmpty(usersForMentions)) <% if (!string.IsNullOrEmpty(usersForMentions))
{ %> { %>
config.events['onRequestUsers'] = function (event) { config.events['onRequestUsers'] = onRequestUsers;
if (event && event.data){
var c = event.data.c;
}
switch (c) {
case "info":
users = [];
var allUsers = <%= usersInfo %>;
for (var i = 0; i < event.data.id.length; i++) {
for (var j = 0; j < allUsers.length; j++) {
if (allUsers[j].id == event.data.id[i]) {
users.push(allUsers[j]);
break;
}
}
}
break;
case "protect":
var users = <%= usersForProtect %>;
break;
default:
users = <%= usersForMentions %>;
}
docEditor.setUsers({
"c": c,
"users": users,
});
};
<% } %> <% } %>
config.events['onRequestSaveAs'] = onRequestSaveAs;
// the user is mentioned in a comment // the user is mentioned in a comment
config.events['onRequestSendNotify'] = function (event) { config.events['onRequestSendNotify'] = onRequestSendNotify;
event.data.actionLink = replaceActionLink(location.href, JSON.stringify(event.data.actionLink));
var data = JSON.stringify(event.data);
innerAlert("onRequestSendNotify: " + data);
};
// prevent file renaming for anonymous users // prevent file renaming for anonymous users
config.events['onRequestRename'] = onRequestRename; config.events['onRequestRename'] = onRequestRename;
config.events['onRequestReferenceData'] = onRequestReferenceData; config.events['onRequestReferenceData'] = onRequestReferenceData;
@ -341,17 +348,7 @@
config.events['onRequestOpen'] = onRequestOpen; config.events['onRequestOpen'] = onRequestOpen;
} }
if (config.editorConfig.createUrl) {
config.events.onRequestSaveAs = onRequestSaveAs;
};
var сonnectEditor = function () { var сonnectEditor = function () {
if ((config.document.fileType === "docxf" || config.document.fileType === "oform")
&& DocsAPI.DocEditor.version().split(".")[0] < 7) {
innerAlert("Please update ONLYOFFICE Docs to version 7.0 to work on fillable forms online.");
return;
}
docEditor = new DocsAPI.DocEditor("iframeEditor", config); docEditor = new DocsAPI.DocEditor("iframeEditor", config);
}; };

View File

@ -0,0 +1,144 @@
<%@ Page Title="ONLYOFFICE" Language="C#" Inherits="System.Web.Mvc.ViewPage<OnlineEditorsExampleMVC.Models.ForgottenFilesModel>" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Web.Configuration" %>
<%@ Import Namespace="OnlineEditorsExampleMVC.Helpers" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width" />
<meta name="server-version" content=<%= DocManagerHelper.GetVersion() %> />
<!--
*
* (c) Copyright Ascensio System SIA 2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
-->
<title>ONLYOFFICE</title>
<link href="<%: Url.Content("~/favicon.ico") %>" rel="shortcut icon" type="image/x-icon" />
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Open+Sans:900,800,700,600,500,400,300&subset=latin,cyrillic-ext,cyrillic,latin-ext" />
<%: Styles.Render("~/Content/css") %>
<%: Styles.Render("~/Content/forgotten") %>
</head>
<body>
<header>
<div class="center main-nav">
<a href="./">
<img src ="content/images/logo.svg" alt="ONLYOFFICE" />
</a>
</div>
<menu class="responsive-nav">
<li>
<a href="#" onclick="toggleSidePanel(event)">
<img src="content/images/mobile-menu.svg" alt="ONLYOFFICE" />
</a>
</li>
<li>
<a href="./">
<img src ="content/images/mobile-logo.svg" alt="ONLYOFFICE" />
</a>
</li>
</menu>
</header>
<div class="center main">
<table class="table-main">
<tbody>
<tr>
<td class="left-panel section"></td>
<td class="section">
<div class="main-panel">
<menu class="links">
<li class="home-link" >
<a href="./">
<img src="content/images/home.svg" alt="Home"/>
</a>
</li>
<li class="active">
<a href="/Forgotten">Forgotten files</a>
</li>
</menu>
<div class="stored-list">
<div class="storedHeader">
<div class="storedHeaderText">
<span class="header-list">Forgotten files</span>
</div>
</div>
<table class="tableHeader" cellspacing="0" cellpadding="0" width="100%">
<thead>
<tr>
<td class="tableHeaderCell">Filename</td>
<td class="tableHeaderCell">Action</td>
</tr>
</thead>
</table>
<div class="scroll-table-body">
<table cellspacing="0" cellpadding="0" width="100%">
<tbody>
<% foreach (var file in Model.files) { %>
<tr class="tableRow" title="<%= file["key"] %>">
<td>
<a class="stored-edit action-link <%= file["type"] %>" href="<%= file["url"] %>" target="_blank">
<span><%= file["key"] %></span>
</a>
</td>
<td>
<a href="<%= file["url"] %>">
<img class="icon-download" src="content/images/download.svg" alt="Download" title="Download" /></a>
<a class="delete-file" data="<%= file["key"] %>">
<img class="icon-action" src="content/images/delete.svg" alt="Delete" title="Delete" /></a>
</td>
</tr>
<% } %>
</tbody>
</table>
</div>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<footer>
<div class="center">
<table>
<tbody>
<tr>
<td>
<a href="http://api.onlyoffice.com/editors/howitworks" target="_blank">API Documentation</a>
</td>
<td>
<a href="mailto:sales@onlyoffice.com">Submit your request</a>
</td>
<td class="copy">
&copy; Ascensio System SIA <%= DateTime.Now.Year.ToString() %>. All rights reserved.
</td>
</tr>
</tbody>
</table>
</div>
</footer>
<%: Scripts.Render("~/bundles/jquery", "~/bundles/forgotten") %>
</body>
</html>

View File

@ -41,11 +41,23 @@
</head> </head>
<body> <body>
<header> <header>
<div class="center"> <div class="center main-nav">
<a href=""> <a href="./">
<img src ="content/images/logo.svg" alt="ONLYOFFICE" /> <img src ="content/images/logo.svg" alt="ONLYOFFICE" />
</a> </a>
</div> </div>
<menu class="responsive-nav">
<li>
<a href="#" onclick="toggleSidePanel(event)">
<img src="content/images/mobile-menu.svg" alt="ONLYOFFICE" />
</a>
</li>
<li>
<a href="./">
<img src ="content/images/mobile-logo.svg" alt="ONLYOFFICE" />
</a>
</li>
</menu>
</header> </header>
<div class="center main"> <div class="center main">
@ -68,7 +80,7 @@
<a class="try-editor slide" data-type="pptx">Presentation</a> <a class="try-editor slide" data-type="pptx">Presentation</a>
</li> </li>
<li> <li>
<a class="try-editor form" data-type="docxf">PDF form</a> <a class="try-editor form" data-type="pdf">PDF form</a>
</li> </li>
</ul> </ul>
<label class="side-option"> <label class="side-option">
@ -122,9 +134,24 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<button class="mobile-close-btn" onclick="toggleSidePanel(event)">
<img src="content/images/close.svg" alt="">
</button>
</td> </td>
<td class="section"> <td class="section">
<div class="main-panel"> <div class="main-panel">
<menu class="links">
<li class="home-link active" >
<a href="./">
<img src="content/images/home.svg" alt="Home"/>
</a>
</li>
<% if (bool.Parse(WebConfigurationManager.AppSettings["enable-forgotten"])) { %>
<li>
<a href="/Forgotten">Forgotten files</a>
</li>
<% } %>
</menu>
<% var storedFiles = DocManagerHelper.GetStoredFiles(); %> <% var storedFiles = DocManagerHelper.GetStoredFiles(); %>
<div id="portal-info" style="display: <%= storedFiles.Any() ? "none" : "table-cell" %>"> <div id="portal-info" style="display: <%= storedFiles.Any() ? "none" : "table-cell" %>">
<span class="portal-name">ONLYOFFICE Document Editors Welcome!</span> <span class="portal-name">ONLYOFFICE Document Editors Welcome!</span>
@ -136,7 +163,7 @@
<span class="portal-descr">You can open the same document using different users in different Web browser sessions, so you can check out multi-user editing functions.</span> <span class="portal-descr">You can open the same document using different users in different Web browser sessions, so you can check out multi-user editing functions.</span>
<% foreach (User user in Users.getAllUsers()) <% foreach (User user in Users.getAllUsers())
{ %> { %>
<div class="user-descr"> <div class="user-descr" onclick="toggleUserDescr(event)">
<b><%= user.name.IsEmpty() ? "Anonymous" : user.name %></b> <b><%= user.name.IsEmpty() ? "Anonymous" : user.name %></b>
<ul> <ul>
<% foreach (string description in user.descriptions) <% foreach (string description in user.descriptions)
@ -151,7 +178,14 @@
if (storedFiles.Any()) if (storedFiles.Any())
{ %> { %>
<div class="stored-list"> <div class="stored-list">
<span class="header-list">Your documents</span> <div class="storedHeader">
<div class="storedHeaderText">
<span class="header-list">Your documents</span>
</div>
<div class="storedHeaderClearAll">
<div class="clear-all">Clear all</div>
</div>
</div>
<table class="tableHeader" cellspacing="0" cellpadding="0" width="100%"> <table class="tableHeader" cellspacing="0" cellpadding="0" width="100%">
<thead> <thead>
<tr> <tr>
@ -193,11 +227,13 @@
<img src="content/images/mobile.svg" alt="Open in editor for mobile devices" title="Open in editor for mobile devices"/> <img src="content/images/mobile.svg" alt="Open in editor for mobile devices" title="Open in editor for mobile devices"/>
</a> </a>
</td> </td>
<td class="contentCells contentCells-icon"> <% if (docType != "pdf") { %>
<a href="<%= Url.Action("Editor", "Home", new { fileName = storedFile.Name, editorsType = "desktop", editorsMode = "comment", directUrl = isEnabledDirectUrl }) %>" target="_blank"> <td class="contentCells contentCells-icon">
<img src="content/images/comment.svg" alt="Open in editor for comment" title="Open in editor for comment"/> <a href="<%= Url.Action("Editor", "Home", new { fileName = storedFile.Name, editorsType = "desktop", editorsMode = "comment", directUrl = isEnabledDirectUrl }) %>" target="_blank">
</a> <img src="content/images/comment.svg" alt="Open in editor for comment" title="Open in editor for comment"/>
</td> </a>
</td>
<% } %>
<% if (docType == "word") { %> <% if (docType == "word") { %>
<td class="contentCells contentCells-icon"> <td class="contentCells contentCells-icon">
<a href="<%= Url.Action("Editor", "Home", new { fileName = storedFile.Name, editorsType = "desktop", editorsMode = "review", directUrl = isEnabledDirectUrl }) %>" target="_blank"> <a href="<%= Url.Action("Editor", "Home", new { fileName = storedFile.Name, editorsType = "desktop", editorsMode = "review", directUrl = isEnabledDirectUrl }) %>" target="_blank">
@ -295,6 +331,15 @@
<div class="describeUpload">After these steps are completed, you can work with your document.</div> <div class="describeUpload">After these steps are completed, you can work with your document.</div>
<span id="step1" class="step">1. Loading the file.</span> <span id="step1" class="step">1. Loading the file.</span>
<span class="step-descr">The loading speed depends on file size and additional elements it contains.</span> <span class="step-descr">The loading speed depends on file size and additional elements it contains.</span>
<div id="select-file-type" class="invisible">
<br />
<span class="step">Please select the current document type</span>
<div class="buttonsMobile indent">
<div class="button file-type document" data="docx">Document</div>
<div class="button file-type spreadsheet" data="xlsx">Spreadsheet</div>
<div class="button file-type presentation" data="pptx">Presentation</div>
</div>
</div>
<br /> <br />
<span id="step2" class="step">2. Conversion.</span> <span id="step2" class="step">2. Conversion.</span>
<span class="step-descr">The file is converted to OOXML so that you can edit it.</span> <span class="step-descr">The file is converted to OOXML so that you can edit it.</span>
@ -357,9 +402,6 @@
<%: Scripts.Render("~/bundles/jquery", "~/bundles/scripts") %> <%: Scripts.Render("~/bundles/jquery", "~/bundles/scripts") %>
<script language="javascript" type="text/javascript"> <script language="javascript" type="text/javascript">
var FillExtList = '<%= string.Join(",", DocManagerHelper.FillFormExts.ToArray()) %>';
var ConverExtList = '<%= string.Join(",", DocManagerHelper.ConvertExts.ToArray()) %>';
var EditedExtList = '<%= string.Join(",", DocManagerHelper.EditedExts.ToArray()) %>';
var UrlConverter = '<%= Url.Content("~/webeditor.ashx?type=convert") %>'; var UrlConverter = '<%= Url.Content("~/webeditor.ashx?type=convert") %>';
var UrlEditor = '<%= Url.Action("editor", "Home") %>'; var UrlEditor = '<%= Url.Action("editor", "Home") %>';
</script> </script>

View File

@ -26,6 +26,7 @@
<hgroup> <hgroup>
<h1>Error.</h1> <h1>Error.</h1>
<h2>An error occurred while processing your request.</h2> <h2>An error occurred while processing your request.</h2>
<p><%= ViewData["Message"] %></p>
</hgroup> </hgroup>
</body> </body>
</html> </html>

View File

@ -69,6 +69,9 @@ namespace OnlineEditorsExampleMVC
case "remove": case "remove":
Remove(context); Remove(context);
break; break;
case "removeforgotten":
RemoveForgotten(context);
break;
case "assets": case "assets":
Assets(context); Assets(context);
break; break;
@ -87,6 +90,9 @@ namespace OnlineEditorsExampleMVC
case "reference": case "reference":
Reference(context); Reference(context);
break; break;
case "formats":
Formats(context);
break;
} }
} }
@ -240,7 +246,13 @@ namespace OnlineEditorsExampleMVC
var fileUri = DocManagerHelper.GetDownloadUrl(fileName); var fileUri = DocManagerHelper.GetDownloadUrl(fileName);
var extension = (Path.GetExtension(fileName).ToLower() ?? "").Trim('.'); var extension = (Path.GetExtension(fileName).ToLower() ?? "").Trim('.');
var internalExtension = "ooxml"; string conversionExtension = "ooxml";
object fileExt;
if (body.TryGetValue("fileExt", out fileExt) && !String.IsNullOrEmpty(fileExt.ToString()))
{
conversionExtension = fileExt.ToString();
}
// check if the file with such an extension can be converted // check if the file with such an extension can be converted
if (DocManagerHelper.ConvertExts.Contains("." + extension)) if (DocManagerHelper.ConvertExts.Contains("." + extension))
@ -258,7 +270,7 @@ namespace OnlineEditorsExampleMVC
// get the url and file type of the converted file // get the url and file type of the converted file
Dictionary<string, string> newFileData; Dictionary<string, string> newFileData;
var result = ServiceConverter.GetConvertedData(downloadUri.ToString(), extension, internalExtension, key, true, out newFileData, filePass, lang); var result = ServiceConverter.GetConvertedData(downloadUri.ToString(), extension, conversionExtension, key, true, out newFileData, filePass, lang);
if (result != 100) if (result != 100)
{ {
context.Response.Write("{ \"step\" : \"" + result + "\", \"filename\" : \"" + fileName + "\"}"); context.Response.Write("{ \"step\" : \"" + result + "\", \"filename\" : \"" + fileName + "\"}");
@ -393,8 +405,17 @@ namespace OnlineEditorsExampleMVC
context.Response.ContentType = "text/plain"; context.Response.ContentType = "text/plain";
try try
{ {
var fileName = Path.GetFileName(context.Request["fileName"]); string fileName = context.Request["fileName"];
Remove(fileName); // remove a file and its history if it exists
if (!String.IsNullOrEmpty(fileName))
{
fileName = Path.GetFileName(context.Request["fileName"]);
Remove(fileName); // remove a file and its history if it exists
}
else
{
RemoveUserDirectory(); // remove the user's directory
}
context.Response.Write("{ \"success\": true }"); context.Response.Write("{ \"success\": true }");
} }
@ -414,6 +435,14 @@ namespace OnlineEditorsExampleMVC
if (Directory.Exists(histDir)) Directory.Delete(histDir, true); if (Directory.Exists(histDir)) Directory.Delete(histDir, true);
} }
// remove the user's directory
private static void RemoveUserDirectory()
{
var path = DocManagerHelper.StoragePath("", null); // get the path to the user directory
if (Directory.Exists(path)) Directory.Delete(path, true);
}
// get files information // get files information
private static void Files(HttpContext context) private static void Files(HttpContext context)
{ {
@ -951,6 +980,51 @@ namespace OnlineEditorsExampleMVC
return history; return history;
} }
// return all the supported formats
private static void Formats(HttpContext context)
{
try
{
Dictionary<string, object> data = new Dictionary<string, object>
{
{ "formats", FormatManager.All() }
};
context.Response.ContentType = "application/json";
var jss = new JavaScriptSerializer();
context.Response.Write(jss.Serialize(data));
}
catch (Exception e)
{
context.Response.Write("{ \"error\": \"" + e.Message + "\"}");
}
}
// delete a forgotten file from the document server
private static void RemoveForgotten(HttpContext context)
{
try
{
if (!bool.Parse(WebConfigurationManager.AppSettings["enable-forgotten"]))
{
throw new HttpException(403, "The forgotten page is disabled");
}
string filename = context.Request["filename"];
if (!String.IsNullOrEmpty(filename))
{
TrackManager.commandRequest("deleteForgotten", filename);
}
context.Response.StatusCode = 204;
}
catch (Exception e)
{
context.Response.StatusCode = 500;
context.Response.Write("{ \"error\": \"" + e.Message + "\"}");
}
}
} }
} }

View File

@ -1,11 +1,12 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<appSettings> <appSettings>
<clear /> <clear />
<add key="version" value="1.9.0"/> <add key="version" value="1.10.0"/>
<add key="filesize-max" value="52428800"/> <add key="filesize-max" value="52428800"/>
<add key="storage-path" value=""/> <add key="storage-path" value=""/>
<add key="filename-max" value="50"/> <add key="filename-max" value="50"/>
<add key="enable-forgotten" value="true"/>
<add key="files.docservice.timeout" value="120000" /> <add key="files.docservice.timeout" value="120000" />
<add key="files.docservice.secret" value="" /> <add key="files.docservice.secret" value="" />

View File

@ -0,0 +1,161 @@
.center {
width: auto;
}
.left-panel {
width: 256px;
}
.main-panel {
width: 832px;
margin: 0 32px;
padding: 48px 0;
left: 0;
}
.tableRow {
display: flex;
width: 100%;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #E2E2E2;
}
.tableRow td:first-child {
width: 70%;
flex-grow: 0;
max-width: none;
}
.tableHeader td:first-child {
text-align: left;
}
.tableHeader td:last-child, .tableRow td:last-child {
width: 10%;
text-align: center;
padding: 0 !important;
}
.tableHeader {
width: 100%;
}
.stored-edit {
display: block;
padding-top: 0;
max-width: none;
}
menu.links {
width: 100%;
}
.scroll-table-body table {
table-layout: fixed;
}
.stored-edit span {
font-size: 12px;
line-height: normal;
position: static;
}
.scroll-table-body {
overflow-y: auto;
}
.stored-list {
height: calc(100% - 58px);
}
header {
min-width:auto;
}
header a {
display: block;
margin: 0 auto;
width: 1152px;
}
@media (max-width: 1279px) and (min-width: 1024px) {
.left-panel {
width: 208px;
}
.main-panel {
width: 688px;
}
header a {
width: 928px;
}
header img {
margin-left: 16px;
}
}
@media (max-width: 1023px) and (min-width: 593px) {
.center {
max-width: 768px;
width: calc(100% - 80px);
}
.table-main {
width: 100%;
}
.left-panel {
width: 208px;
}
.main-panel {
width: calc(100% - 32px);
}
.tableHeader td:last-child, .tableRow td:last-child {
width: 20%;
}
header a {
width: 768px;
}
header img {
margin-left: 40px;
}
}
@media (max-width: 592px) and (min-width: 320px) {
.center, .table-main {
width: 100%;
}
.left-panel {
display: none;
}
.main-panel {
width: 100%;
margin: 0;
padding: 28px 16px;
}
.tableHeader td:last-child, .tableRow td:last-child {
width: 25%;
}
header a {
width: auto;
}
.scroll-table-body {
top: 40px;
}
.tableRow {
padding: 8px 0;
}
}

View File

@ -1 +0,0 @@
<svg width="30" height="40" viewBox="0 0 30 40" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M0 3c0-1.65685 1.34315-3 3-3h20l7 7v30c0 1.6569-1.3431 3-3 3H3c-1.65685 0-3-1.3431-3-3V3z" fill="#27ABA3"/><path d="M23 0l7 7h-4c-1.6569 0-3-1.34315-3-3V0z" fill="#008078"/><path fill-rule="evenodd" clip-rule="evenodd" d="M24 14H6v7h18v-7zm-.8182 5.9978H6.81818V15H23.1818v4.9978zM24 23H6v7h18v-7zm-.8182 5.9978H6.81818V24H23.1818v4.9978z" fill="#fff"/></svg>

Before

Width:  |  Height:  |  Size: 463 B

View File

@ -0,0 +1,5 @@
<svg width="30" height="40" viewBox="0 0 30 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 3C0 1.34315 1.34315 0 3 0H23L30 7V37C30 38.6569 28.6569 40 27 40H3C1.34315 40 0 38.6569 0 37V3Z" fill="#9E1919"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.0062 15V16.0065H24V15H17.0062ZM17.0062 19V20.0163H24V19H17.0062ZM24 23H17.0062V24.0261H24V23ZM6 28V27.0293H24V28H6ZM7 16H14V23H7V16ZM6 15H7H14H15V16V23V24H14H7H6V23V16V15Z" fill="white"/>
<path d="M23 0L30 7H26C24.3431 7 23 5.65685 23 4V0Z" fill="black" fill-opacity="0.25"/>
</svg>

After

Width:  |  Height:  |  Size: 558 B

View File

@ -0,0 +1,3 @@
<svg width="20" height="17" viewBox="0 0 20 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 17V11H12V17H17V9H20L10 0L0 9H3V17H8Z" fill="#444444"/>
</svg>

After

Width:  |  Height:  |  Size: 173 B

View File

@ -0,0 +1 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M2 1h8l4 4v10H2V1z" fill="#fff"/><path fill-rule="evenodd" clip-rule="evenodd" d="M14 5l-4-4H2v14h12V5zm-4-5l5 5v11H1V0h9z" fill="#BFBFBF"/><path fill="#9E1919" d="M3 10h10v4H3z"/><path d="M7 7V2H3v5h4zM8 3V2h2v1H8zM8 5V4h5v1H8zM13 6H8v1h5V6zM13 8H3v1h10V8z" fill="#BFBFBF"/><path opacity=".3" d="M9 1h1v3h4l1 1H9V1z" fill="#333"/></svg>

After

Width:  |  Height:  |  Size: 441 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,5 @@
<svg width="24" height="23" viewBox="0 0 24 23" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5332 22.2243L0.632544 17.5921C-0.210848 17.1877 -0.210848 16.5627 0.632544 16.1951L4.07945 14.5775L10.4966 17.5921C11.34 17.9965 12.6967 17.9965 13.5034 17.5921L19.9206 14.5775L23.3675 16.1951C24.2108 16.5995 24.2108 17.2245 23.3675 17.5921L13.4668 22.2243C12.6967 22.592 11.34 22.592 10.5332 22.2243Z" fill="#FF6F3D"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5011 16.4922L0.630617 11.8546C-0.210206 11.4497 -0.210206 10.824 0.630617 10.456L3.99391 8.87329L10.5011 11.9282C11.342 12.3331 12.6946 12.3331 13.4989 11.9282L20.0061 8.87329L23.3694 10.456C24.2102 10.8608 24.2102 11.4865 23.3694 11.8546L13.4989 16.4922C12.658 16.897 11.3054 16.897 10.5011 16.4922Z" fill="#95C038"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5011 10.8195L0.630617 6.24863C-0.210206 5.84959 -0.210206 5.23289 0.630617 4.87013L10.5011 0.299281C11.342 -0.0997605 12.6946 -0.0997605 13.4989 0.299281L23.3694 4.87013C24.2102 5.26917 24.2102 5.88587 23.3694 6.24863L13.4989 10.8195C12.658 11.1822 11.3054 11.1822 10.5011 10.8195Z" fill="#5DC0E8"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,5 @@
<svg width="20" height="14" viewBox="0 0 20 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="20" height="2" rx="1" fill="white"/>
<rect y="6" width="20" height="2" rx="1" fill="white"/>
<rect y="12" width="20" height="2" rx="1" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 278 B

View File

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 17C13 17.5523 12.5523 18 12 18C11.4477 18 11 17.5523 11 17V13H7C6.44772 13 6 12.5523 6 12C6 11.4477 6.44772 11 7 11H11V7C11 6.44772 11.4477 6 12 6C12.5523 6 13 6.44772 13 7V11H17C17.5523 11 18 11.4477 18 12C18 12.5523 17.5523 13 17 13H13V17Z" fill="#444444"/>
</svg>

After

Width:  |  Height:  |  Size: 416 B

View File

@ -79,7 +79,8 @@
margin-left: 0; margin-left: 0;
} }
.tableRow { .tableRow,
menu.links {
width: 90%; width: 90%;
} }
@ -119,7 +120,8 @@
.contentCells-icon{ .contentCells-icon{
width: 5%; width: 5%;
} }
.tableRow { .tableRow,
menu.links {
width: 55%; width: 55%;
} }
@ -175,7 +177,8 @@
} }
@media (max-width: 715px) { @media (max-width: 715px) {
.tableRow { .tableRow,
menu.links {
width: 45%; width: 45%;
} }
} }
@ -263,7 +266,8 @@
padding-left: 0; padding-left: 0;
} }
.tableRow { .tableRow,
menu.links {
width: 75%; width: 75%;
} }
@ -293,7 +297,8 @@
width: 580px; width: 580px;
} }
.tableRow { .tableRow,
menu.links {
width: 95%; width: 95%;
} }
@ -407,7 +412,8 @@
padding: 16px 0 6px; padding: 16px 0 6px;
} }
.tableRow { .tableRow,
menu.links {
width: 40%; width: 40%;
} }
@ -502,6 +508,17 @@
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
} }
.buttonsMobile.indent {
margin-bottom: 0;
flex-wrap: nowrap;
}
.button.file-type:hover,
.button.file-type {
height: 28px;
width: 100px;
margin-bottom: 10px !important;
font-size: 9px;
}
.button.gray{ .button.gray{
margin: 0; margin: 0;
} }
@ -527,7 +544,8 @@
} }
@media (max-width: 510px) and (min-width: 470px) { @media (max-width: 510px) and (min-width: 470px) {
.tableRow { .tableRow,
menu.links {
width: 35%; width: 35%;
} }
@ -553,7 +571,8 @@
} }
@media (max-width: 470px) and (min-width: 420px) { @media (max-width: 470px) and (min-width: 420px) {
.tableRow { .tableRow,
menu.links {
width: 30%; width: 30%;
} }
.tableRow td:first-child{ .tableRow td:first-child{
@ -588,7 +607,8 @@
} }
@media (max-width: 420px) and (min-width: 320px) { @media (max-width: 420px) and (min-width: 320px) {
.tableRow { .tableRow,
menu.links {
width: 25%; width: 25%;
} }
@ -638,7 +658,8 @@
} }
} }
@media (max-width: 769px) and (min-width: 715px){ @media (max-width: 769px) and (min-width: 715px){
.tableRow{ .tableRow,
menu.links {
width: 50%; width: 50%;
} }
} }
@ -687,3 +708,216 @@
max-width: none; max-width: none;
} }
} }
@media (max-width: 592px) and (min-width: 320px) {
body.menu-open {
overflow: hidden;
}
header {
min-width: auto;
height: fit-content;
}
header a {
display: block;
}
header img {
margin: 0;
}
header, footer {
position: -webkit-sticky; /* Safari */
position: sticky;
top: 0;
z-index: 100;
}
.center {
width: 100%;
margin: 0;
}
.left-panel {
background-color: rgba(186, 186, 186, 0.6);
display: none;
flex-direction: row;
align-items: start;
max-width: none;
width: 100%;
margin: 0;
position: fixed;
left: 0;
height: calc(100% - 124px);
z-index:99;
}
.left-panel.active {
display: flex;
}
.help-block {
height: 100%;
margin: 0;
background-color: #F5F5F5;
width: 248px;
padding-left: 16px;
padding-top: 33px;
padding-bottom: 33px;
padding-right: 40px;
box-sizing: border-box;
overflow-y: auto;
overflow-x: hidden;
}
.table-main {
width: 100%;
}
.mobile-close-btn {
display: block;
width: 48px;
height: 48px;
background-color: #E2E2E2;
border-radius: 2px;
border-color: #E2E2E2;
color: #808080;
cursor: pointer;
outline: inherit;
border: none;
}
.main-panel {
width: 100%;
left: 0;
padding: 28px 16px;
}
#portal-info {
width: 100%;
max-width: fit-content;
}
menu.links {
width: 100%;
margin-top: 0;
padding: 0;
}
span.portal-name {
font-size: 16px;
}
span.portal-descr:first-child {
font-size: 13px;
}
span.portal-descr {
font-size: 12px;
}
.user-descr {
width: 100%;
max-width: none;
min-width: auto;
border-bottom: 1px solid #E5E5E5;
padding: 12px 0;
margin: 0;
cursor: pointer;
}
.user-descr ul {
display: none;
}
.user-descr ul.active {
display: block;
}
.user-descr b {
font-size: 13px;
display: flex;
align-items: center;
column-gap: 8px;
margin: 0;
}
.user-descr b::before {
content: url("images/plus.svg");
display: inline-block;
width: 24px;
height: 24px;
}
.storedHeader {
width: 100%;
}
.storedHeaderClearAll {
padding-right: 0;
}
.scroll-table-body {
top: 36px;
}
.scroll-table-body tr:first-child {
padding-top: 0;
}
.tableRow {
border-bottom: 1px solid #e5e5e5;
padding: 16px 0;
width: 100%;
}
.tableRow td:first-child {
width: 100%;
}
.stored-edit span {
font-size: 14px;
}
.header-list {
font-size: 16px;
}
.firstContentCellViewers {
border-bottom: none !important;
}
.firstContentCellViewers ~ td {
border-bottom: none !important;
}
.downloadContentCellShift:after {
display: none;
}
.main-nav {
display: none;
}
.responsive-nav {
height: 44px;
display: flex;
flex-direction: row;
margin: 0;
align-items: center;
column-gap: 16px;
padding: 10px 16px;
width: 100%;
box-sizing: border-box;
list-style: none;
}
.main {
height: calc(100% - 124px);
}
.user-block-table {
height: auto;
}
}

View File

@ -65,6 +65,14 @@ header img {
margin: 10px 0 22px 32px; margin: 10px 0 22px 32px;
} }
.responsive-nav {
display: none;
}
.mobile-close-btn {
display: none;
}
.center { .center {
position: relative; position: relative;
margin: 0 auto 0; margin: 0 auto 0;
@ -164,7 +172,7 @@ label .checkbox {
} }
.try-editor.form { .try-editor.form {
background-image: url("images/file_docxf.svg"); background-image: url("images/file_pdf.svg");
} }
.side-option { .side-option {
@ -230,6 +238,33 @@ label .checkbox {
color: #FF6F3D; color: #FF6F3D;
} }
.button.file-type {
font-size: 11px;
color: #FFFFFF;
padding: 8px 8px;
margin-right: 10px;
}
.button.file-type.disable {
cursor: default;
}
.button.file-type.pale {
opacity: 30%;
}
.button.file-type.document {
background: #446995;
}
.button.file-type.spreadsheet {
background: #40865C;
}
.button.file-type.presentation {
background: #AA5252;
}
.upload-panel { .upload-panel {
float: left; float: left;
padding: 24px 0; padding: 24px 0;
@ -271,6 +306,43 @@ label .checkbox {
border-bottom: 1px solid #D0D5DA; border-bottom: 1px solid #D0D5DA;
} }
.links {
display: flex;
padding: 0;
column-gap: 30px;
align-items: center;
list-style: none;
border-bottom: 1px solid #E2E2E2;
margin: 0;
margin-bottom: 24px;
}
.links li {
padding: 4px;
border-bottom: 2px solid transparent;
margin-bottom: -1px;
}
.links li.active {
border-bottom: 2px solid #FF6F3D;
}
.links li.active a {
color: #FF6F3D;
}
.links li.active a img {
filter: invert(55%) sepia(67%) saturate(2727%) hue-rotate(335deg) brightness(104%) contrast(101%);
}
.links a {
display: inline-block;
padding: 2px 0;
line-height: 20px;
font-size: 13px;
text-decoration: none;
}
#mainProgress { #mainProgress {
color: #333333; color: #333333;
display: none; display: none;
@ -468,6 +540,11 @@ footer a:hover {
background-image: url("images/icon_pptx.svg"); background-image: url("images/icon_pptx.svg");
} }
.stored-edit.pdf,
.uploadFileName.pdf {
background-image: url("images/icon_pdf.svg");
}
.stored-edit span { .stored-edit span {
font-size: 12px; font-size: 12px;
line-height: 12px; line-height: 12px;
@ -596,6 +673,29 @@ footer a:hover {
width: 4%; width: 4%;
} }
.storedHeader {
display: flex;
width: 100%;
justify-content: space-between;
align-items: center;
}
.storedHeaderClearAll {
padding-right: 52px;
}
.clear-all {
display: inline-block;
width: 100px;
padding: 2px;
outline: 1px solid #E5E5E5;
text-align: center;
cursor:pointer;
text-transform: uppercase;
background-color: #F5F5F5;
color: #666666;
}
.select-user { .select-user {
color: #444444; color: #444444;
font-family: Open Sans; font-family: Open Sans;
@ -745,6 +845,16 @@ html {
margin-left: 25px; margin-left: 25px;
} }
.buttonsMobile.indent{
padding-left: 35px;
margin-top: 10px;
margin-bottom: 10px;
}
.invisible {
display: none;
}
.tooltip { .tooltip {
background: #FFFFFF; background: #FFFFFF;
border-radius: 5px; border-radius: 5px;

View File

@ -45,11 +45,23 @@
<body> <body>
<form id="form1" runat="server"> <form id="form1" runat="server">
<header> <header>
<div class="center"> <div class="center main-nav">
<a href=""> <a href="./">
<img src ="app_themes/images/logo.svg" alt="ONLYOFFICE" /> <img src ="app_themes/images/logo.svg" alt="ONLYOFFICE" />
</a> </a>
</div> </div>
<menu class="responsive-nav">
<li>
<a href="#" onclick="toggleSidePanel(event)">
<img src="app_themes/images/mobile-menu.svg" alt="ONLYOFFICE" />
</a>
</li>
<li>
<a href="./">
<img src ="app_themes/images/mobile-logo.svg" alt="ONLYOFFICE" />
</a>
</li>
</menu>
</header> </header>
<div class="center main"> <div class="center main">
<table class="table-main"> <table class="table-main">
@ -71,7 +83,7 @@
<a class="try-editor slide" data-type="slide">Presentation</a> <a class="try-editor slide" data-type="slide">Presentation</a>
</li> </li>
<li> <li>
<a class="try-editor form" data-type="docxf">PDF form</a> <a class="try-editor form" data-type="pdf">PDF form</a>
</li> </li>
</ul> </ul>
<label class="side-option"> <label class="side-option">
@ -124,10 +136,25 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<button class="mobile-close-btn" onclick="toggleSidePanel(event)">
<img src="app_themes/images/close.svg" alt="">
</button>
</td> </td>
<td class="section"> <td class="section">
<% var storedFiles = GetStoredFiles(); %> <% var storedFiles = GetStoredFiles(); %>
<div class="main-panel"> <div class="main-panel">
<menu class="links">
<li class="home-link active" >
<a href="./">
<img src="app_themes/images/home.svg" alt="Home"/>
</a>
</li>
<% if (bool.Parse(WebConfigurationManager.AppSettings["enable-forgotten"])) { %>
<li>
<a href="/Forgotten.aspx">Forgotten files</a>
</li>
<% } %>
</menu>
<div id="portal-info" style="display: <%= storedFiles.Any() ? "none" : "table-cell" %>"> <div id="portal-info" style="display: <%= storedFiles.Any() ? "none" : "table-cell" %>">
<span class="portal-name">ONLYOFFICE Document Editors Welcome!</span> <span class="portal-name">ONLYOFFICE Document Editors Welcome!</span>
<span class="portal-descr"> <span class="portal-descr">
@ -138,7 +165,7 @@
<span class="portal-descr">You can open the same document using different users in different Web browser sessions, so you can check out multi-user editing functions.</span> <span class="portal-descr">You can open the same document using different users in different Web browser sessions, so you can check out multi-user editing functions.</span>
<% foreach (User user in Users.getAllUsers()) <% foreach (User user in Users.getAllUsers())
{ %> { %>
<div class="user-descr"> <div class="user-descr" onclick="toggleUserDescr(event)">
<b><%= user.name.IsEmpty() ? "Anonymous" : user.name %></b> <b><%= user.name.IsEmpty() ? "Anonymous" : user.name %></b>
<ul> <ul>
<% foreach (string description in user.descriptions) <% foreach (string description in user.descriptions)
@ -153,7 +180,14 @@
if (storedFiles.Any()) if (storedFiles.Any())
{ %> { %>
<div class="stored-list"> <div class="stored-list">
<span class="header-list">Your documents</span> <div class="storedHeader">
<div class="storedHeaderText">
<span class="header-list">Your documents</span>
</div>
<div class="storedHeaderClearAll">
<div class="clear-all">Clear all</div>
</div>
</div>
<table class="tableHeader" cellspacing="0" cellpadding="0" width="100%"> <table class="tableHeader" cellspacing="0" cellpadding="0" width="100%">
<thead> <thead>
<tr > <tr >
@ -195,11 +229,13 @@
<img src="app_themes/images/mobile.svg" alt="Open in editor for mobile devices" title="Open in editor for mobile devices"/> <img src="app_themes/images/mobile.svg" alt="Open in editor for mobile devices" title="Open in editor for mobile devices"/>
</a> </a>
</td> </td>
<td class="contentCells contentCells-icon"> <% if (docType != "pdf") { %>
<a href="<%= editUrl + "&editorsType=desktop&editorsMode=comment" %>" target="_blank"> <td class="contentCells contentCells-icon">
<img src="app_themes/images/comment.svg" alt="Open in editor for comment" title="Open in editor for comment"/> <a href="<%= editUrl + "&editorsType=desktop&editorsMode=comment" %>" target="_blank">
</a> <img src="app_themes/images/comment.svg" alt="Open in editor for comment" title="Open in editor for comment"/>
</td> </a>
</td>
<% } %>
<% if (docType == "word") { %> <% if (docType == "word") { %>
<td class="contentCells contentCells-icon"> <td class="contentCells contentCells-icon">
<a href="<%= editUrl + "&editorsType=desktop&editorsMode=review" %>" target="_blank"> <a href="<%= editUrl + "&editorsType=desktop&editorsMode=review" %>" target="_blank">
@ -297,6 +333,15 @@
<div class="describeUpload">After these steps are completed, you can work with your document.</div> <div class="describeUpload">After these steps are completed, you can work with your document.</div>
<span id="step1" class="step">1. Loading the file.</span> <span id="step1" class="step">1. Loading the file.</span>
<span class="step-descr">The loading speed depends on file size and additional elements it contains.</span> <span class="step-descr">The loading speed depends on file size and additional elements it contains.</span>
<div id="select-file-type" class="invisible">
<br />
<span class="step">Please select the current document type</span>
<div class="buttonsMobile indent">
<div class="button file-type document" data="docx">Document</div>
<div class="button file-type spreadsheet" data="xlsx">Spreadsheet</div>
<div class="button file-type presentation" data="pptx">Presentation</div>
</div>
</div>
<br /> <br />
<span id="step2" class="step">2. Conversion.</span> <span id="step2" class="step">2. Conversion.</span>
<span class="step-descr">The file is converted to OOXML so that you can edit it.</span> <span class="step-descr">The file is converted to OOXML so that you can edit it.</span>
@ -364,12 +409,8 @@
<script language="javascript" type="text/javascript" src="script/jquery.iframe-transport.js"></script> <script language="javascript" type="text/javascript" src="script/jquery.iframe-transport.js"></script>
<script language="javascript" type="text/javascript" src="script/jquery.fileupload.js"></script> <script language="javascript" type="text/javascript" src="script/jquery.fileupload.js"></script>
<script language="javascript" type="text/javascript" src="script/jquery.dropdownToggle.js"></script> <script language="javascript" type="text/javascript" src="script/jquery.dropdownToggle.js"></script>
<script language="javascript" type="text/javascript" src="script/formats.js"></script>
<script language="javascript" type="text/javascript" src="script/jscript.js"></script> <script language="javascript" type="text/javascript" src="script/jscript.js"></script>
<script language="javascript" type="text/javascript">
var FillFormExtList = '<%= string.Join(",", FillFormsExts.ToArray()) %>';
var ConverExtList = '<%= string.Join(",", ConvertExts.ToArray()) %>';
var EditedExtList = '<%= string.Join(",", EditedExts.ToArray()) %>';
</script>
</body> </body>
</html> </html>

View File

@ -250,6 +250,7 @@ namespace OnlineEditorsExample
{ {
var ext = Path.GetExtension(fileName).ToLower(); var ext = Path.GetExtension(fileName).ToLower();
if (FormatManager.PdfExtensions().Contains(ext)) return "pdf"; // pdf for pdf extensions
if (FormatManager.DocumentExtensions().Contains(ext)) return "word"; // word for text document extensions if (FormatManager.DocumentExtensions().Contains(ext)) return "word"; // word for text document extensions
if (FormatManager.SpreadsheetExtensions().Contains(ext)) return "cell"; // cell for spreadsheet extensions if (FormatManager.SpreadsheetExtensions().Contains(ext)) return "cell"; // cell for spreadsheet extensions
if (FormatManager.PresentationExtensions().Contains(ext)) return "slide"; // slide for presentation extensions if (FormatManager.PresentationExtensions().Contains(ext)) return "slide"; // slide for presentation extensions
@ -437,7 +438,14 @@ namespace OnlineEditorsExample
var lang = context.Request.Cookies.GetOrDefault("ulang", null); var lang = context.Request.Cookies.GetOrDefault("ulang", null);
var extension = (Path.GetExtension(_fileName).ToLower() ?? "").Trim('.'); var extension = (Path.GetExtension(_fileName).ToLower() ?? "").Trim('.');
var internalExtension = "ooxml"; string conversionExtension = "ooxml"; // set the default conversion extension as ooxml
object fileExt;
// change the conversion extension if it was provided in the request body
if (body.TryGetValue("fileExt", out fileExt) && !String.IsNullOrEmpty(fileExt.ToString()))
{
conversionExtension = fileExt.ToString();
}
// check if the file with such an extension can be converted // check if the file with such an extension can be converted
if (ConvertExts.Contains("." + extension)) if (ConvertExts.Contains("." + extension))
@ -454,7 +462,7 @@ namespace OnlineEditorsExample
// get the url and file type of the converted file // get the url and file type of the converted file
Dictionary<string, string> newFileData; Dictionary<string, string> newFileData;
var result = ServiceConverter.GetConvertedData(fileUrl.ToString() , extension, internalExtension, key, true, out newFileData, filePass, lang); var result = ServiceConverter.GetConvertedData(fileUrl.ToString() , extension, conversionExtension, key, true, out newFileData, filePass, lang);
if (result != 100) if (result != 100)
{ {
return "{ \"step\" : \"" + result + "\", \"filename\" : \"" + _fileName + "\"}"; return "{ \"step\" : \"" + result + "\", \"filename\" : \"" + _fileName + "\"}";

View File

@ -126,7 +126,7 @@
// the meta information of the document is changed via the meta command // the meta information of the document is changed via the meta command
var onMetaChange = function (event) { var onMetaChange = function (event) {
if (event.data.favorite) { if (event.data.favorite !== undefined) {
var favorite = !!event.data.favorite; var favorite = !!event.data.favorite;
var title = document.title.replace(/^\☆/g, ""); var title = document.title.replace(/^\☆/g, "");
document.title = (favorite ? "☆" : "") + title; document.title = (favorite ? "☆" : "") + title;
@ -233,6 +233,41 @@
} }
}; };
var onRequestUsers = function (event) {
if (event && event.data){
var c = event.data.c;
}
switch (c) {
case "info":
users = [];
var allUsers = <%= UsersInfo %>;
for (var i = 0; i < event.data.id.length; i++) {
for (var j = 0; j < allUsers.length; j++) {
if (allUsers[j].id == event.data.id[i]) {
users.push(allUsers[j]);
break;
}
}
}
break;
case "protect":
var users = <%= UsersForProtect %>;
break;
default:
users = <%= UsersForMentions %>;
}
docEditor.setUsers({
"c": c,
"users": users,
});
};
var onRequestSendNotify = function (event) {
event.data.actionLink = replaceActionLink(location.href, JSON.stringify(event.data.actionLink));
var data = JSON.stringify(event.data);
innerAlert("onRequestSendNotify: " + data);
};
config = <%= DocConfig %>; config = <%= DocConfig %>;
config.width = "100%"; config.width = "100%";
@ -275,65 +310,37 @@
docEditor.setHistoryData(JSON.parse(xhr.responseText)); // send the link to the document for viewing the version history docEditor.setHistoryData(JSON.parse(xhr.responseText)); // send the link to the document for viewing the version history
} }
}; };
config.events['onRequestHistoryClose'] = function () { // the user is trying to go back to the document from viewing the document version history if (config.editorConfig.user.id !== "uid-3") {
document.location.reload(); config.events['onRequestHistoryClose'] = function () { // the user is trying to go back to the document from viewing the document version history
}; document.location.reload();
config.events['onRequestRestore'] = function (event) {
var fileName = "<%= FileName %>";
var version = event.data.version;
var data = {
fileName: fileName,
version: version
}; };
config.events['onRequestRestore'] = function (event) {
var fileName = "<%= FileName %>";
var version = event.data.version;
var data = {
fileName: fileName,
version: version
};
let xhr = new XMLHttpRequest(); let xhr = new XMLHttpRequest();
xhr.open("POST", "webeditor.ashx?type=restore&directUrl=" + !!config.document.directUrl); xhr.open("POST", "webeditor.ashx?type=restore&directUrl=" + !!config.document.directUrl);
xhr.setRequestHeader('Content-Type', 'application/json'); xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify(data)); xhr.send(JSON.stringify(data));
xhr.onload = function () { xhr.onload = function () {
docEditor.refreshHistory(JSON.parse(xhr.responseText)); docEditor.refreshHistory(JSON.parse(xhr.responseText));
} }
}; };
}
// add mentions for not anonymous users // add mentions for not anonymous users
<% if (!string.IsNullOrEmpty(UsersForMentions)) <% if (!string.IsNullOrEmpty(UsersForMentions))
{ %> { %>
config.events['onRequestUsers'] = function (event) { config.events['onRequestUsers'] = onRequestUsers;
if (event && event.data){
var c = event.data.c;
}
switch (c) {
case "info":
users = [];
var allUsers = <%= UsersInfo %>;
for (var i = 0; i < event.data.id.length; i++) {
for (var j = 0; j < allUsers.length; j++) {
if (allUsers[j].id == event.data.id[i]) {
users.push(allUsers[j]);
break;
}
}
}
break;
case "protect":
var users = <%= UsersForProtect %>;
break;
default:
users = <%= UsersForMentions %>;
}
docEditor.setUsers({
"c": c,
"users": users,
});
};
<% } %> <% } %>
config.events['onRequestSaveAs'] = onRequestSaveAs;
// the user is mentioned in a comment // the user is mentioned in a comment
config.events['onRequestSendNotify'] = function (event) { config.events['onRequestSendNotify'] = onRequestSendNotify;
event.data.actionLink = replaceActionLink(location.href, JSON.stringify(event.data.actionLink));
var data = JSON.stringify(event.data);
innerAlert("onRequestSendNotify: " + data);
};
// prevent file renaming for anonymous users // prevent file renaming for anonymous users
config.events['onRequestRename'] = onRequestRename; config.events['onRequestRename'] = onRequestRename;
config.events['onRequestReferenceData'] = onRequestReferenceData; config.events['onRequestReferenceData'] = onRequestReferenceData;
@ -342,17 +349,7 @@
config.events['onRequestOpen'] = onRequestOpen; config.events['onRequestOpen'] = onRequestOpen;
} }
if (config.editorConfig.createUrl) {
config.events.onRequestSaveAs = onRequestSaveAs;
};
var сonnectEditor = function () { var сonnectEditor = function () {
if ((config.document.fileType === "docxf" || config.document.fileType === "oform")
&& DocsAPI.DocEditor.version().split(".")[0] < 7) {
innerAlert("Please update ONLYOFFICE Docs to version 7.0 to work on fillable forms online.");
return;
}
docEditor = new DocsAPI.DocEditor("iframeEditor", config); docEditor = new DocsAPI.DocEditor("iframeEditor", config);
}; };

View File

@ -135,14 +135,14 @@ namespace OnlineEditorsExample
{ {
// create demo document of a specified file type // create demo document of a specified file type
Try(type, Request["sample"], Request); Try(type, Request["sample"], Request);
Response.Redirect("doceditor.aspx?fileID=" + HttpUtility.UrlEncode(FileName)); Response.Redirect("doceditor.aspx?editorsMode=edit&fileID=" + HttpUtility.UrlEncode(FileName));
} }
// get file extension // get file extension
var ext = Path.GetExtension(FileName).ToLower(); var ext = Path.GetExtension(FileName).ToLower();
var canFill = _Default.FillFormsExts.Contains(ext);
// get editor mode or set the default one (edit) // get editor mode or set the default one (edit)
var editorsMode = Request.GetOrDefault("editorsMode", "edit"); var editorsMode = Request.GetOrDefault("editorsMode", canFill ? "fillForms" : "edit");
var canEdit = _Default.EditedExts.Contains(ext); // check if this file can be edited var canEdit = _Default.EditedExts.Contains(ext); // check if this file can be edited
var editorsType = Request.GetOrDefault("editorsType", "desktop"); var editorsType = Request.GetOrDefault("editorsType", "desktop");
@ -150,11 +150,11 @@ namespace OnlineEditorsExample
var id = Request.Cookies.GetOrDefault("uid", null); var id = Request.Cookies.GetOrDefault("uid", null);
var user = Users.getUser(id); // get the user var user = Users.getUser(id); // get the user
if ((!canEdit && editorsMode.Equals("edit") || editorsMode.Equals("fillForms")) && _Default.FillFormsExts.Contains(ext)) { if ((!canEdit && editorsMode.Equals("edit") || editorsMode.Equals("fillForms")) && canFill) {
editorsMode = "fillForms"; editorsMode = "fillForms";
canEdit = true; canEdit = true;
} }
var submitForm = editorsMode.Equals("fillForms") && id.Equals("uid-1"); // check if the Submit form button is displayed or hidden var submitForm = (editorsMode.Equals("fillForms") || editorsMode.Equals("embedded")) && user.id.Equals("uid-1"); // check if the Submit form button is displayed or hidden
var mode = canEdit && editorsMode != "view" ? "edit" : "view"; // get the editor opening mode (edit or view) var mode = canEdit && editorsMode != "view" ? "edit" : "view"; // get the editor opening mode (edit or view)
var jss = new JavaScriptSerializer(); var jss = new JavaScriptSerializer();
@ -282,10 +282,12 @@ namespace OnlineEditorsExample
{ "forcesave", false }, // adds the request for the forced file saving to the callback handler { "forcesave", false }, // adds the request for the forced file saving to the callback handler
{ "submitForm", submitForm }, // if the Submit form button is displayed or not { "submitForm", submitForm }, // if the Submit form button is displayed or not
{ {
"goback", new Dictionary<string, object> // settings for the Open file location menu button and upper right corner button "goback", user.goback != null ? new Dictionary<string, object> // settings for the Open file location menu button and upper right corner button
{ {
{ "url", _Default.GetServerUrl(false) + "default.aspx" } // the absolute URL to the website address which will be opened when clicking the Open file location menu button { "url", _Default.GetServerUrl(false) + "default.aspx" }, // the absolute URL to the website address which will be opened when clicking the Open file location menu button
} { "text", user.goback.text },
{ "blank", user.goback.blank }
} : new Dictionary<string, object>{}
} }
} }
} }
@ -338,17 +340,17 @@ namespace OnlineEditorsExample
var InsertImageUrl = new UriBuilder(_Default.GetServerUrl(true)); var InsertImageUrl = new UriBuilder(_Default.GetServerUrl(true));
InsertImageUrl.Path = HttpRuntime.AppDomainAppVirtualPath InsertImageUrl.Path = HttpRuntime.AppDomainAppVirtualPath
+ (HttpRuntime.AppDomainAppVirtualPath.EndsWith("/") ? "" : "/") + (HttpRuntime.AppDomainAppVirtualPath.EndsWith("/") ? "" : "/")
+ "App_Themes\\images\\logo.png"; + "App_Themes\\images\\logo.svg";
var DirectImageUrl = new UriBuilder(_Default.GetServerUrl(false)); var DirectImageUrl = new UriBuilder(_Default.GetServerUrl(false));
DirectImageUrl.Path = HttpRuntime.AppDomainAppVirtualPath DirectImageUrl.Path = HttpRuntime.AppDomainAppVirtualPath
+ (HttpRuntime.AppDomainAppVirtualPath.EndsWith("/") ? "" : "/") + (HttpRuntime.AppDomainAppVirtualPath.EndsWith("/") ? "" : "/")
+ "App_Themes\\images\\logo.png"; + "App_Themes\\images\\logo.svg";
// create a logo config // create a logo config
Dictionary<string, object> logoConfig = new Dictionary<string, object> Dictionary<string, object> logoConfig = new Dictionary<string, object>
{ {
{ "fileType", "png"}, { "fileType", "svg"},
{ "url", InsertImageUrl.ToString()} { "url", InsertImageUrl.ToString()}
}; };
@ -487,8 +489,8 @@ namespace OnlineEditorsExample
case "slide": case "slide":
ext = ".pptx"; // .pptx for slide document type ext = ".pptx"; // .pptx for slide document type
break; break;
case "docxf": case "pdf":
ext = ".docxf"; ext = ".pdf";
break; break;
default: default:
return; return;

View File

@ -231,6 +231,10 @@ namespace ASC.Api.DocumentConverter
switch (errorCode) switch (errorCode)
{ {
case -9:
// public const int c_nErrorConversionOutputFormatError = -9;
errorMessage = String.Format(errorMessageTemplate, "Error conversion output format");
break;
case -8: case -8:
// public const int c_nErrorFileVKey = -8; // public const int c_nErrorFileVKey = -8;
errorMessage = String.Format(errorMessageTemplate, "Error document VKey"); errorMessage = String.Format(errorMessageTemplate, "Error document VKey");

View File

@ -0,0 +1,147 @@
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Forgotten.aspx.cs" Inherits="OnlineEditorsExample.Forgotten" Title="ONLYOFFICE" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="OnlineEditorsExample" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width" />
<meta name="server-version" content=<%= GetVersion() %> />
<title>ONLYOFFICE</title>
<!--
*
* (c) Copyright Ascensio System SIA 2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
-->
<link rel="icon" href="~/favicon.ico" type="image/x-icon" />
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Open+Sans:900,800,700,600,500,400,300&subset=latin,cyrillic-ext,cyrillic,latin-ext" />
<link rel="stylesheet" type="text/css" href="app_themes/stylesheet.css" />
<link rel="stylesheet" type="text/css" href="app_themes/media.css" />
<link rel="stylesheet" type="text/css" href="app_themes/forgotten.css" />
<link rel="stylesheet" type="text/css" href="app_themes/jquery-ui.css" />
</head>
<body>
<form id="form1" runat="server">
<header>
<div class="center main-nav">
<a href="./">
<img src ="app_themes/images/logo.svg" alt="ONLYOFFICE" />
</a>
</div>
<menu class="responsive-nav">
<li>
<a href="#">
<img src="app_themes/images/mobile-menu.svg" alt="ONLYOFFICE" />
</a>
</li>
<li>
<a href="./">
<img src ="app_themes/images/mobile-logo.svg" alt="ONLYOFFICE" />
</a>
</li>
</menu>
</header>
<div class="center main">
<table class="table-main">
<tbody>
<tr>
<td class="left-panel section"></td>
<td class="section">
<div class="main-panel">
<menu class="links">
<li class="home-link" >
<a href="./">
<img src="app_themes/images/home.svg" alt="Home"/>
</a>
</li>
<li class="active">
<a href="/Forgotten.aspx">Forgotten files</a>
</li>
</menu>
<div class="stored-list">
<div class="storedHeader">
<div class="storedHeaderText">
<span class="header-list">Forgotten files</span>
</div>
</div>
<table class="tableHeader" cellspacing="0" cellpadding="0" width="100%">
<thead>
<tr>
<td class="tableHeaderCell">Filename</td>
<td class="tableHeaderCell">Action</td>
</tr>
</thead>
</table>
<div class="scroll-table-body">
<table cellspacing="0" cellpadding="0" width="100%">
<tbody>
<% foreach (var file in GetForgottenFiles()) { %>
<tr class="tableRow" title="<%= file["key"] %>">
<td>
<a class="stored-edit <%= file["type"] %>" href="<%= file["url"] %>" target="_blank">
<span><%= file["key"] %></span>
</a>
</td>
<td>
<a href="<%= file["url"] %>">
<img class="icon-download" src="app_themes/images/download.svg" alt="Download" title="Download" /></a>
<a class="delete-file" data="<%= file["key"] %>">
<img class="icon-action" src="app_themes/images/delete.svg" alt="Delete" title="Delete" /></a>
</td>
</tr>
<% } %>
</tbody>
</table>
</div>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<footer>
<div class="center">
<table>
<tbody>
<tr>
<td>
<a href="http://api.onlyoffice.com/editors/howitworks" target="_blank">API Documentation</a>
</td>
<td>
<a href="mailto:sales@onlyoffice.com">Submit your request</a>
</td>
<td class="copy">
&copy; Ascensio System SIA <%= DateTime.Now.Year.ToString() %>. All rights reserved.
</td>
</tr>
</tbody>
</table>
</div>
</footer>
</form>
<script language="javascript" type="text/javascript" src="script/forgotten.js"></script>
</body>
</html>

View File

@ -0,0 +1,97 @@
/**
*
* (c) Copyright Ascensio System SIA 2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Web.Configuration;
using System.Web.UI;
namespace OnlineEditorsExample
{
public partial class Forgotten : Page
{
//get server version
public static string GetVersion()
{
return WebConfigurationManager.AppSettings["version"];
}
private static bool? _ismono;
public static bool IsMono
{
get { return _ismono.HasValue ? _ismono.Value : (_ismono = (bool?)(Type.GetType("Mono.Runtime") != null)).Value; }
}
// get the document type
public static string DocumentType(string fileName)
{
var ext = Path.GetExtension(fileName).ToLower();
if (FormatManager.PdfExtensions().Contains(ext)) return "pdf"; // pdf for pdf extensions
if (FormatManager.DocumentExtensions().Contains(ext)) return "word"; // word for text document extensions
if (FormatManager.SpreadsheetExtensions().Contains(ext)) return "cell"; // cell for spreadsheet extensions
if (FormatManager.PresentationExtensions().Contains(ext)) return "slide"; // slide for presentation extensions
return "word"; // the default document type is word
}
protected void Page_Load(object sender, EventArgs e)
{
if (!bool.Parse(WebConfigurationManager.AppSettings["enable-forgotten"]))
{
Response.Clear();
Response.StatusCode = 403;
Response.End();
}
}
// fetch forgotten files from the document server
public static List<Dictionary<string, string>> GetForgottenFiles()
{
var files = new List<Dictionary<string, string>>();
try
{
var response = TrackManager.commandRequest("getForgottenList", null);
ArrayList keys = (ArrayList) response["keys"];
// fetch all the forgotten files from the document server
foreach (string key in keys)
{
var file = new Dictionary<string, string>();
var fileResult = TrackManager.commandRequest("getForgotten", key);
file.Add("key", fileResult["key"].ToString());
file.Add("url", fileResult["url"].ToString());
file.Add("type", DocumentType(fileResult["url"].ToString()));
files.Add(file);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
return files;
}
}
}

View File

@ -0,0 +1,24 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace OnlineEditorsExample {
public partial class Forgotten {
/// <summary>
/// form1 control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::System.Web.UI.HtmlControls.HtmlForm form1;
}
}

View File

@ -150,6 +150,20 @@ namespace OnlineEditorsExample
.ToList(); .ToList();
} }
public static List<string> PdfExtensions()
{
return Pdfs()
.Select(format => format.Extension())
.ToList();
}
public static List<Format> Pdfs()
{
return All()
.Where(format => format.Type == "pdf")
.ToList();
}
public static List<string> AllExtensions() public static List<string> AllExtensions()
{ {
return All() return All()

View File

@ -77,6 +77,7 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="App_Themes\forgotten.css" />
<Content Include="App_Themes\images\block-content.svg" /> <Content Include="App_Themes\images\block-content.svg" />
<Content Include="App_Themes\images\cell.ico" /> <Content Include="App_Themes\images\cell.ico" />
<Content Include="App_Themes\images\close.svg" /> <Content Include="App_Themes\images\close.svg" />
@ -94,6 +95,7 @@
<Content Include="App_Themes\images\file_xlsx.svg" /> <Content Include="App_Themes\images\file_xlsx.svg" />
<Content Include="App_Themes\images\fill-forms.svg" /> <Content Include="App_Themes\images\fill-forms.svg" />
<Content Include="App_Themes\images\filter.svg" /> <Content Include="App_Themes\images\filter.svg" />
<Content Include="App_Themes\images\home.svg" />
<Content Include="App_Themes\images\icon_docx.svg" /> <Content Include="App_Themes\images\icon_docx.svg" />
<Content Include="App_Themes\images\icon_pptx.svg" /> <Content Include="App_Themes\images\icon_pptx.svg" />
<Content Include="App_Themes\images\icon_xlsx.svg" /> <Content Include="App_Themes\images\icon_xlsx.svg" />
@ -104,10 +106,16 @@
<Content Include="App_Themes\images\notdone.svg" /> <Content Include="App_Themes\images\notdone.svg" />
<Content Include="App_Themes\images\review.svg" /> <Content Include="App_Themes\images\review.svg" />
<Content Include="App_Themes\images\slide.ico" /> <Content Include="App_Themes\images\slide.ico" />
<Content Include="App_Themes\images\uid-1.png" />
<Content Include="App_Themes\images\uid-2.png" />
<Content Include="App_Themes\images\word.ico" /> <Content Include="App_Themes\images\word.ico" />
<Content Include="App_Themes\media.css" /> <Content Include="App_Themes\media.css" />
<Content Include="Forgotten.aspx" />
<Content Include="LICENSE" /> <Content Include="LICENSE" />
<Content Include="licenses\jquery.license" /> <Content Include="script\forgotten.js" />
<Content Include="script\formats.js" />
<Content Include="script\jquery-3.6.4.min.js" />
<Content Include="script\jquery-migrate-3.4.1.min.js" />
<Content Include="Web.config" /> <Content Include="Web.config" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -119,6 +127,12 @@
<DependentUpon>DocEditor.aspx</DependentUpon> <DependentUpon>DocEditor.aspx</DependentUpon>
</Compile> </Compile>
<Compile Include="DocumentConverter.cs" /> <Compile Include="DocumentConverter.cs" />
<Compile Include="Forgotten.aspx.cs">
<DependentUpon>Forgotten.aspx</DependentUpon>
</Compile>
<Compile Include="Forgotten.aspx.designer.cs">
<DependentUpon>Forgotten.aspx</DependentUpon>
</Compile>
<Compile Include="FormatManager.cs" /> <Compile Include="FormatManager.cs" />
<Compile Include="JwtManager.cs" /> <Compile Include="JwtManager.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
@ -145,7 +159,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="App_Themes\images\loader16.gif" /> <Content Include="App_Themes\images\loader16.gif" />
<Content Include="App_Themes\images\logo.png" />
<Content Include="App_Themes\jquery-ui.css" /> <Content Include="App_Themes\jquery-ui.css" />
<Content Include="App_Themes\stylesheet.css" /> <Content Include="App_Themes\stylesheet.css" />
<Content Include="DocEditor.aspx" /> <Content Include="DocEditor.aspx" />

View File

@ -285,7 +285,7 @@ namespace OnlineEditorsExample
} }
// create a command request // create a command request
public static void commandRequest(string method, string key, object meta = null) public static Dictionary<string, object> commandRequest(string method, string key, object meta = null)
{ {
_Default.VerifySSL(); _Default.VerifySSL();
@ -348,6 +348,7 @@ namespace OnlineEditorsExample
{ {
throw new Exception(dataResponse); throw new Exception(dataResponse);
} }
return responseObj;
} }
private static void SaveFile(byte[] data, string path) private static void SaveFile(byte[] data, string path)

View File

@ -58,7 +58,9 @@ namespace OnlineEditorsExample
"Cant print the file", "Cant print the file",
"Can create new files from the editor", "Can create new files from the editor",
"Can see the information about Group2 users", "Can see the information about Group2 users",
"Cant submit forms" "Cant submit forms",
"Can't close history",
"Can't restore the file version"
}; };
static List<string> descr_user_0 = new List<string>() static List<string> descr_user_0 = new List<string>()
@ -91,7 +93,8 @@ namespace OnlineEditorsExample
new List<string>(), new List<string>(),
descr_user_1, descr_user_1,
true, true,
true true,
new Goback(null, false)
), ),
new User( new User(
"uid-2", "uid-2",
@ -110,7 +113,8 @@ namespace OnlineEditorsExample
new List<string>(), new List<string>(),
descr_user_2, descr_user_2,
false, false,
true true,
new Goback("Go to Documents",null)
), ),
new User( new User(
"uid-3", "uid-3",
@ -129,7 +133,8 @@ namespace OnlineEditorsExample
new List<string>() { "copy", "download", "print" }, new List<string>() { "copy", "download", "print" },
descr_user_3, descr_user_3,
false, false,
false false,
null
), ),
new User( new User(
"uid-0", "uid-0",
@ -143,7 +148,8 @@ namespace OnlineEditorsExample
new List<string>() { "protect" }, new List<string>() { "protect" },
descr_user_0, descr_user_0,
false, false,
false false,
null
) )
}; };
@ -235,8 +241,9 @@ namespace OnlineEditorsExample
public bool templates; public bool templates;
public List<string> userInfoGroups; public List<string> userInfoGroups;
public bool avatar; public bool avatar;
public Goback goback;
public User(string id, string name, string email, string group, List<string> reviewGroups, Dictionary<string, object> commentGroups, List<string> userInfoGroups, bool? favorite, List<string> deniedPermissions, List<string> descriptions, bool templates, bool avatar) public User(string id, string name, string email, string group, List<string> reviewGroups, Dictionary<string, object> commentGroups, List<string> userInfoGroups, bool? favorite, List<string> deniedPermissions, List<string> descriptions, bool templates, bool avatar, Goback goback)
{ {
this.id = id; this.id = id;
this.name = name; this.name = name;
@ -250,6 +257,21 @@ namespace OnlineEditorsExample
this.templates = templates; this.templates = templates;
this.userInfoGroups = userInfoGroups; this.userInfoGroups = userInfoGroups;
this.avatar = avatar; this.avatar = avatar;
this.goback = goback;
}
}
public class Goback
{
public string text;
public bool? blank;
public Goback(){}
public Goback(string text, bool? blank)
{
this.text = text;
this.blank = blank;
} }
} }
} }

View File

@ -69,6 +69,9 @@ namespace OnlineEditorsExample
case "remove": case "remove":
Remove(context); Remove(context);
break; break;
case "removeforgotten":
RemoveForgotten(context);
break;
case "assets": case "assets":
Assets(context); Assets(context);
break; break;
@ -87,6 +90,9 @@ namespace OnlineEditorsExample
case "reference": case "reference":
Reference(context); Reference(context);
break; break;
case "formats":
Formats(context);
break;
} }
} }
@ -222,12 +228,22 @@ namespace OnlineEditorsExample
context.Response.ContentType = "text/plain"; context.Response.ContentType = "text/plain";
try try
{ {
var fileName = Path.GetFileName(context.Request["fileName"]); string fileName = context.Request["fileName"];
var path = _Default.StoragePath(fileName, HttpUtility.UrlEncode(_Default.CurUserHostAddress(HttpContext.Current.Request.UserHostAddress))); string userAddress = HttpUtility.UrlEncode(_Default.CurUserHostAddress(HttpContext.Current.Request.UserHostAddress));
var histDir = _Default.HistoryDir(path);
if (File.Exists(path)) File.Delete(path); // delete file if (!String.IsNullOrEmpty(fileName))
if (Directory.Exists(histDir)) Directory.Delete(histDir, true); // delete file history {
fileName = Path.GetFileName(fileName);
var path = _Default.StoragePath(fileName, userAddress);
var histDir = _Default.HistoryDir(path);
if (File.Exists(path)) File.Delete(path); // delete file
if (Directory.Exists(histDir)) Directory.Delete(histDir, true); // delete file history
} else
{
string userDir = _Default.StoragePath("", userAddress);
if (Directory.Exists(userDir)) Directory.Delete(userDir, true); // delete the user's directory
}
context.Response.Write("{ \"success\": true }"); context.Response.Write("{ \"success\": true }");
} }
@ -778,5 +794,50 @@ namespace OnlineEditorsExample
+ userAddress; + userAddress;
return fileUrl.ToString(); return fileUrl.ToString();
} }
// return all the supported formats
private static void Formats(HttpContext context)
{
try
{
Dictionary<string, object> data = new Dictionary<string, object>
{
{ "formats", FormatManager.All() }
};
context.Response.ContentType = "application/json";
var jss = new JavaScriptSerializer();
context.Response.Write(jss.Serialize(data));
}
catch (Exception e)
{
context.Response.Write("{ \"error\": \"" + e.Message + "\"}");
}
}
// delete a forgotten file from the document server
private static void RemoveForgotten(HttpContext context)
{
try
{
if (!bool.Parse(WebConfigurationManager.AppSettings["enable-forgotten"]))
{
throw new HttpException(403, "The forgotten page is disabled");
}
string filename = context.Request["filename"];
if (!String.IsNullOrEmpty(filename))
{
TrackManager.commandRequest("deleteForgotten", filename);
}
context.Response.StatusCode = 204;
}
catch (Exception e)
{
context.Response.Write("{ \"error\": \"" + e.Message + "\"}");
}
}
} }
} }

View File

@ -0,0 +1,19 @@
function deleteFile(event) {
let filename = event.currentTarget.getAttribute("data");
filename = encodeURIComponent(filename);
let url = `webeditor.ashx?type=removeforgotten&filename=${filename}`;
fetch(url, {
headers: {
"Content-Type": "application/json",
}
}).then(result => {
if (result.status == 204) {
document.location.reload(true);
}
});
}
document.querySelectorAll('.delete-file').forEach(el => {
el.addEventListener('click', deleteFile);
});

View File

@ -0,0 +1,66 @@
/**
*
* (c) Copyright Ascensio System SIA 2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
class Format {
constructor(name, type, actions, convert, mime) {
this.name = name;
this.type = type;
this.actions = actions;
this.convert = convert;
this.mime = mime;
}
isAutoConvertible() {
return this.actions.includes('auto-convert');
}
isEditable() {
return this.actions.includes('edit') || this.actions.includes('lossy-edit');
}
isFillable() {
return this.actions.includes('fill');
}
}
class FormatManager {
formats = [];
constructor(formats) {
if(Array.isArray(formats)) this.formats = formats;
}
findByExtension(extension) {
return this.formats.find(format => format.name == extension);
}
isAutoConvertible(extension) {
let format = this.findByExtension(extension);
return format !== undefined && format.isAutoConvertible();
}
isEditable(extension) {
let format = this.findByExtension(extension);
return format !== undefined && format.isEditable();
}
isFillable(extension) {
let format = this.findByExtension(extension);
return format !== undefined && format.isFillable();
}
}

View File

@ -17,6 +17,27 @@
*/ */
var directUrl; var directUrl;
var formatManager;
window.onload = function () {
fetch("webeditor.ashx?type=formats")
.then((response) => response.json())
.then((data) => {
if (data.formats) {
let formats = [];
data.formats.forEach(format => {
formats.push(new Format(
format.Name,
format.Type,
format.Actions,
format.Convert,
format.Mime
));
});
formatManager = new FormatManager(formats);
}
})
}
if (typeof jQuery != "undefined") { if (typeof jQuery != "undefined") {
jq = jQuery.noConflict(); jq = jQuery.noConflict();
@ -87,8 +108,8 @@ if (typeof jQuery != "undefined") {
}); });
var timer = null; var timer = null;
var checkConvert = function (filePass) { var checkConvert = function (filePass, fileType) {
filePass = filePass ? filePass : null; filePass = filePass ? filePass : null;
if (timer != null) { if (timer != null) {
clearTimeout(timer); clearTimeout(timer);
} }
@ -100,10 +121,10 @@ if (typeof jQuery != "undefined") {
jq("#filePass").val(""); jq("#filePass").val("");
var fileName = jq("#hiddenFileName").val(); var fileName = jq("#hiddenFileName").val();
var posExt = fileName.lastIndexOf('.'); var posExt = fileName.lastIndexOf('.') + 1;
posExt = 0 <= posExt ? fileName.substring(posExt).trim().toLowerCase() : ''; posExt = 0 <= posExt ? fileName.substring(posExt).trim().toLowerCase() : '';
if (ConverExtList.indexOf(posExt) == -1) { if (!formatManager.isAutoConvertible(posExt)) {
jq("#step2").addClass("done").removeClass("current"); jq("#step2").addClass("done").removeClass("current");
loadScripts(); loadScripts();
return; return;
@ -116,7 +137,7 @@ if (typeof jQuery != "undefined") {
contentType: "text/xml", contentType: "text/xml",
type: "post", type: "post",
dataType: "json", dataType: "json",
data: JSON.stringify({ filename: fileName, filePass: filePass }), data: JSON.stringify({ filename: fileName, filePass: filePass, fileExt: fileType }),
url: requestAddress, url: requestAddress,
complete: function (data) { complete: function (data) {
var responseText = data.responseText; var responseText = data.responseText;
@ -132,6 +153,12 @@ if (typeof jQuery != "undefined") {
} }
return; return;
} else { } else {
if (response.error.includes("Error conversion output format")) {
jq("#select-file-type").removeClass("invisible");
jq("#step2").removeClass("current");
jq("#hiddenFileName").attr("placeholder", filePass);
return;
}
jq(".current").removeClass("current"); jq(".current").removeClass("current");
jq(".step:not(.done)").addClass("error"); jq(".step:not(.done)").addClass("error");
jq("#mainProgress .error-message").show().find("span").text(response.error); jq("#mainProgress .error-message").show().find("span").text(response.error);
@ -143,7 +170,7 @@ if (typeof jQuery != "undefined") {
jq("#hiddenFileName").val(response.filename); jq("#hiddenFileName").val(response.filename);
if (response.step && response.step < 100) { if (response.step && response.step < 100) {
checkConvert(filePass); checkConvert(filePass, fileType);
} else { } else {
jq("#step2").addClass("done").removeClass("current"); jq("#step2").addClass("done").removeClass("current");
loadScripts(); loadScripts();
@ -178,10 +205,10 @@ if (typeof jQuery != "undefined") {
jq("#beginView, #beginEmbedded").removeClass("disable"); jq("#beginView, #beginEmbedded").removeClass("disable");
var fileName = jq("#hiddenFileName").val(); var fileName = jq("#hiddenFileName").val();
var posExt = fileName.lastIndexOf('.'); var posExt = fileName.lastIndexOf('.') + 1;
posExt = 0 <= posExt ? fileName.substring(posExt).trim().toLowerCase() : ''; posExt = 0 <= posExt ? fileName.substring(posExt).trim().toLowerCase() : '';
if (EditedExtList.indexOf(posExt) != -1 || FillFormExtList.indexOf(posExt) != -1) { if (formatManager.isEditable(posExt) || formatManager.isFillable(posExt)) {
jq("#beginEdit").removeClass("disable"); jq("#beginEdit").removeClass("disable");
} }
}; };
@ -213,6 +240,15 @@ if (typeof jQuery != "undefined") {
}); });
}; };
jq(document).on("click", ".file-type:not(.disable)", function () {
const currentElement = jq(this);
var fileType = currentElement.attr("data");
var filePass = jq("#hiddenFileName").attr("placeholder");
jq('.file-type').addClass(["disable", "pale"]);
currentElement.removeClass("pale");
checkConvert(filePass, fileType);
});
jq(document).on("click", "#enterPass", function () { jq(document).on("click", "#enterPass", function () {
var filePass = jq("#filePass").val(); var filePass = jq("#filePass").val();
if (filePass) { if (filePass) {
@ -293,6 +329,24 @@ if (typeof jQuery != "undefined") {
}); });
}); });
jq(document).on("click", ".clear-all", function () {
if (confirm("Delete all the files?")) {
var requestAddress = "webeditor.ashx"
+ "?type=remove";
jq.ajax({
async: true,
contentType: "text/xml",
url: requestAddress,
complete: function (data) {
if (JSON.parse(data.responseText).success) {
window.location.reload(true);
}
}
});
}
});
function showUserTooltip (isMobile) { function showUserTooltip (isMobile) {
if ( jq("div#portal-info").is(":hidden") ) { if ( jq("div#portal-info").is(":hidden") ) {
jq("div#portal-info").show(); jq("div#portal-info").show();
@ -367,4 +421,27 @@ if (typeof jQuery != "undefined") {
}).mouseout(function () { }).mouseout(function () {
jq("div.tooltip").remove(); jq("div.tooltip").remove();
}); });
}
function toggleSidePanel(event) {
event.preventDefault();
let sidePanel = document.querySelector(".left-panel");
let body = document.querySelector("body");
if (sidePanel.classList.contains("active")) {
sidePanel.classList.remove("active");
body.classList.remove("menu-open");
} else {
sidePanel.classList.add("active")
body.classList.add("menu-open");
}
}
function toggleUserDescr(event) {
let list = event.currentTarget.querySelector("ul");
let cursor = window.getComputedStyle(event.currentTarget).getPropertyValue("cursor");
if (cursor === "pointer") {
if (list.classList.contains("active")) list.classList.remove("active");
else list.classList.add("active");
}
} }

View File

@ -1,11 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<appSettings> <appSettings>
<clear /> <clear />
<add key="version" value="1.9.0"/> <add key="version" value="1.10.0"/>
<add key="filesize-max" value="52428800"/> <add key="filesize-max" value="52428800"/>
<add key="storage-path" value=""/> <add key="storage-path" value=""/>
<add key="filename-max" value="50"/> <add key="filename-max" value="50"/>
<add key="enable-forgotten" value="true"/>
<add key="files.docservice.timeout" value="120000" /> <add key="files.docservice.timeout" value="120000" />
<add key="files.docservice.secret" value="" /> <add key="files.docservice.secret" value="" />

View File

@ -1,6 +1,5 @@
ONLYOFFICE Applications example uses code from the following 3rd party projects: ONLYOFFICE Applications example uses code from the following 3rd party projects:
Gson - Gson is a Java library that can be used to convert Java Objects into their JSON representation. (https://github.com/google/gson/blob/master/LICENSE) Gson - Gson is a Java library that can be used to convert Java Objects into their JSON representation. (https://github.com/google/gson/blob/master/LICENSE)
License: Apache 2.0 License: Apache 2.0
License File: gson.license License File: gson.license
@ -13,6 +12,10 @@ Jackson Databind - General-purpose data-binding functionality and tree-model for
License: Apache 2.0 License: Apache 2.0
License File: jackson-databind.license License File: jackson-databind.license
Jackson Dataformat Properties - Support for reading and writing content of "Java Properties" style configuration files as if there was implied nesting structure (by default using dots as separators). (https://github.com/FasterXML/jackson-dataformats-text/blob/master/LICENSE)
License: Apache 2.0
License File: jackson-dataformat-properties.license
jQuery.BlockUI - The jQuery BlockUI Plugin lets you simulate synchronous behavior when using AJAX, without locking the browser. (https://github.com/malsup/blockui/) jQuery.BlockUI - The jQuery BlockUI Plugin lets you simulate synchronous behavior when using AJAX, without locking the browser. (https://github.com/malsup/blockui/)
License: MIT, GPL License: MIT, GPL
License File: jQuery.BlockUI.license License File: jQuery.BlockUI.license
@ -37,6 +40,10 @@ jQuery.UI - jQuery UI is an open source library of interface components —
License: MIT License: MIT
License File: jQuery.UI.license License File: jQuery.UI.license
JSON - JSON is a light-weight, language independent, data interchange format. (https://github.com/stleary/JSON-java/blob/master/LICENSE)
License Public Domain
License File: json.license
JSON.simple - JSON.simple is a simple Java toolkit for JSON. You can use JSON.simple to encode or decode JSON text. (https://github.com/fangyidong/json-simple/blob/master/LICENSE.txt) JSON.simple - JSON.simple is a simple Java toolkit for JSON. You can use JSON.simple to encode or decode JSON text. (https://github.com/fangyidong/json-simple/blob/master/LICENSE.txt)
License: Apache 2.0 License: Apache 2.0
License File: JSON.simple.license License File: JSON.simple.license
@ -49,14 +56,14 @@ ModelMapper - ModelMapper is an intelligent object mapping library that automa
License: Apache 2.0 License: Apache 2.0
License File modelmapper.license License File modelmapper.license
Prime JWT - is intended to be fast and easy to use. Prime JWT has a single external dependency on Jackson. (https://github.com/ws-apps/prime-jwt/blob/master/LICENSE)
License: Apache 2.0
License File: prime-jwt.license
Spring Boot - Helps create Spring-powered, production-grade applications and services. Has external dependencies on Spring Framework. (https://github.com/spring-projects/spring-boot/blob/main/LICENSE.txt) Spring Boot - Helps create Spring-powered, production-grade applications and services. Has external dependencies on Spring Framework. (https://github.com/spring-projects/spring-boot/blob/main/LICENSE.txt)
License: Apache 2.0 License: Apache 2.0
License File: spring-boot.license License File: spring-boot.license
Spring Boot Web - Starter for building web, including RESTful, applications using Spring MVC. Uses Tomcat as the default embedded container. (https://github.com/spring-projects/spring-boot/blob/main/LICENSE.txt)
License: Apache 2.0
License File: spring-boot.license
Spring Boot Devtools - Provides fast application restarts, LiveReload, and configurations for enhanced development experience. (https://github.com/spring-projects/spring-boot/blob/main/LICENSE.txt) Spring Boot Devtools - Provides fast application restarts, LiveReload, and configurations for enhanced development experience. (https://github.com/spring-projects/spring-boot/blob/main/LICENSE.txt)
License: Apache 2.0 License: Apache 2.0
License File: spring-boot.license License File: spring-boot.license

View File

@ -135,9 +135,9 @@ See the detailed guide to learn how to install Document Server [for Linux](https
a) archive with Java-Spring: a) archive with Java-Spring:
``` ```
wget https://api.onlyoffice.com/app_data/editor/Java.Spring.Example.zip wget https://github.com/ONLYOFFICE/document-server-integration/releases/latest/download/Java.Spring.Example.zip
``` ```
``` ```
unzip Java.Spring.Example.zip unzip Java.Spring.Example.zip
``` ```

View File

@ -1,6 +1,5 @@
ONLYOFFICE Applications example uses code from the following 3rd party projects: ONLYOFFICE Applications example uses code from the following 3rd party projects:
Gson - Gson is a Java library that can be used to convert Java Objects into their JSON representation. (https://github.com/google/gson/blob/master/LICENSE) Gson - Gson is a Java library that can be used to convert Java Objects into their JSON representation. (https://github.com/google/gson/blob/master/LICENSE)
License: Apache 2.0 License: Apache 2.0
License File: gson.license License File: gson.license
@ -13,6 +12,10 @@ Jackson Databind - General-purpose data-binding functionality and tree-model for
License: Apache 2.0 License: Apache 2.0
License File: jackson-databind.license License File: jackson-databind.license
Jackson Dataformat Properties - Support for reading and writing content of "Java Properties" style configuration files as if there was implied nesting structure (by default using dots as separators). (https://github.com/FasterXML/jackson-dataformats-text/blob/master/LICENSE)
License: Apache 2.0
License File: jackson-dataformat-properties.license
jQuery.BlockUI - The jQuery BlockUI Plugin lets you simulate synchronous behavior when using AJAX, without locking the browser. (https://github.com/malsup/blockui/) jQuery.BlockUI - The jQuery BlockUI Plugin lets you simulate synchronous behavior when using AJAX, without locking the browser. (https://github.com/malsup/blockui/)
License: MIT, GPL License: MIT, GPL
License File: jQuery.BlockUI.license License File: jQuery.BlockUI.license
@ -37,6 +40,10 @@ jQuery.UI - jQuery UI is an open source library of interface components —
License: MIT License: MIT
License File: jQuery.UI.license License File: jQuery.UI.license
JSON - JSON is a light-weight, language independent, data interchange format. (https://github.com/stleary/JSON-java/blob/master/LICENSE)
License Public Domain
License File: json.license
JSON.simple - JSON.simple is a simple Java toolkit for JSON. You can use JSON.simple to encode or decode JSON text. (https://github.com/fangyidong/json-simple/blob/master/LICENSE.txt) JSON.simple - JSON.simple is a simple Java toolkit for JSON. You can use JSON.simple to encode or decode JSON text. (https://github.com/fangyidong/json-simple/blob/master/LICENSE.txt)
License: Apache 2.0 License: Apache 2.0
License File: JSON.simple.license License File: JSON.simple.license
@ -49,14 +56,14 @@ ModelMapper - ModelMapper is an intelligent object mapping library that automa
License: Apache 2.0 License: Apache 2.0
License File modelmapper.license License File modelmapper.license
Prime JWT - is intended to be fast and easy to use. Prime JWT has a single external dependency on Jackson. (https://github.com/ws-apps/prime-jwt/blob/master/LICENSE)
License: Apache 2.0
License File: prime-jwt.license
Spring Boot - Helps create Spring-powered, production-grade applications and services. Has external dependencies on Spring Framework. (https://github.com/spring-projects/spring-boot/blob/main/LICENSE.txt) Spring Boot - Helps create Spring-powered, production-grade applications and services. Has external dependencies on Spring Framework. (https://github.com/spring-projects/spring-boot/blob/main/LICENSE.txt)
License: Apache 2.0 License: Apache 2.0
License File: spring-boot.license License File: spring-boot.license
Spring Boot Web - Starter for building web, including RESTful, applications using Spring MVC. Uses Tomcat as the default embedded container. (https://github.com/spring-projects/spring-boot/blob/main/LICENSE.txt)
License: Apache 2.0
License File: spring-boot.license
Spring Boot Devtools - Provides fast application restarts, LiveReload, and configurations for enhanced development experience. (https://github.com/spring-projects/spring-boot/blob/main/LICENSE.txt) Spring Boot Devtools - Provides fast application restarts, LiveReload, and configurations for enhanced development experience. (https://github.com/spring-projects/spring-boot/blob/main/LICENSE.txt)
License: Apache 2.0 License: Apache 2.0
License File: spring-boot.license License File: spring-boot.license

View File

@ -1,4 +1,5 @@
Apache License
Apache License
Version 2.0, January 2004 Version 2.0, January 2004
http://www.apache.org/licenses/ http://www.apache.org/licenses/
@ -178,7 +179,7 @@ Apache License
APPENDIX: How to apply the Apache License to your work. APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}" boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a comment syntax for the file format. We also recommend that a
@ -186,7 +187,7 @@ Apache License
same "printed page" as the copyright notice for easier same "printed page" as the copyright notice for easier
identification within third-party archives. identification within third-party archives.
Copyright {yyyy} {name of copyright owner} Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@ -0,0 +1 @@
Public Domain.

View File

@ -50,11 +50,6 @@
<artifactId>gson</artifactId> <artifactId>gson</artifactId>
<version>2.8.9</version> <version>2.8.9</version>
</dependency> </dependency>
<dependency>
<groupId>com.inversoft</groupId>
<artifactId>prime-jwt</artifactId>
<version>1.3.1</version>
</dependency>
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
@ -71,12 +66,39 @@
<artifactId>jackson-databind</artifactId> <artifactId>jackson-databind</artifactId>
<version>2.13.4.2</version> <version>2.13.4.2</version>
</dependency> </dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-properties</artifactId>
<version>2.13.5</version>
</dependency>
<dependency> <dependency>
<groupId>org.modelmapper</groupId> <groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId> <artifactId>modelmapper</artifactId>
<version>2.4.2</version> <version>2.4.2</version>
</dependency> </dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20231013</version>
</dependency>
<dependency>
<groupId>com.onlyoffice</groupId>
<artifactId>docs-integration-sdk</artifactId>
<version>1.2.1-SNAPSHOT</version>
</dependency>
</dependencies> </dependencies>
<repositories>
<repository>
<id>ossrh</id>
<name>Sonatype OSSRH</name>
<url>https://s01.oss.sonatype.org/content/repositories/snapshots/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>

View File

@ -19,6 +19,7 @@
package com.onlyoffice.integration; package com.onlyoffice.integration;
import com.onlyoffice.integration.documentserver.serializers.FilterState; import com.onlyoffice.integration.documentserver.serializers.FilterState;
import com.onlyoffice.integration.entities.Goback;
import com.onlyoffice.integration.services.UserServices; import com.onlyoffice.integration.services.UserServices;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -92,30 +93,34 @@ public class ExampleData {
"Can create a file from an editor", "Can create a file from an editor",
"Can see the information about Group2 users", "Can see the information about Group2 users",
"Can view chat", "Can view chat",
"Cant submit forms" "Cant submit forms",
"Can't close history",
"Can't restore the file version"
); );
// create user 1 with the specified parameters // create user 1 with the specified parameters
userService.createUser("John Smith", "smith@example.com", descriptionUserFirst, userService.createUser("John Smith", "smith@example.com", descriptionUserFirst,
"", List.of(FilterState.NULL.toString()), List.of(FilterState.NULL.toString()), "", List.of(FilterState.NULL.toString()), List.of(FilterState.NULL.toString()),
List.of(FilterState.NULL.toString()), List.of(FilterState.NULL.toString()), List.of(FilterState.NULL.toString()), List.of(FilterState.NULL.toString()),
List.of(FilterState.NULL.toString()), null, true, true, true); List.of(FilterState.NULL.toString()), null, true, true, true,
new Goback(null, false), true);
// create user 2 with the specified parameters // create user 2 with the specified parameters
userService.createUser("Mark Pottato", "pottato@example.com", descriptionUserSecond, userService.createUser("Mark Pottato", "pottato@example.com", descriptionUserSecond,
"group-2", List.of("", "group-2"), List.of(FilterState.NULL.toString()), "group-2", List.of("", "group-2"), List.of(FilterState.NULL.toString()),
List.of("group-2", ""), List.of("group-2"), List.of("group-2", ""), true, true, List.of("group-2", ""), List.of("group-2"), List.of("group-2", ""), true, true,
true, true); true, true, new Goback("Go to Documents", null), false);
// create user 3 with the specified parameters // create user 3 with the specified parameters
userService.createUser("Hamish Mitchell", null, descriptionUserThird, userService.createUser("Hamish Mitchell", null, descriptionUserThird,
"group-3", List.of("group-2"), List.of("group-2", "group-3"), List.of("group-2"), "group-3", List.of("group-2"), List.of("group-2", "group-3"), List.of("group-2"),
new ArrayList<>(), List.of("group-2"), false, true, true, false); new ArrayList<>(), List.of("group-2"), false, true, true, false,
null, false);
// create user 0 with the specified parameters // create user 0 with the specified parameters
userService.createUser("Anonymous", null, descriptionUserZero, "", userService.createUser("Anonymous", null, descriptionUserZero, "",
List.of(FilterState.NULL.toString()), List.of(FilterState.NULL.toString()), List.of(FilterState.NULL.toString()), List.of(FilterState.NULL.toString()),
List.of(FilterState.NULL.toString()), List.of(FilterState.NULL.toString()), List.of(FilterState.NULL.toString()), List.of(FilterState.NULL.toString()),
new ArrayList<>(), null, false, false, false); new ArrayList<>(), null, false, false, false, null, false);
} }
} }

View File

@ -20,6 +20,17 @@ package com.onlyoffice.integration;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.onlyoffice.integration.documentserver.storage.FileStoragePathBuilder; import com.onlyoffice.integration.documentserver.storage.FileStoragePathBuilder;
import com.onlyoffice.manager.document.DocumentManager;
import com.onlyoffice.manager.request.DefaultRequestManager;
import com.onlyoffice.manager.request.RequestManager;
import com.onlyoffice.manager.security.DefaultJwtManager;
import com.onlyoffice.manager.security.JwtManager;
import com.onlyoffice.manager.settings.SettingsManager;
import com.onlyoffice.manager.url.UrlManager;
import com.onlyoffice.service.command.CommandService;
import com.onlyoffice.service.command.DefaultCommandService;
import com.onlyoffice.service.convert.ConvertService;
import com.onlyoffice.service.convert.DefaultConvertService;
import org.json.simple.parser.JSONParser; import org.json.simple.parser.JSONParser;
import org.modelmapper.ModelMapper; import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies; import org.modelmapper.convention.MatchingStrategies;
@ -30,23 +41,15 @@ import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import com.onlyoffice.integration.documentserver.util.SSLUtils;
@Configuration @Configuration
public class IntegrationConfiguration { public class IntegrationConfiguration {
@Value("${files.storage}") @Value("${files.storage}")
private String storageAddress; private String storageAddress;
@Value("${files.docservice.verify-peer-off}")
private String verifyPerrOff;
@Autowired @Autowired
private FileStoragePathBuilder storagePathBuilder; private FileStoragePathBuilder storagePathBuilder;
@Autowired
private SSLUtils ssl;
@Bean @Bean
public ModelMapper mapper() { // create the model mapper public ModelMapper mapper() { // create the model mapper
ModelMapper mapper = new ModelMapper(); ModelMapper mapper = new ModelMapper();
@ -67,21 +70,34 @@ public class IntegrationConfiguration {
@PostConstruct @PostConstruct
public void init() { // initialize the storage path builder public void init() { // initialize the storage path builder
storagePathBuilder.configure(storageAddress.isBlank() ? null : storageAddress); storagePathBuilder.configure(storageAddress.isBlank() ? null : storageAddress);
if (!verifyPerrOff.isEmpty()) {
try {
if (verifyPerrOff.equals("true")) {
ssl.turnOffSslChecking(); //the certificate will be ignored
} else {
ssl.turnOnSslChecking(); //the certificate will be verified
}
} catch (Exception e) {
e.printStackTrace();
}
}
} }
@Bean @Bean
public ObjectMapper objectMapper() { // create the object mapper public ObjectMapper objectMapper() { // create the object mapper
return new ObjectMapper(); return new ObjectMapper();
} }
@Bean
public JwtManager jwtManager(final SettingsManager settingsManager) {
return new DefaultJwtManager(settingsManager);
}
@Bean
public RequestManager requestManager(final UrlManager urlManager, final JwtManager jwtManager,
final SettingsManager settingsManager) {
return new DefaultRequestManager(urlManager, jwtManager, settingsManager);
}
@Bean
public ConvertService convertService(final DocumentManager documentManager, final UrlManager urlManager,
final RequestManager requestManager,
final SettingsManager settingsManager) {
return new DefaultConvertService(documentManager, urlManager, requestManager, settingsManager);
}
@Bean
public CommandService commandService(final RequestManager requestManager) {
return new DefaultCommandService(requestManager);
}
} }

View File

@ -20,19 +20,21 @@ package com.onlyoffice.integration.controllers;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.onlyoffice.integration.documentserver.managers.jwt.JwtManager;
import com.onlyoffice.integration.documentserver.models.enums.Action; import com.onlyoffice.integration.documentserver.models.enums.Action;
import com.onlyoffice.integration.documentserver.storage.FileStoragePathBuilder; import com.onlyoffice.integration.documentserver.storage.FileStoragePathBuilder;
import com.onlyoffice.integration.entities.User;
import com.onlyoffice.integration.dto.Mentions; import com.onlyoffice.integration.dto.Mentions;
import com.onlyoffice.integration.dto.UserInfo;
import com.onlyoffice.integration.dto.Protect; import com.onlyoffice.integration.dto.Protect;
import com.onlyoffice.integration.documentserver.models.enums.Type; import com.onlyoffice.integration.dto.UserInfo;
import com.onlyoffice.integration.documentserver.models.filemodel.FileModel; import com.onlyoffice.integration.entities.User;
import com.onlyoffice.integration.sdk.manager.UrlManager;
import com.onlyoffice.integration.sdk.service.ConfigService;
import com.onlyoffice.integration.services.UserServices; import com.onlyoffice.integration.services.UserServices;
import com.onlyoffice.integration.services.configurers.FileConfigurer; import com.onlyoffice.manager.security.JwtManager;
import com.onlyoffice.integration.services.configurers.wrappers.DefaultFileWrapper; import com.onlyoffice.manager.settings.SettingsManager;
import com.onlyoffice.model.documenteditor.Config;
import com.onlyoffice.model.documenteditor.config.document.Type;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
@ -56,12 +58,6 @@ import static com.onlyoffice.integration.documentserver.util.Constants.ANONYMOUS
@Controller @Controller
public class EditorController { public class EditorController {
@Value("${files.docservice.url.site}")
private String docserviceSite;
@Value("${files.docservice.url.api}")
private String docserviceApiUrl;
@Value("${files.docservice.languages}") @Value("${files.docservice.languages}")
private String langs; private String langs;
@ -78,7 +74,13 @@ public class EditorController {
private ObjectMapper objectMapper; private ObjectMapper objectMapper;
@Autowired @Autowired
private FileConfigurer<DefaultFileWrapper> fileConfigurer; private SettingsManager settingsManager;
@Autowired
private ConfigService configService;
@Autowired
private UrlManager urlManager;
@GetMapping(path = "${url.editor}") @GetMapping(path = "${url.editor}")
// process request to open the editor page // process request to open the editor page
@ -86,20 +88,18 @@ public class EditorController {
@RequestParam(value = "action", required = false) final String actionParam, @RequestParam(value = "action", required = false) final String actionParam,
@RequestParam(value = "type", required = false) final String typeParam, @RequestParam(value = "type", required = false) final String typeParam,
@RequestParam(value = "actionLink", required = false) final String actionLink, @RequestParam(value = "actionLink", required = false) final String actionLink,
@RequestParam(value = "directUrl", required = false,
defaultValue = "false") final Boolean directUrl,
@CookieValue(value = "uid") final String uid, @CookieValue(value = "uid") final String uid,
@CookieValue(value = "ulang") final String lang, @CookieValue(value = "ulang") final String lang,
final Model model) throws JsonProcessingException { final Model model) throws JsonProcessingException {
Action action = Action.edit; Action action = null;
Type type = Type.desktop; Type type = Type.DESKTOP;
Locale locale = new Locale("en"); Locale locale = new Locale("en");
if (actionParam != null) { if (actionParam != null) {
action = Action.valueOf(actionParam); action = Action.valueOf(actionParam);
} }
if (typeParam != null) { if (typeParam != null) {
type = Type.valueOf(typeParam); type = Type.valueOf(typeParam.toUpperCase());
} }
List<String> langsAndKeys = Arrays.asList(langs.split("\\|")); List<String> langsAndKeys = Arrays.asList(langs.split("\\|"));
@ -118,39 +118,34 @@ public class EditorController {
return "index.html"; return "index.html";
} }
User user = optionalUser.get(); Config config = configService.createConfig(
user.setImage(user.getAvatar() ? storagePathBuilder.getServerUrl(true) + "/css/img/uid-" fileName,
+ user.getId() + ".png" : null); action,
type
// get file model with the default file parameters
FileModel fileModel = fileConfigurer.getFileModel(
DefaultFileWrapper
.builder()
.fileName(fileName)
.type(type)
.lang(locale.toLanguageTag())
.action(action)
.user(user)
.actionData(actionLink)
.isEnableDirectUrl(directUrl)
.build()
); );
// add attributes to the specified model JSONObject actionData = null;
// add file model with the default parameters to the original model
model.addAttribute("model", fileModel); if (actionLink != null && !actionLink.isEmpty()) {
actionData = new JSONObject(actionLink);
}
config.getEditorConfig().setActionLink(actionData);
config.getEditorConfig().setLang(locale.toLanguageTag());
model.addAttribute("model", config);
// create the document service api URL and add it to the model // create the document service api URL and add it to the model
model.addAttribute("docserviceApiUrl", docserviceSite + docserviceApiUrl); model.addAttribute("docserviceApiUrl", urlManager.getDocumentServerApiUrl());
// get an image and add it to the model // get an image and add it to the model
model.addAttribute("dataInsertImage", getInsertImage(directUrl)); model.addAttribute("dataInsertImage", getInsertImage());
// get a document for comparison and add it to the model // get a document for comparison and add it to the model
model.addAttribute("dataDocument", getCompareFile(directUrl)); model.addAttribute("dataDocument", getCompareFile());
// get recipients data for mail merging and add it to the model // get recipients data for mail merging and add it to the model
model.addAttribute("dataSpreadsheet", getSpreadsheet(directUrl)); model.addAttribute("dataSpreadsheet", getSpreadsheet());
// get user data for mentions and add it to the model // get user data for mentions and add it to the model
model.addAttribute("usersForMentions", getUserMentions(uid)); model.addAttribute("usersForMentions", getUserMentions(uid));
@ -210,17 +205,13 @@ public class EditorController {
@SneakyThrows @SneakyThrows
private String getInsertImage(final Boolean directUrl) { // get an image that will be inserted into the document private String getInsertImage() { // get an image that will be inserted into the document
Map<String, Object> dataInsertImage = new HashMap<>(); Map<String, Object> dataInsertImage = new HashMap<>();
dataInsertImage.put("fileType", "png"); dataInsertImage.put("fileType", "svg");
dataInsertImage.put("url", storagePathBuilder.getServerUrl(true) + "/css/img/logo.png"); dataInsertImage.put("url", storagePathBuilder.getServerUrl(true) + "/css/img/logo.svg");
if (directUrl) {
dataInsertImage.put("directUrl", storagePathBuilder
.getServerUrl(false) + "/css/img/logo.png");
}
// check if the document token is enabled // check if the document token is enabled
if (jwtManager.tokenEnabled()) { if (settingsManager.isSecurityEnabled()) {
// create token from the dataInsertImage object // create token from the dataInsertImage object
dataInsertImage.put("token", jwtManager.createToken(dataInsertImage)); dataInsertImage.put("token", jwtManager.createToken(dataInsertImage));
@ -232,17 +223,13 @@ public class EditorController {
// get a document that will be compared with the current document // get a document that will be compared with the current document
@SneakyThrows @SneakyThrows
private String getCompareFile(final Boolean directUrl) { private String getCompareFile() {
Map<String, Object> dataDocument = new HashMap<>(); Map<String, Object> dataDocument = new HashMap<>();
dataDocument.put("fileType", "docx"); dataDocument.put("fileType", "docx");
dataDocument.put("url", storagePathBuilder.getServerUrl(true) + "/assets?name=sample.docx"); dataDocument.put("url", storagePathBuilder.getServerUrl(true) + "/assets?name=sample.docx");
if (directUrl) {
dataDocument.put("directUrl", storagePathBuilder
.getServerUrl(false) + "/assets?name=sample.docx");
}
// check if the document token is enabled // check if the document token is enabled
if (jwtManager.tokenEnabled()) { if (settingsManager.isSecurityEnabled()) {
// create token from the dataDocument object // create token from the dataDocument object
dataDocument.put("token", jwtManager.createToken(dataDocument)); dataDocument.put("token", jwtManager.createToken(dataDocument));
@ -252,16 +239,13 @@ public class EditorController {
} }
@SneakyThrows @SneakyThrows
private String getSpreadsheet(final Boolean directUrl) { private String getSpreadsheet() {
Map<String, Object> dataSpreadsheet = new HashMap<>(); // get recipients data for mail merging Map<String, Object> dataSpreadsheet = new HashMap<>(); // get recipients data for mail merging
dataSpreadsheet.put("fileType", "csv"); dataSpreadsheet.put("fileType", "csv");
dataSpreadsheet.put("url", storagePathBuilder.getServerUrl(true) + "/csv"); dataSpreadsheet.put("url", storagePathBuilder.getServerUrl(true) + "/csv");
if (directUrl) {
dataSpreadsheet.put("directUrl", storagePathBuilder.getServerUrl(false) + "/csv");
}
// check if the document token is enabled // check if the document token is enabled
if (jwtManager.tokenEnabled()) { if (settingsManager.isSecurityEnabled()) {
// create token from the dataSpreadsheet object // create token from the dataSpreadsheet object
dataSpreadsheet.put("token", jwtManager.createToken(dataSpreadsheet)); dataSpreadsheet.put("token", jwtManager.createToken(dataSpreadsheet));

View File

@ -21,32 +21,38 @@ package com.onlyoffice.integration.controllers;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import com.onlyoffice.integration.documentserver.callbacks.CallbackHandler;
import com.onlyoffice.integration.documentserver.managers.history.HistoryManager; import com.onlyoffice.integration.documentserver.managers.history.HistoryManager;
import com.onlyoffice.integration.documentserver.managers.jwt.JwtManager;
import com.onlyoffice.integration.documentserver.storage.FileStorageMutator; import com.onlyoffice.integration.documentserver.storage.FileStorageMutator;
import com.onlyoffice.integration.documentserver.storage.FileStoragePathBuilder; import com.onlyoffice.integration.documentserver.storage.FileStoragePathBuilder;
import com.onlyoffice.integration.dto.Converter; import com.onlyoffice.integration.dto.Converter;
import com.onlyoffice.integration.dto.ConvertedData;
import com.onlyoffice.integration.dto.Reference; import com.onlyoffice.integration.dto.Reference;
import com.onlyoffice.integration.dto.ReferenceData;
import com.onlyoffice.integration.dto.Rename; import com.onlyoffice.integration.dto.Rename;
import com.onlyoffice.integration.dto.Restore; import com.onlyoffice.integration.dto.Restore;
import com.onlyoffice.integration.dto.SaveAs; import com.onlyoffice.integration.dto.SaveAs;
import com.onlyoffice.integration.dto.Track;
import com.onlyoffice.integration.entities.User; import com.onlyoffice.integration.entities.User;
import com.onlyoffice.integration.documentserver.models.enums.DocumentType; import com.onlyoffice.integration.sdk.manager.DocumentManager;
import com.onlyoffice.integration.services.UserServices; import com.onlyoffice.integration.services.UserServices;
import com.onlyoffice.integration.documentserver.util.file.FileUtility;
import com.onlyoffice.integration.documentserver.util.service.ServiceConverter;
import com.onlyoffice.integration.documentserver.managers.document.DocumentManager;
import com.onlyoffice.integration.documentserver.managers.callback.CallbackManager;
import com.onlyoffice.manager.request.RequestManager;
import com.onlyoffice.manager.security.JwtManager;
import com.onlyoffice.manager.settings.SettingsManager;
import com.onlyoffice.manager.url.UrlManager;
import com.onlyoffice.model.commandservice.CommandRequest;
import com.onlyoffice.model.commandservice.CommandResponse;
import com.onlyoffice.model.commandservice.commandrequest.Command;
import com.onlyoffice.model.commandservice.commandrequest.Meta;
import com.onlyoffice.model.convertservice.ConvertRequest;
import com.onlyoffice.model.convertservice.ConvertResponse;
import com.onlyoffice.model.documenteditor.Callback;
import com.onlyoffice.model.documenteditor.config.document.ReferenceData;
import com.onlyoffice.service.command.CommandService;
import com.onlyoffice.service.convert.ConvertService;
import com.onlyoffice.service.documenteditor.callback.CallbackService;
import org.apache.http.HttpEntity;
import org.json.simple.JSONArray; import org.json.simple.JSONArray;
import org.json.simple.JSONObject; import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser; import org.json.simple.parser.JSONParser;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource; import org.springframework.core.io.UrlResource;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
@ -74,7 +80,6 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
@ -91,22 +96,6 @@ import java.util.Optional;
@Controller @Controller
public class FileController { public class FileController {
@Value("${files.docservice.header}")
private String documentJwtHeader;
@Value("${filesize-max}")
private String filesizeMax;
@Value("${files.docservice.url.site}")
private String docserviceUrlSite;
@Value("${files.docservice.url.command}")
private String docserviceUrlCommand;
@Autowired
private FileUtility fileUtility;
@Autowired
private DocumentManager documentManager;
@Autowired @Autowired
private JwtManager jwtManager; private JwtManager jwtManager;
@Autowired @Autowired
@ -116,20 +105,29 @@ public class FileController {
@Autowired @Autowired
private UserServices userService; private UserServices userService;
@Autowired @Autowired
private CallbackHandler callbackHandler;
@Autowired
private ObjectMapper objectMapper; private ObjectMapper objectMapper;
@Autowired @Autowired
private ServiceConverter serviceConverter;
@Autowired
private CallbackManager callbackManager;
@Autowired
private HistoryManager historyManager; private HistoryManager historyManager;
@Autowired
private DocumentManager documentManager;
@Autowired
private ConvertService convertService;
@Autowired
private RequestManager requestManager;
@Autowired
private SettingsManager settingsManager;
@Autowired
private CallbackService callbackService;
@Autowired
private CommandService commandService;
@Autowired
private UrlManager urlManager;
// create user metadata // create user metadata
private String createUserMetadata(final String uid, final String fullFileName) { private String createUserMetadata(final String uid, final String fullFileName) {
Optional<User> optionalUser = userService.findUserById(Integer.parseInt(uid)); // find a user by their ID Optional<User> optionalUser = userService.findUserById(Integer.parseInt(uid)); // find a user by their ID
String documentType = fileUtility.getDocumentType(fullFileName).toString().toLowerCase(); // get document type // get document type
String documentType = documentManager.getDocumentType(fullFileName).toString().toLowerCase();
if (optionalUser.isPresent()) { if (optionalUser.isPresent()) {
User user = optionalUser.get(); User user = optionalUser.get();
storageMutator.createMeta(fullFileName, // create meta information with the user ID and name specified storageMutator.createMeta(fullFileName, // create meta information with the user ID and name specified
@ -196,17 +194,17 @@ public class FileController {
@CookieValue("uid") final String uid) { @CookieValue("uid") final String uid) {
try { try {
String fullFileName = file.getOriginalFilename(); // get file name String fullFileName = file.getOriginalFilename(); // get file name
String fileExtension = fileUtility.getFileExtension(fullFileName); // get file extension String fileExtension = documentManager.getExtension(fullFileName); // get file extension
long fileSize = file.getSize(); // get file size long fileSize = file.getSize(); // get file size
byte[] bytes = file.getBytes(); // get file in bytes byte[] bytes = file.getBytes(); // get file in bytes
// check if the file size exceeds the maximum file size or is less than 0 // check if the file size exceeds the maximum file size or is less than 0
if (fileUtility.getMaxFileSize() < fileSize || fileSize <= 0) { if (documentManager.getMaxFileSize() < fileSize || fileSize <= 0) {
return "{ \"error\": \"File size is incorrect\"}"; // if so, write an error message to the response return "{ \"error\": \"File size is incorrect\"}"; // if so, write an error message to the response
} }
// check if file extension is supported by the editor // check if file extension is supported by the editor
if (!fileUtility.getFileExts().contains(fileExtension)) { if (documentManager.getDocumentType(fullFileName) == null) {
// if not, write an error message to the response // if not, write an error message to the response
return "{ \"error\": \"File type is not supported\"}"; return "{ \"error\": \"File type is not supported\"}";
@ -217,7 +215,7 @@ public class FileController {
throw new IOException("Could not update a file"); // if the file cannot be updated, an error occurs throw new IOException("Could not update a file"); // if the file cannot be updated, an error occurs
} }
fullFileName = fileUtility.getFileNameWithoutExtension(fileNamePath) fullFileName = documentManager.getBaseName(fileNamePath)
+ "." + fileExtension; // get full file name + "." + fileExtension; // get full file name
return createUserMetadata(uid, fullFileName); // create user metadata and return it return createUserMetadata(uid, fullFileName); // create user metadata and return it
@ -235,80 +233,87 @@ public class FileController {
@CookieValue("uid") final String uid, @CookieValue("ulang") final String lang) { @CookieValue("uid") final String uid, @CookieValue("ulang") final String lang) {
// get file name // get file name
String fileName = body.getFileName(); String fileName = body.getFileName();
// get URL for downloading a file with the specified name
String fileUri = documentManager.getDownloadUrl(fileName, true);
// get file password if it exists // get file password if it exists
String filePass = body.getFilePass() != null ? body.getFilePass() : null; String filePass = body.getFilePass() != null ? body.getFilePass() : null;
// get an auto-conversion extension from the request body or set it to the ooxml extension
// get file extension String conversionExtension = body.getFileExt() != null ? body.getFileExt() : "ooxml";
String fileExt = fileUtility.getFileExtension(fileName);
// get document type (word, cell or slide)
DocumentType type = fileUtility.getDocumentType(fileName);
// convert to .ooxml
String internalFileExt = "ooxml";
try { try {
// check if the file with such an extension can be converted // check if the file with such an extension can be converted
if (fileUtility.getConvertExts().contains(fileExt)) { if (documentManager.getDefaultConvertExtension(fileName) != null) {
String key = serviceConverter.generateRevisionId(fileUri); // generate document key ConvertRequest convertRequest = ConvertRequest.builder()
ConvertedData response = serviceConverter // get the URL to the converted file .password(filePass)
.getConvertedData(fileUri, fileExt, internalFileExt, key, filePass, true, lang); .outputtype(conversionExtension)
.region(lang)
.async(true)
.build();
String newFileUri = response.getUri(); ConvertResponse convertResponse = convertService.processConvert(convertRequest, fileName);
String newFileType = "." + response.getFileType();
if (newFileUri.isEmpty()) { if (convertResponse.getError() != null || convertResponse.getFileUrl() == null) {
return "{ \"step\" : \"0\", \"filename\" : \"" + fileName + "\"}"; return objectMapper.writeValueAsString(convertResponse);
} }
String newFileUri = convertResponse.getFileUrl();
String newFileType = convertResponse.getFileType();
/* get a file name of an internal file extension with an index if the file /* get a file name of an internal file extension with an index if the file
with such a name already exists */ with such a name already exists */
String nameWithInternalExt = fileUtility.getFileNameWithoutExtension(fileName) + newFileType; final String oldFileName = fileName;
String nameWithInternalExt = documentManager.getBaseName(fileName) + "." + newFileType;
String correctedName = documentManager.getCorrectName(nameWithInternalExt); String correctedName = documentManager.getCorrectName(nameWithInternalExt);
URL url = new URL(newFileUri); fileName = requestManager.executeGetRequest(newFileUri, new RequestManager.Callback<String>() {
java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection(); public String doWork(final Object response) throws IOException {
InputStream stream = connection.getInputStream(); // get input stream of the converted file InputStream stream = ((HttpEntity) response).getContent(); // get input stream of the converted
// file
if (stream == null) { if (stream == null) {
connection.disconnect(); throw new RuntimeException("Input stream is null");
throw new RuntimeException("Input stream is null"); }
}
// remove source file
storageMutator.deleteFile(fileName);
// create the converted file with input stream // remove source file
storageMutator.createFile(Path.of(storagePathBuilder.getFileLocation(correctedName)), stream); storageMutator.deleteFile(oldFileName);
fileName = correctedName;
// create the converted file with input stream
storageMutator.createFile(Path.of(storagePathBuilder.getFileLocation(correctedName)), stream);
return correctedName;
}
});
} }
// create meta information about the converted file with the user ID and name specified // create meta information about the converted file with the user ID and name specified
return createUserMetadata(uid, fileName); return createUserMetadata(uid, fileName);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
// if the operation of file converting is unsuccessful, an error occurs
return "{ \"error\": \"" + e.getMessage() + "\"}";
} }
// if the operation of file converting is unsuccessful, an error occurs
return "{ \"error\": \"" + "The file can't be converted.\"}";
} }
@PostMapping("/delete") @PostMapping("/delete")
@ResponseBody @ResponseBody
public String delete(@RequestBody final Converter body) { // delete a file public String delete(@RequestBody final Converter body) { // delete a file
try { try {
String fullFileName = fileUtility.getFileName(body.getFileName()); // get full file name String filename = body.getFileName();
boolean success = false;
// delete a file from the storage and return the status of this operation (true or false) if (filename != null) {
boolean fileSuccess = storageMutator.deleteFile(fullFileName); String fullFileName = documentManager.getDocumentName(filename); // get full file name
// delete file history and return the status of this operation (true or false) // delete a file from the storage and return the status of this operation (true or false)
boolean historySuccess = storageMutator.deleteFileHistory(fullFileName); boolean fileSuccess = storageMutator.deleteFile(fullFileName);
return "{ \"success\": \"" + (fileSuccess && historySuccess) + "\"}"; // delete file history and return the status of this operation (true or false)
boolean historySuccess = storageMutator.deleteFileHistory(fullFileName);
success = fileSuccess && historySuccess;
} else {
// delete the user's folder and return the boolean status
success = storageMutator.deleteUserFolder();
}
return "{ \"success\": \"" + (success) + "\"}";
} catch (Exception e) { } catch (Exception e) {
// if the operation of file deleting is unsuccessful, an error occurs // if the operation of file deleting is unsuccessful, an error occurs
return "{ \"error\": \"" + e.getMessage() + "\"}"; return "{ \"error\": \"" + e.getMessage() + "\"}";
@ -322,13 +327,12 @@ public class FileController {
@RequestParam("file") final String file) { // history file @RequestParam("file") final String file) { // history file
try { try {
// check if a token is enabled or not // check if a token is enabled or not
if (jwtManager.tokenEnabled() && jwtManager.tokenUseForRequest()) { if (settingsManager.isSecurityEnabled()) {
String header = request.getHeader(documentJwtHeader == null // get the document JWT header String header = request.getHeader(settingsManager.getSecurityHeader());
|| documentJwtHeader.isEmpty() ? "Authorization" : documentJwtHeader);
if (header != null && !header.isEmpty()) { if (header != null && !header.isEmpty()) {
String token = header String token = header
.replace("Bearer ", ""); // token is the header without the Bearer prefix .replace("Bearer ", ""); // token is the header without the Bearer prefix
jwtManager.readToken(token); // read the token jwtManager.verify(token); // read the token
} else { } else {
return null; return null;
} }
@ -346,13 +350,12 @@ public class FileController {
final String userAddress) { final String userAddress) {
try { try {
// check if a token is enabled or not // check if a token is enabled or not
if (jwtManager.tokenEnabled() && userAddress != null && jwtManager.tokenUseForRequest()) { if (settingsManager.isSecurityEnabled() && userAddress != null) {
String header = request.getHeader(documentJwtHeader == null // get the document JWT header String header = request.getHeader(settingsManager.getSecurityHeader());
|| documentJwtHeader.isEmpty() ? "Authorization" : documentJwtHeader);
if (header != null && !header.isEmpty()) { if (header != null && !header.isEmpty()) {
String token = header String token = header
.replace("Bearer ", ""); // token is the header without the Bearer prefix .replace("Bearer ", ""); // token is the header without the Bearer prefix
jwtManager.readToken(token); // read the token jwtManager.verify(token); // read the token
} else { } else {
return null; return null;
} }
@ -382,10 +385,10 @@ public class FileController {
sampleData, sampleData,
uid, uid,
user.get().getName()); // create a demo document with the sample data user.get().getName()); // create a demo document with the sample data
if (fileName.isBlank() || fileName == null) { if (fileName == null || fileName.isBlank()) {
throw new RuntimeException("You must have forgotten to add asset files"); throw new RuntimeException("You must have forgotten to add asset files");
} }
return "redirect:editor?fileName=" + URLEncoder return "redirect:editor?action=edit&fileName=" + URLEncoder
.encode(fileName, StandardCharsets.UTF_8); // redirect the request .encode(fileName, StandardCharsets.UTF_8); // redirect the request
} catch (Exception ex) { } catch (Exception ex) {
model.addAttribute("error", ex.getMessage()); model.addAttribute("error", ex.getMessage());
@ -418,28 +421,29 @@ public class FileController {
public String track(final HttpServletRequest request, // track file changes public String track(final HttpServletRequest request, // track file changes
@RequestParam("fileName") final String fileName, @RequestParam("fileName") final String fileName,
@RequestParam("userAddress") final String userAddress, @RequestParam("userAddress") final String userAddress,
@RequestBody final Track body) { @RequestBody final Callback body) {
Track track; Callback callback;
try { try {
String bodyString = objectMapper String bodyString = objectMapper
.writeValueAsString(body); // write the request body to the object mapper as a string .writeValueAsString(body); // write the request body to the object mapper as a string
String header = request.getHeader(documentJwtHeader == null // get the request header
|| documentJwtHeader.isEmpty() ? "Authorization" : documentJwtHeader);
if (bodyString.isEmpty()) { // if the request body is empty, an error occurs if (bodyString.isEmpty()) { // if the request body is empty, an error occurs
throw new RuntimeException("{\"error\":1,\"message\":\"Request payload is empty\"}"); throw new RuntimeException("{\"error\":1,\"message\":\"Request payload is empty\"}");
} }
JSONObject bodyCheck = jwtManager.parseBody(bodyString, header); // parse the request body String authorizationHeader = request.getHeader(settingsManager.getSecurityHeader());
track = objectMapper.readValue(bodyCheck.toJSONString(), Track.class); // read the request body callback = callbackService.verifyCallback(body, authorizationHeader);
callbackService.processCallback(callback, fileName);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); String message = e.getMessage();
return e.getMessage(); if (!message.contains("\"error\":1")) {
e.printStackTrace();
}
return message;
} }
int error = callbackHandler.handle(track, fileName); return "{\"error\":\"0\"}";
return "{\"error\":" + error + "}";
} }
@PostMapping("/saveas") @PostMapping("/saveas")
@ -447,24 +451,30 @@ public class FileController {
public String saveAs(@RequestBody final SaveAs body, @CookieValue("uid") final String uid) { public String saveAs(@RequestBody final SaveAs body, @CookieValue("uid") final String uid) {
try { try {
String fileName = documentManager.getCorrectName(body.getTitle()); String fileName = documentManager.getCorrectName(body.getTitle());
String curExt = fileUtility.getFileExtension(fileName);
if (!fileUtility.getFileExts().contains(curExt)) { if (documentManager.getDocumentType(fileName) == null) {
return "{\"error\":\"File type is not supported\"}"; return "{\"error\":\"File type is not supported\"}";
} }
URL url = new URL(body.getUrl()); String url = body.getUrl();
java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection();
InputStream stream = connection.getInputStream();
if (Integer.parseInt(filesizeMax) < stream.available() || stream.available() <= 0) { url = urlManager.replaceToInnerDocumentServerUrl(url);
return "{\"error\":\"File size is incorrect\"}";
}
storageMutator.createFile(Path.of(storagePathBuilder.getFileLocation(fileName)), stream);
createUserMetadata(uid, fileName);
return "{\"file\": \"" + fileName + "\"}"; return requestManager.executeGetRequest(url, new RequestManager.Callback<String>() {
} catch (IOException e) { @Override
public String doWork(final Object response) throws Exception {
InputStream stream = ((HttpEntity) response).getContent();
if (documentManager.getMaxFileSize() < stream.available() || stream.available() <= 0) {
return "{\"error\":\"File size is incorrect\"}";
}
storageMutator.createFile(Path.of(storagePathBuilder.getFileLocation(fileName)), stream);
createUserMetadata(uid, fileName);
return "{\"file\": \"" + fileName + "\"}";
}
});
} catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
return "{ \"error\" : 1, \"message\" : \"" + e.getMessage() + "\"}"; return "{ \"error\" : 1, \"message\" : \"" + e.getMessage() + "\"}";
} }
@ -473,14 +483,18 @@ public class FileController {
@PostMapping("/rename") @PostMapping("/rename")
@ResponseBody @ResponseBody
public String rename(@RequestBody final Rename body) { public String rename(@RequestBody final Rename body) {
String fileName = body.getFileName(); CommandRequest commandRequest = CommandRequest.builder()
.key(body.getFileKey())
HashMap<String, String> meta = new HashMap<>(); .c(Command.META)
meta.put("title", fileName + "." + body.getFileType()); .meta(Meta.builder()
.title(body.getFileName() + "." + body.getFileType())
.build())
.build();
try { try {
callbackManager.commandRequest("meta", body.getFileKey(), meta);
return "result ok"; CommandResponse commandResponse = commandService.processCommand(commandRequest, body.getFileName());
return commandResponse.getError().getDescription();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
return e.getMessage(); return e.getMessage();
@ -514,7 +528,6 @@ public class FileController {
if (!link.contains(storagePathBuilder.getServerUrl(true))) { if (!link.contains(storagePathBuilder.getServerUrl(true))) {
HashMap<String, String> data = new HashMap<>(); HashMap<String, String> data = new HashMap<>();
data.put("url", link); data.put("url", link);
data.put("directUrl", link);
return gson.toJson(data); return gson.toJson(data);
} }
@ -529,7 +542,8 @@ public class FileController {
if (fileName.equals("")) { if (fileName.equals("")) {
try { try {
String path = (String) body.getPath(); String path = (String) body.getPath();
path = fileUtility.getFileName(path); path = path.substring(path.lastIndexOf('/') + 1);
path = path.split("\\?")[0];
File f = new File(storagePathBuilder.getFileLocation(path)); File f = new File(storagePathBuilder.getFileLocation(path));
if (f.exists()) { if (f.exists()) {
fileName = path; fileName = path;
@ -552,19 +566,18 @@ public class FileController {
referenceData.put("fileKey", gson.toJson(fileKey)); referenceData.put("fileKey", gson.toJson(fileKey));
HashMap<String, Object> data = new HashMap<>(); HashMap<String, Object> data = new HashMap<>();
data.put("fileType", fileUtility.getFileExtension(fileName)); data.put("fileType", documentManager.getDocumentName(fileName));
data.put("key", serviceConverter.generateRevisionId( data.put("key", documentManager.generateRevisionId(
storagePathBuilder.getStorageLocation() storagePathBuilder.getStorageLocation()
+ "/" + fileName + "/" + "/" + fileName + "/"
+ new File(storagePathBuilder.getFileLocation(fileName)).lastModified() + new File(storagePathBuilder.getFileLocation(fileName)).lastModified()
)); ));
data.put("url", documentManager.getDownloadUrl(fileName, true)); data.put("url", urlManager.getFileUrl(fileName));
data.put("directUrl", body.getDirectUrl() ? documentManager.getDownloadUrl(fileName, false) : null);
data.put("referenceData", referenceData); data.put("referenceData", referenceData);
data.put("path", fileName); data.put("path", fileName);
data.put("link", storagePathBuilder.getServerUrl(true) + "/editor?fileName=" + fileName); data.put("link", storagePathBuilder.getServerUrl(true) + "/editor?fileName=" + fileName);
if (jwtManager.tokenEnabled()) { if (settingsManager.isSecurityEnabled()) {
String token = jwtManager.createToken(data); String token = jwtManager.createToken(data);
data.put("token", token); data.put("token", token);
} }
@ -584,9 +597,8 @@ public class FileController {
@GetMapping("/historydata") @GetMapping("/historydata")
@ResponseBody @ResponseBody
public String history(@RequestParam("fileName") final String fileName, public String history(@RequestParam("fileName") final String fileName,
@RequestParam("version") final String version, @RequestParam("version") final String version) {
@RequestParam(value = "directUrl", defaultValue = "false") final Boolean directUrl) { return historyManager.getHistoryData(fileName, version);
return historyManager.getHistoryData(fileName, version, directUrl);
} }
@PutMapping("/restore") @PutMapping("/restore")
@ -599,7 +611,7 @@ public class FileController {
String historyDirectory = storagePathBuilder.getHistoryDir(sourcePathFile.toString()); String historyDirectory = storagePathBuilder.getHistoryDir(sourcePathFile.toString());
Integer bumpedVersion = storagePathBuilder.getFileVersion(historyDirectory, false); Integer bumpedVersion = storagePathBuilder.getFileVersion(historyDirectory, false);
String bumpedVersionStringDirectory = documentManager.versionDir(historyDirectory, bumpedVersion, true); String bumpedVersionStringDirectory = historyManager.versionDir(historyDirectory, bumpedVersion, true);
File bumpedVersionDirectory = new File(bumpedVersionStringDirectory); File bumpedVersionDirectory = new File(bumpedVersionStringDirectory);
if (!bumpedVersionDirectory.exists()) { if (!bumpedVersionDirectory.exists()) {
bumpedVersionDirectory.mkdir(); bumpedVersionDirectory.mkdir();
@ -608,7 +620,7 @@ public class FileController {
Path bumpedKeyPathFile = Paths.get(bumpedVersionStringDirectory, "key.txt"); Path bumpedKeyPathFile = Paths.get(bumpedVersionStringDirectory, "key.txt");
String bumpedKeyStringFile = bumpedKeyPathFile.toString(); String bumpedKeyStringFile = bumpedKeyPathFile.toString();
File bumpedKeyFile = new File(bumpedKeyStringFile); File bumpedKeyFile = new File(bumpedKeyStringFile);
String bumpedKey = serviceConverter.generateRevisionId( String bumpedKey = documentManager.generateRevisionId(
storagePathBuilder.getStorageLocation() storagePathBuilder.getStorageLocation()
+ "/" + "/"
+ body.getFileName() + body.getFileName()
@ -642,13 +654,13 @@ public class FileController {
bumpedChangesFileWriter.write(bumpedChangesContent); bumpedChangesFileWriter.write(bumpedChangesContent);
bumpedChangesFileWriter.close(); bumpedChangesFileWriter.close();
String sourceExtension = fileUtility.getFileExtension(body.getFileName()); String sourceExtension = documentManager.getExtension(body.getFileName());
String previousBasename = "prev." + sourceExtension; String previousBasename = "prev." + sourceExtension;
Path bumpedFile = Paths.get(bumpedVersionStringDirectory, previousBasename); Path bumpedFile = Paths.get(bumpedVersionStringDirectory, previousBasename);
Files.move(sourcePathFile, bumpedFile); Files.move(sourcePathFile, bumpedFile);
String recoveryVersionStringDirectory = documentManager.versionDir( String recoveryVersionStringDirectory = historyManager.versionDir(
historyDirectory, historyDirectory,
body.getVersion(), body.getVersion(),
true true

View File

@ -0,0 +1,121 @@
/**
*
* (c) Copyright Ascensio System SIA 2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.onlyoffice.integration.controllers;
import com.onlyoffice.integration.dto.ForgottenFile;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.http.ResponseEntity;
import org.springframework.http.HttpStatus;
import com.onlyoffice.model.commandservice.CommandRequest;
import com.onlyoffice.model.commandservice.CommandResponse;
import com.onlyoffice.model.commandservice.commandrequest.Command;
import com.onlyoffice.manager.document.DocumentManager;
import com.onlyoffice.service.command.CommandService;
import java.util.ArrayList;
import java.util.List;
@CrossOrigin("*")
@Controller
public class ForgottenController {
@Value("${server.version}")
private String serverVersion;
@Value("${enable-forgotten}")
private String enableForgotten;
@Autowired
private CommandService commandService;
@Autowired
private DocumentManager documentManager;
@GetMapping("${url.forgotten}")
public String index(final Model model) {
if (!forgottenEnabled()) {
model.addAttribute("error", "The forgotten page is disabled");
return "error.html";
}
model.addAttribute("files", getForgottenFiles());
model.addAttribute("serverVersion", serverVersion);
return "forgotten.html";
}
private ArrayList<ForgottenFile> getForgottenFiles() {
ArrayList<ForgottenFile> files = new ArrayList<ForgottenFile>();
try {
CommandRequest commandRequest = CommandRequest.builder()
.c(Command.GET_FORGOTTEN_LIST)
.build();
CommandResponse commandResponse = commandService.processCommand(commandRequest, null);
List<String> keys = commandResponse.getKeys();
for (int i = 0; i < keys.size(); i++) {
commandRequest = CommandRequest.builder()
.c(Command.GET_FORGOTTEN)
.key(keys.get(i))
.build();
commandResponse = commandService.processCommand(commandRequest, null);
ForgottenFile file = new ForgottenFile(
commandResponse.getKey(),
documentManager.getDocumentType(commandResponse.getUrl()).toString().toLowerCase(),
commandResponse.getUrl()
);
files.add(file);
}
} catch (Exception e) {
e.printStackTrace();
}
return files;
}
private boolean forgottenEnabled() {
return Boolean.valueOf(enableForgotten);
}
@DeleteMapping("/forgotten/{filename}")
public ResponseEntity<String> delete(@PathVariable("filename") final String filename) {
if (!forgottenEnabled()) {
return new ResponseEntity<>(HttpStatus.FORBIDDEN);
}
try {
CommandRequest commandRequest = CommandRequest.builder()
.c(Command.DELETE_FORGOTTEN)
.key(filename)
.build();
CommandResponse commandResponse = commandService.processCommand(commandRequest, null);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} catch (Exception e) {
e.printStackTrace();
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}

View File

@ -21,9 +21,11 @@ package com.onlyoffice.integration.controllers;
import com.onlyoffice.integration.documentserver.storage.FileStorageMutator; import com.onlyoffice.integration.documentserver.storage.FileStorageMutator;
import com.onlyoffice.integration.documentserver.storage.FileStoragePathBuilder; import com.onlyoffice.integration.documentserver.storage.FileStoragePathBuilder;
import com.onlyoffice.integration.documentserver.util.Misc; import com.onlyoffice.integration.documentserver.util.Misc;
import com.onlyoffice.integration.documentserver.util.file.FileUtility;
import com.onlyoffice.integration.entities.User; import com.onlyoffice.integration.entities.User;
import com.onlyoffice.integration.sdk.manager.DocumentManager;
import com.onlyoffice.integration.sdk.manager.UrlManager;
import com.onlyoffice.integration.services.UserServices; import com.onlyoffice.integration.services.UserServices;
import com.onlyoffice.integration.dto.FormatsList;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
@ -31,8 +33,8 @@ import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.http.ResponseEntity;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -52,20 +54,17 @@ public class IndexController {
@Autowired @Autowired
private FileStoragePathBuilder storagePathBuilder; private FileStoragePathBuilder storagePathBuilder;
@Autowired
private FileUtility fileUtility;
@Autowired @Autowired
private Misc mistUtility; private Misc mistUtility;
@Autowired @Autowired
private UserServices userService; private UserServices userService;
@Value("${files.docservice.url.site}") @Autowired
private String docserviceSite; private DocumentManager documentManager;
@Value("${files.docservice.url.preloader}") @Autowired
private String docservicePreloader; private UrlManager urlManager;
@Value("${url.converter}") @Value("${url.converter}")
private String urlConverter; private String urlConverter;
@ -79,9 +78,11 @@ public class IndexController {
@Value("${server.version}") @Value("${server.version}")
private String serverVersion; private String serverVersion;
@Value("${enable-forgotten}")
private String enableForgotten;
@GetMapping("${url.index}") @GetMapping("${url.index}")
public String index(@RequestParam(value = "directUrl", required = false) final Boolean directUrl, public String index(final Model model) {
final Model model) {
java.io.File[] files = storageMutator.getStoredFiles(); // get all the stored files from the storage java.io.File[] files = storageMutator.getStoredFiles(); // get all the stored files from the storage
List<String> docTypes = new ArrayList<>(); List<String> docTypes = new ArrayList<>();
List<Boolean> filesEditable = new ArrayList<>(); List<Boolean> filesEditable = new ArrayList<>();
@ -105,15 +106,14 @@ public class IndexController {
for (java.io.File file:files) { // run through all the files for (java.io.File file:files) { // run through all the files
String fileName = file.getName(); // get file name String fileName = file.getName(); // get file name
docTypes.add(fileUtility docTypes.add(documentManager
.getDocumentType(fileName) .getDocumentType(fileName)
.toString() .toString()
.toLowerCase()); // add a document type of each file to the list .toLowerCase()); // add a document type of each file to the list
filesEditable.add(fileUtility.getEditedExts() filesEditable.add(documentManager.isEditable(fileName)); // specify if a file is editable or not
.contains(fileUtility.getFileExtension(fileName))); // specify if a file is editable or not
versions.add(" [" + storagePathBuilder. versions.add(" [" + storagePathBuilder.
getFileVersion(fileName, true) + "]"); // add a file version to the list getFileVersion(fileName, true) + "]"); // add a file version to the list
isFillFormDoc.add(fileUtility.getFillExts().contains(fileUtility.getFileExtension(fileName))); isFillFormDoc.add(documentManager.isFillable(fileName));
} }
// add all the parameters to the model // add all the parameters to the model
@ -122,12 +122,12 @@ public class IndexController {
model.addAttribute("files", files); model.addAttribute("files", files);
model.addAttribute("docTypes", docTypes); model.addAttribute("docTypes", docTypes);
model.addAttribute("filesEditable", filesEditable); model.addAttribute("filesEditable", filesEditable);
model.addAttribute("datadocs", docserviceSite + docservicePreloader); model.addAttribute("datadocs", urlManager.getDocumentServerPreloaderApiUrl());
model.addAttribute("tooltip", tooltip); model.addAttribute("tooltip", tooltip);
model.addAttribute("users", users); model.addAttribute("users", users);
model.addAttribute("languages", languages); model.addAttribute("languages", languages);
model.addAttribute("directUrl", directUrl);
model.addAttribute("serverVersion", serverVersion); model.addAttribute("serverVersion", serverVersion);
model.addAttribute("enableForgotten", Boolean.valueOf(enableForgotten));
return "index.html"; return "index.html";
} }
@ -136,16 +136,16 @@ public class IndexController {
@ResponseBody @ResponseBody
public HashMap<String, String> configParameters() { // get configuration parameters public HashMap<String, String> configParameters() { // get configuration parameters
HashMap<String, String> configuration = new HashMap<>(); HashMap<String, String> configuration = new HashMap<>();
configuration.put("FillExtList", String.join(",", fileUtility
.getFillExts())); // put a list of the extensions that can be filled to config
configuration.put("ConverExtList", String.join(",", fileUtility
.getConvertExts())); // put a list of the extensions that can be converted to config
configuration.put("EditedExtList", String.join(",", fileUtility
.getEditedExts())); // put a list of the extensions that can be edited to config
configuration.put("UrlConverter", urlConverter); configuration.put("UrlConverter", urlConverter);
configuration.put("UrlEditor", urlEditor); configuration.put("UrlEditor", urlEditor);
return configuration; return configuration;
} }
@GetMapping("/formats")
@ResponseBody
public ResponseEntity<FormatsList> formats() { // return all the supported formats
FormatsList list = new FormatsList(documentManager.getFormats());
return ResponseEntity.ok(list);
}
} }

View File

@ -1,32 +0,0 @@
/**
*
* (c) Copyright Ascensio System SIA 2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.onlyoffice.integration.documentserver.callbacks;
import com.onlyoffice.integration.dto.Track;
import org.springframework.beans.factory.annotation.Autowired;
// specify the callback handler functions
public interface Callback {
int handle(Track body, String fileName); // handle the callback
int getStatus(); // get document status
@Autowired
default void selfRegistration(CallbackHandler callbackHandler) { // register a callback handler
callbackHandler.register(getStatus(), this);
}
}

View File

@ -1,50 +0,0 @@
/**
*
* (c) Copyright Ascensio System SIA 2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.onlyoffice.integration.documentserver.callbacks;
import com.onlyoffice.integration.dto.Track;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
public class CallbackHandler {
private Logger logger = LoggerFactory.getLogger(CallbackHandler.class);
private Map<Integer, Callback> callbackHandlers = new HashMap<>();
public void register(final int code, final Callback callback) { // register a callback handler
callbackHandlers.put(code, callback);
}
public int handle(final Track body, final String fileName) { // handle a callback
Callback callback = callbackHandlers.get(body.getStatus());
if (callback == null) {
logger.warn("Callback status " + body.getStatus() + " is not supported yet");
return 0;
}
int result = callback.handle(body, fileName);
return result;
}
}

View File

@ -1,35 +0,0 @@
/**
*
* (c) Copyright Ascensio System SIA 2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.onlyoffice.integration.documentserver.callbacks;
// document status
public enum Status {
EDITING(1), // 1 - document is being edited
SAVE(2), // 2 - document is ready for saving
CORRUPTED(3), // 3 - document saving error has occurred
MUST_FORCE_SAVE(6), // 6 - document is being edited, but the current document state is saved
CORRUPTED_FORCE_SAVE(7); // 7 - error has occurred while force saving the document
private int code;
Status(final int codeParam) {
this.code = codeParam;
}
public int getCode() { // get document status
return this.code;
}
}

View File

@ -1,59 +0,0 @@
/**
*
* (c) Copyright Ascensio System SIA 2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.onlyoffice.integration.documentserver.callbacks.implementations;
import com.onlyoffice.integration.documentserver.callbacks.Callback;
import com.onlyoffice.integration.documentserver.callbacks.Status;
import com.onlyoffice.integration.documentserver.managers.callback.CallbackManager;
import com.onlyoffice.integration.dto.Action;
import com.onlyoffice.integration.dto.Track;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class EditCallback implements Callback {
@Autowired
private CallbackManager callbackManager;
@Override
public int handle(final Track body,
final String fileName) { // handle the callback when the document is being edited
int result = 0;
Action action = body.getActions().get(0); // get the user ID who is editing the document
if (action.getType().equals(com.onlyoffice.integration.documentserver.models.enums
.Action.edit)) { // if this value is not equal to the user ID
String user = action.getUserid(); // get user ID
if (!body.getUsers().contains(user)) { // if this user is not specified in the body
String key = body.getKey(); // get document key
try {
// create a command request to forcibly save the document being edited without closing it
callbackManager.commandRequest("forcesave", key, null);
} catch (Exception e) {
e.printStackTrace();
result = 1;
}
}
}
return result;
}
@Override
public int getStatus() { // get document status
return Status.EDITING.getCode(); // return status 1 - document is being edited
}
}

View File

@ -1,50 +0,0 @@
/**
*
* (c) Copyright Ascensio System SIA 2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.onlyoffice.integration.documentserver.callbacks.implementations;
import com.onlyoffice.integration.documentserver.callbacks.Callback;
import com.onlyoffice.integration.documentserver.callbacks.Status;
import com.onlyoffice.integration.documentserver.managers.callback.CallbackManager;
import com.onlyoffice.integration.dto.Track;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class ForcesaveCallback implements Callback {
@Autowired
private CallbackManager callbackManager;
@Override
public int handle(final Track body,
final String fileName) { // handle the callback when the force saving request is performed
int result = 0;
try {
callbackManager.processForceSave(body, fileName); // file force saving process
} catch (Exception ex) {
ex.printStackTrace();
result = 1;
}
return result;
}
@Override
public int getStatus() { // get document status
// return status 6 - document is being edited, but the current document state is saved
return Status.MUST_FORCE_SAVE.getCode();
}
}

View File

@ -1,50 +0,0 @@
/**
*
* (c) Copyright Ascensio System SIA 2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.onlyoffice.integration.documentserver.callbacks.implementations;
import com.onlyoffice.integration.documentserver.callbacks.Callback;
import com.onlyoffice.integration.documentserver.callbacks.Status;
import com.onlyoffice.integration.documentserver.managers.callback.CallbackManager;
import com.onlyoffice.integration.dto.Track;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class SaveCallback implements Callback {
@Autowired
private CallbackManager callbackManager;
@Override
public int handle(final Track body,
final String fileName) { // handle the callback when the saving request is performed
int result = 0;
try {
callbackManager.processSave(body, fileName); // file saving process
} catch (Exception ex) {
ex.printStackTrace();
result = 1;
}
return result;
}
@Override
public int getStatus() { // get document status
return Status.SAVE.getCode(); // return status 2 - document is ready for saving
}
}

View File

@ -18,11 +18,10 @@
package com.onlyoffice.integration.documentserver.managers.callback; package com.onlyoffice.integration.documentserver.managers.callback;
import com.onlyoffice.integration.dto.Track; import com.onlyoffice.model.documenteditor.Callback;
import java.util.HashMap;
public interface CallbackManager { // specify the callback manager functions public interface CallbackManager { // specify the callback manager functions
void processSave(Track body, String fileName); // file saving process void processEditing(Callback callback, String fileName);
void commandRequest(String method, String key, HashMap meta); // create a command request void processSave(Callback callback, String fileName);
void processForceSave(Track body, String fileName); // file force saving process void processForceSave(Callback callback, String fileName);
} }

View File

@ -19,62 +19,60 @@
package com.onlyoffice.integration.documentserver.managers.callback; package com.onlyoffice.integration.documentserver.managers.callback;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.onlyoffice.integration.documentserver.managers.document.DocumentManager; import com.onlyoffice.integration.documentserver.managers.history.HistoryManager;
import com.onlyoffice.integration.documentserver.managers.jwt.JwtManager;
import com.onlyoffice.integration.documentserver.storage.FileStorageMutator; import com.onlyoffice.integration.documentserver.storage.FileStorageMutator;
import com.onlyoffice.integration.documentserver.storage.FileStoragePathBuilder; import com.onlyoffice.integration.documentserver.storage.FileStoragePathBuilder;
import com.onlyoffice.integration.documentserver.util.file.FileUtility; import com.onlyoffice.integration.sdk.manager.DocumentManager;
import com.onlyoffice.integration.dto.Action; import com.onlyoffice.integration.sdk.manager.UrlManager;
import com.onlyoffice.integration.documentserver.util.service.ServiceConverter; import com.onlyoffice.manager.request.RequestManager;
import com.onlyoffice.integration.dto.Track; import com.onlyoffice.model.commandservice.CommandRequest;
import com.onlyoffice.model.commandservice.commandrequest.Command;
import com.onlyoffice.model.convertservice.ConvertRequest;
import com.onlyoffice.model.convertservice.ConvertResponse;
import com.onlyoffice.model.documenteditor.Callback;
import com.onlyoffice.model.documenteditor.callback.Action;
import com.onlyoffice.model.documenteditor.callback.ForcesaveType;
import com.onlyoffice.model.documenteditor.callback.action.Type;
import com.onlyoffice.service.command.CommandService;
import com.onlyoffice.service.convert.ConvertService;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import org.apache.http.HttpEntity;
import org.json.simple.JSONObject; import org.json.simple.JSONObject;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import static com.onlyoffice.integration.documentserver.util.Constants.FILE_SAVE_TIMEOUT;
// todo: Refactoring // todo: Refactoring
@Component @Component
@Primary @Primary
public class DefaultCallbackManager implements CallbackManager { public class DefaultCallbackManager implements CallbackManager {
@Value("${files.docservice.url.site}")
private String docserviceUrlSite;
@Value("${files.docservice.url.command}")
private String docserviceUrlCommand;
@Value("${files.docservice.header}")
private String documentJwtHeader;
@Autowired @Autowired
private DocumentManager documentManager; private DocumentManager documentManager;
@Autowired @Autowired
private JwtManager jwtManager;
@Autowired
private FileUtility fileUtility;
@Autowired
private FileStorageMutator storageMutator; private FileStorageMutator storageMutator;
@Autowired @Autowired
private FileStoragePathBuilder storagePathBuilder; private FileStoragePathBuilder storagePathBuilder;
@Autowired @Autowired
private ObjectMapper objectMapper; private ObjectMapper objectMapper;
@Autowired @Autowired
private ServiceConverter serviceConverter; private ConvertService convertService;
@Autowired
private RequestManager requestManager;
@Autowired
private CommandService commandService;
@Autowired
private HistoryManager historyManager;
@Autowired
private UrlManager urlManager;
// download file from url // download file from url
@SneakyThrows @SneakyThrows
@ -83,23 +81,18 @@ public class DefaultCallbackManager implements CallbackManager {
throw new RuntimeException("Url argument is not specified"); // URL isn't specified throw new RuntimeException("Url argument is not specified"); // URL isn't specified
} }
URL uri = new URL(url); return requestManager.executeGetRequest(url, new RequestManager.Callback<byte[]>() {
java.net.HttpURLConnection connection = (java.net.HttpURLConnection) uri.openConnection(); public byte[] doWork(final Object response) throws IOException {
connection.setConnectTimeout(FILE_SAVE_TIMEOUT); InputStream stream = ((HttpEntity) response).getContent(); // get input stream of the converted
InputStream stream = connection.getInputStream(); // get input stream of the file information from the URL // file
int statusCode = connection.getResponseCode(); if (stream == null) {
if (statusCode != HttpStatus.OK.value()) { // checking status code throw new RuntimeException("Input stream is null");
connection.disconnect(); }
throw new RuntimeException("Document editing service returned status: " + statusCode);
}
if (stream == null) { return stream.readAllBytes();
connection.disconnect(); }
throw new RuntimeException("Input stream is null"); });
}
return stream.readAllBytes();
} }
// file saving // file saving
@ -112,40 +105,76 @@ public class DefaultCallbackManager implements CallbackManager {
storageMutator.createOrUpdateFile(path, new ByteArrayInputStream(byteArray)); storageMutator.createOrUpdateFile(path, new ByteArrayInputStream(byteArray));
} }
@Override
public void processEditing(final Callback callback, final String fileName) {
Action action = callback.getActions().get(0); // get the user ID who is editing the document
if (action.getType().equals(Type.CONNECTED)) { // if this value is not equal to the user ID
String user = action.getUserid(); // get user ID
if (!callback.getUsers().contains(user)) { // if this user is not specified in the body
CommandRequest commandRequest = CommandRequest.builder()
.c(Command.FORCESAVE)
.key(callback.getKey())
.build();
// create a command request to forcibly save the document being edited without closing it
try {
commandService.processCommand(commandRequest, fileName);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
@SneakyThrows @SneakyThrows
public void processSave(final Track body, final String fileName) { // file saving process public void processSave(final Callback callback, final String fileName) { // file saving process
String downloadUri = body.getUrl(); String downloadUri = callback.getUrl();
String changesUri = body.getChangesurl(); String changesUri = callback.getChangesurl();
String key = body.getKey(); String key = callback.getKey();
String newFileName = fileName; String newFileName = fileName;
String curExt = fileUtility.getFileExtension(fileName); // get current file extension String curExt = documentManager.getExtension(fileName); // get current file extension
String downloadExt = body.getFiletype(); // get an extension of the downloaded file String downloadExt = callback.getFiletype(); // get an extension of the downloaded file
String storagePath = storagePathBuilder.getFileLocation(newFileName); // get the path to a new file
if (!Paths.get(storagePath).toFile().exists()) {
throw new RuntimeException("{\"error\":1, \"message\":\"file does not exist\"}");
}
downloadUri = urlManager.replaceToInnerDocumentServerUrl(downloadUri);
changesUri = urlManager.replaceToInnerDocumentServerUrl(changesUri);
// todo: Refactoring // todo: Refactoring
// convert downloaded file to the file with the current extension if these extensions aren't equal // convert downloaded file to the file with the current extension if these extensions aren't equal
if (!curExt.equals(downloadExt)) { if (!curExt.equals(downloadExt)) {
try { try {
String newFileUri = serviceConverter ConvertRequest convertRequest = ConvertRequest.builder()
.getConvertedData(downloadUri, downloadExt, curExt, .key(documentManager.generateRevisionId(downloadUri))
serviceConverter.generateRevisionId(downloadUri), null, false, .url(downloadUri)
null).getUri(); // convert a file and get URL to a new file .outputtype(curExt)
if (newFileUri.isEmpty()) { .async(false)
.build();
// convert a file and get URL to a new file
ConvertResponse convertResponse = convertService.processConvert(convertRequest, fileName);
String newFileUri = convertResponse.getFileUrl();
if (newFileUri == null || newFileUri.isEmpty()) {
newFileName = documentManager newFileName = documentManager
.getCorrectName(fileUtility.getFileNameWithoutExtension(fileName) + "." .getCorrectName(documentManager.getBaseName(fileName) + "."
+ downloadExt); // get the correct file name if it already exists + downloadExt); // get the correct file name if it already exists
} else { } else {
downloadUri = newFileUri; downloadUri = newFileUri;
} }
} catch (Exception e) { } catch (Exception e) {
newFileName = documentManager newFileName = documentManager
.getCorrectName(fileUtility.getFileNameWithoutExtension(fileName) + "." + downloadExt); .getCorrectName(documentManager.getBaseName(fileName) + "." + downloadExt);
} }
} }
byte[] byteArrayFile = getDownloadFile(downloadUri); // download document file byte[] byteArrayFile = getDownloadFile(downloadUri); // download document file
String storagePath = storagePathBuilder.getFileLocation(newFileName); // get the path to a new file
Path lastVersion = Paths.get(storagePathBuilder Path lastVersion = Paths.get(storagePathBuilder
.getFileLocation(fileName)); // get the path to the last file version .getFileLocation(fileName)); // get the path to the last file version
@ -153,7 +182,7 @@ public class DefaultCallbackManager implements CallbackManager {
Path histDir = Paths.get(storagePathBuilder.getHistoryDir(storagePath)); // get the history directory Path histDir = Paths.get(storagePathBuilder.getHistoryDir(storagePath)); // get the history directory
storageMutator.createDirectory(histDir); // and create it storageMutator.createDirectory(histDir); // and create it
String versionDir = documentManager String versionDir = historyManager
.versionDir(histDir.toAbsolutePath().toString(), // get the file version directory .versionDir(histDir.toAbsolutePath().toString(), // get the file version directory
storagePathBuilder storagePathBuilder
.getFileVersion(histDir.toAbsolutePath().toString(), false), true); .getFileVersion(histDir.toAbsolutePath().toString(), false), true);
@ -172,13 +201,13 @@ public class DefaultCallbackManager implements CallbackManager {
.of(versionDir + File.separator + "diff.zip")); // save file changes to the diff.zip archive .of(versionDir + File.separator + "diff.zip")); // save file changes to the diff.zip archive
JSONObject jsonChanges = new JSONObject(); // create a json object for document changes JSONObject jsonChanges = new JSONObject(); // create a json object for document changes
jsonChanges.put("changes", body.getHistory().getChanges()); // put the changes to the json object jsonChanges.put("changes", callback.getHistory().getChanges()); // put the changes to the json object
jsonChanges.put("serverVersion", body.getHistory() jsonChanges.put("serverVersion", callback.getHistory()
.getServerVersion()); // put the server version to the json object .getServerVersion()); // put the server version to the json object
String history = objectMapper.writeValueAsString(jsonChanges); String history = objectMapper.writeValueAsString(jsonChanges);
if (history == null && body.getHistory() != null) { if (history == null && callback.getHistory() != null) {
history = objectMapper.writeValueAsString(body.getHistory()); history = objectMapper.writeValueAsString(callback.getHistory());
} }
if (history != null && !history.isEmpty()) { if (history != null && !history.isEmpty()) {
@ -194,82 +223,16 @@ public class DefaultCallbackManager implements CallbackManager {
} }
} }
// todo: Replace (String method) with (Enum method)
@SneakyThrows @SneakyThrows
public void commandRequest(final String method, public void processForceSave(final Callback callback, final String fileNameParam) { // file force saving process
final String key,
final HashMap meta) { // create a command request
String documentCommandUrl = docserviceUrlSite + docserviceUrlCommand;
URL url = new URL(documentCommandUrl); String downloadUri = callback.getUrl();
java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection();
HashMap<String, Object> params = new HashMap<String, Object>();
params.put("c", method);
params.put("key", key);
if (meta != null) {
params.put("meta", meta);
}
String headerToken;
// check if a secret key to generate token exists or not
if (jwtManager.tokenEnabled() && jwtManager.tokenUseForRequest()) {
Map<String, Object> payloadMap = new HashMap<>();
payloadMap.put("payload", params);
headerToken = jwtManager.createToken(payloadMap); // encode a payload object into a header token
// add a header Authorization with a header token and Authorization prefix in it
connection.setRequestProperty(documentJwtHeader.equals("")
? "Authorization" : documentJwtHeader, "Bearer " + headerToken);
String token = jwtManager.createToken(params); // encode a payload object into a body token
params.put("token", token);
}
String bodyString = objectMapper.writeValueAsString(params);
byte[] bodyByte = bodyString.getBytes(StandardCharsets.UTF_8);
connection.setRequestMethod("POST"); // set the request method
connection
.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); // set the Content-Type header
connection.setDoOutput(true); // set the doOutput field to true
connection.connect();
try (OutputStream os = connection.getOutputStream()) {
os.write(bodyByte); // write bytes to the output stream
}
InputStream stream = connection.getInputStream(); // get input stream
if (stream == null) {
throw new RuntimeException("Could not get an answer");
}
String jsonString = serviceConverter.convertStreamToString(stream); // convert stream to json string
connection.disconnect();
JSONObject response = serviceConverter.convertStringToJSON(jsonString); // convert json string to json object
// todo: Add errors ENUM
String responseCode = response.get("error").toString();
switch (responseCode) {
case "0":
case "4":
break;
default:
throw new RuntimeException(response.toJSONString());
}
}
@SneakyThrows
public void processForceSave(final Track body, final String fileNameParam) { // file force saving process
String downloadUri = body.getUrl();
String fileName = fileNameParam; String fileName = fileNameParam;
String curExt = fileUtility.getFileExtension(fileName); // get current file extension String curExt = documentManager.getExtension(fileName); // get current file extension
String downloadExt = body.getFiletype(); // get an extension of the downloaded file String downloadExt = callback.getFiletype(); // get an extension of the downloaded file
downloadUri = urlManager.replaceToInnerDocumentServerUrl(downloadUri);
Boolean newFileName = false; Boolean newFileName = false;
@ -277,11 +240,19 @@ public class DefaultCallbackManager implements CallbackManager {
// todo: Extract function // todo: Extract function
if (!curExt.equals(downloadExt)) { if (!curExt.equals(downloadExt)) {
try { try {
// convert file and get URL to a new file ConvertRequest convertRequest = ConvertRequest.builder()
String newFileUri = serviceConverter .key(documentManager.generateRevisionId(downloadUri))
.getConvertedData(downloadUri, downloadExt, curExt, serviceConverter .url(downloadUri)
.generateRevisionId(downloadUri), null, false, null).getUri(); .outputtype(curExt)
if (newFileUri.isEmpty()) { .async(false)
.build();
// convert a file and get URL to a new file
ConvertResponse convertResponse = convertService.processConvert(convertRequest, fileName);
String newFileUri = convertResponse.getFileUrl();
if (newFileUri == null || newFileUri.isEmpty()) {
newFileName = true; newFileName = true;
} else { } else {
downloadUri = newFileUri; downloadUri = newFileUri;
@ -296,32 +267,34 @@ public class DefaultCallbackManager implements CallbackManager {
// todo: Use ENUMS // todo: Use ENUMS
// todo: Pointless toString conversion // todo: Pointless toString conversion
boolean isSubmitForm = body.getForcesavetype().toString().equals("3"); boolean isSubmitForm = callback.getForcesavetype().equals(ForcesaveType.SUBMIT_FORM);
// todo: Extract function // todo: Extract function
if (isSubmitForm) { // if the form is submitted if (isSubmitForm) { // if the form is submitted
if (newFileName) { if (newFileName) {
// get the correct file name if it already exists // get the correct file name if it already exists
fileName = documentManager fileName = documentManager
.getCorrectName(fileUtility .getCorrectName(documentManager
.getFileNameWithoutExtension(fileName) + "-form." + downloadExt); .getBaseName(fileName) + "-form." + downloadExt);
} else { } else {
fileName = documentManager fileName = documentManager
.getCorrectName(fileUtility.getFileNameWithoutExtension(fileName) + "-form." + curExt); .getCorrectName(documentManager.getBaseName(fileName) + "-form." + curExt);
} }
forcesavePath = storagePathBuilder.getFileLocation(fileName); // create forcesave path if it doesn't exist forcesavePath = storagePathBuilder.getFileLocation(fileName); // create forcesave path if it doesn't exist
List<Action> actions = body.getActions(); List<com.onlyoffice.model.documenteditor.callback.Action> actions = callback.getActions();
Action action = actions.get(0); com.onlyoffice.model.documenteditor.callback.Action action = actions.get(0);
String user = action.getUserid(); // get the user ID String user = action.getUserid(); // get the user ID
// create meta data for the forcesaved file // create meta data for the forcesaved file
storageMutator.createMeta(fileName, user, "Filling Form"); storageMutator.createMeta(fileName, user, "Filling Form");
try { try {
String formsDataUrl = body.getFormsdataurl(); String formsDataUrl = callback.getFormsdataurl();
formsDataUrl = urlManager.replaceToInnerDocumentServerUrl(formsDataUrl);
if (formsDataUrl != null && !formsDataUrl.isEmpty()) { if (formsDataUrl != null && !formsDataUrl.isEmpty()) {
String formsName = documentManager.getCorrectName(fileUtility String formsName = documentManager.getCorrectName(documentManager
.getFileNameWithoutExtension(fileName) + ".txt"); .getBaseName(fileName) + ".txt");
String formsPath = storagePathBuilder.getFileLocation(formsName); String formsPath = storagePathBuilder.getFileLocation(formsName);
byte[] byteArrayFormsData = getDownloadFile(formsDataUrl); byte[] byteArrayFormsData = getDownloadFile(formsDataUrl);
@ -336,7 +309,7 @@ public class DefaultCallbackManager implements CallbackManager {
} else { } else {
if (newFileName) { if (newFileName) {
fileName = documentManager fileName = documentManager
.getCorrectName(fileUtility.getFileNameWithoutExtension(fileName) + downloadExt); .getCorrectName(documentManager.getBaseName(fileName) + "." + downloadExt);
} }
forcesavePath = storagePathBuilder.getForcesavePath(fileName, false); forcesavePath = storagePathBuilder.getForcesavePath(fileName, false);

View File

@ -1,246 +0,0 @@
/**
*
* (c) Copyright Ascensio System SIA 2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.onlyoffice.integration.documentserver.managers.document;
import com.onlyoffice.integration.documentserver.storage.FileStorageMutator;
import com.onlyoffice.integration.documentserver.storage.FileStoragePathBuilder;
import com.onlyoffice.integration.documentserver.util.file.FileUtility;
import com.onlyoffice.integration.documentserver.util.service.ServiceConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.InetAddress;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import static com.onlyoffice.integration.documentserver.util.Constants.KILOBYTE_SIZE;
@Component
@Primary
public class DefaultDocumentManager implements DocumentManager {
@Value("${files.storage.folder}")
private String storageFolder;
@Value("${files.storage}")
private String filesStorage;
@Value("${url.track}")
private String trackUrl;
@Value("${url.download}")
private String downloadUrl;
@Autowired
private FileStorageMutator storageMutator;
@Autowired
private FileStoragePathBuilder storagePathBuilder;
@Autowired
private FileUtility fileUtility;
@Autowired
private ServiceConverter serviceConverter;
// get URL to the created file
public String getCreateUrl(final String fileName, final Boolean sample) {
String fileExt = fileUtility.getFileExtension(fileName);
String url = storagePathBuilder.getServerUrl(true)
+ "/create?fileExt=" + fileExt + "&sample=" + sample;
return url;
}
// get a file name with an index if the file with such a name already exists
public String getCorrectName(final String fileName) {
String baseName = fileUtility.getFileNameWithoutExtension(fileName); // get file name without extension
String ext = fileUtility.getFileExtension(fileName); // get file extension
String name = baseName + "." + ext; // create a full file name
Path path = Paths.get(storagePathBuilder.getFileLocation(name));
// run through all the files with such a name in the storage directory
for (int i = 1; Files.exists(path); i++) {
name = baseName + " (" + i + ")." + ext; // and add an index to the base name
path = Paths.get(storagePathBuilder.getFileLocation(name));
}
return name;
}
// get file URL
public String getFileUri(final String fileName, final Boolean forDocumentServer) {
try {
String serverPath = storagePathBuilder.getServerUrl(forDocumentServer); // get server URL
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);
if (!filesStorage.isEmpty() && filePathDownload.contains(filesStorage)) {
filePathDownload = filePathDownload.substring(filesStorage.length() + 1);
}
String filePath = serverPath + "/download?fileName=" + URLEncoder
.encode(filePathDownload, java.nio.charset.StandardCharsets.UTF_8.toString()) + "&userAddress"
+ URLEncoder.encode(hostAddress, java.nio.charset.StandardCharsets.UTF_8.toString());
return filePath;
} catch (UnsupportedEncodingException | UnknownHostException e) {
return "";
}
}
// get file URL
public String getHistoryFileUrl(final String fileName, final Integer version, final String file,
final Boolean forDocumentServer) {
try {
String serverPath = storagePathBuilder.getServerUrl(forDocumentServer); // get server URL
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;
return filePath;
} catch (UnsupportedEncodingException | UnknownHostException e) {
return "";
}
}
// get the callback URL
public String getCallback(final String fileName) {
String serverPath = storagePathBuilder.getServerUrl(true);
String storageAddress = storagePathBuilder.getStorageLocation();
try {
String query = trackUrl + "?fileName="
+ URLEncoder.encode(fileName, java.nio.charset.StandardCharsets.UTF_8.toString())
+ "&userAddress=" + URLEncoder
.encode(storageAddress, java.nio.charset.StandardCharsets.UTF_8.toString());
return serverPath + query;
} catch (UnsupportedEncodingException e) {
return "";
}
}
// get URL to download a file
public String getDownloadUrl(final String fileName, final 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;
return serverPath + query;
} catch (UnsupportedEncodingException e) {
return "";
}
}
// get file information
public ArrayList<Map<String, Object>> getFilesInfo() {
ArrayList<Map<String, Object>> files = new ArrayList<>();
// run through all the stored files
for (File file : storageMutator.getStoredFiles()) {
Map<String, Object> map = new LinkedHashMap<>(); // write all the parameters to the map
map.put("version", storagePathBuilder.getFileVersion(file.getName(), false));
map.put("id", serviceConverter
.generateRevisionId(storagePathBuilder.getStorageLocation()
+ "/" + file.getName() + "/"
+ Paths.get(storagePathBuilder.getFileLocation(file.getName()))
.toFile()
.lastModified()));
map.put("contentLength", new BigDecimal(String.valueOf((file.length() / Double.valueOf(KILOBYTE_SIZE))))
.setScale(2, RoundingMode.HALF_UP) + " KB");
map.put("pureContentLength", file.length());
map.put("title", file.getName());
map.put("updated", String.valueOf(new Date(file.lastModified())));
files.add(map);
}
return files;
}
// get file information by its ID
public ArrayList<Map<String, Object>> getFilesInfo(final String fileId) {
ArrayList<Map<String, Object>> file = new ArrayList<>();
for (Map<String, Object> map : getFilesInfo()) {
if (map.get("id").equals(fileId)) {
file.add(map);
break;
}
}
return file;
}
// get the path to the file version by the history path and file version
public String versionDir(final String path, final Integer version, final boolean historyPath) {
if (!historyPath) {
return storagePathBuilder.getHistoryDir(storagePathBuilder.getFileLocation(path)) + version;
}
return path + File.separator + version;
}
// create demo document
public String createDemo(final String fileExt, final Boolean sample, final String uid, final String uname) {
String demoName = (sample ? "sample." : "new.")
+ fileExt; // create sample or new template file with the necessary extension
String demoPath =
"assets"
+ File.separator
+ "document-templates"
+ File.separator
+ (sample ? "sample" : "new")
+ File.separator
+ demoName;
// get a file name with an index if the file with such a name already exists
String fileName = getCorrectName(demoName);
InputStream stream = Thread.currentThread()
.getContextClassLoader()
.getResourceAsStream(demoPath); // get the input file stream
if (stream == null) {
return null;
}
storageMutator.createFile(Path.of(storagePathBuilder
.getFileLocation(fileName)), stream); // create a file in the specified directory
storageMutator.createMeta(fileName, uid, uname); // create meta information of the demo file
return fileName;
}
}

View File

@ -1,42 +0,0 @@
/**
*
* (c) Copyright Ascensio System SIA 2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.onlyoffice.integration.documentserver.managers.document;
import java.util.ArrayList;
import java.util.Map;
// specify the document manager functions
public interface DocumentManager {
// get a file name with an index if the file with such a name already exists
String getCorrectName(String fileName);
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, 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
// get the path to the file version by the history path and file version
String versionDir(String path, Integer version, boolean historyPath);
// create demo document
String createDemo(String fileExt, Boolean sample, String uid, String uname) throws Exception;
String getCreateUrl(String fileName, Boolean sample); // get URL to the created file
}

View File

@ -20,14 +20,17 @@ package com.onlyoffice.integration.documentserver.managers.history;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.onlyoffice.integration.documentserver.managers.document.DocumentManager;
import com.onlyoffice.integration.documentserver.managers.jwt.JwtManager;
import com.onlyoffice.integration.documentserver.models.filemodel.Document;
import com.onlyoffice.integration.documentserver.storage.FileStoragePathBuilder; import com.onlyoffice.integration.documentserver.storage.FileStoragePathBuilder;
import com.onlyoffice.integration.documentserver.util.file.FileUtility; import com.onlyoffice.integration.sdk.manager.DocumentManager;
import com.onlyoffice.integration.documentserver.util.service.ServiceConverter; import com.onlyoffice.integration.sdk.manager.UrlManager;
import com.onlyoffice.manager.security.JwtManager;
import com.onlyoffice.manager.settings.SettingsManager;
import com.onlyoffice.model.common.User;
import com.onlyoffice.model.documenteditor.HistoryData;
import com.onlyoffice.model.documenteditor.callback.History;
import com.onlyoffice.model.documenteditor.history.Version;
import com.onlyoffice.model.documenteditor.historydata.Previous;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject; import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser; import org.json.simple.parser.JSONParser;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -35,6 +38,7 @@ import org.springframework.stereotype.Component;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -49,15 +53,9 @@ public class DefaultHistoryManager implements HistoryManager {
@Autowired @Autowired
private FileStoragePathBuilder storagePathBuilder; private FileStoragePathBuilder storagePathBuilder;
@Autowired
private DocumentManager documentManager;
@Autowired @Autowired
private JwtManager jwtManager; private JwtManager jwtManager;
@Autowired
private FileUtility fileUtility;
@Autowired @Autowired
private JSONParser parser; private JSONParser parser;
@ -65,109 +63,13 @@ public class DefaultHistoryManager implements HistoryManager {
private ObjectMapper objectMapper; private ObjectMapper objectMapper;
@Autowired @Autowired
private ServiceConverter serviceConverter; private SettingsManager settingsManager;
// todo: Refactoring @Autowired
@SneakyThrows private UrlManager urlManager;
public String[] getHistory(final Document document) { // get document history
// get history directory @Autowired
String histDir = storagePathBuilder.getHistoryDir(storagePathBuilder.getFileLocation(document.getTitle())); private DocumentManager documentManager;
Integer curVer = storagePathBuilder.getFileVersion(histDir, false); // get current file version
if (curVer > 0) { // check if the current file version is greater than 0
List<Object> hist = new ArrayList<>();
Map<String, Object> histData = new HashMap<>();
for (Integer i = 1; i <= curVer; i++) { // run through all the file versions
Map<String, Object> obj = new HashMap<String, Object>();
Map<String, Object> dataObj = new HashMap<String, Object>();
String verDir = documentManager
.versionDir(histDir, i, true); // get the path to the given file version
String key = i == curVer ? document.getKey() : readFileToEnd(new File(verDir
+ File.separator + "key.txt")); // get document key
obj.put("key", key);
obj.put("version", i);
if (i == 1) { // check if the version number is equal to 1
String createdInfo = readFileToEnd(new File(histDir
+ File.separator + "createdInfo.json")); // get file with meta data
JSONObject json = (JSONObject) parser.parse(createdInfo); // and turn it into json object
// write meta information to the object (user information and creation date)
obj.put("created", json.get("created"));
Map<String, Object> user = new HashMap<String, Object>();
user.put("id", json.get("id"));
user.put("name", json.get("name"));
obj.put("user", user);
}
dataObj.put("fileType", fileUtility
.getFileExtension(document.getTitle()));
dataObj.put("key", key);
dataObj.put("url", i == curVer ? document.getUrl()
: documentManager.getHistoryFileUrl(document.getTitle(), i, "prev." + fileUtility
.getFileExtension(document.getTitle()), true));
if (!document.getDirectUrl().equals("")) {
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
// if so, get the path to the changes.json file
JSONObject changes = (JSONObject) parser.parse(readFileToEnd(new File(documentManager
.versionDir(histDir, i - 1, true) + File.separator + "changes.json")));
JSONObject change = (JSONObject) ((JSONArray) changes.get("changes")).get(0);
// write information about changes to the object
obj.put("changes", changes.get("changes"));
obj.put("serverVersion", changes.get("serverVersion"));
obj.put("created", change.get("created"));
obj.put("user", change.get("user"));
// get the history data from the previous file version
Map<String, Object> prev = (Map<String, Object>) histData.get(Integer.toString(i - 2));
Map<String, Object> prevInfo = new HashMap<String, Object>();
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"));
if (!document.getDirectUrl().equals("")) {
prevInfo.put("directUrl", prev.get("directUrl"));
}
// write information about previous file version to the data object
dataObj.put("previous", prevInfo);
// write the path to the diff.zip archive with differences in this file version
Integer verdiff = i - 1;
dataObj.put("changesUrl", documentManager
.getHistoryFileUrl(document.getTitle(), verdiff, "diff.zip", true));
}
if (jwtManager.tokenEnabled()) {
dataObj.put("token", jwtManager.createToken(dataObj));
}
hist.add(obj);
histData.put(Integer.toString(i - 1), dataObj);
}
// write history information about the current file version to the history object
Map<String, Object> histObj = new HashMap<String, Object>();
histObj.put("currentVersion", curVer);
histObj.put("history", hist);
try {
return new String[]{objectMapper.writeValueAsString(histObj),
objectMapper.writeValueAsString(histData)};
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
return new String[]{"", ""};
}
// todo: Refactoring // todo: Refactoring
@SneakyThrows @SneakyThrows
@ -178,16 +80,14 @@ public class DefaultHistoryManager implements HistoryManager {
Integer curVer = storagePathBuilder.getFileVersion(histDir, false); // get current file version Integer curVer = storagePathBuilder.getFileVersion(histDir, false); // get current file version
if (curVer > 0) { // check if the current file version is greater than 0 if (curVer > 0) { // check if the current file version is greater than 0
List<Object> hist = new ArrayList<>(); List<Version> history = new ArrayList<>();
for (Integer i = 1; i <= curVer; i++) { // run through all the file versions for (Integer i = 1; i <= curVer; i++) { // run through all the file versions
Map<String, Object> obj = new HashMap<String, Object>(); String verDir = versionDir(histDir, i, true); // get the path to the given file version
String verDir = documentManager
.versionDir(histDir, i, true); // get the path to the given file version
String key; String key;
if (i == curVer) { if (i == curVer) {
key = serviceConverter key = documentManager
.generateRevisionId(storagePathBuilder.getStorageLocation() .generateRevisionId(storagePathBuilder.getStorageLocation()
+ "/" + fileName + "/" + "/" + fileName + "/"
+ new File(storagePathBuilder.getFileLocation(fileName)).lastModified()); + new File(storagePathBuilder.getFileLocation(fileName)).lastModified());
@ -195,8 +95,10 @@ public class DefaultHistoryManager implements HistoryManager {
key = readFileToEnd(new File(verDir + File.separator + "key.txt")); key = readFileToEnd(new File(verDir + File.separator + "key.txt"));
} }
obj.put("key", key); Version version = Version.builder()
obj.put("version", i); .key(key)
.version(String.valueOf(i))
.build();
if (i == 1) { // check if the version number is equal to 1 if (i == 1) { // check if the version number is equal to 1
String createdInfo = readFileToEnd(new File(histDir String createdInfo = readFileToEnd(new File(histDir
@ -204,33 +106,35 @@ public class DefaultHistoryManager implements HistoryManager {
JSONObject json = (JSONObject) parser.parse(createdInfo); // and turn it into json object JSONObject json = (JSONObject) parser.parse(createdInfo); // and turn it into json object
// write meta information to the object (user information and creation date) // write meta information to the object (user information and creation date)
obj.put("created", json.get("created")); version.setCreated(String.valueOf(json.get("created")));
Map<String, Object> user = new HashMap<String, Object>(); version.setUser(User.builder()
user.put("id", json.get("id")); .id(String.valueOf(json.get("id")))
user.put("name", json.get("name")); .name(String.valueOf(json.get("name")))
obj.put("user", user); .build()
);
} }
if (i > 1) { //check if the version number is greater than 1 if (i > 1) { //check if the version number is greater than 1
// if so, get the path to the changes.json file // if so, get the path to the changes.json file
JSONObject changes = (JSONObject) parser.parse(readFileToEnd(new File(documentManager InputStream changesSteam = new FileInputStream(
.versionDir(histDir, i - 1, true) + File.separator + "changes.json"))); versionDir(histDir, i - 1, true) + File.separator + "changes.json");
JSONObject change = (JSONObject) ((JSONArray) changes.get("changes")).get(0);
History changes = objectMapper.readValue(changesSteam, History.class);
// write information about changes to the object // write information about changes to the object
obj.put("changes", changes.get("changes")); version.setChanges(changes.getChanges());
obj.put("serverVersion", changes.get("serverVersion")); version.setServerVersion(changes.getServerVersion());
obj.put("created", change.get("created")); version.setCreated(changes.getChanges().get(0).getCreated());
obj.put("user", change.get("user")); version.setUser(changes.getChanges().get(0).getUser());
} }
hist.add(obj); history.add(version);
} }
// write history information about the current file version to the history object // write history information about the current file version to the history object
Map<String, Object> histObj = new HashMap<String, Object>(); Map<String, Object> histObj = new HashMap<String, Object>();
histObj.put("currentVersion", curVer); histObj.put("currentVersion", curVer);
histObj.put("history", hist); histObj.put("history", history);
try { try {
return objectMapper.writeValueAsString(histObj); return objectMapper.writeValueAsString(histObj);
@ -243,74 +147,65 @@ public class DefaultHistoryManager implements HistoryManager {
// todo: Refactoring // todo: Refactoring
@SneakyThrows @SneakyThrows
public String getHistoryData(final String fileName, final String version, final Boolean directUrl) { public String getHistoryData(final String fileName, final String version) {
// get history directory // get history directory
String histDir = storagePathBuilder.getHistoryDir(storagePathBuilder.getFileLocation(fileName)); String histDir = storagePathBuilder.getHistoryDir(storagePathBuilder.getFileLocation(fileName));
Integer curVer = storagePathBuilder.getFileVersion(histDir, false); // get current file version Integer curVer = storagePathBuilder.getFileVersion(histDir, false); // get current file version
if (curVer > 0) { // check if the current file version is greater than 0 if (curVer > 0) { // check if the current file version is greater than 0
Map<String, Object> histData = new HashMap<>(); Map<String, HistoryData> historyDataMap = new HashMap<>();
for (Integer i = 1; i <= curVer; i++) { // run through all the file versions for (Integer i = 1; i <= curVer; i++) { // run through all the file versions
Map<String, Object> dataObj = new HashMap<String, Object>(); String verDir = versionDir(histDir, i, true); // get the path to the given file version
String verDir = documentManager
.versionDir(histDir, i, true); // get the path to the given file version
String key; String key;
if (i == curVer) { if (i == curVer) {
key = serviceConverter key = documentManager
.generateRevisionId(storagePathBuilder.getStorageLocation() .generateRevisionId(storagePathBuilder.getStorageLocation()
+ "/" + fileName + "/" + "/" + fileName + "/"
+ new File(storagePathBuilder.getFileLocation(fileName)).lastModified()); + new File(storagePathBuilder.getFileLocation(fileName)).lastModified());
} else { } else {
key = readFileToEnd(new File(verDir + File.separator + "key.txt")); key = readFileToEnd(new File(verDir + File.separator + "key.txt"));
} }
HistoryData historyData = HistoryData.builder()
.fileType(documentManager.getExtension(fileName))
.key(key)
.url(i == curVer ? urlManager.getFileUrl(fileName)
: urlManager.getHistoryFileUrl(fileName, i, "prev" + documentManager
.getExtension(fileName), true))
.build();
dataObj.put("fileType", fileUtility historyData.setVersion(String.valueOf(i));
.getFileExtension(fileName).replace(".", ""));
dataObj.put("key", key);
dataObj.put("url", i == curVer ? documentManager.getDownloadUrl(fileName, true)
: documentManager.getHistoryFileUrl(fileName, i, "prev" + fileUtility
.getFileExtension(fileName), true));
if (directUrl) {
dataObj.put("directUrl", i == curVer
? documentManager.getDownloadUrl(fileName, false)
: documentManager.getHistoryFileUrl(fileName, i, "prev" + fileUtility
.getFileExtension(fileName), false));
}
dataObj.put("version", i);
if (i > 1) { //check if the version number is greater than 1 if (i > 1) { //check if the version number is greater than 1
Integer verdiff = i - 1; Integer verdiff = i - 1;
// get the history data from the previous file version // get the history data from the previous file version
Map<String, Object> prev = (Map<String, Object>) histData.get(Integer.toString(verdiff)); HistoryData historyDataPrev = historyDataMap.get(Integer.toString(verdiff));
Map<String, Object> prevInfo = new HashMap<String, Object>(); Previous previous = Previous.builder()
prevInfo.put("fileType", prev.get("fileType")); .fileType(historyDataPrev.getFileType())
prevInfo.put("key", prev.get("key")); // write key and URL information about previous file version .key(historyDataPrev.getKey())
prevInfo.put("url", prev.get("url")); .url(historyDataPrev.getUrl())
if (directUrl) { .build();
prevInfo.put("directUrl", prev.get("directUrl"));
}
// write information about previous file version to the data object // write information about previous file version to the data object
dataObj.put("previous", prevInfo); historyData.setPrevious(previous);
if (diffExists(histDir, verdiff)) { if (diffExists(histDir, verdiff)) {
// write the path to the diff.zip archive with differences in this file version // write the path to the diff.zip archive with differences in this file version
dataObj.put("changesUrl", documentManager historyData.setChangesUrl(urlManager
.getHistoryFileUrl(fileName, verdiff, "diff.zip", true)); .getHistoryFileUrl(fileName, verdiff, "diff.zip", true));
} }
} }
if (jwtManager.tokenEnabled()) { if (settingsManager.isSecurityEnabled()) {
dataObj.put("token", jwtManager.createToken(dataObj)); historyData.setToken(jwtManager.createToken(historyData));
} }
histData.put(Integer.toString(i), dataObj); historyDataMap.put(Integer.toString(i), historyData);
} }
try { try {
return objectMapper.writeValueAsString(histData.get(version)); return objectMapper.writeValueAsString(historyDataMap.get(version));
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -318,6 +213,14 @@ public class DefaultHistoryManager implements HistoryManager {
return ""; return "";
} }
@Override
public String versionDir(final String path, final Integer version, final boolean historyPath) {
if (!historyPath) {
return storagePathBuilder.getHistoryDir(storagePathBuilder.getFileLocation(path)) + version;
}
return path + File.separator + version;
}
// read a file // read a file
private String readFileToEnd(final File file) { private String readFileToEnd(final File file) {

View File

@ -21,6 +21,6 @@ package com.onlyoffice.integration.documentserver.managers.history;
// specify the history manager functions // specify the history manager functions
public interface HistoryManager { public interface HistoryManager {
String getHistory(String fileName); // get document history String getHistory(String fileName); // get document history
String getHistoryData(String fileName, String version); // get document history data
String getHistoryData(String fileName, String version, Boolean directUrl); // get document history data String versionDir(String path, Integer version, boolean historyPath);
} }

View File

@ -1,130 +0,0 @@
/**
*
* (c) Copyright Ascensio System SIA 2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.onlyoffice.integration.documentserver.managers.jwt;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.primeframework.jwt.Signer;
import org.primeframework.jwt.Verifier;
import org.primeframework.jwt.domain.JWT;
import org.primeframework.jwt.hmac.HMACSigner;
import org.primeframework.jwt.hmac.HMACVerifier;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.LinkedHashMap;
import java.util.Map;
@Component
public class DefaultJwtManager implements JwtManager {
@Value("${files.docservice.secret}")
private String tokenSecret;
@Value("${files.docservice.token-use-for-request}")
private String tokenUseForRequest;
@Autowired
private ObjectMapper objectMapper;
@Autowired
private JSONParser parser;
// create document token
public String createToken(final Map<String, Object> payloadClaims) {
try {
// build a HMAC signer using a SHA-256 hash
Signer signer = HMACSigner.newSHA256Signer(tokenSecret);
JWT jwt = new JWT();
for (String key : payloadClaims.keySet()) { // run through all the keys from the payload
jwt.addClaim(key, payloadClaims.get(key)); // and write each claim to the jwt
}
return JWT.getEncoder().encode(jwt, signer); // sign and encode the JWT to a JSON string representation
} catch (Exception e) {
return "";
}
}
// check if the token is enabled
public boolean tokenEnabled() {
return tokenSecret != null && !tokenSecret.isEmpty();
}
public boolean tokenUseForRequest() {
return Boolean.parseBoolean(tokenUseForRequest) && !tokenUseForRequest.isEmpty();
}
// read document token
public JWT readToken(final String token) {
try {
// build a HMAC verifier using the token secret
Verifier verifier = HMACVerifier.newVerifier(tokenSecret);
// verify and decode the encoded string JWT to a rich object
return JWT.getDecoder().decode(token, verifier);
} catch (Exception exception) {
return null;
}
}
// parse the body
public JSONObject parseBody(final String payload, final String header) {
JSONObject body;
try {
Object obj = parser.parse(payload); // get body parameters by parsing the payload
body = (JSONObject) obj;
} catch (Exception ex) {
throw new RuntimeException("{\"error\":1,\"message\":\"JSON Parsing error\"}");
}
if (tokenEnabled() && tokenUseForRequest()) { // check if the token is enabled
String token = (String) body.get("token"); // get token from the body
if (token == null) { // if token is empty
if (header != null && !header.isBlank()) { // and the header is defined
// get token from the header (it is placed after the Bearer prefix if it exists)
token = header.startsWith("Bearer ") ? header.substring("Bearer ".length()) : header;
}
}
if (token == null || token.isBlank()) {
throw new RuntimeException("{\"error\":1,\"message\":\"JWT expected\"}");
}
JWT jwt = readToken(token); // read token
if (jwt == null) {
throw new RuntimeException("{\"error\":1,\"message\":\"JWT validation failed\"}");
}
if (jwt.getObject("payload") != null) { // get payload from the token and check if it is not empty
try {
@SuppressWarnings("unchecked") LinkedHashMap<String, Object> jwtPayload =
(LinkedHashMap<String, Object>) jwt.getObject("payload");
jwt.claims = jwtPayload;
} catch (Exception ex) {
throw new RuntimeException("{\"error\":1,\"message\":\"Wrong payload\"}");
}
}
try {
Object obj = parser.parse(objectMapper.writeValueAsString(jwt.claims));
body = (JSONObject) obj;
} catch (Exception ex) {
throw new RuntimeException("{\"error\":1,\"message\":\"Parsing error\"}");
}
}
return body;
}
}

View File

@ -1,33 +0,0 @@
/**
*
* (c) Copyright Ascensio System SIA 2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.onlyoffice.integration.documentserver.managers.jwt;
import org.json.simple.JSONObject;
import org.primeframework.jwt.domain.JWT;
import java.util.Map;
// specify the jwt manager functions
public interface JwtManager {
boolean tokenEnabled(); // check if the token is enabled
boolean tokenUseForRequest(); // check if the token is enabled
String createToken(Map<String, Object> payloadClaims); // create document token
JWT readToken(String token); // read document token
JSONObject parseBody(String payload, String header); // parse the body
}

View File

@ -1,70 +0,0 @@
/**
*
* (c) Copyright Ascensio System SIA 2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.onlyoffice.integration.documentserver.managers.template;
import com.onlyoffice.integration.documentserver.models.enums.DocumentType;
import com.onlyoffice.integration.documentserver.models.filemodel.Template;
import com.onlyoffice.integration.documentserver.managers.document.DocumentManager;
import com.onlyoffice.integration.documentserver.storage.FileStoragePathBuilder;
import com.onlyoffice.integration.documentserver.util.file.FileUtility;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
@Qualifier("sample")
public class SampleTemplateManager implements TemplateManager {
@Autowired
private DocumentManager documentManager;
@Autowired
private FileStoragePathBuilder storagePathBuilder;
@Autowired
private FileUtility fileUtility;
// create a template document with the specified name
public List<Template> createTemplates(final String fileName) {
List<Template> templates = List.of(
new Template("", "Blank", documentManager
.getCreateUrl(fileName, false)), // create a blank template
new Template(getTemplateImageUrl(fileName), "With sample content", documentManager
.getCreateUrl(fileName,
true)) // create a template with sample content using the template image
);
return templates;
}
// get the template image URL for the specified file
public String getTemplateImageUrl(final String fileName) {
DocumentType fileType = fileUtility.getDocumentType(fileName); // get the file type
String path = storagePathBuilder.getServerUrl(true); // get server URL
if (fileType.equals(DocumentType.word)) { // get URL to the template image for the word document type
return path + "/css/img/file_docx.svg";
} else if (fileType.equals(DocumentType.slide)) { // get URL to the template image for the slide document type
return path + "/css/img/file_pptx.svg";
} else if (fileType.equals(DocumentType.cell)) { // get URL to the template image for the cell document type
return path + "/css/img/file_xlsx.svg";
}
return path + "/css/img/file_docx.svg"; // get URL to the template image for the default document type (word)
}
}

View File

@ -1,36 +0,0 @@
/**
*
* (c) Copyright Ascensio System SIA 2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.onlyoffice.integration.documentserver.models;
import java.util.List;
import com.onlyoffice.integration.documentserver.models.enums.DocumentType;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
public class Format {
private String name;
private DocumentType type;
private List<String> actions;
private List<String> convert;
private List<String> mime;
}

View File

@ -1,51 +0,0 @@
/**
*
* (c) Copyright Ascensio System SIA 2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.onlyoffice.integration.documentserver.models.configurations;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope("prototype")
@Getter
@Setter
/* 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. */
public class Customization {
@Autowired
private Goback goback; // the settings for the Open file location menu button and upper right corner button
private Boolean autosave = true; // if the Autosave menu option is enabled or disabled
private Boolean comments = true; // if the Comments menu button is displayed or hidden
private Boolean compactHeader = false; /* if the additional action buttons are displayed
in the upper part of the editor window header next to the logo (false) or in the toolbar (true) */
private Boolean compactToolbar = false; // if the top toolbar type displayed is full (false) or compact (true)
private Boolean compatibleFeatures = false; // the use of functionality only compatible with the OOXML format
private Boolean forcesave = false; /* add the request for the forced file saving to the callback handler
when saving the document within the document editing service */
private Boolean help = true; // if the Help menu button is displayed or hidden
private Boolean hideRightMenu = false; // if the right menu is displayed or hidden on first loading
private Boolean hideRulers = false; // if the editor rulers are displayed or hidden
private Boolean submitForm = true; // if the Submit form button is displayed or hidden
private Boolean about = true;
private Boolean feedback = true;
}

View File

@ -1,40 +0,0 @@
/**
*
* (c) Copyright Ascensio System SIA 2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.onlyoffice.integration.documentserver.models.configurations;
import com.onlyoffice.integration.documentserver.models.enums.ToolbarDocked;
import lombok.Getter;
import lombok.Setter;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope("prototype")
@Getter
@Setter
/* The parameters which allow to change the settings
which define the behavior of the buttons in the embedded mode */
public class Embedded {
private String embedUrl; /* the absolute URL to the document serving as a source file for the document embedded
into the web page */
private String saveUrl; /* the absolute URL that will allow the document to be saved
onto the user personal computer */
private String shareUrl; // the absolute URL that will allow other users to share this document
private ToolbarDocked toolbarDocked; // the place for the embedded viewer toolbar, can be either top or bottom
}

View File

@ -1,48 +0,0 @@
/**
*
* (c) Copyright Ascensio System SIA 2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.onlyoffice.integration.documentserver.models.configurations;
import com.onlyoffice.integration.documentserver.storage.FileStoragePathBuilder;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Component
@Scope("prototype")
public class Goback { // the settings for the Open file location menu button and upper right corner button
@Autowired
private FileStoragePathBuilder storagePathBuilder;
@Value("${url.index}")
private String indexMapping;
@Getter
private String url; /* the absolute URL to the website address which will be opened
when clicking the Open file location menu button */
@PostConstruct
private void init() {
this.url = storagePathBuilder.getServerUrl(false) + indexMapping;
}
}

View File

@ -1,45 +0,0 @@
/**
*
* (c) Copyright Ascensio System SIA 2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.onlyoffice.integration.documentserver.models.configurations;
import lombok.Getter;
import lombok.Setter;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
@Component
@Scope("prototype")
@Getter
@Setter
/* The additional parameters for the document (document owner, folder where the document is stored,
uploading date, sharing settings) */
public class Info {
private String owner = "Me"; // the name of the document owner/creator
private Boolean favorite = null; // the highlighting state of the Favorite icon
private String uploaded = getDate(); // the document uploading date
private String getDate() {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEE MMM dd yyyy", Locale.US);
return simpleDateFormat.format(new Date());
}
}

View File

@ -1,48 +0,0 @@
/**
*
* (c) Copyright Ascensio System SIA 2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.onlyoffice.integration.documentserver.models.enums;
public enum ConvertErrorType {
EMPTY_ERROR(0, ""),
CONVERTATION_UNKNOWN(-1, "Error convertation unknown"),
CONVERTATION_TIMEOUT(-2, "Error convertation timeout"),
CONVERTATION_ERROR(-3, "Error convertation error"),
DOWNLOAD_ERROR(-4, "Error download error"),
UNEXPECTED_GUID_ERROR(-5, "Error unexpected guid"),
DATABASE_ERROR(-6, "Error database"),
DOCUMENT_REQUEST_ERROR(-7, "Error document request"),
DOCUMENT_VKEY_ERROR(-8, "Error document VKey");
private final int code;
private final String label;
ConvertErrorType(final int codeParam, final String labelParam) {
this.code = codeParam;
this.label = labelParam;
}
public static String labelOfCode(final int code) {
for (ConvertErrorType convertErrorType : values()) {
if (convertErrorType.code == code) {
return convertErrorType.label;
}
}
return "ErrorCode = " + code;
}
}

View File

@ -1,24 +0,0 @@
/**
*
* (c) Copyright Ascensio System SIA 2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.onlyoffice.integration.documentserver.models.enums;
public enum Mode {
edit,
view
}

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