diff --git a/.github/workflows/artifact-php-laravel.yml b/.github/workflows/artifact-php-laravel.yml new file mode 100644 index 00000000..030f3fc7 --- /dev/null +++ b/.github/workflows/artifact-php-laravel.yml @@ -0,0 +1,31 @@ +name: Artifact PHP Laravel + +on: + workflow_dispatch: + push: + branches: [master] + paths: ['web/documentserver-example/php-laravel/**'] + pull_request: + branches: [master] + paths: ['web/documentserver-example/php-laravel/**'] + +jobs: + artifact: + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + - name: Build Artifact + run: | + cd ${{ github.workspace }} + git submodule update --init --recursive + cd ./web/documentserver-example/php-laravel + mkdir -p ./deploy/'PHP Laravel Example' + rsync -av --exclude='deploy' ./ ./deploy/'PHP Laravel Example' + rm -rf ./deploy/'PHP Laravel Example'/public/assets/document-formats/.git + rm -rf ./deploy/'PHP Laravel Example'/public/assets/document-templates/.git + - name: Upload Artifact + uses: actions/upload-artifact@v4 + with: + name: PHP-Laravel.Example + path: ${{ github.workspace }}/web/documentserver-example/php-laravel/deploy \ No newline at end of file diff --git a/.github/workflows/lint-php-laravel.yml b/.github/workflows/lint-php-laravel.yml new file mode 100644 index 00000000..8b8eba4d --- /dev/null +++ b/.github/workflows/lint-php-laravel.yml @@ -0,0 +1,37 @@ +name: Laravel Pint + +on: + workflow_dispatch: + push: + branches: [master, main] + paths: ['web/documentserver-example/php-laravel/**'] + pull_request: + branches: [master, main, develop] + paths: ['web/documentserver-example/php-laravel/**'] + +jobs: + lint: + runs-on: ubuntu-latest + strategy: + fail-fast: true + matrix: + php: [8.2] + defaults: + run: + working-directory: ./web/documentserver-example/php-laravel + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: json, dom, curl, libxml, mbstring + coverage: none + + - name: Install Pint + run: composer global require laravel/pint + + - name: Run Pint + run: pint --test diff --git a/.github/workflows/lint-php.yml b/.github/workflows/lint-php.yml index d14311fb..07937918 100644 --- a/.github/workflows/lint-php.yml +++ b/.github/workflows/lint-php.yml @@ -17,7 +17,7 @@ jobs: working-directory: ./web/documentserver-example/php steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/.github/workflows/lint-ruby.yml b/.github/workflows/lint-ruby.yml index 4b476edf..12e5cff2 100644 --- a/.github/workflows/lint-ruby.yml +++ b/.github/workflows/lint-ruby.yml @@ -30,7 +30,7 @@ jobs: - name: Install Dependencies run: | - bundle update + bundle install - name: Rubocop run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 382e1971..415d449e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -63,6 +63,14 @@ jobs: rsync -av --exclude='PHP Example' ./ ./'PHP Example' rm -rf ./'PHP Example'/assets/document-formats/.git rm -rf ./'PHP Example'/assets/document-templates/.git + - name: Build PHP Laravel Artifact + run: | + cd ${{ github.workspace }} + cd ./web/documentserver-example/php-laravel + mkdir -p ./'PHP Laravel Example' + rsync -av --exclude='PHP Laravel Example' ./ ./'PHP Laravel Example' + rm -rf ./'PHP Laravel Example'/public/assets/document-formats/.git + rm -rf ./'PHP Laravel Example'/public/assets/document-templates/.git - name: Build Python Artifact run: | cd ${{ github.workspace }} @@ -107,6 +115,8 @@ jobs: zip -r Node.js.Example.zip ./'Node.js Example' cd ${{ github.workspace }}/web/documentserver-example/php zip -r PHP.Example.zip ./'PHP Example' + cd ${{ github.workspace }}/web/documentserver-example/php-laravel + zip -r PHP.Laravel.Example.zip ./'PHP Laravel Example' cd ${{ github.workspace }}/web/documentserver-example/python zip -r Python.Example.zip ./'Python Example' cd ${{ github.workspace }}/web/documentserver-example/ruby @@ -126,6 +136,7 @@ jobs: ${{ github.workspace }}/web/documentserver-example/java/Java.Example.zip, ${{ github.workspace }}/web/documentserver-example/nodejs/Node.js.Example.zip, ${{ github.workspace }}/web/documentserver-example/php/PHP.Example.zip, + ${{ github.workspace }}/web/documentserver-example/php-laravel/PHP.Laravel.Example.zip, ${{ github.workspace }}/web/documentserver-example/python/Python.Example.zip, ${{ github.workspace }}/web/documentserver-example/ruby/Ruby.Example.zip, ${{ github.workspace }}/web/documentserver-example/java-spring/Java.Spring.Example.zip, diff --git a/.gitmodules b/.gitmodules index 21a6d8e4..57b5ecd0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -5,7 +5,6 @@ [submodule "web/documentserver-example/nodejs/public/assets/document-formats"] path = web/documentserver-example/nodejs/public/assets/document-formats url = https://github.com/ONLYOFFICE/document-formats - branch = feature/v8.0 [submodule "web/documentserver-example/csharp-mvc/assets/document-templates"] path = web/documentserver-example/csharp-mvc/assets/document-templates url = https://github.com/ONLYOFFICE/document-templates @@ -57,10 +56,6 @@ path = web/documentserver-example/java/src/main/resources/assets/document-formats url = https://github.com/ONLYOFFICE/document-formats 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"] path = web/documentserver-example/csharp/assets/document-templates url = https://github.com/ONLYOFFICE/document-templates @@ -68,3 +63,11 @@ [submodule "web/documentserver-example/csharp/assets/document-formats"] path = web/documentserver-example/csharp/assets/document-formats url = https://github.com/ONLYOFFICE/document-formats +[submodule "web/documentserver-example/php-laravel/public/assets/document-formats"] + path = web/documentserver-example/php-laravel/public/assets/document-formats + url = https://github.com/ONLYOFFICE/document-formats + branch = master +[submodule "web/documentserver-example/php-laravel/public/assets/document-templates"] + path = web/documentserver-example/php-laravel/public/assets/document-templates + url = https://github.com/ONLYOFFICE/document-templates + branch = main/en diff --git a/3rd-Party.license b/3rd-Party.license index d762967c..2dd945f6 100644 --- a/3rd-Party.license +++ b/3rd-Party.license @@ -1,6 +1,5 @@ Document Server integration example uses code from the following 3rd party projects. - 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/) @@ -132,6 +131,10 @@ Jackson Databind - General-purpose data-binding functionality and tree-model for License: Apache 2.0 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/) License: MIT, GPL License File: jQuery.BlockUI.license @@ -156,6 +159,10 @@ jQuery.UI - jQuery UI is an open source library of interface components — License: MIT 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) License: Apache 2.0 License File: JSON.simple.license @@ -168,14 +175,14 @@ ModelMapper - ModelMapper is an intelligent object mapping library that automa License: Apache 2.0 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) License: Apache 2.0 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) License: Apache 2.0 License File: spring-boot.license @@ -271,6 +278,9 @@ urllib - Request HTTP URLs in a complex world — basic and digest authen License: MIT 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 diff --git a/CHANGELOG.md b/CHANGELOG.md index b1b36c14..1ca9b8b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,41 @@ # Change Log + - golang: new integration example - golang: upload files to the server - golang: create blank files and files with sample content - glang: edit uploaded files in onlyoffice editor - golang: delete files on the server - golang: show files history +- php-laravel: new integration example +- php-laravel: upload files to the server +- php-laravel: create blank files and files with sample content +- php-laravel: edit uploaded files in onlyoffice editor +- php-laravel: delete files on the server +- php-laravel: create, edit, and submit pdf forms +- php-laravel: show forgotten files on a seperate page + +## 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 +- nodejs: filling by default +- nodejs: docxf, oform as pdf documentType +- nodejs: creating and editing pdf instead docxf +- nodejs: wopi formsubmit icon +- nodejs: close editor +- en-GB, sr-Cyrl-RS skin languages +- switching from filling to editing +- fill permission in embedded mode ## 1.8.0 - nodejs: pdf, djvu, xps, oxps as pdf documentType diff --git a/web/documentserver-example/csharp-mvc/App_Start/BundleConfig.cs b/web/documentserver-example/csharp-mvc/App_Start/BundleConfig.cs index 0613f38b..659039ce 100644 --- a/web/documentserver-example/csharp-mvc/App_Start/BundleConfig.cs +++ b/web/documentserver-example/csharp-mvc/App_Start/BundleConfig.cs @@ -38,9 +38,15 @@ namespace OnlineEditorsExampleMVC // create the main script bundle bundles.Add(new ScriptBundle("~/bundles/scripts").Include( + "~/Scripts/formats.js", "~/Scripts/jscript.js" )); + // create the forgotten page script bundle + bundles.Add(new ScriptBundle("~/bundles/forgotten").Include( + "~/Scripts/forgotten.js" + )); + // create a style bundle bundles.Add(new StyleBundle("~/Content/css").Include( "~/Content/stylesheet.css", @@ -52,6 +58,11 @@ namespace OnlineEditorsExampleMVC bundles.Add(new StyleBundle("~/Content/editor").Include( "~/Content/editor.css" )); + + // create the forgotten page style bundle + bundles.Add(new StyleBundle("~/Content/forgotten").Include( + "~/Content/forgotten.css" + )); } } } \ No newline at end of file diff --git a/web/documentserver-example/csharp-mvc/Content/forgotten.css b/web/documentserver-example/csharp-mvc/Content/forgotten.css new file mode 100644 index 00000000..4256b01b --- /dev/null +++ b/web/documentserver-example/csharp-mvc/Content/forgotten.css @@ -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; + } +} \ No newline at end of file diff --git a/web/documentserver-example/csharp-mvc/Content/images/file_docxf.svg b/web/documentserver-example/csharp-mvc/Content/images/file_docxf.svg deleted file mode 100644 index ab5f0268..00000000 --- a/web/documentserver-example/csharp-mvc/Content/images/file_docxf.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/web/documentserver-example/csharp-mvc/Content/images/file_pdf.svg b/web/documentserver-example/csharp-mvc/Content/images/file_pdf.svg new file mode 100644 index 00000000..33694e18 --- /dev/null +++ b/web/documentserver-example/csharp-mvc/Content/images/file_pdf.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/web/documentserver-example/csharp-mvc/Content/images/home.svg b/web/documentserver-example/csharp-mvc/Content/images/home.svg new file mode 100644 index 00000000..e67b1299 --- /dev/null +++ b/web/documentserver-example/csharp-mvc/Content/images/home.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/documentserver-example/csharp-mvc/Content/images/icon_pdf.svg b/web/documentserver-example/csharp-mvc/Content/images/icon_pdf.svg new file mode 100644 index 00000000..1867081a --- /dev/null +++ b/web/documentserver-example/csharp-mvc/Content/images/icon_pdf.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/documentserver-example/csharp-mvc/Content/images/mobile-logo.svg b/web/documentserver-example/csharp-mvc/Content/images/mobile-logo.svg new file mode 100644 index 00000000..e8b8cb5d --- /dev/null +++ b/web/documentserver-example/csharp-mvc/Content/images/mobile-logo.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/web/documentserver-example/csharp-mvc/Content/images/mobile-menu.svg b/web/documentserver-example/csharp-mvc/Content/images/mobile-menu.svg new file mode 100644 index 00000000..b40f1158 --- /dev/null +++ b/web/documentserver-example/csharp-mvc/Content/images/mobile-menu.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/web/documentserver-example/csharp-mvc/Content/images/plus.svg b/web/documentserver-example/csharp-mvc/Content/images/plus.svg new file mode 100644 index 00000000..673a1f23 --- /dev/null +++ b/web/documentserver-example/csharp-mvc/Content/images/plus.svg @@ -0,0 +1,3 @@ + + + diff --git a/web/documentserver-example/csharp-mvc/Content/media.css b/web/documentserver-example/csharp-mvc/Content/media.css index d5bde136..93baf339 100644 --- a/web/documentserver-example/csharp-mvc/Content/media.css +++ b/web/documentserver-example/csharp-mvc/Content/media.css @@ -79,7 +79,8 @@ margin-left: 0; } - .tableRow { + .tableRow, + menu.links { width: 90%; } @@ -119,7 +120,8 @@ .contentCells-icon{ width: 5%; } - .tableRow { + .tableRow, + menu.links { width: 55%; } @@ -175,7 +177,8 @@ } @media (max-width: 715px) { - .tableRow { + .tableRow, + menu.links { width: 45%; } } @@ -263,7 +266,8 @@ padding-left: 0; } - .tableRow { + .tableRow, + menu.links { width: 75%; } @@ -293,7 +297,8 @@ width: 580px; } - .tableRow { + .tableRow, + menu.links { width: 95%; } @@ -407,7 +412,8 @@ padding: 16px 0 6px; } - .tableRow { + .tableRow, + menu.links { width: 40%; } @@ -538,7 +544,8 @@ } @media (max-width: 510px) and (min-width: 470px) { - .tableRow { + .tableRow, + menu.links { width: 35%; } @@ -564,7 +571,8 @@ } @media (max-width: 470px) and (min-width: 420px) { - .tableRow { + .tableRow, + menu.links { width: 30%; } .tableRow td:first-child{ @@ -599,7 +607,8 @@ } @media (max-width: 420px) and (min-width: 320px) { - .tableRow { + .tableRow, + menu.links { width: 25%; } @@ -649,7 +658,8 @@ } } @media (max-width: 769px) and (min-width: 715px){ - .tableRow{ + .tableRow, + menu.links { width: 50%; } } @@ -698,3 +708,216 @@ 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; + } +} diff --git a/web/documentserver-example/csharp-mvc/Content/stylesheet.css b/web/documentserver-example/csharp-mvc/Content/stylesheet.css index 0e83d62c..2de064da 100644 --- a/web/documentserver-example/csharp-mvc/Content/stylesheet.css +++ b/web/documentserver-example/csharp-mvc/Content/stylesheet.css @@ -65,6 +65,14 @@ header img { margin: 10px 0 22px 32px; } +.responsive-nav { + display: none; +} + +.mobile-close-btn { + display: none; +} + .center { position:relative; margin: 0 auto 0; @@ -164,7 +172,7 @@ label .checkbox { } .try-editor.form { - background-image: url("images/file_docxf.svg"); + background-image: url("images/file_pdf.svg"); } .side-option { @@ -298,6 +306,43 @@ label .checkbox { 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 { color: #333333; display: none; @@ -491,6 +536,11 @@ footer table tr td:first-child { background-image: url("images/icon_pptx.svg"); } +.stored-edit.pdf, +.uploadFileName.pdf { + background-image: url("images/icon_pdf.svg"); +} + .stored-edit span { font-size: 12px; line-height: 12px; diff --git a/web/documentserver-example/csharp-mvc/Controllers/HomeController.cs b/web/documentserver-example/csharp-mvc/Controllers/HomeController.cs index c863de2e..eb39e565 100644 --- a/web/documentserver-example/csharp-mvc/Controllers/HomeController.cs +++ b/web/documentserver-example/csharp-mvc/Controllers/HomeController.cs @@ -17,10 +17,13 @@ */ using System; +using System.Collections.Generic; +using System.Collections; using System.IO; using System.Web.Mvc; using OnlineEditorsExampleMVC.Helpers; using OnlineEditorsExampleMVC.Models; +using System.Web.Configuration; namespace OnlineEditorsExampleMVC.Controllers { @@ -31,6 +34,46 @@ namespace OnlineEditorsExampleMVC.Controllers 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>(); + + 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(); + 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 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 user = Users.getUser(id); 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; } } diff --git a/web/documentserver-example/csharp-mvc/Helpers/TrackManager.cs b/web/documentserver-example/csharp-mvc/Helpers/TrackManager.cs index 27ff3f00..79261415 100644 --- a/web/documentserver-example/csharp-mvc/Helpers/TrackManager.cs +++ b/web/documentserver-example/csharp-mvc/Helpers/TrackManager.cs @@ -282,7 +282,7 @@ namespace OnlineEditorsExampleMVC.Helpers } // create a command request - public static void commandRequest(string method, string key, object meta = null) + public static Dictionary commandRequest(string method, string key, object meta = null) { DocManagerHelper.VerifySSL(); @@ -345,6 +345,7 @@ namespace OnlineEditorsExampleMVC.Helpers { throw new Exception(dataResponse); } + return responseObj; } // save file diff --git a/web/documentserver-example/csharp-mvc/Models/FileModel.cs b/web/documentserver-example/csharp-mvc/Models/FileModel.cs index b88a060a..29147473 100755 --- a/web/documentserver-example/csharp-mvc/Models/FileModel.cs +++ b/web/documentserver-example/csharp-mvc/Models/FileModel.cs @@ -77,18 +77,19 @@ namespace OnlineEditorsExampleMVC.Models var jss = new JavaScriptSerializer(); 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 id = request.Cookies.GetOrDefault("uid", null); 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"; 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 // favorite icon state diff --git a/web/documentserver-example/csharp-mvc/Models/FileUtility.cs b/web/documentserver-example/csharp-mvc/Models/FileUtility.cs index 99b2357d..e55797fb 100644 --- a/web/documentserver-example/csharp-mvc/Models/FileUtility.cs +++ b/web/documentserver-example/csharp-mvc/Models/FileUtility.cs @@ -33,7 +33,8 @@ namespace OnlineEditorsExampleMVC.Models { Word, Cell, - Slide + Slide, + Pdf } // get file type @@ -41,6 +42,7 @@ namespace OnlineEditorsExampleMVC.Models { 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.SpreadsheetExtensions().Contains(ext)) return FileType.Cell; // cell type for spreadsheet extensions if (FormatManager.PresentationExtensions().Contains(ext)) return FileType.Slide; // slide type for presentation extensions @@ -185,6 +187,20 @@ namespace OnlineEditorsExampleMVC.Models .ToList(); } + public static List PdfExtensions() + { + return Pdfs() + .Select(format => format.Extension()) + .ToList(); + } + + public static List Pdfs() + { + return All() + .Where(format => format.Type == FileType.Pdf) + .ToList(); + } + public static List AllExtensions() { return All() diff --git a/web/documentserver-example/csharp-mvc/Models/ForgottenFilesModel.cs b/web/documentserver-example/csharp-mvc/Models/ForgottenFilesModel.cs new file mode 100755 index 00000000..04a927f9 --- /dev/null +++ b/web/documentserver-example/csharp-mvc/Models/ForgottenFilesModel.cs @@ -0,0 +1,30 @@ +/** + * + * (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.Collections.Generic; + +namespace OnlineEditorsExampleMVC.Models +{ + // create file model + public class ForgottenFilesModel + { + public List> files { get; set; } + + public ForgottenFilesModel(List> files) { this.files = files; } + } +} \ No newline at end of file diff --git a/web/documentserver-example/csharp-mvc/OnlineEditorsExampleMVC.csproj b/web/documentserver-example/csharp-mvc/OnlineEditorsExampleMVC.csproj index d8b4bf0a..9c1fe77b 100644 --- a/web/documentserver-example/csharp-mvc/OnlineEditorsExampleMVC.csproj +++ b/web/documentserver-example/csharp-mvc/OnlineEditorsExampleMVC.csproj @@ -116,6 +116,7 @@ + WebEditor.ashx @@ -123,6 +124,7 @@ + @@ -140,6 +142,7 @@ + @@ -151,6 +154,8 @@ + + @@ -158,6 +163,10 @@ + + + + @@ -165,17 +174,13 @@ + Designer - - - - - diff --git a/web/documentserver-example/csharp-mvc/Scripts/forgotten.js b/web/documentserver-example/csharp-mvc/Scripts/forgotten.js new file mode 100755 index 00000000..05658379 --- /dev/null +++ b/web/documentserver-example/csharp-mvc/Scripts/forgotten.js @@ -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); +}); \ No newline at end of file diff --git a/web/documentserver-example/csharp-mvc/Scripts/jscript.js b/web/documentserver-example/csharp-mvc/Scripts/jscript.js index d74be36c..ca5a789d 100644 --- a/web/documentserver-example/csharp-mvc/Scripts/jscript.js +++ b/web/documentserver-example/csharp-mvc/Scripts/jscript.js @@ -421,4 +421,27 @@ if (typeof jQuery != "undefined") { }).mouseout(function () { jq("div.tooltip").remove(); }); -} \ No newline at end of file +} + +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"); + } +} diff --git a/web/documentserver-example/csharp-mvc/Views/Home/Editor.aspx b/web/documentserver-example/csharp-mvc/Views/Home/Editor.aspx index 966f5ced..5468103b 100644 --- a/web/documentserver-example/csharp-mvc/Views/Home/Editor.aspx +++ b/web/documentserver-example/csharp-mvc/Views/Home/Editor.aspx @@ -68,7 +68,7 @@ // the user is trying to switch the document from the viewing into the editing mode var onRequestEditRights = function () { - location.href = location.href.replace(RegExp("editorsMode=view\&?", "i"), ""); + location.href = location.href.replace(RegExp("editorsMode=\\w+\&?", "i"), "") + "&editorsMode=edit"; }; // an error or some other specific event occurs @@ -108,7 +108,7 @@ // the meta information of the document is changed via the meta command var onMetaChange = function (event) { - if (event.data.favorite) { + if (event.data.favorite !== undefined) { var favorite = !!event.data.favorite; var title = document.title.replace(/^\☆/g, ""); document.title = (favorite ? "☆" : "") + title; diff --git a/web/documentserver-example/csharp-mvc/Views/Home/Forgotten.aspx b/web/documentserver-example/csharp-mvc/Views/Home/Forgotten.aspx new file mode 100755 index 00000000..8e1d1670 --- /dev/null +++ b/web/documentserver-example/csharp-mvc/Views/Home/Forgotten.aspx @@ -0,0 +1,144 @@ +<%@ Page Title="ONLYOFFICE" Language="C#" Inherits="System.Web.Mvc.ViewPage" %> + +<%@ Import Namespace="System.IO" %> +<%@ Import Namespace="System.Web.Configuration" %> +<%@ Import Namespace="OnlineEditorsExampleMVC.Helpers" %> + + + + + + + + + /> + + ONLYOFFICE + + " rel="shortcut icon" type="image/x-icon" /> + + + + <%: Styles.Render("~/Content/css") %> + <%: Styles.Render("~/Content/forgotten") %> + + +
+ + +
  • + + ONLYOFFICE + +
  • +
  • + + ONLYOFFICE + +
  • +
    +
    + +
    + + + + + + + +
    +
    + + +
  • + Forgotten files +
  • +
    +
    +
    +
    + Forgotten files +
    +
    + + + + + + + +
    FilenameAction
    +
    + + + <% foreach (var file in Model.files) { %> + "> + + + + <% } %> + +
    + " href="<%= file["url"] %>" target="_blank"> + <%= file["key"] %> + + + "> + Download + "> + Delete +
    +
    +
    +
    +
    +
    + + + + <%: Scripts.Render("~/bundles/jquery", "~/bundles/forgotten") %> + + diff --git a/web/documentserver-example/csharp-mvc/Views/Home/Index.aspx b/web/documentserver-example/csharp-mvc/Views/Home/Index.aspx index fdf0ec31..421f7c2c 100644 --- a/web/documentserver-example/csharp-mvc/Views/Home/Index.aspx +++ b/web/documentserver-example/csharp-mvc/Views/Home/Index.aspx @@ -41,11 +41,23 @@
    -
    - + + +
  • + + ONLYOFFICE + +
  • +
  • + + ONLYOFFICE + +
  • +
    @@ -68,7 +80,7 @@ Presentation
  • - PDF form + PDF form
  • +
    + + + <% if (bool.Parse(WebConfigurationManager.AppSettings["enable-forgotten"])) { %> +
  • + Forgotten files +
  • + <% } %> +
    <% var storedFiles = DocManagerHelper.GetStoredFiles(); %>
    "> ONLYOFFICE Document Editors – Welcome! @@ -136,7 +163,7 @@ You can open the same document using different users in different Web browser sessions, so you can check out multi-user editing functions. <% foreach (User user in Users.getAllUsers()) { %> -
    +
    <%= user.name.IsEmpty() ? "Anonymous" : user.name %>
      <% foreach (string description in user.descriptions) @@ -200,11 +227,13 @@ Open in editor for mobile devices - - " target="_blank"> - Open in editor for comment - - + <% if (docType != "pdf") { %> + + " target="_blank"> + Open in editor for comment + + + <% } %> <% if (docType == "word") { %> " target="_blank"> diff --git a/web/documentserver-example/csharp-mvc/Views/Shared/Error.aspx b/web/documentserver-example/csharp-mvc/Views/Shared/Error.aspx index 6b0cdb5c..7b07b291 100644 --- a/web/documentserver-example/csharp-mvc/Views/Shared/Error.aspx +++ b/web/documentserver-example/csharp-mvc/Views/Shared/Error.aspx @@ -26,6 +26,7 @@

      Error.

      An error occurred while processing your request.

      +

      <%= ViewData["Message"] %>

      \ No newline at end of file diff --git a/web/documentserver-example/csharp-mvc/WebEditor.ashx.cs b/web/documentserver-example/csharp-mvc/WebEditor.ashx.cs index f3c0bb01..79183f3e 100644 --- a/web/documentserver-example/csharp-mvc/WebEditor.ashx.cs +++ b/web/documentserver-example/csharp-mvc/WebEditor.ashx.cs @@ -69,6 +69,9 @@ namespace OnlineEditorsExampleMVC case "remove": Remove(context); break; + case "removeforgotten": + RemoveForgotten(context); + break; case "assets": Assets(context); break; @@ -996,6 +999,32 @@ namespace OnlineEditorsExampleMVC 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 + "\"}"); + } + } } } \ No newline at end of file diff --git a/web/documentserver-example/csharp-mvc/assets/document-formats b/web/documentserver-example/csharp-mvc/assets/document-formats index 730e13c8..dbd7ce51 160000 --- a/web/documentserver-example/csharp-mvc/assets/document-formats +++ b/web/documentserver-example/csharp-mvc/assets/document-formats @@ -1 +1 @@ -Subproject commit 730e13c89d717c03eabd845a51ad1ddc673b13ac +Subproject commit dbd7ce514589be824ba9c3a4793c00937a35947e diff --git a/web/documentserver-example/csharp-mvc/assets/document-templates b/web/documentserver-example/csharp-mvc/assets/document-templates index 1fc823af..25a14d7a 160000 --- a/web/documentserver-example/csharp-mvc/assets/document-templates +++ b/web/documentserver-example/csharp-mvc/assets/document-templates @@ -1 +1 @@ -Subproject commit 1fc823afa909e4c49551e4e5b945189a21ff1999 +Subproject commit 25a14d7ad901cb13e112c33b8171d831ea25a8f2 diff --git a/web/documentserver-example/csharp-mvc/web.appsettings.config b/web/documentserver-example/csharp-mvc/web.appsettings.config index b66c599a..7d16f715 100644 --- a/web/documentserver-example/csharp-mvc/web.appsettings.config +++ b/web/documentserver-example/csharp-mvc/web.appsettings.config @@ -1,11 +1,12 @@ - + + @@ -15,7 +16,7 @@ - + diff --git a/web/documentserver-example/csharp/App_Themes/forgotten.css b/web/documentserver-example/csharp/App_Themes/forgotten.css new file mode 100755 index 00000000..4256b01b --- /dev/null +++ b/web/documentserver-example/csharp/App_Themes/forgotten.css @@ -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; + } +} \ No newline at end of file diff --git a/web/documentserver-example/csharp/App_Themes/images/file_docxf.svg b/web/documentserver-example/csharp/App_Themes/images/file_docxf.svg deleted file mode 100644 index ab5f0268..00000000 --- a/web/documentserver-example/csharp/App_Themes/images/file_docxf.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/web/documentserver-example/csharp/App_Themes/images/file_pdf.svg b/web/documentserver-example/csharp/App_Themes/images/file_pdf.svg new file mode 100644 index 00000000..33694e18 --- /dev/null +++ b/web/documentserver-example/csharp/App_Themes/images/file_pdf.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/web/documentserver-example/csharp/App_Themes/images/home.svg b/web/documentserver-example/csharp/App_Themes/images/home.svg new file mode 100644 index 00000000..e67b1299 --- /dev/null +++ b/web/documentserver-example/csharp/App_Themes/images/home.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/web/documentserver-example/csharp/App_Themes/images/icon_pdf.svg b/web/documentserver-example/csharp/App_Themes/images/icon_pdf.svg new file mode 100644 index 00000000..1867081a --- /dev/null +++ b/web/documentserver-example/csharp/App_Themes/images/icon_pdf.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/documentserver-example/csharp/App_Themes/images/mobile-logo.svg b/web/documentserver-example/csharp/App_Themes/images/mobile-logo.svg new file mode 100644 index 00000000..e8b8cb5d --- /dev/null +++ b/web/documentserver-example/csharp/App_Themes/images/mobile-logo.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/web/documentserver-example/csharp/App_Themes/images/mobile-menu.svg b/web/documentserver-example/csharp/App_Themes/images/mobile-menu.svg new file mode 100644 index 00000000..b40f1158 --- /dev/null +++ b/web/documentserver-example/csharp/App_Themes/images/mobile-menu.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/web/documentserver-example/csharp/App_Themes/images/plus.svg b/web/documentserver-example/csharp/App_Themes/images/plus.svg new file mode 100644 index 00000000..673a1f23 --- /dev/null +++ b/web/documentserver-example/csharp/App_Themes/images/plus.svg @@ -0,0 +1,3 @@ + + + diff --git a/web/documentserver-example/csharp/App_Themes/media.css b/web/documentserver-example/csharp/App_Themes/media.css index d5bde136..93baf339 100644 --- a/web/documentserver-example/csharp/App_Themes/media.css +++ b/web/documentserver-example/csharp/App_Themes/media.css @@ -79,7 +79,8 @@ margin-left: 0; } - .tableRow { + .tableRow, + menu.links { width: 90%; } @@ -119,7 +120,8 @@ .contentCells-icon{ width: 5%; } - .tableRow { + .tableRow, + menu.links { width: 55%; } @@ -175,7 +177,8 @@ } @media (max-width: 715px) { - .tableRow { + .tableRow, + menu.links { width: 45%; } } @@ -263,7 +266,8 @@ padding-left: 0; } - .tableRow { + .tableRow, + menu.links { width: 75%; } @@ -293,7 +297,8 @@ width: 580px; } - .tableRow { + .tableRow, + menu.links { width: 95%; } @@ -407,7 +412,8 @@ padding: 16px 0 6px; } - .tableRow { + .tableRow, + menu.links { width: 40%; } @@ -538,7 +544,8 @@ } @media (max-width: 510px) and (min-width: 470px) { - .tableRow { + .tableRow, + menu.links { width: 35%; } @@ -564,7 +571,8 @@ } @media (max-width: 470px) and (min-width: 420px) { - .tableRow { + .tableRow, + menu.links { width: 30%; } .tableRow td:first-child{ @@ -599,7 +607,8 @@ } @media (max-width: 420px) and (min-width: 320px) { - .tableRow { + .tableRow, + menu.links { width: 25%; } @@ -649,7 +658,8 @@ } } @media (max-width: 769px) and (min-width: 715px){ - .tableRow{ + .tableRow, + menu.links { width: 50%; } } @@ -698,3 +708,216 @@ 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; + } +} diff --git a/web/documentserver-example/csharp/App_Themes/stylesheet.css b/web/documentserver-example/csharp/App_Themes/stylesheet.css index c63f59a0..71f7e375 100644 --- a/web/documentserver-example/csharp/App_Themes/stylesheet.css +++ b/web/documentserver-example/csharp/App_Themes/stylesheet.css @@ -65,6 +65,14 @@ header img { margin: 10px 0 22px 32px; } +.responsive-nav { + display: none; +} + +.mobile-close-btn { + display: none; +} + .center { position: relative; margin: 0 auto 0; @@ -164,7 +172,7 @@ label .checkbox { } .try-editor.form { - background-image: url("images/file_docxf.svg"); + background-image: url("images/file_pdf.svg"); } .side-option { @@ -298,6 +306,43 @@ label .checkbox { 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 { color: #333333; display: none; @@ -495,6 +540,11 @@ footer a:hover { background-image: url("images/icon_pptx.svg"); } +.stored-edit.pdf, +.uploadFileName.pdf { + background-image: url("images/icon_pdf.svg"); +} + .stored-edit span { font-size: 12px; line-height: 12px; diff --git a/web/documentserver-example/csharp/Default.aspx b/web/documentserver-example/csharp/Default.aspx index a0f027e2..bffd08d9 100644 --- a/web/documentserver-example/csharp/Default.aspx +++ b/web/documentserver-example/csharp/Default.aspx @@ -45,11 +45,23 @@
      -
      @@ -71,7 +83,7 @@ Presentation
    • - PDF form + PDF form
    • + <% var storedFiles = GetStoredFiles(); %>
      + + + <% if (bool.Parse(WebConfigurationManager.AppSettings["enable-forgotten"])) { %> +
    • + Forgotten files +
    • + <% } %> +
      "> ONLYOFFICE Document Editors – Welcome! @@ -138,7 +165,7 @@ You can open the same document using different users in different Web browser sessions, so you can check out multi-user editing functions. <% foreach (User user in Users.getAllUsers()) { %> -
      +
      <%= user.name.IsEmpty() ? "Anonymous" : user.name %>
        <% foreach (string description in user.descriptions) @@ -202,11 +229,13 @@ Open in editor for mobile devices - - " target="_blank"> - Open in editor for comment - - + <% if (docType != "pdf") { %> + + " target="_blank"> + Open in editor for comment + + + <% } %> <% if (docType == "word") { %> " target="_blank"> diff --git a/web/documentserver-example/csharp/Default.aspx.cs b/web/documentserver-example/csharp/Default.aspx.cs index 24324d3b..dfa6a4c5 100644 --- a/web/documentserver-example/csharp/Default.aspx.cs +++ b/web/documentserver-example/csharp/Default.aspx.cs @@ -250,6 +250,7 @@ namespace OnlineEditorsExample { 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 diff --git a/web/documentserver-example/csharp/DocEditor.aspx b/web/documentserver-example/csharp/DocEditor.aspx index 05792ce6..fac16c6c 100644 --- a/web/documentserver-example/csharp/DocEditor.aspx +++ b/web/documentserver-example/csharp/DocEditor.aspx @@ -86,7 +86,7 @@ // the user is trying to switch the document from the viewing into the editing mode var onRequestEditRights = function () { - location.href = location.href.replace(RegExp("editorsMode=view\&?", "i"), ""); + location.href = location.href.replace(RegExp("editorsMode=\\w+\&?", "i"), "") + "&editorsMode=edit"; }; // an error or some other specific event occurs @@ -126,7 +126,7 @@ // the meta information of the document is changed via the meta command var onMetaChange = function (event) { - if (event.data.favorite) { + if (event.data.favorite !== undefined) { var favorite = !!event.data.favorite; var title = document.title.replace(/^\☆/g, ""); document.title = (favorite ? "☆" : "") + title; diff --git a/web/documentserver-example/csharp/DocEditor.aspx.cs b/web/documentserver-example/csharp/DocEditor.aspx.cs index 8cbe9f2f..cd878f9e 100755 --- a/web/documentserver-example/csharp/DocEditor.aspx.cs +++ b/web/documentserver-example/csharp/DocEditor.aspx.cs @@ -135,14 +135,14 @@ namespace OnlineEditorsExample { // create demo document of a specified file type 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 var ext = Path.GetExtension(FileName).ToLower(); - + var canFill = _Default.FillFormsExts.Contains(ext); // 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 editorsType = Request.GetOrDefault("editorsType", "desktop"); @@ -150,11 +150,11 @@ namespace OnlineEditorsExample var id = Request.Cookies.GetOrDefault("uid", null); 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"; 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 jss = new JavaScriptSerializer(); @@ -489,8 +489,8 @@ namespace OnlineEditorsExample case "slide": ext = ".pptx"; // .pptx for slide document type break; - case "docxf": - ext = ".docxf"; + case "pdf": + ext = ".pdf"; break; default: return; diff --git a/web/documentserver-example/csharp/Forgotten.aspx b/web/documentserver-example/csharp/Forgotten.aspx new file mode 100755 index 00000000..cbf6f3eb --- /dev/null +++ b/web/documentserver-example/csharp/Forgotten.aspx @@ -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" %> + + + + + + + + /> + ONLYOFFICE + + + + + + + + + + + + + + +
        + + +
      • + + ONLYOFFICE + +
      • +
      • + + ONLYOFFICE + +
      • +
        +
        +
        + + + + + + + +
        +
        + + +
      • + Forgotten files +
      • +
        +
        +
        +
        + Forgotten files +
        +
        + + + + + + + +
        FilenameAction
        +
        + + + <% foreach (var file in GetForgottenFiles()) { %> + "> + + + + <% } %> + +
        + " href="<%= file["url"] %>" target="_blank"> + <%= file["key"] %> + + + "> + Download + "> + Delete +
        +
        +
        +
        +
        +
        + + + + + + + + diff --git a/web/documentserver-example/csharp/Forgotten.aspx.cs b/web/documentserver-example/csharp/Forgotten.aspx.cs new file mode 100755 index 00000000..43249e42 --- /dev/null +++ b/web/documentserver-example/csharp/Forgotten.aspx.cs @@ -0,0 +1,96 @@ +/** + * + * (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.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> GetForgottenFiles() + { + var files = new List>(); + + 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(); + 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; + } + } +} \ No newline at end of file diff --git a/web/documentserver-example/csharp/Forgotten.aspx.designer.cs b/web/documentserver-example/csharp/Forgotten.aspx.designer.cs new file mode 100755 index 00000000..5e04674f --- /dev/null +++ b/web/documentserver-example/csharp/Forgotten.aspx.designer.cs @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace OnlineEditorsExample { + + + public partial class Forgotten { + + /// + /// form1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlForm form1; + } +} diff --git a/web/documentserver-example/csharp/FormatManager.cs b/web/documentserver-example/csharp/FormatManager.cs index a845a20f..5c70d3b2 100644 --- a/web/documentserver-example/csharp/FormatManager.cs +++ b/web/documentserver-example/csharp/FormatManager.cs @@ -150,6 +150,20 @@ namespace OnlineEditorsExample .ToList(); } + public static List PdfExtensions() + { + return Pdfs() + .Select(format => format.Extension()) + .ToList(); + } + + public static List Pdfs() + { + return All() + .Where(format => format.Type == "pdf") + .ToList(); + } + public static List AllExtensions() { return All() diff --git a/web/documentserver-example/csharp/OnlineEditorsExample.csproj b/web/documentserver-example/csharp/OnlineEditorsExample.csproj index ada0f7e9..571806a2 100644 --- a/web/documentserver-example/csharp/OnlineEditorsExample.csproj +++ b/web/documentserver-example/csharp/OnlineEditorsExample.csproj @@ -77,6 +77,7 @@ + @@ -94,6 +95,7 @@ + @@ -104,10 +106,16 @@ + + + - + + + + @@ -119,6 +127,12 @@ DocEditor.aspx + + Forgotten.aspx + + + Forgotten.aspx + diff --git a/web/documentserver-example/csharp/TrackManager.cs b/web/documentserver-example/csharp/TrackManager.cs index 5ac89a80..c4d18526 100644 --- a/web/documentserver-example/csharp/TrackManager.cs +++ b/web/documentserver-example/csharp/TrackManager.cs @@ -285,7 +285,7 @@ namespace OnlineEditorsExample } // create a command request - public static void commandRequest(string method, string key, object meta = null) + public static Dictionary commandRequest(string method, string key, object meta = null) { _Default.VerifySSL(); @@ -348,6 +348,7 @@ namespace OnlineEditorsExample { throw new Exception(dataResponse); } + return responseObj; } private static void SaveFile(byte[] data, string path) diff --git a/web/documentserver-example/csharp/WebEditor.ashx.cs b/web/documentserver-example/csharp/WebEditor.ashx.cs index f567af51..c208a9d1 100644 --- a/web/documentserver-example/csharp/WebEditor.ashx.cs +++ b/web/documentserver-example/csharp/WebEditor.ashx.cs @@ -69,6 +69,9 @@ namespace OnlineEditorsExample case "remove": Remove(context); break; + case "removeforgotten": + RemoveForgotten(context); + break; case "assets": Assets(context); break; @@ -811,5 +814,30 @@ namespace OnlineEditorsExample 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 + "\"}"); + } + } } } \ No newline at end of file diff --git a/web/documentserver-example/csharp/assets/document-formats b/web/documentserver-example/csharp/assets/document-formats index 730e13c8..dbd7ce51 160000 --- a/web/documentserver-example/csharp/assets/document-formats +++ b/web/documentserver-example/csharp/assets/document-formats @@ -1 +1 @@ -Subproject commit 730e13c89d717c03eabd845a51ad1ddc673b13ac +Subproject commit dbd7ce514589be824ba9c3a4793c00937a35947e diff --git a/web/documentserver-example/csharp/assets/document-templates b/web/documentserver-example/csharp/assets/document-templates index 1fc823af..25a14d7a 160000 --- a/web/documentserver-example/csharp/assets/document-templates +++ b/web/documentserver-example/csharp/assets/document-templates @@ -1 +1 @@ -Subproject commit 1fc823afa909e4c49551e4e5b945189a21ff1999 +Subproject commit 25a14d7ad901cb13e112c33b8171d831ea25a8f2 diff --git a/web/documentserver-example/csharp/script/forgotten.js b/web/documentserver-example/csharp/script/forgotten.js new file mode 100755 index 00000000..e5a9af2b --- /dev/null +++ b/web/documentserver-example/csharp/script/forgotten.js @@ -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); +}); \ No newline at end of file diff --git a/web/documentserver-example/csharp/script/jscript.js b/web/documentserver-example/csharp/script/jscript.js index 9a8cd0ec..d64ff30b 100644 --- a/web/documentserver-example/csharp/script/jscript.js +++ b/web/documentserver-example/csharp/script/jscript.js @@ -421,4 +421,27 @@ if (typeof jQuery != "undefined") { }).mouseout(function () { 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"); + } } \ No newline at end of file diff --git a/web/documentserver-example/csharp/settings.config b/web/documentserver-example/csharp/settings.config index 385e9e16..ac30df39 100644 --- a/web/documentserver-example/csharp/settings.config +++ b/web/documentserver-example/csharp/settings.config @@ -1,11 +1,12 @@ - + + @@ -14,7 +15,7 @@ - + diff --git a/web/documentserver-example/java-spring/3rd-Party.license b/web/documentserver-example/java-spring/3rd-Party.license index bd056534..ccb8e57e 100755 --- a/web/documentserver-example/java-spring/3rd-Party.license +++ b/web/documentserver-example/java-spring/3rd-Party.license @@ -1,6 +1,5 @@ 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) License: Apache 2.0 License File: gson.license @@ -13,6 +12,10 @@ Jackson Databind - General-purpose data-binding functionality and tree-model for License: Apache 2.0 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/) License: MIT, GPL License File: jQuery.BlockUI.license @@ -37,6 +40,10 @@ jQuery.UI - jQuery UI is an open source library of interface components — License: MIT 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) License: Apache 2.0 License File: JSON.simple.license @@ -49,14 +56,14 @@ ModelMapper - ModelMapper is an intelligent object mapping library that automa License: Apache 2.0 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) License: Apache 2.0 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) License: Apache 2.0 License File: spring-boot.license diff --git a/web/documentserver-example/java-spring/README.md b/web/documentserver-example/java-spring/README.md index a6cb879a..f8e7370f 100755 --- a/web/documentserver-example/java-spring/README.md +++ b/web/documentserver-example/java-spring/README.md @@ -135,9 +135,9 @@ See the detailed guide to learn how to install Document Server [for Linux](https 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 ``` diff --git a/web/documentserver-example/java-spring/licenses/3rd-Party.license b/web/documentserver-example/java-spring/licenses/3rd-Party.license index bd056534..ccb8e57e 100644 --- a/web/documentserver-example/java-spring/licenses/3rd-Party.license +++ b/web/documentserver-example/java-spring/licenses/3rd-Party.license @@ -1,6 +1,5 @@ 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) License: Apache 2.0 License File: gson.license @@ -13,6 +12,10 @@ Jackson Databind - General-purpose data-binding functionality and tree-model for License: Apache 2.0 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/) License: MIT, GPL License File: jQuery.BlockUI.license @@ -37,6 +40,10 @@ jQuery.UI - jQuery UI is an open source library of interface components — License: MIT 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) License: Apache 2.0 License File: JSON.simple.license @@ -49,14 +56,14 @@ ModelMapper - ModelMapper is an intelligent object mapping library that automa License: Apache 2.0 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) License: Apache 2.0 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) License: Apache 2.0 License File: spring-boot.license diff --git a/web/documentserver-example/java-spring/licenses/prime-jwt.license b/web/documentserver-example/java-spring/licenses/jackson-databind-properties.license similarity index 99% rename from web/documentserver-example/java-spring/licenses/prime-jwt.license rename to web/documentserver-example/java-spring/licenses/jackson-databind-properties.license index ad410e11..7a4a3ea2 100644 --- a/web/documentserver-example/java-spring/licenses/prime-jwt.license +++ b/web/documentserver-example/java-spring/licenses/jackson-databind-properties.license @@ -1,4 +1,5 @@ -Apache License + + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -178,7 +179,7 @@ Apache License APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" + boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a @@ -186,7 +187,7 @@ Apache License same "printed page" as the copyright notice for easier 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"); you may not use this file except in compliance with the License. diff --git a/web/documentserver-example/java-spring/licenses/json.license b/web/documentserver-example/java-spring/licenses/json.license new file mode 100644 index 00000000..227f84cb --- /dev/null +++ b/web/documentserver-example/java-spring/licenses/json.license @@ -0,0 +1 @@ +Public Domain. \ No newline at end of file diff --git a/web/documentserver-example/java-spring/pom.xml b/web/documentserver-example/java-spring/pom.xml index ba4ac4d3..cce34e2e 100644 --- a/web/documentserver-example/java-spring/pom.xml +++ b/web/documentserver-example/java-spring/pom.xml @@ -50,11 +50,6 @@ gson 2.8.9 - - com.inversoft - prime-jwt - 1.3.1 - org.projectlombok lombok @@ -71,12 +66,39 @@ jackson-databind 2.13.4.2 + + com.fasterxml.jackson.dataformat + jackson-dataformat-properties + 2.13.5 + org.modelmapper modelmapper 2.4.2 + + org.json + json + 20231013 + + + com.onlyoffice + docs-integration-sdk + 1.1.4-SNAPSHOT + + + + + ossrh + Sonatype OSSRH + https://s01.oss.sonatype.org/content/repositories/snapshots/ + + true + + + + diff --git a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/ExampleData.java b/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/ExampleData.java index a23f3802..9b6630ae 100644 --- a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/ExampleData.java +++ b/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/ExampleData.java @@ -102,23 +102,25 @@ public class ExampleData { 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()), null, true, true, true, new Goback(null, false)); + List.of(FilterState.NULL.toString()), null, true, true, true, + new Goback(null, false), true); // create user 2 with the specified parameters userService.createUser("Mark Pottato", "pottato@example.com", descriptionUserSecond, "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, - true, true, new Goback("Go to Documents", null)); + true, true, new Goback("Go to Documents", null), false); // create user 3 with the specified parameters userService.createUser("Hamish Mitchell", null, descriptionUserThird, "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, null); + new ArrayList<>(), List.of("group-2"), false, true, true, false, + null, false); // create user 0 with the specified parameters 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()), - new ArrayList<>(), null, false, false, false, null); + new ArrayList<>(), null, false, false, false, null, false); } } diff --git a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/IntegrationConfiguration.java b/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/IntegrationConfiguration.java index a1912085..36fdde6e 100644 --- a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/IntegrationConfiguration.java +++ b/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/IntegrationConfiguration.java @@ -20,6 +20,17 @@ package com.onlyoffice.integration; import com.fasterxml.jackson.databind.ObjectMapper; 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.modelmapper.ModelMapper; import org.modelmapper.convention.MatchingStrategies; @@ -30,23 +41,15 @@ import org.springframework.context.annotation.Configuration; import javax.annotation.PostConstruct; -import com.onlyoffice.integration.documentserver.util.SSLUtils; - @Configuration public class IntegrationConfiguration { @Value("${files.storage}") private String storageAddress; - @Value("${files.docservice.verify-peer-off}") - private String verifyPerrOff; - @Autowired private FileStoragePathBuilder storagePathBuilder; - @Autowired - private SSLUtils ssl; - @Bean public ModelMapper mapper() { // create the model mapper ModelMapper mapper = new ModelMapper(); @@ -67,21 +70,34 @@ public class IntegrationConfiguration { @PostConstruct public void init() { // initialize the storage path builder 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 public ObjectMapper objectMapper() { // create the object mapper 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); + } + } diff --git a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/controllers/EditorController.java b/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/controllers/EditorController.java index 99821ea6..dcca7775 100755 --- a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/controllers/EditorController.java +++ b/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/controllers/EditorController.java @@ -20,19 +20,22 @@ package com.onlyoffice.integration.controllers; import com.fasterxml.jackson.core.JsonProcessingException; 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.storage.FileStoragePathBuilder; -import com.onlyoffice.integration.entities.User; import com.onlyoffice.integration.dto.Mentions; -import com.onlyoffice.integration.dto.UserInfo; import com.onlyoffice.integration.dto.Protect; -import com.onlyoffice.integration.documentserver.models.enums.Type; -import com.onlyoffice.integration.documentserver.models.filemodel.FileModel; +import com.onlyoffice.integration.dto.UserInfo; +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.configurers.FileConfigurer; -import com.onlyoffice.integration.services.configurers.wrappers.DefaultFileWrapper; +import com.onlyoffice.manager.security.JwtManager; +import com.onlyoffice.manager.settings.SettingsManager; +import com.onlyoffice.model.documenteditor.Config; +import com.onlyoffice.model.documenteditor.config.document.Type; +import com.onlyoffice.model.settings.SettingsConstants; import lombok.SneakyThrows; +import org.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; @@ -56,12 +59,6 @@ import static com.onlyoffice.integration.documentserver.util.Constants.ANONYMOUS @Controller public class EditorController { - @Value("${files.docservice.url.site}") - private String docserviceSite; - - @Value("${files.docservice.url.api}") - private String docserviceApiUrl; - @Value("${files.docservice.languages}") private String langs; @@ -78,7 +75,13 @@ public class EditorController { private ObjectMapper objectMapper; @Autowired - private FileConfigurer fileConfigurer; + private SettingsManager settingsManager; + + @Autowired + private ConfigService configService; + + @Autowired + private UrlManager urlManager; @GetMapping(path = "${url.editor}") // process request to open the editor page @@ -91,17 +94,19 @@ public class EditorController { @CookieValue(value = "uid") final String uid, @CookieValue(value = "ulang") final String lang, final Model model) throws JsonProcessingException { - Action action = Action.edit; - Type type = Type.desktop; + Action action = null; + Type type = Type.DESKTOP; Locale locale = new Locale("en"); if (actionParam != null) { action = Action.valueOf(actionParam); } if (typeParam != null) { - type = Type.valueOf(typeParam); + type = Type.valueOf(typeParam.toUpperCase()); } + settingsManager.setSetting(SettingsConstants.DIRECT_URL, String.valueOf(directUrl)); + List langsAndKeys = Arrays.asList(langs.split("\\|")); for (String langAndKey : langsAndKeys) { String[] couple = langAndKey.split(":"); @@ -118,30 +123,25 @@ public class EditorController { return "index.html"; } - User user = optionalUser.get(); - user.setImage(user.getAvatar() ? storagePathBuilder.getServerUrl(true) + "/css/img/uid-" - + user.getId() + ".png" : null); - - // 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() + Config config = configService.createConfig( + fileName, + action, + type ); - // add attributes to the specified model - // add file model with the default parameters to the original model - model.addAttribute("model", fileModel); + JSONObject actionData = null; + + 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 - model.addAttribute("docserviceApiUrl", docserviceSite + docserviceApiUrl); + model.addAttribute("docserviceApiUrl", urlManager.getDocumentServerApiUrl()); // get an image and add it to the model model.addAttribute("dataInsertImage", getInsertImage(directUrl)); @@ -220,7 +220,7 @@ public class EditorController { } // check if the document token is enabled - if (jwtManager.tokenEnabled()) { + if (settingsManager.isSecurityEnabled()) { // create token from the dataInsertImage object dataInsertImage.put("token", jwtManager.createToken(dataInsertImage)); @@ -242,7 +242,7 @@ public class EditorController { } // check if the document token is enabled - if (jwtManager.tokenEnabled()) { + if (settingsManager.isSecurityEnabled()) { // create token from the dataDocument object dataDocument.put("token", jwtManager.createToken(dataDocument)); @@ -261,7 +261,7 @@ public class EditorController { } // check if the document token is enabled - if (jwtManager.tokenEnabled()) { + if (settingsManager.isSecurityEnabled()) { // create token from the dataSpreadsheet object dataSpreadsheet.put("token", jwtManager.createToken(dataSpreadsheet)); diff --git a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/controllers/FileController.java b/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/controllers/FileController.java index f1fa321c..5e48ac40 100755 --- a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/controllers/FileController.java +++ b/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/controllers/FileController.java @@ -21,32 +21,38 @@ package com.onlyoffice.integration.controllers; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; 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.jwt.JwtManager; import com.onlyoffice.integration.documentserver.storage.FileStorageMutator; import com.onlyoffice.integration.documentserver.storage.FileStoragePathBuilder; import com.onlyoffice.integration.dto.Converter; -import com.onlyoffice.integration.dto.ConvertedData; import com.onlyoffice.integration.dto.Reference; -import com.onlyoffice.integration.dto.ReferenceData; import com.onlyoffice.integration.dto.Rename; import com.onlyoffice.integration.dto.Restore; import com.onlyoffice.integration.dto.SaveAs; -import com.onlyoffice.integration.dto.Track; 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.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.JSONObject; import org.json.simple.parser.JSONParser; 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.UrlResource; import org.springframework.http.HttpHeaders; @@ -74,7 +80,6 @@ import java.io.IOException; import java.io.InputStream; import java.net.InetAddress; import java.net.MalformedURLException; -import java.net.URL; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -91,22 +96,6 @@ import java.util.Optional; @Controller 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 private JwtManager jwtManager; @Autowired @@ -116,20 +105,29 @@ public class FileController { @Autowired private UserServices userService; @Autowired - private CallbackHandler callbackHandler; - @Autowired private ObjectMapper objectMapper; @Autowired - private ServiceConverter serviceConverter; - @Autowired - private CallbackManager callbackManager; - @Autowired 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 private String createUserMetadata(final String uid, final String fullFileName) { Optional 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()) { User user = optionalUser.get(); 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) { try { 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 byte[] bytes = file.getBytes(); // get file in bytes // 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 } // 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 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 } - fullFileName = fileUtility.getFileNameWithoutExtension(fileNamePath) + fullFileName = documentManager.getBaseName(fileNamePath) + "." + fileExtension; // get full file name return createUserMetadata(uid, fullFileName); // create user metadata and return it @@ -235,56 +233,54 @@ public class FileController { @CookieValue("uid") final String uid, @CookieValue("ulang") final String lang) { // get file name 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 String filePass = body.getFilePass() != null ? body.getFilePass() : null; - - // get file extension - String fileExt = fileUtility.getFileExtension(fileName); - - // get document type (word, cell or slide) - DocumentType type = fileUtility.getDocumentType(fileName); - // get an auto-conversion extension from the request body or set it to the ooxml extension String conversionExtension = body.getFileExt() != null ? body.getFileExt() : "ooxml"; try { // check if the file with such an extension can be converted - if (fileUtility.getConvertExts().contains(fileExt)) { - String key = serviceConverter.generateRevisionId(fileUri); // generate document key - ConvertedData response = serviceConverter // get the URL to the converted file - .getConvertedData(fileUri, fileExt, conversionExtension, key, filePass, true, lang); + if (documentManager.getDefaultConvertExtension(fileName) != null) { + ConvertRequest convertRequest = ConvertRequest.builder() + .password(filePass) + .outputtype(conversionExtension) + .region(lang) + .async(true) + .build(); - String newFileUri = response.getUri(); - String newFileType = "." + response.getFileType(); + ConvertResponse convertResponse = convertService.processConvert(convertRequest, fileName); - if (newFileUri.isEmpty()) { - return "{ \"step\" : \"0\", \"filename\" : \"" + fileName + "\"}"; + if (convertResponse.getError() != null || convertResponse.getFileUrl() == null) { + 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 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); - URL url = new URL(newFileUri); - java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection(); - InputStream stream = connection.getInputStream(); // get input stream of the converted file + fileName = requestManager.executeGetRequest(newFileUri, new RequestManager.Callback() { + public String doWork(final Object response) throws IOException { + InputStream stream = ((HttpEntity) response).getContent(); // get input stream of the converted + // file - if (stream == null) { - connection.disconnect(); - throw new RuntimeException("Input stream is null"); - } + if (stream == null) { + throw new RuntimeException("Input stream is null"); + } - // remove source file - storageMutator.deleteFile(fileName); - // create the converted file with input stream - storageMutator.createFile(Path.of(storagePathBuilder.getFileLocation(correctedName)), stream); - fileName = correctedName; + // remove source file + storageMutator.deleteFile(oldFileName); + + // 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 @@ -305,7 +301,7 @@ public class FileController { boolean success = false; if (filename != null) { - String fullFileName = fileUtility.getFileName(filename); // get full file name + String fullFileName = documentManager.getDocumentName(filename); // get full file name // delete a file from the storage and return the status of this operation (true or false) boolean fileSuccess = storageMutator.deleteFile(fullFileName); @@ -331,13 +327,12 @@ public class FileController { @RequestParam("file") final String file) { // history file try { // check if a token is enabled or not - if (jwtManager.tokenEnabled() && jwtManager.tokenUseForRequest()) { - String header = request.getHeader(documentJwtHeader == null // get the document JWT header - || documentJwtHeader.isEmpty() ? "Authorization" : documentJwtHeader); + if (settingsManager.isSecurityEnabled()) { + String header = request.getHeader(settingsManager.getSecurityHeader()); if (header != null && !header.isEmpty()) { String token = header .replace("Bearer ", ""); // token is the header without the Bearer prefix - jwtManager.readToken(token); // read the token + jwtManager.verify(token); // read the token } else { return null; } @@ -355,13 +350,12 @@ public class FileController { final String userAddress) { try { // check if a token is enabled or not - if (jwtManager.tokenEnabled() && userAddress != null && jwtManager.tokenUseForRequest()) { - String header = request.getHeader(documentJwtHeader == null // get the document JWT header - || documentJwtHeader.isEmpty() ? "Authorization" : documentJwtHeader); + if (settingsManager.isSecurityEnabled() && userAddress != null) { + String header = request.getHeader(settingsManager.getSecurityHeader()); if (header != null && !header.isEmpty()) { String token = header .replace("Bearer ", ""); // token is the header without the Bearer prefix - jwtManager.readToken(token); // read the token + jwtManager.verify(token); // read the token } else { return null; } @@ -391,10 +385,10 @@ public class FileController { sampleData, uid, 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"); } - return "redirect:editor?fileName=" + URLEncoder + return "redirect:editor?action=edit&fileName=" + URLEncoder .encode(fileName, StandardCharsets.UTF_8); // redirect the request } catch (Exception ex) { model.addAttribute("error", ex.getMessage()); @@ -427,28 +421,26 @@ public class FileController { public String track(final HttpServletRequest request, // track file changes @RequestParam("fileName") final String fileName, @RequestParam("userAddress") final String userAddress, - @RequestBody final Track body) { - Track track; + @RequestBody final Callback body) { + Callback callback; try { String bodyString = objectMapper .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 throw new RuntimeException("{\"error\":1,\"message\":\"Request payload is empty\"}"); } - JSONObject bodyCheck = jwtManager.parseBody(bodyString, header); // parse the request body - track = objectMapper.readValue(bodyCheck.toJSONString(), Track.class); // read the request body + String authorizationHeader = request.getHeader(settingsManager.getSecurityHeader()); + callback = callbackService.verifyCallback(body, authorizationHeader); + + callbackService.processCallback(callback, fileName); } catch (Exception e) { e.printStackTrace(); return e.getMessage(); } - int error = callbackHandler.handle(track, fileName); - - return "{\"error\":" + error + "}"; + return "{\"error\":\"0\"}"; } @PostMapping("/saveas") @@ -456,24 +448,30 @@ public class FileController { public String saveAs(@RequestBody final SaveAs body, @CookieValue("uid") final String uid) { try { 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\"}"; } - URL url = new URL(body.getUrl()); - java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection(); - InputStream stream = connection.getInputStream(); + String url = body.getUrl(); - if (Integer.parseInt(filesizeMax) < stream.available() || stream.available() <= 0) { - return "{\"error\":\"File size is incorrect\"}"; - } - storageMutator.createFile(Path.of(storagePathBuilder.getFileLocation(fileName)), stream); - createUserMetadata(uid, fileName); + url = urlManager.replaceToInnerDocumentServerUrl(url); - return "{\"file\": \"" + fileName + "\"}"; - } catch (IOException e) { + return requestManager.executeGetRequest(url, new RequestManager.Callback() { + @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(); return "{ \"error\" : 1, \"message\" : \"" + e.getMessage() + "\"}"; } @@ -482,14 +480,18 @@ public class FileController { @PostMapping("/rename") @ResponseBody public String rename(@RequestBody final Rename body) { - String fileName = body.getFileName(); - - HashMap meta = new HashMap<>(); - meta.put("title", fileName + "." + body.getFileType()); + CommandRequest commandRequest = CommandRequest.builder() + .key(body.getFileKey()) + .c(Command.META) + .meta(Meta.builder() + .title(body.getFileName() + "." + body.getFileType()) + .build()) + .build(); try { - callbackManager.commandRequest("meta", body.getFileKey(), meta); - return "result ok"; + + CommandResponse commandResponse = commandService.processCommand(commandRequest, body.getFileName()); + return commandResponse.getError().getDescription(); } catch (Exception e) { e.printStackTrace(); return e.getMessage(); @@ -538,7 +540,8 @@ public class FileController { if (fileName.equals("")) { try { 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)); if (f.exists()) { fileName = path; @@ -561,19 +564,19 @@ public class FileController { referenceData.put("fileKey", gson.toJson(fileKey)); HashMap data = new HashMap<>(); - data.put("fileType", fileUtility.getFileExtension(fileName)); - data.put("key", serviceConverter.generateRevisionId( + data.put("fileType", documentManager.getDocumentName(fileName)); + data.put("key", documentManager.generateRevisionId( storagePathBuilder.getStorageLocation() + "/" + fileName + "/" + new File(storagePathBuilder.getFileLocation(fileName)).lastModified() )); - data.put("url", documentManager.getDownloadUrl(fileName, true)); - data.put("directUrl", body.getDirectUrl() ? documentManager.getDownloadUrl(fileName, false) : null); + data.put("url", urlManager.getFileUrl(fileName)); + data.put("directUrl", body.getDirectUrl() ? urlManager.getDirectFileUrl(fileName) : null); data.put("referenceData", referenceData); data.put("path", fileName); data.put("link", storagePathBuilder.getServerUrl(true) + "/editor?fileName=" + fileName); - if (jwtManager.tokenEnabled()) { + if (settingsManager.isSecurityEnabled()) { String token = jwtManager.createToken(data); data.put("token", token); } @@ -608,7 +611,7 @@ public class FileController { String historyDirectory = storagePathBuilder.getHistoryDir(sourcePathFile.toString()); 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); if (!bumpedVersionDirectory.exists()) { bumpedVersionDirectory.mkdir(); @@ -617,7 +620,7 @@ public class FileController { Path bumpedKeyPathFile = Paths.get(bumpedVersionStringDirectory, "key.txt"); String bumpedKeyStringFile = bumpedKeyPathFile.toString(); File bumpedKeyFile = new File(bumpedKeyStringFile); - String bumpedKey = serviceConverter.generateRevisionId( + String bumpedKey = documentManager.generateRevisionId( storagePathBuilder.getStorageLocation() + "/" + body.getFileName() @@ -651,13 +654,13 @@ public class FileController { bumpedChangesFileWriter.write(bumpedChangesContent); bumpedChangesFileWriter.close(); - String sourceExtension = fileUtility.getFileExtension(body.getFileName()); + String sourceExtension = documentManager.getExtension(body.getFileName()); String previousBasename = "prev." + sourceExtension; Path bumpedFile = Paths.get(bumpedVersionStringDirectory, previousBasename); Files.move(sourcePathFile, bumpedFile); - String recoveryVersionStringDirectory = documentManager.versionDir( + String recoveryVersionStringDirectory = historyManager.versionDir( historyDirectory, body.getVersion(), true diff --git a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/controllers/ForgottenController.java b/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/controllers/ForgottenController.java new file mode 100755 index 00000000..09a3346d --- /dev/null +++ b/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/controllers/ForgottenController.java @@ -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 getForgottenFiles() { + ArrayList files = new ArrayList(); + try { + CommandRequest commandRequest = CommandRequest.builder() + .c(Command.GET_FORGOTTEN_LIST) + .build(); + CommandResponse commandResponse = commandService.processCommand(commandRequest, null); + List 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 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); + } + } +} diff --git a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/controllers/IndexController.java b/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/controllers/IndexController.java index dba6bfc1..892f1853 100755 --- a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/controllers/IndexController.java +++ b/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/controllers/IndexController.java @@ -21,9 +21,9 @@ package com.onlyoffice.integration.controllers; import com.onlyoffice.integration.documentserver.storage.FileStorageMutator; import com.onlyoffice.integration.documentserver.storage.FileStoragePathBuilder; import com.onlyoffice.integration.documentserver.util.Misc; -import com.onlyoffice.integration.documentserver.util.file.FileUtility; -import com.onlyoffice.integration.documentserver.util.service.FormatService; 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.dto.FormatsList; import org.springframework.beans.factory.annotation.Autowired; @@ -55,9 +55,6 @@ public class IndexController { @Autowired private FileStoragePathBuilder storagePathBuilder; - @Autowired - private FileUtility fileUtility; - @Autowired private Misc mistUtility; @@ -65,13 +62,10 @@ public class IndexController { private UserServices userService; @Autowired - private FormatService formatService; + private DocumentManager documentManager; - @Value("${files.docservice.url.site}") - private String docserviceSite; - - @Value("${files.docservice.url.preloader}") - private String docservicePreloader; + @Autowired + private UrlManager urlManager; @Value("${url.converter}") private String urlConverter; @@ -85,6 +79,9 @@ public class IndexController { @Value("${server.version}") private String serverVersion; + @Value("${enable-forgotten}") + private String enableForgotten; + @GetMapping("${url.index}") public String index(@RequestParam(value = "directUrl", required = false) final Boolean directUrl, final Model model) { @@ -111,15 +108,14 @@ public class IndexController { for (java.io.File file:files) { // run through all the files String fileName = file.getName(); // get file name - docTypes.add(fileUtility + docTypes.add(documentManager .getDocumentType(fileName) .toString() .toLowerCase()); // add a document type of each file to the list - filesEditable.add(fileUtility.getEditedExts() - .contains(fileUtility.getFileExtension(fileName))); // specify if a file is editable or not + filesEditable.add(documentManager.isEditable(fileName)); // specify if a file is editable or not versions.add(" [" + storagePathBuilder. 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 @@ -128,12 +124,13 @@ public class IndexController { model.addAttribute("files", files); model.addAttribute("docTypes", docTypes); model.addAttribute("filesEditable", filesEditable); - model.addAttribute("datadocs", docserviceSite + docservicePreloader); + model.addAttribute("datadocs", urlManager.getDocumentServerPreloaderApiUrl()); model.addAttribute("tooltip", tooltip); model.addAttribute("users", users); model.addAttribute("languages", languages); model.addAttribute("directUrl", directUrl); model.addAttribute("serverVersion", serverVersion); + model.addAttribute("enableForgotten", Boolean.valueOf(enableForgotten)); return "index.html"; } @@ -151,7 +148,7 @@ public class IndexController { @GetMapping("/formats") @ResponseBody public ResponseEntity formats() { // return all the supported formats - FormatsList list = new FormatsList(formatService.getFormats()); + FormatsList list = new FormatsList(documentManager.getFormats()); return ResponseEntity.ok(list); } } diff --git a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/callbacks/CallbackHandler.java b/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/callbacks/CallbackHandler.java deleted file mode 100644 index a773d8e2..00000000 --- a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/callbacks/CallbackHandler.java +++ /dev/null @@ -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 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; - } -} diff --git a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/callbacks/Status.java b/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/callbacks/Status.java deleted file mode 100644 index 6de4d8fe..00000000 --- a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/callbacks/Status.java +++ /dev/null @@ -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; - } -} diff --git a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/callbacks/implementations/EditCallback.java b/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/callbacks/implementations/EditCallback.java deleted file mode 100644 index 126778cf..00000000 --- a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/callbacks/implementations/EditCallback.java +++ /dev/null @@ -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 - } -} diff --git a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/callbacks/implementations/ForcesaveCallback.java b/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/callbacks/implementations/ForcesaveCallback.java deleted file mode 100644 index 8543361a..00000000 --- a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/callbacks/implementations/ForcesaveCallback.java +++ /dev/null @@ -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(); - } -} diff --git a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/callbacks/implementations/SaveCallback.java b/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/callbacks/implementations/SaveCallback.java deleted file mode 100644 index 3448c660..00000000 --- a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/callbacks/implementations/SaveCallback.java +++ /dev/null @@ -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 - } -} diff --git a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/managers/callback/CallbackManager.java b/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/managers/callback/CallbackManager.java index 8ef83928..746954b2 100644 --- a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/managers/callback/CallbackManager.java +++ b/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/managers/callback/CallbackManager.java @@ -18,11 +18,10 @@ package com.onlyoffice.integration.documentserver.managers.callback; -import com.onlyoffice.integration.dto.Track; -import java.util.HashMap; +import com.onlyoffice.model.documenteditor.Callback; public interface CallbackManager { // specify the callback manager functions - void processSave(Track body, String fileName); // file saving process - void commandRequest(String method, String key, HashMap meta); // create a command request - void processForceSave(Track body, String fileName); // file force saving process + void processEditing(Callback callback, String fileName); + void processSave(Callback callback, String fileName); + void processForceSave(Callback callback, String fileName); } diff --git a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/managers/callback/DefaultCallbackManager.java b/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/managers/callback/DefaultCallbackManager.java index 8323b413..9de63f62 100755 --- a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/managers/callback/DefaultCallbackManager.java +++ b/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/managers/callback/DefaultCallbackManager.java @@ -19,62 +19,60 @@ package com.onlyoffice.integration.documentserver.managers.callback; 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.managers.history.HistoryManager; 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.dto.Action; -import com.onlyoffice.integration.documentserver.util.service.ServiceConverter; -import com.onlyoffice.integration.dto.Track; +import com.onlyoffice.integration.sdk.manager.DocumentManager; +import com.onlyoffice.integration.sdk.manager.UrlManager; +import com.onlyoffice.manager.request.RequestManager; +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 org.apache.http.HttpEntity; import org.json.simple.JSONObject; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Primary; -import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import java.io.ByteArrayInputStream; import java.io.File; +import java.io.IOException; 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.Paths; -import java.util.HashMap; import java.util.List; -import java.util.Map; - -import static com.onlyoffice.integration.documentserver.util.Constants.FILE_SAVE_TIMEOUT; - // todo: Refactoring @Component @Primary 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 private DocumentManager documentManager; @Autowired - private JwtManager jwtManager; - @Autowired - private FileUtility fileUtility; - @Autowired private FileStorageMutator storageMutator; @Autowired private FileStoragePathBuilder storagePathBuilder; @Autowired private ObjectMapper objectMapper; @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 @SneakyThrows @@ -83,23 +81,18 @@ public class DefaultCallbackManager implements CallbackManager { throw new RuntimeException("Url argument is not specified"); // URL isn't specified } - URL uri = new URL(url); - java.net.HttpURLConnection connection = (java.net.HttpURLConnection) uri.openConnection(); - connection.setConnectTimeout(FILE_SAVE_TIMEOUT); - InputStream stream = connection.getInputStream(); // get input stream of the file information from the URL + return requestManager.executeGetRequest(url, new RequestManager.Callback() { + public byte[] doWork(final Object response) throws IOException { + InputStream stream = ((HttpEntity) response).getContent(); // get input stream of the converted + // file - int statusCode = connection.getResponseCode(); - if (statusCode != HttpStatus.OK.value()) { // checking status code - connection.disconnect(); - throw new RuntimeException("Document editing service returned status: " + statusCode); - } + if (stream == null) { + throw new RuntimeException("Input stream is null"); + } - if (stream == null) { - connection.disconnect(); - throw new RuntimeException("Input stream is null"); - } - - return stream.readAllBytes(); + return stream.readAllBytes(); + } + }); } // file saving @@ -112,34 +105,66 @@ public class DefaultCallbackManager implements CallbackManager { 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 - public void processSave(final Track body, final String fileName) { // file saving process - String downloadUri = body.getUrl(); - String changesUri = body.getChangesurl(); - String key = body.getKey(); + public void processSave(final Callback callback, final String fileName) { // file saving process + String downloadUri = callback.getUrl(); + String changesUri = callback.getChangesurl(); + String key = callback.getKey(); String newFileName = fileName; - String curExt = fileUtility.getFileExtension(fileName); // get current file extension - String downloadExt = body.getFiletype(); // get an extension of the downloaded file + String curExt = documentManager.getExtension(fileName); // get current file extension + String downloadExt = callback.getFiletype(); // get an extension of the downloaded file + + downloadUri = urlManager.replaceToInnerDocumentServerUrl(downloadUri); + changesUri = urlManager.replaceToInnerDocumentServerUrl(changesUri); // todo: Refactoring // convert downloaded file to the file with the current extension if these extensions aren't equal if (!curExt.equals(downloadExt)) { try { - String newFileUri = serviceConverter - .getConvertedData(downloadUri, downloadExt, curExt, - serviceConverter.generateRevisionId(downloadUri), null, false, - null).getUri(); // convert a file and get URL to a new file - if (newFileUri.isEmpty()) { + ConvertRequest convertRequest = ConvertRequest.builder() + .key(documentManager.generateRevisionId(downloadUri)) + .url(downloadUri) + .outputtype(curExt) + .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 - .getCorrectName(fileUtility.getFileNameWithoutExtension(fileName) + "." + .getCorrectName(documentManager.getBaseName(fileName) + "." + downloadExt); // get the correct file name if it already exists } else { downloadUri = newFileUri; } } catch (Exception e) { newFileName = documentManager - .getCorrectName(fileUtility.getFileNameWithoutExtension(fileName) + "." + downloadExt); + .getCorrectName(documentManager.getBaseName(fileName) + "." + downloadExt); } } @@ -153,7 +178,7 @@ public class DefaultCallbackManager implements CallbackManager { Path histDir = Paths.get(storagePathBuilder.getHistoryDir(storagePath)); // get the history directory storageMutator.createDirectory(histDir); // and create it - String versionDir = documentManager + String versionDir = historyManager .versionDir(histDir.toAbsolutePath().toString(), // get the file version directory storagePathBuilder .getFileVersion(histDir.toAbsolutePath().toString(), false), true); @@ -172,13 +197,13 @@ public class DefaultCallbackManager implements CallbackManager { .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 - jsonChanges.put("changes", body.getHistory().getChanges()); // put the changes to the json object - jsonChanges.put("serverVersion", body.getHistory() + jsonChanges.put("changes", callback.getHistory().getChanges()); // put the changes to the json object + jsonChanges.put("serverVersion", callback.getHistory() .getServerVersion()); // put the server version to the json object String history = objectMapper.writeValueAsString(jsonChanges); - if (history == null && body.getHistory() != null) { - history = objectMapper.writeValueAsString(body.getHistory()); + if (history == null && callback.getHistory() != null) { + history = objectMapper.writeValueAsString(callback.getHistory()); } if (history != null && !history.isEmpty()) { @@ -194,82 +219,16 @@ public class DefaultCallbackManager implements CallbackManager { } } - // todo: Replace (String method) with (Enum method) @SneakyThrows - public void commandRequest(final String method, - final String key, - final HashMap meta) { // create a command request - String documentCommandUrl = docserviceUrlSite + docserviceUrlCommand; + public void processForceSave(final Callback callback, final String fileNameParam) { // file force saving process - URL url = new URL(documentCommandUrl); - java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection(); - - HashMap params = new HashMap(); - 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 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 downloadUri = callback.getUrl(); String fileName = fileNameParam; - String curExt = fileUtility.getFileExtension(fileName); // get current file extension - String downloadExt = body.getFiletype(); // get an extension of the downloaded file + String curExt = documentManager.getExtension(fileName); // get current file extension + String downloadExt = callback.getFiletype(); // get an extension of the downloaded file + + downloadUri = urlManager.replaceToInnerDocumentServerUrl(downloadUri); Boolean newFileName = false; @@ -277,11 +236,19 @@ public class DefaultCallbackManager implements CallbackManager { // todo: Extract function if (!curExt.equals(downloadExt)) { try { - // convert file and get URL to a new file - String newFileUri = serviceConverter - .getConvertedData(downloadUri, downloadExt, curExt, serviceConverter - .generateRevisionId(downloadUri), null, false, null).getUri(); - if (newFileUri.isEmpty()) { + ConvertRequest convertRequest = ConvertRequest.builder() + .key(documentManager.generateRevisionId(downloadUri)) + .url(downloadUri) + .outputtype(curExt) + .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; } else { downloadUri = newFileUri; @@ -296,32 +263,34 @@ public class DefaultCallbackManager implements CallbackManager { // todo: Use ENUMS // todo: Pointless toString conversion - boolean isSubmitForm = body.getForcesavetype().toString().equals("3"); + boolean isSubmitForm = callback.getForcesavetype().equals(ForcesaveType.SUBMIT_FORM); // todo: Extract function if (isSubmitForm) { // if the form is submitted if (newFileName) { // get the correct file name if it already exists fileName = documentManager - .getCorrectName(fileUtility - .getFileNameWithoutExtension(fileName) + "-form." + downloadExt); + .getCorrectName(documentManager + .getBaseName(fileName) + "-form." + downloadExt); } else { 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 - List actions = body.getActions(); - Action action = actions.get(0); + List actions = callback.getActions(); + com.onlyoffice.model.documenteditor.callback.Action action = actions.get(0); String user = action.getUserid(); // get the user ID // create meta data for the forcesaved file storageMutator.createMeta(fileName, user, "Filling Form"); try { - String formsDataUrl = body.getFormsdataurl(); + String formsDataUrl = callback.getFormsdataurl(); + + formsDataUrl = urlManager.replaceToInnerDocumentServerUrl(formsDataUrl); if (formsDataUrl != null && !formsDataUrl.isEmpty()) { - String formsName = documentManager.getCorrectName(fileUtility - .getFileNameWithoutExtension(fileName) + ".txt"); + String formsName = documentManager.getCorrectName(documentManager + .getBaseName(fileName) + ".txt"); String formsPath = storagePathBuilder.getFileLocation(formsName); byte[] byteArrayFormsData = getDownloadFile(formsDataUrl); @@ -336,7 +305,7 @@ public class DefaultCallbackManager implements CallbackManager { } else { if (newFileName) { fileName = documentManager - .getCorrectName(fileUtility.getFileNameWithoutExtension(fileName) + downloadExt); + .getCorrectName(documentManager.getBaseName(fileName) + "." + downloadExt); } forcesavePath = storagePathBuilder.getForcesavePath(fileName, false); diff --git a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/managers/document/DefaultDocumentManager.java b/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/managers/document/DefaultDocumentManager.java deleted file mode 100755 index 13980d72..00000000 --- a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/managers/document/DefaultDocumentManager.java +++ /dev/null @@ -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> getFilesInfo() { - ArrayList> files = new ArrayList<>(); - - // run through all the stored files - for (File file : storageMutator.getStoredFiles()) { - Map 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> getFilesInfo(final String fileId) { - ArrayList> file = new ArrayList<>(); - - for (Map 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; - } -} diff --git a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/managers/document/DocumentManager.java b/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/managers/document/DocumentManager.java deleted file mode 100755 index cb84ae4a..00000000 --- a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/managers/document/DocumentManager.java +++ /dev/null @@ -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> getFilesInfo(); // get file information - ArrayList> 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 -} diff --git a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/managers/history/DefaultHistoryManager.java b/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/managers/history/DefaultHistoryManager.java index 60db4d20..05c6a95e 100644 --- a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/managers/history/DefaultHistoryManager.java +++ b/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/managers/history/DefaultHistoryManager.java @@ -20,14 +20,17 @@ package com.onlyoffice.integration.documentserver.managers.history; import com.fasterxml.jackson.core.JsonProcessingException; 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.util.file.FileUtility; -import com.onlyoffice.integration.documentserver.util.service.ServiceConverter; +import com.onlyoffice.integration.sdk.manager.DocumentManager; +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 org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.springframework.beans.factory.annotation.Autowired; @@ -35,6 +38,7 @@ import org.springframework.stereotype.Component; import java.io.File; import java.io.FileInputStream; +import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -49,15 +53,9 @@ public class DefaultHistoryManager implements HistoryManager { @Autowired private FileStoragePathBuilder storagePathBuilder; - @Autowired - private DocumentManager documentManager; - @Autowired private JwtManager jwtManager; - @Autowired - private FileUtility fileUtility; - @Autowired private JSONParser parser; @@ -65,109 +63,13 @@ public class DefaultHistoryManager implements HistoryManager { private ObjectMapper objectMapper; @Autowired - private ServiceConverter serviceConverter; + private SettingsManager settingsManager; - // todo: Refactoring - @SneakyThrows - public String[] getHistory(final Document document) { // get document history + @Autowired + private UrlManager urlManager; - // get history directory - String histDir = storagePathBuilder.getHistoryDir(storagePathBuilder.getFileLocation(document.getTitle())); - Integer curVer = storagePathBuilder.getFileVersion(histDir, false); // get current file version - - if (curVer > 0) { // check if the current file version is greater than 0 - List hist = new ArrayList<>(); - Map histData = new HashMap<>(); - - for (Integer i = 1; i <= curVer; i++) { // run through all the file versions - Map obj = new HashMap(); - Map dataObj = new HashMap(); - 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 user = new HashMap(); - 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 prev = (Map) histData.get(Integer.toString(i - 2)); - Map prevInfo = new HashMap(); - 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 histObj = new HashMap(); - 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[]{"", ""}; - } + @Autowired + private DocumentManager documentManager; // todo: Refactoring @SneakyThrows @@ -178,16 +80,14 @@ public class DefaultHistoryManager implements HistoryManager { Integer curVer = storagePathBuilder.getFileVersion(histDir, false); // get current file version if (curVer > 0) { // check if the current file version is greater than 0 - List hist = new ArrayList<>(); + List history = new ArrayList<>(); for (Integer i = 1; i <= curVer; i++) { // run through all the file versions - Map obj = new HashMap(); - String verDir = documentManager - .versionDir(histDir, i, true); // get the path to the given file version + String verDir = versionDir(histDir, i, true); // get the path to the given file version String key; if (i == curVer) { - key = serviceConverter + key = documentManager .generateRevisionId(storagePathBuilder.getStorageLocation() + "/" + fileName + "/" + new File(storagePathBuilder.getFileLocation(fileName)).lastModified()); @@ -195,8 +95,10 @@ public class DefaultHistoryManager implements HistoryManager { key = readFileToEnd(new File(verDir + File.separator + "key.txt")); } - obj.put("key", key); - obj.put("version", i); + Version version = Version.builder() + .key(key) + .version(String.valueOf(i)) + .build(); if (i == 1) { // check if the version number is equal to 1 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 // write meta information to the object (user information and creation date) - obj.put("created", json.get("created")); - Map user = new HashMap(); - user.put("id", json.get("id")); - user.put("name", json.get("name")); - obj.put("user", user); + version.setCreated(String.valueOf(json.get("created"))); + version.setUser(User.builder() + .id(String.valueOf(json.get("id"))) + .name(String.valueOf(json.get("name"))) + .build() + ); } 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); + InputStream changesSteam = new FileInputStream( + versionDir(histDir, i - 1, true) + File.separator + "changes.json"); + + History changes = objectMapper.readValue(changesSteam, History.class); // 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")); + version.setChanges(changes.getChanges()); + version.setServerVersion(changes.getServerVersion()); + version.setCreated(changes.getChanges().get(0).getCreated()); + 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 Map histObj = new HashMap(); histObj.put("currentVersion", curVer); - histObj.put("history", hist); + histObj.put("history", history); try { return objectMapper.writeValueAsString(histObj); @@ -249,68 +153,69 @@ public class DefaultHistoryManager implements HistoryManager { Integer curVer = storagePathBuilder.getFileVersion(histDir, false); // get current file version if (curVer > 0) { // check if the current file version is greater than 0 - Map histData = new HashMap<>(); + Map historyDataMap = new HashMap<>(); for (Integer i = 1; i <= curVer; i++) { // run through all the file versions - Map dataObj = new HashMap(); - String verDir = documentManager - .versionDir(histDir, i, true); // get the path to the given file version + String verDir = versionDir(histDir, i, true); // get the path to the given file version String key; if (i == curVer) { - key = serviceConverter + key = documentManager .generateRevisionId(storagePathBuilder.getStorageLocation() + "/" + fileName + "/" + new File(storagePathBuilder.getFileLocation(fileName)).lastModified()); } else { 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 - .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)); + historyData.setDirectUrl(i == curVer + ? urlManager.getDirectFileUrl(fileName) + : urlManager.getHistoryFileUrl(fileName, i, "prev" + documentManager + .getExtension(fileName), false) + ); } - dataObj.put("version", i); + historyData.setVersion(String.valueOf(i)); if (i > 1) { //check if the version number is greater than 1 Integer verdiff = i - 1; // get the history data from the previous file version - Map prev = (Map) histData.get(Integer.toString(verdiff)); - Map prevInfo = new HashMap(); - 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")); + HistoryData historyDataPrev = historyDataMap.get(Integer.toString(verdiff)); + Previous previous = Previous.builder() + .fileType(historyDataPrev.getFileType()) + .key(historyDataPrev.getKey()) + .url(historyDataPrev.getUrl()) + .build(); if (directUrl) { - prevInfo.put("directUrl", prev.get("directUrl")); + previous.setDirectUrl(historyDataPrev.getDirectUrl()); } // write information about previous file version to the data object - dataObj.put("previous", prevInfo); + historyData.setPrevious(previous); if (diffExists(histDir, verdiff)) { // 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)); } } - if (jwtManager.tokenEnabled()) { - dataObj.put("token", jwtManager.createToken(dataObj)); + if (settingsManager.isSecurityEnabled()) { + historyData.setToken(jwtManager.createToken(historyData)); } - histData.put(Integer.toString(i), dataObj); + historyDataMap.put(Integer.toString(i), historyData); } try { - return objectMapper.writeValueAsString(histData.get(version)); + return objectMapper.writeValueAsString(historyDataMap.get(version)); } catch (JsonProcessingException e) { e.printStackTrace(); } @@ -318,6 +223,14 @@ public class DefaultHistoryManager implements HistoryManager { 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 private String readFileToEnd(final File file) { diff --git a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/managers/history/HistoryManager.java b/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/managers/history/HistoryManager.java index af72f58d..4fc6e23b 100644 --- a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/managers/history/HistoryManager.java +++ b/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/managers/history/HistoryManager.java @@ -21,6 +21,6 @@ package com.onlyoffice.integration.documentserver.managers.history; // specify the history manager functions public interface HistoryManager { String getHistory(String fileName); // get document history - String getHistoryData(String fileName, String version, Boolean directUrl); // get document history data + String versionDir(String path, Integer version, boolean historyPath); } diff --git a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/managers/jwt/DefaultJwtManager.java b/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/managers/jwt/DefaultJwtManager.java deleted file mode 100644 index b0f90852..00000000 --- a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/managers/jwt/DefaultJwtManager.java +++ /dev/null @@ -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 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 jwtPayload = - (LinkedHashMap) 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; - } -} diff --git a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/managers/jwt/JwtManager.java b/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/managers/jwt/JwtManager.java deleted file mode 100644 index 65190cc1..00000000 --- a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/managers/jwt/JwtManager.java +++ /dev/null @@ -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 payloadClaims); // create document token - JWT readToken(String token); // read document token - JSONObject parseBody(String payload, String header); // parse the body -} diff --git a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/managers/template/SampleTemplateManager.java b/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/managers/template/SampleTemplateManager.java deleted file mode 100644 index ef528fbb..00000000 --- a/web/documentserver-example/java-spring/src/main/java/com/onlyoffice/integration/documentserver/managers/template/SampleTemplateManager.java +++ /dev/null @@ -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