Compare commits

...

94 Commits

Author SHA1 Message Date
461a005cfa Merge pull request #237 from ONLYOFFICE/develop
Release/1.1.0
2021-12-13 11:45:38 +03:00
24cfd661ef 1.1.0 2021-12-13 11:34:05 +03:00
6481ac7e13 update presentation sample 2021-12-13 11:21:56 +03:00
778cb91317 Merge pull request #235 from ONLYOFFICE/feature/docserv_old_version
docxf and oform since v7.0
2021-12-08 14:03:12 +03:00
beb9885b03 docxf and oform since v7.0 2021-12-08 14:00:04 +03:00
65e34e52f9 nodejs: docxf and oform since v7.0 2021-12-07 18:41:42 +03:00
600570f7c7 Merge pull request #232 from ONLYOFFICE/feature/rename-master-form
Feature/rename master form
2021-12-02 11:54:00 +03:00
20f28afec1 rename master form to form template 2021-12-02 11:32:07 +03:00
2768ca5f6c nodejs: rename master form to form template 2021-12-02 11:21:53 +03:00
2425898cc8 nodejs: doc is not fillable format 2021-11-30 09:07:43 +03:00
8d85281be8 nodejs: frame allow attributes 2021-11-29 11:16:05 +03:00
081d704a8b nodejs: frame sanbox attributes 2021-11-29 11:00:53 +03:00
2949d24b49 Merge pull request #228 from ONLYOFFICE/bugfix/wopi-hover-fix
nodejs: wopi fix layout on hover
2021-11-26 10:20:10 +03:00
2f0d783ba6 nodejs: wopi fix layout on hover 2021-11-25 17:51:17 +03:00
644d9b8ba6 nodejs: create docxf(wopi) 2021-11-25 16:12:40 +03:00
7184928a6c update assets 2021-11-25 11:02:20 +03:00
2d7df99daf python: fix readme 2021-11-22 11:21:53 +03:00
5339b60226 Merge pull request #227 from ONLYOFFICE/bugfix/fix-styles
ruby and java: fix media styles
2021-11-22 10:03:35 +03:00
5e0f54be68 ruby and java: fix media styles 2021-11-21 14:13:46 +03:00
77c6715481 Merge pull request #223 from ONLYOFFICE/feature/java-spring-comments
Comments for Java Spring
2021-11-18 13:03:07 +03:00
d3639d1697 java-sprong: added comments 2021-11-18 12:47:26 +03:00
b6c0bf4db0 Merge pull request #225 from ONLYOFFICE/feature/docxf
Feature/docxf
2021-11-17 18:31:47 +03:00
3be5035ab3 php: docxf 2021-11-17 16:52:10 +03:00
ad9d627c82 csharp: docxf 2021-11-17 16:46:09 +03:00
a26a559921 csharp-mvc: docxf 2021-11-17 16:34:20 +03:00
cf2f60f8cd ruby: docxf 2021-11-17 16:24:46 +03:00
80e1be2261 python: docxf 2021-11-17 15:38:14 +03:00
a3e84f50fa java: docxf 2021-11-17 15:28:19 +03:00
796433cf8a java-spring: docxf 2021-11-17 15:15:31 +03:00
7309d942d6 java-spring: fix permissions 2021-11-17 15:12:04 +03:00
b5c90ee7d5 Merge remote-tracking branch 'remotes/origin/develop' into feature/docxf 2021-11-17 13:04:36 +03:00
8688ef1db5 Merge pull request #224 from ONLYOFFICE/feature/oform
Feature/oform
2021-11-17 13:01:49 +03:00
56a4f6073d php: opening uploaded form 2021-11-17 12:14:32 +03:00
6d67b740b6 php: filling on mobile 2021-11-17 12:10:18 +03:00
e97fe56914 php: revert filling in docx 2021-11-17 12:06:34 +03:00
946819926f php: hide submit button 2021-11-17 11:55:18 +03:00
e89b6040fb php: oform 2021-11-17 11:53:52 +03:00
d673fd86e0 csharp: opening uploaded form 2021-11-17 11:10:14 +03:00
901b2a83fa csharp: filling on mobile 2021-11-17 11:08:22 +03:00
08119d7458 csharp: revert filling in docx 2021-11-17 11:00:49 +03:00
d1fee87d8d python, csharp-mvc: fix layout 2021-11-17 11:00:26 +03:00
83cefae85b csharp: hide submit button 2021-11-17 10:48:36 +03:00
f1a64c92d2 csharp: oform 2021-11-17 10:47:50 +03:00
cb583e48e3 csharp-mvc: opening uploaded form 2021-11-16 17:43:47 +03:00
c3878a9431 csharp-mvc: filling on mobile 2021-11-16 17:37:05 +03:00
50821067d7 csharp-mvc: revert filling in docx 2021-11-16 17:32:09 +03:00
18bd45e4ab csharp-mvc: hide submit button 2021-11-16 17:28:06 +03:00
79aac3ccd6 csharp-mvc: oform 2021-11-16 16:55:49 +03:00
fac436f5e6 python: opening uploaded form 2021-11-16 16:20:08 +03:00
a4600b31a6 python: filling on mobile 2021-11-16 16:16:15 +03:00
b52196ec91 python: revert filling in docx 2021-11-16 16:13:39 +03:00
2314e74ade python: hide submit button 2021-11-16 16:08:40 +03:00
59801951e6 python: oform 2021-11-16 16:07:30 +03:00
3233e257cb ruby: opening uploaded form 2021-11-16 15:20:09 +03:00
6b4f56373e ruby: filling on mobile 2021-11-16 15:17:20 +03:00
cd1a312aba ruby: revert filling in docx 2021-11-16 15:13:32 +03:00
8cd0297917 ruby: hide submit button 2021-11-16 15:06:03 +03:00
4568dcfdce ruby: oform 2021-11-16 15:05:06 +03:00
ace2677062 java: revert filling in docx 2021-11-16 13:56:06 +03:00
d3cd62f5ae java: opening uploaded form 2021-11-16 13:35:55 +03:00
c7cd0ba828 java: filling on mobile 2021-11-16 13:32:15 +03:00
5cb3267660 java: hide submit button 2021-11-16 13:25:33 +03:00
1a2c68abe8 java: oform 2021-11-16 13:24:37 +03:00
cbaebe50c0 Merge pull request #222 from ONLYOFFICE/feature/save-as
Feature/save as
2021-11-16 13:02:36 +03:00
c8aac65af6 java-spring: opening uploaded form 2021-11-16 12:56:28 +03:00
5f8e9370a7 java-spring: mobile-fill-forms.svg 2021-11-16 12:52:39 +03:00
7fd0882c06 java-spring: filling on mobile 2021-11-16 12:51:52 +03:00
c6a8cd0ff1 java-spring: revert filling in docx 2021-11-16 12:32:21 +03:00
73797dcf31 java-spring: hide submit button 2021-11-16 12:22:06 +03:00
19ebecd4d0 java-spring: oform 2021-11-16 12:20:00 +03:00
bee95b4e6a php: add Copy File as... 2021-11-15 18:15:30 +03:00
4418d8e5d5 csharp and csharp-mvc: fix error messages 2021-11-15 17:38:46 +03:00
873267a784 csharp-mvc: add Copy File as... 2021-11-15 17:08:24 +03:00
605028151f csharp: add Copy File as... 2021-11-15 17:00:04 +03:00
2429668b57 ruby: add Copy File as... 2021-11-15 16:02:11 +03:00
5ecb4226e8 python: add Save Copy as... 2021-11-15 14:24:03 +03:00
7f4165f958 java: add Save Copy as... 2021-11-15 13:49:30 +03:00
7cd7f1bf66 java-spring: add Save Copy as... 2021-11-15 13:12:42 +03:00
608c21c60b Merge remote-tracking branch 'remotes/origin/develop' into feature/save-as 2021-11-15 12:17:25 +03:00
f44af24267 nodejs: docxf 2021-11-15 12:15:34 +03:00
be7a1e8ab2 nodejs: opening uploaded form 2021-11-15 12:15:09 +03:00
9bab8edc3c using innerAlert in onRequestSendNotify 2021-11-15 11:48:17 +03:00
4d852a23cd nodejs: filling on mobile 2021-11-15 11:20:08 +03:00
a497401b30 nodejs: revert filling in docx 2021-11-15 11:20:07 +03:00
94cb70c0db nodejs: hide submit button 2021-11-15 11:20:07 +03:00
8e60c60e46 nodejs: oform 2021-11-15 11:20:06 +03:00
1cd275188b nodejs: onRequestSaveAs event 2021-11-15 11:20:05 +03:00
70836bb67d nodejs: using innerAlert 2021-11-09 14:42:35 +03:00
0de0b172c0 Merge pull request #221 from ONLYOFFICE/dependabot/bundler/web/documentserver-example/ruby/nokogiri-1.12.5
build(deps): bump nokogiri from 1.11.5 to 1.12.5 in /web/documentserver-example/ruby
2021-11-08 11:48:48 +03:00
adc549bf7e Merge remote-tracking branch 'remotes/origin/develop' into dependabot/bundler/web/documentserver-example/ruby/nokogiri-1.12.5
# Conflicts:
#	web/documentserver-example/ruby/Gemfile.lock
2021-11-08 11:46:30 +03:00
8de69d18cb build(deps): bump nokogiri in /web/documentserver-example/ruby
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.11.5 to 1.12.5.
- [Release notes](https://github.com/sparklemotion/nokogiri/releases)
- [Changelog](https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.11.5...v1.12.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-04 19:39:12 +00:00
02eceb6565 Merge pull request #220 from ONLYOFFICE/bugfix/fix-discovery
nodejs: check discovery["wopi-discovery"] is exist
2021-11-02 14:50:45 +03:00
f5371db950 nodejs: check discovery["wopi-discovery"] is exist 2021-11-02 13:53:36 +03:00
3a11a42f9e Merge pull request #210 from ONLYOFFICE/bugfix/sharp-mvc-fix
csharp-mvc: fix
2021-10-15 16:37:01 +03:00
156 changed files with 1962 additions and 789 deletions

View File

@ -0,0 +1,6 @@
<svg width="30" height="40" viewBox="0 0 30 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 3C0 1.34315 1.34315 0 3 0H22.9167L30 7.08333V37C30 38.6569 28.6569 40 27 40H3C1.34315 40 0 38.6569 0 37V3Z" fill="#27ABA3"/>
<path d="M22.9165 0L29.9998 7.08333H25.9165C24.2597 7.08333 22.9165 5.74019 22.9165 4.08333V0Z" fill="#008078"/>
<rect x="6.5" y="15.5" width="17" height="5" stroke="white"/>
<rect x="6.5" y="23.5" width="17" height="5" stroke="white"/>
</svg>

After

Width:  |  Height:  |  Size: 477 B

View File

@ -0,0 +1,6 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 5C5 3.89543 5.89543 3 7 3H16C17.1046 3 18 3.89543 18 5V8H17V6H6V18H17V16H18V19C18 20.1046 17.1046 21 16 21H7C5.89543 21 5 20.1046 5 19V5ZM13 4H10V5H13V4ZM12 19.5C12 19.7761 11.7761 20 11.5 20C11.2239 20 11 19.7761 11 19.5C11 19.2239 11.2239 19 11.5 19C11.7761 19 12 19.2239 12 19.5Z" fill="#444444"/>
<rect x="7" y="9" width="1" height="6" fill="#444444"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 8V9L11 9V15H12V16H9V15H10V9H9V8H12Z" fill="#444444"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M15 10H18V14H15H14H13V15H14H18C18.5523 15 19 14.5523 19 14V10C19 9.44772 18.5523 9 18 9H14H13V10H14H15Z" fill="#444444"/>
</svg>

After

Width:  |  Height:  |  Size: 790 B

View File

@ -157,6 +157,10 @@ label .checkbox {
background-image: url("images/file_pptx.svg");
}
.try-editor.form {
background-image: url("images/file_docxf.svg");
}
.create-sample {
color: #666666;
line-height: 24px;

View File

@ -44,7 +44,7 @@ namespace OnlineEditorsExampleMVC.Helpers
// get all the supported file extensions
public static List<string> FileExts
{
get { return ViewedExts.Concat(EditedExts).Concat(ConvertExts).ToList(); }
get { return ViewedExts.Concat(EditedExts).Concat(ConvertExts).Concat(FillFormExts).ToList(); }
}
// get file extensions that can be viewed
@ -53,6 +53,11 @@ namespace OnlineEditorsExampleMVC.Helpers
get { return (WebConfigurationManager.AppSettings["files.docservice.viewed-docs"] ?? "").Split(new char[] { '|', ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(); }
}
public static List<string> FillFormExts
{
get { return (WebConfigurationManager.AppSettings["files.docservice.fillform-docs"] ?? "").Split(new char[] { '|', ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(); }
}
// get file extensions that can be edited
public static List<string> EditedExts
{

View File

@ -79,11 +79,16 @@ namespace OnlineEditorsExampleMVC.Models
var editorsMode = Mode ?? "edit"; // get editor mode
var canEdit = DocManagerHelper.EditedExts.Contains(ext); // check if the file with such an extension can be edited
var mode = canEdit && editorsMode != "view" ? "edit" : "view"; // set the mode parameter: change it to view if the document can't be edited
var submitForm = canEdit && (editorsMode.Equals("edit") || editorsMode.Equals("fillForms")); // check if the Submit form button is displayed or not
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)) {
editorsMode = "fillForms";
canEdit = true;
}
var submitForm = editorsMode.Equals("fillForms") && id.Equals("uid-1") && false; // 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
bool? favorite = user.favorite;

View File

@ -49,7 +49,7 @@ namespace OnlineEditorsExampleMVC.Models
".dot", ".dotx", ".dotm",
".odt", ".fodt", ".ott", ".rtf", ".txt",
".html", ".htm", ".mht", ".xml",
".pdf", ".djvu", ".fb2", ".epub", ".xps", ".oxps"
".pdf", ".djvu", ".fb2", ".epub", ".xps", ".oxps", ".oform"
};
// spreadsheet extensions

View File

@ -131,6 +131,7 @@
<Content Include="Content\images\embeded.svg" />
<Content Include="Content\images\error.svg" />
<Content Include="Content\images\file_docx.svg" />
<Content Include="Content\images\file_docxf.svg" />
<Content Include="Content\images\file_pptx.svg" />
<Content Include="Content\images\file_upload.svg" />
<Content Include="Content\images\file_xlsx.svg" />
@ -143,6 +144,7 @@
<Content Include="Content\images\loader16.gif" />
<Content Include="Content\images\logo.png" />
<Content Include="Content\images\logo.svg" />
<Content Include="Content\images\mobile-fill-forms.svg" />
<Content Include="Content\images\mobile.svg" />
<Content Include="Content\images\notdone.svg" />
<Content Include="Content\images\review.svg" />

View File

@ -168,7 +168,7 @@ if (typeof jQuery != "undefined") {
var posExt = fileName.lastIndexOf('.');
posExt = 0 <= posExt ? fileName.substring(posExt).trim().toLowerCase() : '';
if (EditedExtList.indexOf(posExt) != -1) {
if (EditedExtList.indexOf(posExt) != -1 || FillExtList.indexOf(posExt) != -1) {
jq("#beginEdit").removeClass("disable");
}
};

View File

@ -47,9 +47,11 @@
var docEditor;
var innerAlert = function (message) {
var innerAlert = function (message, inEditor) {
if (console && console.log)
console.log(message);
if (inEditor && docEditor)
docEditor.showMessage(message);
};
// the application is loaded into the browser
@ -134,6 +136,23 @@
<% Model.GetMailMergeConfig(out dataMailMergeRecipients); %>
docEditor.setMailMergeRecipients(<%= dataMailMergeRecipients%>); // insert recipient data for mail merge into the file
};
var onRequestSaveAs = function (event) { // the user is trying to save file by clicking Save Copy as... button
var title = event.data.title;
var url = event.data.url;
var data = {
title: title,
url: url
};
let xhr = new XMLHttpRequest();
xhr.open("POST", "webeditor.ashx?type=saveas");
xhr.setRequestHeader( 'Content-Type', 'application/json');
xhr.send(JSON.stringify(data));
xhr.onload = function () {
innerAlert(xhr.responseText);
innerAlert(JSON.parse(xhr.responseText).file, true);
}
};
var config = <%= Model.GetDocConfig(Request, Url) %>;
@ -185,14 +204,23 @@
};
// the user is mentioned in a comment
config.events['onRequestSendNotify'] = function (event) {
var actionLink = JSON.stringify(event.data.actionLink);
console.log("onRequestSendNotify:");
console.log(event.data);
console.log("Link to comment: " + replaceActionLink(location.href, actionLink));
event.data.actionLink = replaceActionLink(location.href, event.data.actionLink);
var data = JSON.stringify(event.data);
innerAlert("onRequestSendNotify: " + data);
};
<% } %>
if (config.editorConfig.createUrl) {
config.events.onRequestSaveAs = onRequestSaveAs;
};
var сonnectEditor = function () {
if ((config.document.fileType === "docxf" || config.document.fileType === "oform")
&& DocsAPI.DocEditor.version().split(".")[0] < 7) {
innerAlert("Please update ONLYOFFICE Docs to version 7.0 to work on fillable forms online.");
return;
}
docEditor = new DocsAPI.DocEditor("iframeEditor", config);
};

View File

@ -65,6 +65,9 @@
<li>
<a class="try-editor slide" data-type="pptx">Presentation</a>
</li>
<li>
<a class="try-editor form" data-type="docxf">Form template</a>
</li>
</ul>
<label class="create-sample">
<input id="createSample" class="checkbox" type="checkbox" />With sample content
@ -178,8 +181,10 @@
<% foreach (var storedFile in storedFiles)
{
var editUrl = "doceditor.aspx?fileID=" + HttpUtility.UrlEncode(storedFile.Name);
var docType = FileUtility.GetFileType(storedFile.Name).ToString().ToLower();
var canEdit = DocManagerHelper.EditedExts.Contains(Path.GetExtension(storedFile.Name).ToLower());
var docType = FileUtility.GetFileType(storedFile.Name).ToString().ToLower();
var ext = Path.GetExtension(storedFile.Name).ToLower();
var canEdit = DocManagerHelper.EditedExts.Contains(ext);
var isFillFormDoc = DocManagerHelper.FillFormExts.Contains(ext);
%>
<tr class="tableRow" title="<%= storedFile.Name %> [<%= DocManagerHelper.GetFileVersion(storedFile.Name, HttpContext.Current.Request.UserHostAddress) %>]">
@ -222,25 +227,40 @@
<% } %>
<% if (docType == "word") { %>
<td class="contentCells contentCells-icon">
<a href="<%= Url.Action("Editor", "Home", new { fileName = storedFile.Name, editorsType = "desktop", editorsMode = "fillForms" }) %>" target="_blank">
<img src="content/images/fill-forms.svg" alt="Open in editor for filling in forms" title="Open in editor for filling in forms"/>
</a>
</td>
<% } else { %>
<td class="contentCells contentCells-icon"></td>
<% } %>
<% if (docType == "word"){ %>
<td class="contentCells contentCells-shift contentCells-icon firstContentCellShift">
<a href="<%= Url.Action("Editor", "Home", new { fileName = storedFile.Name, editorsType = "desktop", editorsMode = "blockcontent" }) %>" target="_blank">
<img src="content/images/block-content.svg" alt="Open in editor without content control modification" title="Open in editor without content control modification"/>
</a>
</td>
<% } else { %>
<td class="contentCells contentCells-shift contentCells-icon firstContentCellShift"></td>
<td class="contentCells contentCells-icon"></td>
<% } %>
<% if (docType != "word" && docType != "cell") { %>
<td class="contentCells contentCells-icon "></td>
<% } %>
<% if (isFillFormDoc) { %>
<td class="contentCells contentCells-shift contentCells-icon firstContentCellShift">
<a href="<%= Url.Action("Editor", "Home", new { fileName = storedFile.Name, editorsType = "desktop", editorsMode = "fillForms" }) %>" target="_blank">
<img src="content/images/fill-forms.svg" alt="Open in editor for filling in forms" title="Open in editor for filling in forms"/>
</a>
</td>
<% } else { %>
<td class="contentCells contentCells-shift contentCells-icon firstContentCellShift"></td>
<% } %>
<% } else if (isFillFormDoc) { %>
<td class="contentCells contentCells-icon "></td>
<td class="contentCells contentCells-icon">
<a href="<%= Url.Action("Editor", "Home", new { fileName = storedFile.Name, editorsType = "mobile", editorsMode = "fillForms" }) %>" target="_blank">
<img src="content/images/mobile-fill-forms.svg" alt="Open in editor for filling in forms for mobile devices" title="Open in editor for filling in forms for mobile devices"/>
</a>
</td>
<td class="contentCells contentCells-icon "></td>
<td class="contentCells contentCells-icon "></td>
<td class="contentCells contentCells-icon "></td>
<td class="contentCells contentCells-shift contentCells-icon firstContentCellShift">
<a href="<%= Url.Action("Editor", "Home", new { fileName = storedFile.Name, editorsType = "desktop", editorsMode = "fillForms" }) %>" target="_blank">
<img src="content/images/fill-forms.svg" alt="Open in editor for filling in forms" title="Open in editor for filling in forms"/>
</a>
</td>
<% } else { %>
<td class="contentCells contentCells-shift contentCells-icon contentCellsEmpty" colspan="6"></td>
<% } %>
@ -351,6 +371,7 @@
<%: Scripts.Render("~/bundles/jquery", "~/bundles/scripts") %>
<script language="javascript" type="text/javascript">
var FillExtList = '<%= string.Join(",", DocManagerHelper.FillFormExts.ToArray()) %>';
var ConverExtList = '<%= string.Join(",", DocManagerHelper.ConvertExts.ToArray()) %>';
var EditedExtList = '<%= string.Join(",", DocManagerHelper.EditedExts.ToArray()) %>';
var UrlConverter = '<%= Url.Content("~/webeditor.ashx?type=convert") %>';

View File

@ -64,6 +64,81 @@ namespace OnlineEditorsExampleMVC
case "files":
Files(context);
break;
case "saveas":
SaveAs(context);
break;
}
}
private static void SaveAs(HttpContext context)
{
context.Response.ContentType = "text/plain";
try
{
string fileData;
try
{
using (var receiveStream = context.Request.InputStream)
using (var readStream = new StreamReader(receiveStream))
{
fileData = readStream.ReadToEnd();
if (string.IsNullOrEmpty(fileData)) context.Response.Write("{\"error\":\"Request stream is empty\"}");
}
}
catch (Exception e)
{
throw new HttpException((int)HttpStatusCode.BadRequest, e.Message);
}
var jss = new JavaScriptSerializer();
var body = jss.Deserialize<Dictionary<string, object>>(fileData);
var fileUrl = (string) body["url"];
var title = (string) body["title"];
var fileName = DocManagerHelper.GetCorrectName(title);
var extension = "." + (Path.GetExtension(fileName).ToLower() ?? "").Trim('.');
var allExt = DocManagerHelper.ConvertExts
.Concat(DocManagerHelper.EditedExts)
.Concat(DocManagerHelper.ViewedExts)
.Concat(DocManagerHelper.FillFormExts)
.ToArray();
if (!allExt.Contains(extension))
{
context.Response.Write("{\"error\":\"File type is not supported\"}");
}
var req = (HttpWebRequest)WebRequest.Create(fileUrl);
using (var stream = req.GetResponse().GetResponseStream())
{
if (stream == null || req.GetResponse().ContentLength <= 0 || req.GetResponse().ContentLength > DocManagerHelper.MaxFileSize)
{
context.Response.Write("{\"error\": \"File size is incorrect\"}");
}
const int bufferSize = 4096;
using (var fs = File.Open(DocManagerHelper.StoragePath(fileName, null), FileMode.Create))
{
var buffer = new byte[bufferSize];
int readed;
while ((readed = stream.Read(buffer, 0, bufferSize)) != 0)
{
fs.Write(buffer, 0, readed); // write bytes to the output stream
}
}
}
var id = context.Request.Cookies.GetOrDefault("uid", null);
var user = Users.getUser(id); // get the user
DocManagerHelper.CreateMeta(fileName, user.id, user.name, null);
context.Response.Write("{ \"file\": \"" + fileName + "\"}");
}
catch (Exception e)
{
context.Response.Write("{ \"error\": \"" + 1 + "\", \"message\": \"" + e.Message + "\"}");
}
}

View File

@ -1,12 +1,13 @@
<?xml version="1.0"?>
<appSettings>
<clear />
<add key="version" value="1.0.0"/>
<add key="version" value="1.1.0"/>
<add key="filesize-max" value="52428800"/>
<add key="files.docservice.fillform-docs" value=".oform|.docx"/>
<add key="files.docservice.viewed-docs" value=".pdf|.djvu|.xps|.oxps"/>
<add key="files.docservice.edited-docs" value=".docx|.xlsx|.csv|.pptx|.txt"/>
<add key="files.docservice.edited-docs" value=".docx|.xlsx|.csv|.pptx|.txt|.docxf"/>
<add key="files.docservice.convert-docs" value=".docm|.dotx|.dotm|.dot|.doc|.odt|.fodt|.ott|.xlsm|.xltx|.xltm|.xlt|.xls|.ods|.fods|.ots|.pptm|.ppt|.ppsx|.ppsm|.pps|.potx|.potm|.pot|.odp|.fodp|.otp|.rtf|.mht|.html|.htm|.xml|.epub|.fb2"/>
<add key="files.docservice.timeout" value="120000" />
<add key="files.docservice.secret" value="" />

View File

@ -0,0 +1,6 @@
<svg width="30" height="40" viewBox="0 0 30 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 3C0 1.34315 1.34315 0 3 0H22.9167L30 7.08333V37C30 38.6569 28.6569 40 27 40H3C1.34315 40 0 38.6569 0 37V3Z" fill="#27ABA3"/>
<path d="M22.9165 0L29.9998 7.08333H25.9165C24.2597 7.08333 22.9165 5.74019 22.9165 4.08333V0Z" fill="#008078"/>
<rect x="6.5" y="15.5" width="17" height="5" stroke="white"/>
<rect x="6.5" y="23.5" width="17" height="5" stroke="white"/>
</svg>

After

Width:  |  Height:  |  Size: 477 B

View File

@ -0,0 +1,6 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 5C5 3.89543 5.89543 3 7 3H16C17.1046 3 18 3.89543 18 5V8H17V6H6V18H17V16H18V19C18 20.1046 17.1046 21 16 21H7C5.89543 21 5 20.1046 5 19V5ZM13 4H10V5H13V4ZM12 19.5C12 19.7761 11.7761 20 11.5 20C11.2239 20 11 19.7761 11 19.5C11 19.2239 11.2239 19 11.5 19C11.7761 19 12 19.2239 12 19.5Z" fill="#444444"/>
<rect x="7" y="9" width="1" height="6" fill="#444444"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 8V9L11 9V15H12V16H9V15H10V9H9V8H12Z" fill="#444444"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M15 10H18V14H15H14H13V15H14H18C18.5523 15 19 14.5523 19 14V10C19 9.44772 18.5523 9 18 9H14H13V10H14H15Z" fill="#444444"/>
</svg>

After

Width:  |  Height:  |  Size: 790 B

View File

@ -157,6 +157,10 @@ label .checkbox {
background-image: url("images/file_pptx.svg");
}
.try-editor.form {
background-image: url("images/file_docxf.svg");
}
.create-sample {
color: #666666;
line-height: 24px;

View File

@ -67,6 +67,9 @@
<li>
<a class="try-editor slide" data-type="slide">Presentation</a>
</li>
<li>
<a class="try-editor form" data-type="docxf">Form template</a>
</li>
</ul>
<label class="create-sample">
<input id="createSample" class="checkbox" type="checkbox" />With sample content
@ -179,8 +182,10 @@
<% foreach (var storedFile in storedFiles)
{
var editUrl = "doceditor.aspx?fileID=" + HttpUtility.UrlEncode(storedFile.Name);
var ext = Path.GetExtension(storedFile.Name).ToLower();
var docType = DocumentType(storedFile.Name);
var canEdit = EditedExts.Contains(Path.GetExtension(storedFile.Name).ToLower());
var canEdit = EditedExts.Contains(ext);
var isFillFormDoc = FillFormsExts.Contains(ext);
%>
<tr class="tableRow" title="<%= storedFile.Name %> [<%= GetFileVersion(storedFile.Name, HttpContext.Current.Request.UserHostAddress) %>]">
@ -223,25 +228,40 @@
<% } %>
<% if (docType == "word") { %>
<td class="contentCells contentCells-icon">
<a href="<%= editUrl + "&editorsType=desktop&editorsMode=fillForms" %>" target="_blank">
<img src="app_themes/images/fill-forms.svg" alt="Open in editor for filling in forms" title="Open in editor for filling in forms"/>
</a>
</td>
<% } else { %>
<td class="contentCells contentCells-icon"></td>
<% } %>
<% if (docType == "word") { %>
<td class="contentCells contentCells-shift contentCells-icon firstContentCellShift">
<a href="<%= editUrl + "&editorsType=desktop&editorsMode=blockcontent" %>" target="_blank">
<img src="app_themes/images/block-content.svg" alt="Open in editor without content control modification" title="Open in editor without content control modification"/>
</a>
</td>
<% } else{%>
<td class="contentCells contentCells-shift contentCells-icon firstContentCellShift"></td>
<td class="contentCells contentCells-icon"></td>
<%} %>
<%if (docType != "word" && docType != "cell"){%>
<td class="contentCells contentCells-icon "></td>
<% } %>
<% if (isFillFormDoc) { %>
<td class="contentCells contentCells-shift contentCells-icon firstContentCellShift">
<a href="<%= editUrl + "&editorsType=desktop&editorsMode=fillForms" %>" target="_blank">
<img src="app_themes/images/fill-forms.svg" alt="Open in editor for filling in forms" title="Open in editor for filling in forms"/>
</a>
</td>
<% } else { %>
<td class="contentCells contentCells-shift contentCells-icon firstContentCellShift"></td>
<% } %>
<% } else if (isFillFormDoc) { %>
<td class="contentCells contentCells-icon "></td>
<td class="contentCells contentCells-icon">
<a href="<%= editUrl + "&editorsType=mobile&editorsMode=fillForms" %>" target="_blank">
<img src="app_themes/images/mobile-fill-forms.svg" alt="Open in editor for filling in forms for mobile devices" title="Open in editor for filling in forms for mobile devices"/>
</a>
</td>
<td class="contentCells contentCells-icon "></td>
<td class="contentCells contentCells-icon "></td>
<td class="contentCells contentCells-icon "></td>
<td class="contentCells contentCells-shift contentCells-icon firstContentCellShift">
<a href="<%= editUrl + "&editorsType=desktop&editorsMode=fillForms" %>" target="_blank">
<img src="app_themes/images/fill-forms.svg" alt="Open in editor for filling in forms" title="Open in editor for filling in forms"/>
</a>
</td>
<% } else { %>
<td class="contentCells contentCells-shift contentCells-icon contentCellsEmpty" colspan="6"></td>
<% } %>
@ -358,6 +378,7 @@
<script language="javascript" type="text/javascript" src="script/jquery.dropdownToggle.js"></script>
<script language="javascript" type="text/javascript" src="script/jscript.js"></script>
<script language="javascript" type="text/javascript">
var FillFormExtList = '<%= string.Join(",", FillFormsExts.ToArray()) %>';
var ConverExtList = '<%= string.Join(",", ConvertExts.ToArray()) %>';
var EditedExtList = '<%= string.Join(",", EditedExts.ToArray()) %>';
</script>

View File

@ -56,7 +56,7 @@ namespace OnlineEditorsExample
".dot", ".dotx", ".dotm",
".odt", ".fodt", ".ott", ".rtf", ".txt",
".html", ".htm", ".mht", ".xml",
".pdf", ".djvu", ".fb2", ".epub", ".xps", ".oxps"
".pdf", ".djvu", ".fb2", ".epub", ".xps", ".oxps", ".oform"
};
// get an internal file extension
@ -107,7 +107,7 @@ namespace OnlineEditorsExample
// get all the supported file extensions
private static List<string> FileExts
{
get { return ViewedExts.Concat(EditedExts).Concat(ConvertExts).ToList(); }
get { return ViewedExts.Concat(EditedExts).Concat(ConvertExts).Concat(FillFormsExts).ToList(); }
}
// file extensions that can be viewed
@ -115,6 +115,11 @@ namespace OnlineEditorsExample
{
get { return (WebConfigurationManager.AppSettings["files.docservice.viewed-docs"] ?? "").Split(new char[] { '|', ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(); }
}
public static List<string> FillFormsExts
{
get { return (WebConfigurationManager.AppSettings["files.docservice.fillform-docs"] ?? "").Split(new char[] { '|', ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(); }
}
// file extensions that can be edited
public static List<string> EditedExts
@ -346,6 +351,72 @@ namespace OnlineEditorsExample
return _fileName;
}
public static string DoSaveAs(HttpContext context)
{
string fileData;
try
{
using (var receiveStream = context.Request.InputStream)
using (var readStream = new StreamReader(receiveStream))
{
fileData = readStream.ReadToEnd();
if (string.IsNullOrEmpty(fileData)) return "{\"error\":\"Request stream is empty\"}";
}
}
catch (Exception e)
{
throw new HttpException((int)HttpStatusCode.BadRequest, e.Message);
}
var jss = new JavaScriptSerializer();
var body = jss.Deserialize<Dictionary<string, object>>(fileData);
var fileUrl = (string) body["url"];
var title = (string) body["title"];
var fileName = GetCorrectName(title);
var extension = "." + (Path.GetExtension(fileName).ToLower() ?? "").Trim('.');
var allExt = ConvertExts.Concat(EditedExts).Concat(ViewedExts).Concat(FillFormsExts).ToArray();
if (!allExt.Contains(extension))
{
return "{\"error\":\"File type is not supported\"}";
}
var req = (HttpWebRequest)WebRequest.Create(fileUrl);
// hack. http://ubuntuforums.org/showthread.php?t=1841740
if (IsMono)
{
ServicePointManager.ServerCertificateValidationCallback += (s, ce, ca, p) => true;
}
using (var stream = req.GetResponse().GetResponseStream())
{
if (stream == null || req.GetResponse().ContentLength <= 0 || req.GetResponse().ContentLength > MaxFileSize)
{
return "{\"error\": \"File size is incorrect\"}";
}
const int bufferSize = 4096;
using (var fs = File.Open(StoragePath(fileName, null), FileMode.Create))
{
var buffer = new byte[bufferSize];
int readed;
while ((readed = stream.Read(buffer, 0, bufferSize)) != 0)
{
fs.Write(buffer, 0, readed); // write bytes to the output stream
}
}
}
var id = context.Request.Cookies.GetOrDefault("uid", null);
var user = Users.getUser(id); // get the user
DocEditor.CreateMeta(fileName, user.id, user.name, null);
return "{\"file\": \"" + fileName + "\"}";
}
// converting a file
public static string DoConvert(HttpContext context)
{

View File

@ -65,9 +65,11 @@
var docEditor;
var innerAlert = function (message) {
var innerAlert = function (message, inEditor) {
if (console && console.log)
console.log(message);
if (inEditor && docEditor)
docEditor.showMessage(message);
};
// the application is loaded into the browser
@ -146,6 +148,23 @@
var onRequestMailMergeRecipients = function (event) {
docEditor.setMailMergeRecipients(<%= DataMailMergeRecipients %>); // insert recipient data for mail merge into the file
};
var onRequestSaveAs = function (event) { // the user is trying to save file by clicking Save Copy as... button
var title = event.data.title;
var url = event.data.url;
var data = {
title: title,
url: url
};
let xhr = new XMLHttpRequest();
xhr.open("POST", "webeditor.ashx?type=saveas");
xhr.setRequestHeader( 'Content-Type', 'application/json');
xhr.send(JSON.stringify(data));
xhr.onload = function () {
innerAlert(xhr.responseText);
innerAlert(JSON.parse(xhr.responseText).file, true);
}
};
var config = <%= DocConfig %>;
@ -190,14 +209,23 @@
};
// the user is mentioned in a comment
config.events['onRequestSendNotify'] = function (event) {
var actionLink = JSON.stringify(event.data.actionLink);
console.log("onRequestSendNotify:");
console.log(event.data);
console.log("Link to comment: " + replaceActionLink(location.href, actionLink));
event.data.actionLink = replaceActionLink(location.href, event.data.actionLink);
var data = JSON.stringify(event.data);
innerAlert("onRequestSendNotify: " + data);
};
<% } %>
if (config.editorConfig.createUrl) {
config.events.onRequestSaveAs = onRequestSaveAs;
};
var сonnectEditor = function () {
if ((config.document.fileType === "docxf" || config.document.fileType === "oform")
&& DocsAPI.DocEditor.version().split(".")[0] < 7) {
innerAlert("Please update ONLYOFFICE Docs to version 7.0 to work on fillable forms online.");
return;
}
docEditor = new DocsAPI.DocEditor("iframeEditor", config);
};

View File

@ -148,12 +148,17 @@ namespace OnlineEditorsExample
var editorsMode = Request.GetOrDefault("editorsMode", "edit");
var canEdit = _Default.EditedExts.Contains(ext); // check if this file can be edited
var mode = canEdit && editorsMode != "view" ? "edit" : "view"; // get the editor opening mode (edit or view)
var submitForm = canEdit && (editorsMode.Equals("edit") || editorsMode.Equals("fillForms")); // check if the Submit form button is displayed or hidden
var editorsType = Request.GetOrDefault("editorsType", "desktop");
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)) {
editorsMode = "fillForms";
canEdit = true;
}
var submitForm = editorsMode.Equals("fillForms") && id.Equals("uid-1") && false; // 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();
@ -517,6 +522,9 @@ namespace OnlineEditorsExample
case "slide":
ext = ".pptx"; // .pptx for slide document type
break;
case "docxf":
ext = ".docxf";
break;
default:
return;
}

View File

@ -80,6 +80,7 @@
<Content Include="App_Themes\images\embeded.svg" />
<Content Include="App_Themes\images\error.svg" />
<Content Include="App_Themes\images\file_docx.svg" />
<Content Include="App_Themes\images\file_docxf.svg" />
<Content Include="App_Themes\images\file_pptx.svg" />
<Content Include="App_Themes\images\file_upload.svg" />
<Content Include="App_Themes\images\file_xlsx.svg" />
@ -90,6 +91,7 @@
<Content Include="App_Themes\images\icon_xlsx.svg" />
<Content Include="App_Themes\images\info.svg" />
<Content Include="App_Themes\images\logo.svg" />
<Content Include="App_Themes\images\mobile-fill-forms.svg" />
<Content Include="App_Themes\images\mobile.svg" />
<Content Include="App_Themes\images\notdone.svg" />
<Content Include="App_Themes\images\review.svg" />

View File

@ -62,6 +62,23 @@ namespace OnlineEditorsExample
case "files":
Files(context);
break;
case "saveas":
SaveAs(context);
break;
}
}
private static void SaveAs(HttpContext context)
{
context.Response.ContentType = "text/plain";
try
{
var result = _Default.DoSaveAs(context);
context.Response.Write(result);
}
catch (Exception e)
{
context.Response.Write("{ \"error\": \"" + 1 + "\", \"message\": \"" + e.Message + "\"}");
}
}

View File

@ -168,7 +168,7 @@ if (typeof jQuery != "undefined") {
var posExt = fileName.lastIndexOf('.');
posExt = 0 <= posExt ? fileName.substring(posExt).trim().toLowerCase() : '';
if (EditedExtList.indexOf(posExt) != -1) {
if (EditedExtList.indexOf(posExt) != -1 || FillFormExtList.indexOf(posExt) != -1) {
jq("#beginEdit").removeClass("disable");
}
};

View File

@ -1,13 +1,14 @@
<?xml version="1.0" encoding="utf-8" ?>
<appSettings>
<clear />
<add key="version" value="1.0.0"/>
<add key="version" value="1.1.0"/>
<add key="filesize-max" value="52428800"/>
<add key="storage-path" value=""/>
<add key="files.docservice.fillform-docs" value=".oform|.docx"/>
<add key="files.docservice.viewed-docs" value=".pdf|.djvu|.xps|.oxps"/>
<add key="files.docservice.edited-docs" value=".docx|.xlsx|.csv|.pptx|.txt"/>
<add key="files.docservice.edited-docs" value=".docx|.xlsx|.csv|.pptx|.txt|.docxf"/>
<add key="files.docservice.convert-docs" value=".docm|.dotx|.dotm|.dot|.doc|.odt|.fodt|.ott|.xlsm|.xltx|.xltm|.xlt|.xls|.ods|.fods|.ots|.pptm|.ppt|.ppsx|.ppsm|.pps|.potx|.potm|.pot|.odp|.fodp|.otp|.rtf|.mht|.html|.htm|.xml|.epub|.fb2"/>
<add key="files.docservice.timeout" value="120000" />
<add key="files.docservice.secret" value="" />

View File

@ -33,7 +33,7 @@ public class ExampleData {
private UserServices userService;
@PostConstruct
public void init(){
List<String> description_user_0=List.of(
List<String> description_user_0=List.of( // the description for user 0
"The name is requested when the editor is opened",
"Doesnt belong to any group",
"Can review all the changes",
@ -42,7 +42,7 @@ public class ExampleData {
"Can't mention others in comments",
"Can't create new files from the editor"
);
List<String> description_user_1 = List.of(
List<String> description_user_1 = List.of( // the description for user 1
"File author by default",
"He doesnt belong to any of the groups",
"He can review all the changes",
@ -50,14 +50,14 @@ public class ExampleData {
"The file favorite state is undefined",
"Can create a file from a template with data from the editor"
);
List<String> description_user_2 = List.of(
List<String> description_user_2 = List.of( // the description for user 2
"He belongs to Group2",
"He can review only his own changes or the changes made by the users who dont belong to any of the groups",
"He can view every comment, edit his comments and the comments left by the users who don't belong to any of the groups and remove only his comments",
"This file is favorite",
"Can create a file from an editor"
);
List<String> description_user_3 = List.of(
List<String> description_user_3 = List.of( // the description for user 3
"He belongs to Group3",
"He can review only the changes made by the users from Group2",
"He can view the comments left by the users from Group2 and Group3 and edit the comments left by the users from Group2",
@ -67,18 +67,18 @@ public class ExampleData {
"He cant print the file",
"Can create a file from an editor"
);
userService.createUser("John Smith", "smith@example.com",
userService.createUser("John Smith", "smith@example.com", // create user 1 with the specified parameters
description_user_1, null, List.of(FilterState.NULL.toString()),
List.of(FilterState.NULL.toString()),
List.of(FilterState.NULL.toString()),
List.of(FilterState.NULL.toString()), null);
userService.createUser("Mark Pottato", "pottato@example.com",
userService.createUser("Mark Pottato", "pottato@example.com", // create user 2 with the specified parameters
description_user_2, "group-2", List.of("","group-2"), List.of(FilterState.NULL.toString()),
List.of("group-2", ""), List.of("group-2"), true);
userService.createUser("Hamish Mitchell", "mitchell@example.com",
userService.createUser("Hamish Mitchell", "mitchell@example.com", // create user 3 with the specified parameters
description_user_3, "group-3", List.of("group-2"), List.of("group-2", "group-3"),
List.of("group-2"), new ArrayList<>(), false);
userService.createUser("Anonymous",null,
userService.createUser("Anonymous",null, // create user 0 with the specified parameters
description_user_0,null, List.of(FilterState.NULL.toString()), List.of(FilterState.NULL.toString()),
List.of(FilterState.NULL.toString()), List.of(FilterState.NULL.toString()), null);
}

View File

@ -24,6 +24,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class IntegrationApplication {
// run the SpringApplication from the IntagrationApplication with the specified parameters
public static void main(String[] args) {
SpringApplication.run(IntegrationApplication.class, args);
}

View File

@ -40,28 +40,28 @@ public class IntegrationConfiguration {
private FileStoragePathBuilder storagePathBuilder;
@Bean
public ModelMapper mapper(){
public ModelMapper mapper(){ // create the model mapper
ModelMapper mapper = new ModelMapper();
mapper.getConfiguration()
.setMatchingStrategy(MatchingStrategies.STRICT)
.setFieldMatchingEnabled(true)
.setSkipNullEnabled(true)
.setFieldAccessLevel(org.modelmapper.config.Configuration.AccessLevel.PRIVATE);
mapper.getConfiguration() // get the mapper configuration and set new parameters to it
.setMatchingStrategy(MatchingStrategies.STRICT) // specify the STRICT matching strategy
.setFieldMatchingEnabled(true) // define if the field matching is enabled or not
.setSkipNullEnabled(true) // define if null value will be skipped or not
.setFieldAccessLevel(org.modelmapper.config.Configuration.AccessLevel.PRIVATE); // specify the PRIVATE field access level
return mapper;
}
@Bean
public JSONParser jsonParser(){
public JSONParser jsonParser(){ // create JSON parser
return new JSONParser();
}
@PostConstruct
public void init(){
public void init(){ // initialize the storage path builder
storagePathBuilder.configure(storageAddress.isBlank() ? null : storageAddress);
}
@Bean
public ObjectMapper objectMapper(){
public ObjectMapper objectMapper(){ // create the object mapper
return new ObjectMapper();
}
}

View File

@ -72,6 +72,7 @@ public class EditorController {
private FileConfigurer<DefaultFileWrapper> fileConfigurer;
@GetMapping(path = "${url.editor}")
// process request to open the editor page
public String index(@RequestParam("fileName") String fileName,
@RequestParam(value = "action", required = false) String actionParam,
@RequestParam(value = "type", required = false) String typeParam,
@ -89,10 +90,12 @@ public class EditorController {
Optional<User> optionalUser = userService.findUserById(Integer.parseInt(uid));
// if the user is not present, return the ONLYOFFICE start page
if(!optionalUser.isPresent()) return "index.html";
User user = optionalUser.get();
// get file model with the default file parameters
FileModel fileModel = fileConfigurer.getFileModel(
DefaultFileWrapper
.builder()
@ -105,23 +108,24 @@ public class EditorController {
.build()
);
model.addAttribute("model", fileModel);
model.addAttribute("fileHistory", historyManager.getHistory(fileModel.getDocument()));
model.addAttribute("docserviceApiUrl",docserviceSite + docserviceApiUrl);
model.addAttribute("dataInsertImage", getInsertImage());
model.addAttribute("dataCompareFile", getCompareFile());
model.addAttribute("dataMailMergeRecipients", getMailMerge());
model.addAttribute("usersForMentions", getUserMentions(uid));
// add attributes to the specified model
model.addAttribute("model", fileModel); // add file model with the default parameters to the original model
model.addAttribute("fileHistory", historyManager.getHistory(fileModel.getDocument())); // get file history and add it to the model
model.addAttribute("docserviceApiUrl",docserviceSite + docserviceApiUrl); // create the document service api URL and add it to the model
model.addAttribute("dataInsertImage", getInsertImage()); // get an image and add it to the model
model.addAttribute("dataCompareFile", getCompareFile()); // get a document for comparison and add it to the model
model.addAttribute("dataMailMergeRecipients", getMailMerge()); // get recipients data for mail merging and add it to the model
model.addAttribute("usersForMentions", getUserMentions(uid)); // get user data for mentions and add it to the model
return "editor.html";
}
private List<Mentions> getUserMentions(String uid){
private List<Mentions> getUserMentions(String uid){ // get user data for mentions
List<Mentions> usersForMentions=new ArrayList<>();
if(uid!=null && !uid.equals("4")) {
List<User> list = userService.findAll();
for (User u : list) {
if (u.getId()!=Integer.parseInt(uid) && u.getId()!=4) {
usersForMentions.add(new Mentions(u.getName(),u.getEmail()));
usersForMentions.add(new Mentions(u.getName(),u.getEmail())); // user data includes user names and emails
}
}
}
@ -130,26 +134,28 @@ public class EditorController {
}
@SneakyThrows
private String getInsertImage() {
private String getInsertImage() { // get an image that will be inserted into the document
Map<String, Object> dataInsertImage = new HashMap<>();
dataInsertImage.put("fileType", "png");
dataInsertImage.put("url", storagePathBuilder.getServerUrl(true) + "/css/img/logo.png");
// check if the document token is enabled
if(jwtManager.tokenEnabled()){
dataInsertImage.put("token", jwtManager.createToken(dataInsertImage));
dataInsertImage.put("token", jwtManager.createToken(dataInsertImage)); // create token from the dataInsertImage object
}
return objectMapper.writeValueAsString(dataInsertImage).substring(1, objectMapper.writeValueAsString(dataInsertImage).length()-1);
}
@SneakyThrows
private String getCompareFile(){
private String getCompareFile(){ // get a document that will be compared with the current document
Map<String, Object> dataCompareFile = new HashMap<>();
dataCompareFile.put("fileType", "docx");
dataCompareFile.put("url", storagePathBuilder.getServerUrl(true) + "/assets?name=sample.docx");
// check if the document token is enabled
if(jwtManager.tokenEnabled()){
dataCompareFile.put("token", jwtManager.createToken(dataCompareFile));
dataCompareFile.put("token", jwtManager.createToken(dataCompareFile)); // create token from the dataCompareFile object
}
return objectMapper.writeValueAsString(dataCompareFile);
@ -157,12 +163,13 @@ public class EditorController {
@SneakyThrows
private String getMailMerge(){
Map<String, Object> dataMailMergeRecipients = new HashMap<>();
Map<String, Object> dataMailMergeRecipients = new HashMap<>(); // get recipients data for mail merging
dataMailMergeRecipients.put("fileType", "csv");
dataMailMergeRecipients.put("url", storagePathBuilder.getServerUrl(true) + "/csv");
// check if the document token is enabled
if(jwtManager.tokenEnabled()){
dataMailMergeRecipients.put("token", jwtManager.createToken(dataMailMergeRecipients));
dataMailMergeRecipients.put("token", jwtManager.createToken(dataMailMergeRecipients)); // create token from the dataMailMergeRecipients object
}
return objectMapper.writeValueAsString(dataMailMergeRecipients);

View File

@ -58,6 +58,9 @@ public class FileController {
@Value("${files.docservice.header}")
private String documentJwtHeader;
@Value("${filesize-max}")
private String filesizeMax;
@Autowired
private FileUtility fileUtility;
@Autowired
@ -77,21 +80,24 @@ public class FileController {
@Autowired
private ServiceConverter serviceConverter;
// create user metadata
private String createUserMetadata(String uid, String fullFileName) {
Optional<User> optionalUser = userService.findUserById(Integer.parseInt(uid));
String documentType = fileUtility.getDocumentType(fullFileName).toString().toLowerCase();
Optional<User> optionalUser = userService.findUserById(Integer.parseInt(uid)); // find a user by their ID
String documentType = fileUtility.getDocumentType(fullFileName).toString().toLowerCase(); // get document type
if(optionalUser.isPresent()){
User user = optionalUser.get();
storageMutator.createMeta(fullFileName,
storageMutator.createMeta(fullFileName, // create meta information with the user ID and name specified
String.valueOf(user.getId()), user.getName());
}
return "{ \"filename\": \"" + fullFileName + "\", \"documentType\": \"" + documentType + "\" }";
}
// download data from the specified file
private ResponseEntity<Resource> downloadFile(String fileName){
Resource resource = storageMutator.loadFileAsResource(fileName);
Resource resource = storageMutator.loadFileAsResource(fileName); // load the specified file as a resource
String contentType = "application/octet-stream";
// create a response with the content type, header and body with the file data
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType(contentType))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
@ -100,130 +106,136 @@ public class FileController {
@PostMapping("/upload")
@ResponseBody
public String upload(@RequestParam("file") MultipartFile file,
public String upload(@RequestParam("file") MultipartFile file, // upload a file
@CookieValue("uid") String uid){
try {
String fullFileName = file.getOriginalFilename();
String fileExtension = fileUtility.getFileExtension(fullFileName);
long fileSize = file.getSize();
byte[] bytes = file.getBytes();
String fullFileName = file.getOriginalFilename(); // get file name
String fileExtension = fileUtility.getFileExtension(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){
return "{ \"error\": \"File size is incorrect\"}";
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)){
return "{ \"error\": \"File type is not supported\"}";
return "{ \"error\": \"File type is not supported\"}"; // if not, write an error message to the response
}
String fileNamePath = storageMutator.updateFile(fullFileName, bytes);
String fileNamePath = storageMutator.updateFile(fullFileName, bytes); // update a file
if (fileNamePath.isBlank()){
throw new IOException("Could not update a file");
throw new IOException("Could not update a file"); // if the file cannot be updated, an error occurs
}
fullFileName = fileUtility.getFileNameWithoutExtension(fileNamePath) + fileExtension;
fullFileName = fileUtility.getFileNameWithoutExtension(fileNamePath) + fileExtension; // get full file name
return createUserMetadata(uid, fullFileName);
return createUserMetadata(uid, fullFileName); // create user metadata and return it
} catch (Exception e) {
e.printStackTrace();
}
return "{ \"error\": \"Something went wrong when uploading the file.\"}";
return "{ \"error\": \"Something went wrong when uploading the file.\"}"; // if the operation of file uploading is unsuccessful, an error occurs
}
@PostMapping(path = "${url.converter}")
@ResponseBody
public String convert(@RequestBody Converter body,
public String convert(@RequestBody Converter body, // convert a file
@CookieValue("uid") String uid, @CookieValue("ulang") String lang){
String fileName = body.getFileName();
String fileUri = documentManager.getDownloadUrl(fileName);
String filePass = body.getFilePass() != null ? body.getFilePass() : null;
String fileExt = fileUtility.getFileExtension(fileName);
DocumentType type = fileUtility.getDocumentType(fileName);
String internalFileExt = fileUtility.getInternalExtension(type);
String fileName = body.getFileName(); // get file name
String fileUri = documentManager.getDownloadUrl(fileName); // get URL for downloading a file with the specified name
String filePass = body.getFilePass() != null ? body.getFilePass() : null; // get file password if it exists
String fileExt = fileUtility.getFileExtension(fileName); // get file extension
DocumentType type = fileUtility.getDocumentType(fileName); // get document type (word, cell or slide)
String internalFileExt = fileUtility.getInternalExtension(type); // get an editor internal extension (".docx", ".xlsx" or ".pptx")
try{
if(fileUtility.getConvertExts().contains(fileExt)){
String key = serviceConverter.generateRevisionId(fileUri);
String newFileUri = serviceConverter
if(fileUtility.getConvertExts().contains(fileExt)){ // check if the file with such an extension can be converted
String key = serviceConverter.generateRevisionId(fileUri); // generate document key
String newFileUri = serviceConverter // get the URL to the converted file
.getConvertedUri(fileUri, fileExt, internalFileExt, key, filePass, true, lang);
if(newFileUri.isEmpty()){
return "{ \"step\" : \"0\", \"filename\" : \"" + fileName + "\"}";
}
// 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) + internalFileExt;
String correctedName = documentManager.getCorrectName(nameWithInternalExt);
URL url = new URL(newFileUri);
java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection();
InputStream stream = connection.getInputStream();
InputStream stream = connection.getInputStream(); // get input stream of the converted file
if (stream == null){
connection.disconnect();
throw new RuntimeException("Input stream is null");
}
// create the converted file with input stream
storageMutator.createFile(Path.of(storagePathBuilder.getFileLocation(correctedName)), stream);
fileName = correctedName;
}
// create meta information about the converted file with the user ID and name specified
return createUserMetadata(uid, fileName);
}catch (Exception e) {
e.printStackTrace();
}
return "{ \"error\": \"" + "The file can't be converted.\"}";
return "{ \"error\": \"" + "The file can't be converted.\"}"; // if the operation of file converting is unsuccessful, an error occurs
}
@PostMapping("/delete")
@ResponseBody
public String delete(@RequestBody Converter body){
public String delete(@RequestBody Converter body){ // delete a file
try
{
String fullFileName = fileUtility.getFileName(body.getFileName());
boolean fileSuccess = storageMutator.deleteFile(fullFileName);
boolean historySuccess = storageMutator.deleteFileHistory(fullFileName);
String fullFileName = fileUtility.getFileName(body.getFileName()); // get full file name
boolean fileSuccess = storageMutator.deleteFile(fullFileName); // delete a file from the storage and return the status of this operation (true or false)
boolean historySuccess = storageMutator.deleteFileHistory(fullFileName); // delete file history and return the status of this operation (true or false)
return "{ \"success\": \""+ (fileSuccess && historySuccess) +"\"}";
}
catch (Exception e)
{
return "{ \"error\": \"" + e.getMessage() + "\"}";
return "{ \"error\": \"" + e.getMessage() + "\"}"; // if the operation of file deleting is unsuccessful, an error occurs
}
}
@GetMapping(path = "${url.download}")
public ResponseEntity<Resource> download(HttpServletRequest request,
public ResponseEntity<Resource> download(HttpServletRequest request, // download a file
@RequestParam("fileName") String fileName){
try{
// check if a token is enabled or not
if(jwtManager.tokenEnabled()){
String header = request.getHeader(documentJwtHeader == null
String header = request.getHeader(documentJwtHeader == null // get the document JWT header
|| documentJwtHeader.isEmpty() ? "Authorization" : documentJwtHeader);
if(header != null && !header.isEmpty()){
String token = header.replace("Bearer ", "");
jwtManager.readToken(token);
String token = header.replace("Bearer ", ""); // token is the header without the Bearer prefix
jwtManager.readToken(token); // read the token
}
}
return downloadFile(fileName);
return downloadFile(fileName); // download data from the specified file
} catch(Exception e){
return null;
}
}
@GetMapping("/create")
public String create(@RequestParam("fileExt") String fileExt,
public String create(@RequestParam("fileExt") String fileExt, // create a sample file of the specified extension
@RequestParam(value = "sample", required = false) Optional<Boolean> isSample,
@CookieValue(value = "uid", required = false) String uid,
Model model){
Boolean sampleData = (isSample.isPresent() && !isSample.isEmpty()) && isSample.get();
Boolean sampleData = (isSample.isPresent() && !isSample.isEmpty()) && isSample.get(); // specify if the sample data exists or not
if(fileExt != null){
try{
Optional<User> user = userService.findUserById(Integer.parseInt(uid));
if (!user.isPresent()) throw new RuntimeException("Could not fine any user with id = "+uid);
String fileName = documentManager.createDemo(fileExt, sampleData, uid, user.get().getName());
Optional<User> user = userService.findUserById(Integer.parseInt(uid)); // find a user by their ID
if (!user.isPresent()) throw new RuntimeException("Could not fine any user with id = "+uid); // if the user with the specified ID doesn't exist, an error occurs
String fileName = documentManager.createDemo(fileExt, sampleData, uid, user.get().getName()); // create a demo document with the sample data
if (fileName.isBlank() || fileName == null) {
throw new RuntimeException("You must have forgotten to add asset files");
}
return "redirect:editor?fileName=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8);
return "redirect:editor?fileName=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8); // redirect the request
}catch (Exception ex){
model.addAttribute("error", ex.getMessage());
return "error.html";
@ -233,14 +245,14 @@ public class FileController {
}
@GetMapping("/assets")
public ResponseEntity<Resource> assets(@RequestParam("name") String name)
public ResponseEntity<Resource> assets(@RequestParam("name") String name) // get sample files from the assests
{
String fileName = Path.of("assets", "sample", fileUtility.getFileName(name)).toString();
return downloadFile(fileName);
}
@GetMapping("/csv")
public ResponseEntity<Resource> csv()
public ResponseEntity<Resource> csv() // download a csv file
{
String fileName = Path.of("assets", "sample", "csv.csv").toString();
return downloadFile(fileName);
@ -248,27 +260,27 @@ public class FileController {
@GetMapping("/files")
@ResponseBody
public ArrayList<Map<String, Object>> files(@RequestParam(value = "fileId", required = false) String fileId){
public ArrayList<Map<String, Object>> files(@RequestParam(value = "fileId", required = false) String fileId){ // get files information
return fileId == null ? documentManager.getFilesInfo() : documentManager.getFilesInfo(fileId);
}
@PostMapping(path = "${url.track}")
@ResponseBody
public String track(HttpServletRequest request,
public String track(HttpServletRequest request, // track file changes
@RequestParam("fileName") String fileName,
@RequestParam("userAddress") String userAddress,
@RequestBody Track body){
try {
String bodyString = objectMapper.writeValueAsString(body);
String header = request.getHeader(documentJwtHeader == null
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 (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);
body = objectMapper.readValue(bodyCheck.toJSONString(), Track.class);
JSONObject bodyCheck = jwtManager.parseBody(bodyString, header); // parse the request body
body = objectMapper.readValue(bodyCheck.toJSONString(), Track.class); // read the request body
} catch (Exception e) {
e.printStackTrace();
return e.getMessage();
@ -278,4 +290,35 @@ public class FileController {
return"{\"error\":" + error + "}";
}
@PostMapping("/saveas")
@ResponseBody
public String saveAs(@RequestBody JSONObject body, @CookieValue("uid") String uid) {
String title = (String) body.get("title");
String saveAsFileUrl = (String) body.get("url");
try {
String fileName = documentManager.getCorrectName(title);
String curExt = fileUtility.getFileExtension(fileName);
if (!fileUtility.getFileExts().contains(curExt)) {
return "{\"error\":\"File type is not supported\"}";
}
URL url = new URL(saveAsFileUrl);
java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection();
InputStream stream = connection.getInputStream();
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);
return "{\"file\": \"" + fileName + "\"}";
} catch (IOException e) {
e.printStackTrace();
return "{ \"error\" : 1, \"message\" : \"" + e.getMessage() + "\"}";
}
}
}

View File

@ -66,24 +66,28 @@ public class IndexController {
@GetMapping("${url.index}")
public String index(Model model){
java.io.File[] files = storageMutator.getStoredFiles();
java.io.File[] files = storageMutator.getStoredFiles(); // get all the stored files from the storage
List<String> docTypes = new ArrayList<>();
List<Boolean> filesEditable = new ArrayList<>();
List<String> versions = new ArrayList<>();
List<Boolean> isFillFormDoc = new ArrayList<>();
List<User> users = userService.findAll();
List<User> users = userService.findAll(); // get a list of all the users
String tooltip = users.stream()
.map(user -> mistUtility.convertUserDescriptions(user.getName(), user.getDescriptions()))
String tooltip = users.stream() // get the tooltip with the user descriptions
.map(user -> mistUtility.convertUserDescriptions(user.getName(), user.getDescriptions())) // convert user descriptions to the specified format
.collect(Collectors.joining());
for(java.io.File file:files){
String fileName = file.getName();
docTypes.add(fileUtility.getDocumentType(fileName).toString().toLowerCase());
filesEditable.add(fileUtility.getEditedExts().contains(fileUtility.getFileExtension(fileName)));
versions.add(" ["+storagePathBuilder.getFileVersion(fileName, true)+"]");
for(java.io.File file:files){ // run through all the files
String fileName = file.getName(); // get file name
docTypes.add(fileUtility.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
versions.add(" ["+storagePathBuilder.getFileVersion(fileName, true)+"]"); // add a file version to the list
isFillFormDoc.add(fileUtility.getFillExts().contains(fileUtility.getFileExtension(fileName)));
}
// add all the parameters to the model
model.addAttribute("isFillFormDoc", isFillFormDoc);
model.addAttribute("versions",versions);
model.addAttribute("files", files);
model.addAttribute("docTypes", docTypes);
@ -97,11 +101,12 @@ public class IndexController {
@PostMapping("/config")
@ResponseBody
public HashMap<String, String> configParameters(){
public HashMap<String, String> configParameters(){ // get configuration parameters
HashMap<String, String> configuration = new HashMap<>();
configuration.put("ConverExtList", String.join(",",fileUtility.getConvertExts()));
configuration.put("EditedExtList", String.join(",",fileUtility.getEditedExts()));
configuration.put("FillExtList", String.join(",", fileUtility.getFillExts())); // put a list of the extensions that can be filled to config
configuration.put("ConverExtList", String.join(",", fileUtility.getConvertExts())); // put a list of the extensions that can be converted to config
configuration.put("EditedExtList", String.join(",", fileUtility.getEditedExts())); // put a list of the extensions that can be edited to config
configuration.put("UrlConverter", urlConverter);
configuration.put("UrlEditor", urlEditor);

View File

@ -21,11 +21,12 @@ package com.onlyoffice.integration.documentserver.callbacks;
import com.onlyoffice.integration.dto.Track;
import org.springframework.beans.factory.annotation.Autowired;
// specify the callback handler functions
public interface Callback {
int handle(Track body, String fileName);
int getStatus();
int handle(Track body, String fileName); // handle the callback
int getStatus(); // get document status
@Autowired
default void selfRegistration(CallbackHandler callbackHandler){
default void selfRegistration(CallbackHandler callbackHandler){ // register a callback handler
callbackHandler.register(getStatus(), this);
}
}

View File

@ -33,11 +33,11 @@ public class CallbackHandler {
private Map<Integer, Callback> callbackHandlers = new HashMap<>();
public void register(int code, Callback callback){
public void register(int code, Callback callback){ // register a callback handler
callbackHandlers.put(code, callback);
}
public int handle(Track body, String fileName){
public int handle(Track body, String fileName){ // handle a callback
Callback callback = callbackHandlers.get(body.getStatus());
if (callback == null){
logger.warn("Callback status "+body.getStatus()+" is not supported yet");

View File

@ -18,17 +18,18 @@
package com.onlyoffice.integration.documentserver.callbacks;
// document status
public enum Status {
EDITING(1),
SAVE(2),
CORRUPTED(3),
MUST_FORCE_SAVE(6),
CORRUPTED_FORCE_SAVE(7);
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(int code){
this.code = code;
}
public int getCode(){
public int getCode(){ // get document status
return this.code;
}
}

View File

@ -31,15 +31,15 @@ public class EditCallback implements Callback {
@Autowired
private CallbackManager callbackManager;
@Override
public int handle(Track body, String fileName) {
public int handle(Track body, String fileName) { // handle the callback when the document is being edited
int result = 0;
Action action = body.getActions().get(0);
if (action.getType().equals(com.onlyoffice.integration.documentserver.models.enums.Action.edit)) {
String user = action.getUserid();
if (!body.getUsers().contains(user)) {
String key = body.getKey();
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 {
callbackManager.commandRequest("forcesave", key);
callbackManager.commandRequest("forcesave", key); // create a command request to forcibly save the document being edited without closing it
} catch (Exception e) {
e.printStackTrace();
result = 1;
@ -50,7 +50,7 @@ public class EditCallback implements Callback {
}
@Override
public int getStatus() {
return Status.EDITING.getCode();
public int getStatus() { // get document status
return Status.EDITING.getCode(); // return status 1 - document is being edited
}
}

View File

@ -30,10 +30,10 @@ public class ForcesaveCallback implements Callback {
@Autowired
private CallbackManager callbackManager;
@Override
public int handle(Track body, String fileName) {
public int handle(Track body, String fileName) { // handle the callback when the force saving request is performed
int result = 0;
try {
callbackManager.processForceSave(body, fileName);
callbackManager.processForceSave(body, fileName); // file force saving process
} catch (Exception ex) {
ex.printStackTrace();
result = 1;
@ -42,7 +42,7 @@ public class ForcesaveCallback implements Callback {
}
@Override
public int getStatus() {
return Status.MUST_FORCE_SAVE.getCode();
public int getStatus() { // get document status
return Status.MUST_FORCE_SAVE.getCode(); // return status 6 - document is being edited, but the current document state is saved
}
}

View File

@ -30,10 +30,10 @@ public class SaveCallback implements Callback {
@Autowired
private CallbackManager callbackManager;
@Override
public int handle(Track body, String fileName) {
public int handle(Track body, String fileName) { // handle the callback when the saving request is performed
int result = 0;
try {
callbackManager.processSave(body, fileName);
callbackManager.processSave(body, fileName); // file saving process
} catch (Exception ex) {
ex.printStackTrace();
result = 1;
@ -43,7 +43,7 @@ public class SaveCallback implements Callback {
}
@Override
public int getStatus() {
return Status.SAVE.getCode();
public int getStatus() { // get document status
return Status.SAVE.getCode(); // return status 2 - document is ready for saving
}
}

View File

@ -20,8 +20,8 @@ package com.onlyoffice.integration.documentserver.managers.callback;
import com.onlyoffice.integration.dto.Track;
public interface CallbackManager {
void processSave(Track body, String fileName);
void commandRequest(String method, String key);
void processForceSave(Track body, String fileName);
public interface CallbackManager { // specify the callback manager functions
void processSave(Track body, String fileName); // file saving process
void commandRequest(String method, String key); // create a command request
void processForceSave(Track body, String fileName); // file force saving process
}

View File

@ -69,36 +69,37 @@ public class DefaultCallbackManager implements CallbackManager {
@Autowired
private ServiceConverter serviceConverter;
// save file information from the URL to the file specified
private void downloadToFile(String url, Path path) throws Exception {
if (url == null || url.isEmpty()) throw new RuntimeException("Url argument is not specified");
if (path == null) throw new RuntimeException("Path argument is not specified");
if (url == null || url.isEmpty()) throw new RuntimeException("Url argument is not specified"); // URL isn't specified
if (path == null) throw new RuntimeException("Path argument is not specified"); // file isn't specified
URL uri = new URL(url);
java.net.HttpURLConnection connection = (java.net.HttpURLConnection) uri.openConnection();
InputStream stream = connection.getInputStream();
InputStream stream = connection.getInputStream(); // get input stream of the file information from the URL
if (stream == null) {
connection.disconnect();
throw new RuntimeException("Input stream is null");
}
storageMutator.createOrUpdateFile(path, stream);
storageMutator.createOrUpdateFile(path, stream); // update a file or create a new one
}
@SneakyThrows
public void processSave(Track body, String fileName) {
public void processSave(Track body, String fileName) { // file saving process
String downloadUri = body.getUrl();
String changesUri = body.getChangesurl();
String key = body.getKey();
String newFileName = fileName;
String curExt = fileUtility.getFileExtension(fileName);
String downloadExt = fileUtility.getFileExtension(downloadUri);
String curExt = fileUtility.getFileExtension(fileName); // get current file extension
String downloadExt = fileUtility.getFileExtension(downloadUri); // get an extension of the downloaded file
//TODO: Refactoring
if (!curExt.equals(downloadExt)) {
if (!curExt.equals(downloadExt)) { // convert downloaded file to the file with the current extension if these extensions aren't equal
try {
String newFileUri = serviceConverter.getConvertedUri(downloadUri, downloadExt, curExt, serviceConverter.generateRevisionId(downloadUri), null, false, null); // convert file and get url to a new file
String newFileUri = serviceConverter.getConvertedUri(downloadUri, downloadExt, curExt, serviceConverter.generateRevisionId(downloadUri), null, false, null); // convert a file and get URL to a new file
if (newFileUri.isEmpty()) {
newFileName = documentManager
.getCorrectName(fileUtility.getFileNameWithoutExtension(fileName) + downloadExt); // get the correct file name if it already exists
@ -110,28 +111,28 @@ public class DefaultCallbackManager implements CallbackManager {
}
}
String storagePath = storagePathBuilder.getFileLocation(newFileName);
Path lastVersion = Paths.get(storagePathBuilder.getFileLocation(fileName));
String storagePath = storagePathBuilder.getFileLocation(newFileName); // get the path to a new file
Path lastVersion = Paths.get(storagePathBuilder.getFileLocation(fileName)); // get the path to the last file version
if (lastVersion.toFile().exists()) {
Path histDir = Paths.get(storagePathBuilder.getHistoryDir(storagePath));
storageMutator.createDirectory(histDir);
if (lastVersion.toFile().exists()) { // if the last file version exists
Path histDir = Paths.get(storagePathBuilder.getHistoryDir(storagePath)); // get the history directory
storageMutator.createDirectory(histDir); // and create it
String versionDir = documentManager.versionDir(histDir.toAbsolutePath().toString(),
String versionDir = documentManager.versionDir(histDir.toAbsolutePath().toString(), // get the file version directory
storagePathBuilder.getFileVersion(histDir.toAbsolutePath().toString(), false), true);
Path ver = Paths.get(versionDir);
Path toSave = Paths.get(storagePath);
storageMutator.createDirectory(ver);
storageMutator.moveFile(lastVersion, Paths.get(versionDir + File.separator + "prev" + curExt));
storageMutator.createDirectory(ver); // create the file version directory
storageMutator.moveFile(lastVersion, Paths.get(versionDir + File.separator + "prev" + curExt)); // move the last file version to the file version directory with the "prev" postfix
downloadToFile(downloadUri, toSave);
downloadToFile(changesUri, Path.of(versionDir + File.separator + "diff.zip"));
downloadToFile(downloadUri, toSave); // save file to the storage path
downloadToFile(changesUri, Path.of(versionDir + File.separator + "diff.zip")); // save file changes to the diff.zip archive
JSONObject jsonChanges = new JSONObject();
jsonChanges.put("changes", body.getHistory().getChanges());
jsonChanges.put("serverVersion", body.getHistory().getServerVersion());
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().getServerVersion()); // put the server version to the json object
String history = objectMapper.writeValueAsString(jsonChanges);
if (history == null && body.getHistory() != null) {
@ -139,17 +140,17 @@ public class DefaultCallbackManager implements CallbackManager {
}
if (history != null && !history.isEmpty()) {
storageMutator.writeToFile(versionDir + File.separator + "changes.json", history);
storageMutator.writeToFile(versionDir + File.separator + "changes.json", history); // write the history changes to the changes.json file
}
storageMutator.writeToFile(versionDir + File.separator + "key.txt", key);
storageMutator.deleteFile(storagePathBuilder.getForcesavePath(newFileName, false));
storageMutator.writeToFile(versionDir + File.separator + "key.txt", key); // write the key value to the key.txt file
storageMutator.deleteFile(storagePathBuilder.getForcesavePath(newFileName, false)); // get the path to the forcesaved file version and remove it
}
}
//TODO: Replace (String method) with (Enum method)
@SneakyThrows
public void commandRequest(String method, String key) {
public void commandRequest(String method, String key) { // create a command request
String DocumentCommandUrl = docserviceUrlSite + docserviceUrlCommand;
URL url = new URL(DocumentCommandUrl);
@ -160,14 +161,14 @@ public class DefaultCallbackManager implements CallbackManager {
params.put("key", key);
String headerToken;
if (jwtManager.tokenEnabled())
if (jwtManager.tokenEnabled()) // check if a secret key to generate token exists or not
{
Map<String, Object> payloadMap = new HashMap<>();
payloadMap.put("payload", params);
headerToken = jwtManager.createToken(payloadMap);
connection.setRequestProperty(documentJwtHeader.equals("") ? "Authorization" : documentJwtHeader, "Bearer " + headerToken);
headerToken = jwtManager.createToken(payloadMap); // encode a payload object into a header token
connection.setRequestProperty(documentJwtHeader.equals("") ? "Authorization" : documentJwtHeader, "Bearer " + headerToken); // add a header Authorization with a header token and Authorization prefix in it
String token = jwtManager.createToken(params);
String token = jwtManager.createToken(params); // encode a payload object into a body token
params.put("token", token);
}
@ -175,23 +176,23 @@ public class DefaultCallbackManager implements CallbackManager {
byte[] bodyByte = bodyString.getBytes(StandardCharsets.UTF_8);
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
connection.setDoOutput(true);
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);
os.write(bodyByte); // write bytes to the output stream
}
InputStream stream = connection.getInputStream();
InputStream stream = connection.getInputStream(); // get input stream
if (stream == null) throw new RuntimeException("Could not get an answer");
String jsonString = serviceConverter.convertStreamToString(stream);
String jsonString = serviceConverter.convertStreamToString(stream); // convert stream to json string
connection.disconnect();
JSONObject response = serviceConverter.convertStringToJSON(jsonString);
JSONObject response = serviceConverter.convertStringToJSON(jsonString); // convert json string to json object
//TODO: Add errors ENUM
String responseCode = response.get("error").toString();
switch(responseCode) {
@ -206,12 +207,12 @@ public class DefaultCallbackManager implements CallbackManager {
}
@SneakyThrows
public void processForceSave(Track body, String fileName) {
public void processForceSave(Track body, String fileName) { // file force saving process
String downloadUri = body.getUrl();
String curExt = fileUtility.getFileExtension(fileName);
String downloadExt = fileUtility.getFileExtension(downloadUri);
String curExt = fileUtility.getFileExtension(fileName); // get current file extension
String downloadExt = fileUtility.getFileExtension(downloadUri); // get an extension of the downloaded file
Boolean newFileName = false;
// convert downloaded file to the file with the current extension if these extensions aren't equal
@ -219,7 +220,7 @@ public class DefaultCallbackManager implements CallbackManager {
if (!curExt.equals(downloadExt)) {
try {
String newFileUri = serviceConverter.getConvertedUri(downloadUri, downloadExt,
curExt, serviceConverter.generateRevisionId(downloadUri), null, false, null); // convert file and get url to a new file
curExt, serviceConverter.generateRevisionId(downloadUri), null, false, null); // convert file and get URL to a new file
if (newFileUri.isEmpty()) {
newFileName = true;
} else {
@ -237,18 +238,18 @@ public class DefaultCallbackManager implements CallbackManager {
boolean isSubmitForm = body.getForcesavetype().toString().equals("3");
//TODO: Extract function
if (isSubmitForm) {
if (isSubmitForm) { // if the form is submitted
if (newFileName){
fileName = documentManager
.getCorrectName(fileUtility.getFileNameWithoutExtension(fileName) + "-form" + downloadExt); // get the correct file name if it already exists
} else {
fileName = documentManager.getCorrectName(fileUtility.getFileNameWithoutExtension(fileName) + "-form" + curExt);
}
forcesavePath = storagePathBuilder.getFileLocation(fileName);
forcesavePath = storagePathBuilder.getFileLocation(fileName); // create forcesave path if it doesn't exist
List<Action> actions = body.getActions();
Action action = actions.get(0);
String user = action.getUserid();
storageMutator.createMeta(fileName, user, "Filling Form");
String user = action.getUserid(); // get the user ID
storageMutator.createMeta(fileName, user, "Filling Form"); // create meta data for the forcesaved file
} else {
if (newFileName){
fileName = documentManager.getCorrectName(fileUtility.getFileNameWithoutExtension(fileName) + downloadExt);

View File

@ -59,35 +59,38 @@ public class DefaultDocumentManager implements DocumentManager {
@Autowired
private ServiceConverter serviceConverter;
// get URL to the created file
public String getCreateUrl(String fileName, Boolean sample){
String fileExt = fileName.substring(fileName.length() - 4);
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(String fileName)
{
String baseName = fileUtility.getFileNameWithoutExtension(fileName);
String ext = fileUtility.getFileExtension(fileName);
String name = baseName + ext;
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));
for (int i = 1; Files.exists(path); i++)
for (int i = 1; Files.exists(path); i++) // run through all the files with such a name in the storage directory
{
name = baseName + " (" + i + ")" + ext;
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(String fileName, Boolean forDocumentServer)
{
try
{
String serverPath = storagePathBuilder.getServerUrl(forDocumentServer);
String hostAddress = storagePathBuilder.getStorageLocation();
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);
@ -101,6 +104,7 @@ public class DefaultDocumentManager implements DocumentManager {
}
}
// get the callback URL
public String getCallback(String fileName)
{
String serverPath = storagePathBuilder.getServerUrl(true);
@ -118,6 +122,7 @@ public class DefaultDocumentManager implements DocumentManager {
}
}
// get URL to download a file
public String getDownloadUrl(String fileName) {
String serverPath = storagePathBuilder.getServerUrl(true);
String storageAddress = storagePathBuilder.getStorageLocation();
@ -136,11 +141,13 @@ public class DefaultDocumentManager implements DocumentManager {
}
}
// get file information
public ArrayList<Map<String, Object>> getFilesInfo(){
ArrayList<Map<String, Object>> files = new ArrayList<>();
// run through all the stored files
for(File file : storageMutator.getStoredFiles()){
Map<String, Object> map = new LinkedHashMap<>();
Map<String, Object> map = new LinkedHashMap<>(); // write all the parameters to the map
map.put("version", storagePathBuilder.getFileVersion(file.getName(), false));
map.put("id", serviceConverter
.generateRevisionId(storagePathBuilder.getStorageLocation() +
@ -159,6 +166,7 @@ public class DefaultDocumentManager implements DocumentManager {
return files;
}
// get file information by its ID
public ArrayList<Map<String, Object>> getFilesInfo(String fileId){
ArrayList<Map<String, Object>> file = new ArrayList<>();
@ -172,6 +180,7 @@ public class DefaultDocumentManager implements DocumentManager {
return file;
}
// get the path to the file version by the history path and file version
public String versionDir(String path, Integer version, boolean historyPath) {
if (!historyPath){
return storagePathBuilder.getHistoryDir(storagePathBuilder.getFileLocation(path)) + version;
@ -179,19 +188,20 @@ public class DefaultDocumentManager implements DocumentManager {
return path + File.separator + version;
}
// create demo document
public String createDemo(String fileExt,Boolean sample,String uid,String uname) {
String demoName = (sample ? "sample." : "new.") + fileExt;
String demoPath = "assets" + File.separator + (sample ? "sample" : "new") + File.separator + demoName;
String fileName = getCorrectName(demoName);
String demoName = (sample ? "sample." : "new.") + fileExt; // create sample or new template file with the necessary extension
String demoPath = "assets" + File.separator + (sample ? "sample" : "new") + File.separator + demoName; // get the path to the sample document
String fileName = getCorrectName(demoName); // get a file name with an index if the file with such a name already exists
InputStream stream = Thread.currentThread()
.getContextClassLoader()
.getResourceAsStream(demoPath);
.getResourceAsStream(demoPath); // get the input file stream
if (stream == null) return null;
storageMutator.createFile(Path.of(storagePathBuilder.getFileLocation(fileName)), stream);
storageMutator.createMeta(fileName, uid, uname);
storageMutator.createFile(Path.of(storagePathBuilder.getFileLocation(fileName)), stream); // create a file in the specified directory
storageMutator.createMeta(fileName, uid, uname); // create meta information of the demo file
return fileName;
}

View File

@ -21,14 +21,15 @@ package com.onlyoffice.integration.documentserver.managers.document;
import java.util.ArrayList;
import java.util.Map;
// specify the document manager functions
public interface DocumentManager {
String getCorrectName(String fileName);
String getFileUri(String fileName, Boolean forDocumentServer);
String getCallback(String fileName);
String getDownloadUrl(String fileName);
ArrayList<Map<String, Object>> getFilesInfo();
ArrayList<Map<String, Object>> getFilesInfo(String fileId);
String versionDir(String path, Integer version, boolean historyPath);
String createDemo(String fileExt,Boolean sample,String uid,String uname) throws Exception;
String getCreateUrl(String fileName, Boolean sample);
String getCorrectName(String fileName); // get a file name with an index if the file with such a name already exists
String getFileUri(String fileName, Boolean forDocumentServer); // get file URL
String getCallback(String fileName); // get the callback URL
String getDownloadUrl(String fileName); // get URL to download a file
ArrayList<Map<String, Object>> getFilesInfo(); // get file information
ArrayList<Map<String, Object>> getFilesInfo(String fileId); // get file information by its ID
String versionDir(String path, Integer version, boolean historyPath); // get the path to the file version by the history path and file version
String createDemo(String fileExt,Boolean sample,String uid,String uname) throws Exception; // create demo document
String getCreateUrl(String fileName, Boolean sample); // get URL to the created file
}

View File

@ -58,27 +58,28 @@ public class DefaultHistoryManager implements HistoryManager {
//TODO: Refactoring
@SneakyThrows
public String[] getHistory(Document document) {
String histDir = storagePathBuilder.getHistoryDir(storagePathBuilder.getFileLocation(document.getTitle()));
Integer curVer = storagePathBuilder.getFileVersion(histDir, false);
public String[] getHistory(Document document) { // get document history
String histDir = storagePathBuilder.getHistoryDir(storagePathBuilder.getFileLocation(document.getTitle())); // get history directory
Integer curVer = storagePathBuilder.getFileVersion(histDir, false); // get current file version
if (curVer > 0) {
if (curVer > 0) { // check if the current file version is greater than 0
List<Object> hist = new ArrayList<>();
Map<String, Object> histData = new HashMap<>();
for (Integer i = 1; i <= curVer; i++) {
for (Integer i = 1; i <= curVer; i++) { // run through all the file versions
Map<String, Object> obj = new HashMap<String, Object>();
Map<String, Object> dataObj = new HashMap<String, Object>();
String verDir = documentManager.versionDir(histDir, i, true);
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"));
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) {
String createdInfo = readFileToEnd(new File(histDir + File.separator + "createdInfo.json"));
JSONObject json = (JSONObject) parser.parse(createdInfo);
if (i == 1) { // check if the version number is equal to 1
String createdInfo = readFileToEnd(new File(histDir + File.separator + "createdInfo.json")); // get file with meta data
JSONObject json = (JSONObject) parser.parse(createdInfo); // and turn it into json object
// write meta information to the object (user information and creation date)
obj.put("created", json.get("created"));
Map<String, Object> user = new HashMap<String, Object>();
user.put("id", json.get("id"));
@ -91,20 +92,23 @@ public class DefaultHistoryManager implements HistoryManager {
documentManager.getFileUri(documentManager.versionDir(histDir, i, true) + File.separator + "prev" + fileUtility.getFileExtension(document.getTitle()), true));
dataObj.put("version", i);
if (i > 1) {
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"));
Map<String, Object> prev = (Map<String, Object>) histData.get(Integer.toString(i - 2));
Map<String, Object> prev = (Map<String, Object>) histData.get(Integer.toString(i - 2)); // get the history data from the previous file version
Map<String, Object> prevInfo = new HashMap<String, Object>();
prevInfo.put("key", prev.get("key"));
prevInfo.put("key", prev.get("key")); // write key and URL information about previous file version
prevInfo.put("url", prev.get("url"));
dataObj.put("previous", prevInfo);
dataObj.put("previous", prevInfo); // write information about previous file version to the data object
// write the path to the diff.zip archive with differences in this file version
dataObj.put("changesUrl", documentManager.getFileUri(documentManager.versionDir(histDir, i - 1, true) + File.separator + "diff.zip", true));
}
@ -114,6 +118,7 @@ public class DefaultHistoryManager implements HistoryManager {
histData.put(Integer.toString(i - 1), dataObj);
}
// write history information about the current file version to the history object
Map<String, Object> histObj = new HashMap<String, Object>();
histObj.put("currentVersion", curVer);
histObj.put("history", hist);
@ -127,11 +132,12 @@ public class DefaultHistoryManager implements HistoryManager {
return new String[]{"", ""};
}
// read a file
private String readFileToEnd(File file) {
String output = "";
try {
try (FileInputStream is = new FileInputStream(file)) {
Scanner scanner = new Scanner(is);
Scanner scanner = new Scanner(is); // read data from the source
scanner.useDelimiter("\\A");
while (scanner.hasNext()) {
output += scanner.next();

View File

@ -20,6 +20,7 @@ package com.onlyoffice.integration.documentserver.managers.history;
import com.onlyoffice.integration.documentserver.models.filemodel.Document;
// specify the history manager functions
public interface HistoryManager {
String[] getHistory(Document document);
String[] getHistory(Document document); // get document history
}

View File

@ -42,56 +42,62 @@ public class DefaultJwtManager implements JwtManager {
@Autowired
private JSONParser parser;
// create document token
public String createToken(Map<String, Object> payloadClaims) {
try {
// build a HMAC signer using a SHA-256 hash
Signer signer = HMACSigner.newSHA256Signer(tokenSecret);
JWT jwt = new JWT();
for (String key : payloadClaims.keySet()) {
jwt.addClaim(key, payloadClaims.get(key));
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);
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();
}
// read document token
public JWT readToken(String token) {
try {
// build a HMAC verifier using the token secret
Verifier verifier = HMACVerifier.newVerifier(tokenSecret);
return JWT.getDecoder().decode(token, verifier);
return JWT.getDecoder().decode(token, verifier); // verify and decode the encoded string JWT to a rich object
} catch (Exception exception) {
return null;
}
}
// parse the body
public JSONObject parseBody(String payload, String header) {
JSONObject body;
try {
Object obj = parser.parse(payload);
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()) {
String token = (String) body.get("token");
if (token == null) {
if (header != null && !header.isBlank()) {
token = header.startsWith("Bearer ") ? header.substring(7) : header;
if (tokenEnabled()) { // 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
token = header.startsWith("Bearer ") ? header.substring(7) : header; // get token from the header (it is placed after the Bearer prefix if it exists)
}
}
if (token == null || token.isBlank()) {
throw new RuntimeException("{\"error\":1,\"message\":\"JWT expected\"}");
}
JWT jwt = readToken(token);
JWT jwt = readToken(token); // read token
if (jwt == null) {
throw new RuntimeException("{\"error\":1,\"message\":\"JWT validation failed\"}");
}
if (jwt.getObject("payload") != null) {
if (jwt.getObject("payload") != null) { // get payload from the token and check if it is not empty
try {
@SuppressWarnings("unchecked") LinkedHashMap<String, Object> jwtPayload =
(LinkedHashMap<String, Object>)jwt.getObject("payload");

View File

@ -23,9 +23,10 @@ import org.primeframework.jwt.domain.JWT;
import java.util.Map;
// specify the jwt manager functions
public interface JwtManager {
boolean tokenEnabled();
String createToken(Map<String, Object> payloadClaims);
JWT readToken(String token);
JSONObject parseBody(String payload, String header);
boolean tokenEnabled(); // check if the token is enabled
String createToken(Map<String, Object> payloadClaims); // create document token
JWT readToken(String token); // read document token
JSONObject parseBody(String payload, String header); // parse the body
}

View File

@ -41,25 +41,27 @@ public class SampleTemplateManager implements TemplateManager {
@Autowired
private FileUtility fileUtility;
// create a template document with the specified name
public List<Template> createTemplates(String fileName){
List<Template> templates = List.of(
new Template("", "Blank", documentManager.getCreateUrl(fileName, false)),
new Template(getTemplateImageUrl(fileName), "With sample content", documentManager.getCreateUrl(fileName, true))
new Template("", "Blank", documentManager.getCreateUrl(fileName, false)), // create a blank template
new Template(getTemplateImageUrl(fileName), "With sample content", documentManager.getCreateUrl(fileName, true)) // create a template with sample content using the template image
);
return templates;
}
// get the template image URL for the specified file
public String getTemplateImageUrl(String fileName){
DocumentType fileType = fileUtility.getDocumentType(fileName);
String path = storagePathBuilder.getServerUrl(true);
if(fileType.equals(DocumentType.word)){
DocumentType fileType = fileUtility.getDocumentType(fileName); // get the file type
String path = storagePathBuilder.getServerUrl(true); // get server URL
if(fileType.equals(DocumentType.word)){ // get URL to the template image for the word document type
return path + "/css/img/file_docx.svg";
} else if(fileType.equals(DocumentType.slide)){
} else if(fileType.equals(DocumentType.slide)){ // get URL to the template image for the slide document type
return path + "/css/img/file_pptx.svg";
} else if(fileType.equals(DocumentType.cell)){
} else if(fileType.equals(DocumentType.cell)){ // get URL to the template image for the cell document type
return path + "/css/img/file_xlsx.svg";
}
return path + "/css/img/file_docx.svg";
return path + "/css/img/file_docx.svg"; // get URL to the template image for the default document type (word)
}
}

View File

@ -21,7 +21,8 @@ package com.onlyoffice.integration.documentserver.managers.template;
import com.onlyoffice.integration.documentserver.models.filemodel.Template;
import java.util.List;
// specify the template manager functions
public interface TemplateManager {
List<Template> createTemplates(String fileName);
String getTemplateImageUrl(String fileName);
List<Template> createTemplates(String fileName); // create a template document with the specified name
String getTemplateImageUrl(String fileName); // get the template image URL for the specified file
}

View File

@ -28,20 +28,20 @@ import org.springframework.stereotype.Component;
@Scope("prototype")
@Getter
@Setter
public class Customization {
public class Customization { // the parameters which allow to customize the editor interface so that it looked like your other products (if there are any) and change the presence or absence of the additional buttons, links, change logos and editor owner details
@Autowired
private Logo logo;
private Logo logo; // the image file at the top left corner of the Editor header
@Autowired
private Goback goback;
private Boolean autosave = true;
private Boolean chat = true;
private Boolean comments = true;
private Boolean compactHeader = false;
private Boolean compactToolbar = false;
private Boolean compatibleFeatures = false;
private Boolean forcesave = false;
private Boolean help = true;
private Boolean hideRightMenu = false;
private Boolean hideRulers = false;
private Boolean submitForm = false;
private Goback goback; // the settings for the Open file location menu button and upper right corner button
private Boolean autosave = true; // if the Autosave menu option is enabled or disabled
private Boolean chat = true; // if the Chat menu button is displayed or hidden
private Boolean comments = true; // if the Comments menu button is displayed or hidden
private Boolean compactHeader = false; // if the additional action buttons are displayed in the upper part of the editor window header next to the logo (false) or in the toolbar (true)
private Boolean compactToolbar = false; // if the top toolbar type displayed is full (false) or compact (true)
private Boolean compatibleFeatures = false; // the use of functionality only compatible with the OOXML format
private Boolean forcesave = false; // add the request for the forced file saving to the callback handler when saving the document within the document editing service
private Boolean help = true; // if the Help menu button is displayed or hidden
private Boolean hideRightMenu = false; // if the right menu is displayed or hidden on first loading
private Boolean hideRulers = false; // if the editor rulers are displayed or hidden
private Boolean submitForm = false; // if the Submit form button is displayed or hidden
}

View File

@ -28,9 +28,9 @@ import org.springframework.stereotype.Component;
@Scope("prototype")
@Getter
@Setter
public class Embedded {
private String embedUrl;
private String saveUrl;
private String shareUrl;
private ToolbarDocked toolbarDocked;
public class Embedded { // the parameters which allow to change the settings which define the behavior of the buttons in the embedded mode
private String embedUrl; // the absolute URL to the document serving as a source file for the document embedded into the web page
private String saveUrl; // the absolute URL that will allow the document to be saved onto the user personal computer
private String shareUrl; // the absolute URL that will allow other users to share this document
private ToolbarDocked toolbarDocked; // the place for the embedded viewer toolbar, can be either top or bottom
}

View File

@ -32,14 +32,14 @@ import javax.annotation.PostConstruct;
@Scope("prototype")
@Getter
@Setter
public class Goback {
public class Goback { // the settings for the Open file location menu button and upper right corner button
@Autowired
private FileStoragePathBuilder storagePathBuilder;
@Value("${url.index}")
private String indexMapping;
private String url;
private String url; // the absolute URL to the website address which will be opened when clicking the Open file location menu button
@PostConstruct
private void init(){

View File

@ -31,10 +31,10 @@ import java.util.Locale;
@Scope("prototype")
@Getter
@Setter
public class Info {
private String owner = "Me";
private Boolean favorite = null;
private String uploaded = getDate();
public class Info { // the additional parameters for the document (document owner, folder where the document is stored, uploading date, sharing settings)
private String owner = "Me"; // the name of the document owner/creator
private Boolean favorite = null; // the highlighting state of the Favorite icon
private String uploaded = getDate(); // the document uploading date
private String getDate() {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEE MMM dd yyyy", Locale.US);

View File

@ -28,11 +28,11 @@ import org.springframework.stereotype.Component;
@Scope("prototype")
@Getter
@Setter
public class Logo {
public class Logo { // the image file at the top left corner of the Editor header
@Value("${logo.image}")
private String image;
private String image; // the path to the image file used to show in common work mode
@Value("${logo.imageEmbedded}")
private String imageEmbedded;
private String imageEmbedded; // the path to the image file used to show in the embedded mode
@Value("${logo.url}")
private String url;
private String url; // the absolute URL which will be used when someone clicks the logo image
}

View File

@ -33,9 +33,9 @@ import java.util.List;
@AllArgsConstructor
public class CommentGroup {
@JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = SerializerFilter.class)
private List<String> view;
private List<String> view; // define a list of groups whose comments the user can view
@JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = SerializerFilter.class)
private List<String> edit;
private List<String> edit; // define a list of groups whose comments the user can edit
@JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = SerializerFilter.class)
private List<String> remove;
private List<String> remove; // define a list of groups whose comments the user can remove
}

View File

@ -29,14 +29,14 @@ import org.springframework.stereotype.Component;
@Scope("prototype")
@Getter
@Setter
public class Document {
public class Document { // the parameters pertaining to the document (title, url, file type, etc.)
@Autowired
private Info info;
private Info info; // additional parameters for the document (document owner, folder where the document is stored, uploading date, sharing settings)
@Autowired
private Permission permissions;
private String fileType;
private String key;
private String urlUser;
private String title;
private String url;
private Permission permissions; // the permission for the document to be edited and downloaded or not
private String fileType; // the file type for the source viewed or edited document
private String key; // the unique document identifier used by the service to recognize the document
private String urlUser; // the absolute URL that will allow the document to be saved onto the user personal computer
private String title; // the desired file name for the viewed or edited document which will also be used as file name when the document is downloaded
private String url; // the absolute URL where the source viewed or edited document is stored
}

View File

@ -35,17 +35,17 @@ import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class EditorConfig {
private HashMap<String, Object> actionLink = null;
private String callbackUrl;
private String createUrl;
public class EditorConfig { // the parameters pertaining to the editor interface: opening mode (viewer or editor), interface language, additional buttons, etc.
private HashMap<String, Object> actionLink = null; // the data which contains the information about the action in the document that will be scrolled to
private String callbackUrl; // the absolute URL to the document storage service
private String createUrl; // the absolute URL of the document where it will be created and available after creation
@Autowired
private Customization customization;
private Customization customization; // the parameters which allow to customize the editor interface so that it looked like your other products (if there are any) and change the presence or absence of the additional buttons, links, change logos and editor owner details
@Autowired
private Embedded embedded;
private Language lang;
private Mode mode;
private Embedded embedded; // the parameters which allow to change the settings which define the behavior of the buttons in the embedded mode
private Language lang; // the editor interface language
private Mode mode; // the editor opening mode
@Autowired
private User user;
private List<Template> templates;
private User user; // the user currently viewing or editing the document
private List<Template> templates; // the presence or absence of the templates in the <b>Create New...</b> menu option
}

View File

@ -30,12 +30,12 @@ import org.springframework.stereotype.Component;
@Scope("prototype")
@Getter
@Setter
public class FileModel {
public class FileModel { // the file base parameters which include the platform type used, document display size (width and height) and type of the document opened
@Autowired
private Document document;
private DocumentType documentType;
private Document document; // the parameters pertaining to the document (title, url, file type, etc.)
private DocumentType documentType; // the document type to be opened
@Autowired
private EditorConfig editorConfig;
private String token;
private Type type;
private EditorConfig editorConfig; // the parameters pertaining to the editor interface: opening mode (viewer or editor), interface language, additional buttons, etc.
private String token; // the encrypted signature added to the Document Server config
private Type type; // the platform type used to access the document
}

View File

@ -33,18 +33,18 @@ import java.util.List;
@Scope("prototype")
@Getter
@Setter
public class Permission extends AbstractModel {
private Boolean comment = true;
private Boolean copy = true;
private Boolean download = true;
private Boolean edit = true;
private Boolean print = true;
private Boolean fillForms = true;
private Boolean modifyFilter = true;
private Boolean modifyContentControl = true;
private Boolean review = true;
public class Permission extends AbstractModel { // the permission for the document to be edited and downloaded or not
private Boolean comment = true; // if the document can be commented or not
private Boolean copy = true; // if the content can be copied to the clipboard or not
private Boolean download = true; // if the document can be downloaded or only viewed or edited online
private Boolean edit = true; // if the document can be edited or only viewed
private Boolean print = true; // if the document can be printed or not
private Boolean fillForms = true; // if the forms can be filled
private Boolean modifyFilter = true; // if the filter can applied globally (true) affecting all the other users, or locally (false)
private Boolean modifyContentControl = true; // if the content control settings can be changed
private Boolean review = true; // if the document can be reviewed or not
@JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = SerializerFilter.class)
private List<String> reviewGroups;
private List<String> reviewGroups; // the groups whose changes the user can accept/reject
@Autowired
private CommentGroup commentGroups;
private CommentGroup commentGroups; // the groups whose comments the user can edit, remove and/or view
}

View File

@ -7,8 +7,8 @@ import lombok.Setter;
@Getter
@Setter
@AllArgsConstructor
public class Template {
private String image;
private String title;
private String url;
public class Template { // the document template parameters
private String image; // the absolute URL to the image for template
private String title; // the template title that will be displayed in the <b>Create New...</b> menu option
private String url; // the absolute URL to the document where it will be created and available after creation
}

View File

@ -33,9 +33,10 @@ public class User extends AbstractModel {
private String name;
private String group;
// the user configuration parameters
public void configure(int id, String name, String group){
this.id = "uid-"+id;
this.name = name;
this.group = group;
this.id = "uid-"+id; // the user id
this.name = name; // the user name
this.group = group; // the group the user belongs to
}
}

View File

@ -6,16 +6,17 @@ import java.io.File;
import java.io.InputStream;
import java.nio.file.Path;
// specify the file storage mutator functions
public interface FileStorageMutator {
void createDirectory(Path path);
boolean createFile(Path path, InputStream stream);
boolean deleteFile(String fileName);
boolean deleteFileHistory(String fileName);
String updateFile(String fileName, byte[] bytes);
boolean writeToFile(String pathName, String payload);
boolean moveFile(Path source, Path destination);
Resource loadFileAsResource(String fileName);
File[] getStoredFiles();
void createMeta(String fileName, String uid, String uname);
boolean createOrUpdateFile(Path path, InputStream stream);
void createDirectory(Path path); // create a new directory if it does not exist
boolean createFile(Path path, InputStream stream); // create a new file if it does not exist
boolean deleteFile(String fileName); // delete a file
boolean deleteFileHistory(String fileName); // delete file history
String updateFile(String fileName, byte[] bytes); // update a file
boolean writeToFile(String pathName, String payload); // write the payload to the file
boolean moveFile(Path source, Path destination); // move a file to the specified destination
Resource loadFileAsResource(String fileName); // load file as a resource
File[] getStoredFiles(); // get a collection of all the stored files
void createMeta(String fileName, String uid, String uname); // create the file meta information
boolean createOrUpdateFile(Path path, InputStream stream); // create or update a file
}

View File

@ -1,11 +1,12 @@
package com.onlyoffice.integration.documentserver.storage;
// specify the file storage path builder functions
public interface FileStoragePathBuilder {
void configure(String address);
String getStorageLocation();
String getFileLocation(String fileName);
String getServerUrl(Boolean forDocumentServer);
String getHistoryDir(String fileName);
int getFileVersion(String historyPath, Boolean ifIndexPage);
String getForcesavePath(String fileName, Boolean create);
void configure(String address); // create a new storage folder
String getStorageLocation(); // get the storage directory
String getFileLocation(String fileName); // get the directory of the specified file
String getServerUrl(Boolean forDocumentServer); // get the server URL
String getHistoryDir(String fileName); // get the history directory
int getFileVersion(String historyPath, Boolean ifIndexPage); // get the file version
String getForcesavePath(String fileName, Boolean create); // get the path where all the forcely saved file versions are saved or create it
}

View File

@ -82,9 +82,10 @@ public class LocalFileStorage implements FileStorageMutator, FileStoragePathBuil
createDirectory(Paths.get(getStorageLocation()));
}
// get the storage directory
public String getStorageLocation(){
String serverPath = System.getProperty("user.dir");
String directory = serverPath
String serverPath = System.getProperty("user.dir"); // get the path to the server
String directory = serverPath // create the storage directory
+ File.separator + storageFolder
+ File.separator + this.storageAddress
+ File.separator;
@ -92,6 +93,7 @@ public class LocalFileStorage implements FileStorageMutator, FileStoragePathBuil
return directory;
}
// get the directory of the specified file
public String getFileLocation(String fileName){
if (fileName.contains(File.separator)) {
return getStorageLocation() + fileName;
@ -99,6 +101,7 @@ public class LocalFileStorage implements FileStorageMutator, FileStoragePathBuil
return getStorageLocation() + fileUtility.getFileName(fileName);
}
// create a new directory if it does not exist
public void createDirectory(Path path){
if (Files.exists(path)) return;
try {
@ -108,21 +111,22 @@ public class LocalFileStorage implements FileStorageMutator, FileStoragePathBuil
}
}
// create a new file if it does not exist
public boolean createFile(Path path, InputStream stream){
if (Files.exists(path)){
return true;
}
try {
File file = Files.createFile(path).toFile();
File file = Files.createFile(path).toFile(); // create a new file in the specified path
try (FileOutputStream out = new FileOutputStream(file))
{
int read;
final byte[] bytes = new byte[1024];
while ((read = stream.read(bytes)) != -1)
{
out.write(bytes, 0, read);
out.write(bytes, 0, read); // write bytes to the output stream
}
out.flush();
out.flush(); // force write data to the output stream that can be cached in the current thread
}
} catch (IOException e) {
e.printStackTrace();
@ -130,38 +134,41 @@ public class LocalFileStorage implements FileStorageMutator, FileStoragePathBuil
return false;
}
// delete a file
public boolean deleteFile(String fileName){
fileName = URLDecoder.decode(fileName, StandardCharsets.UTF_8);
fileName = URLDecoder.decode(fileName, StandardCharsets.UTF_8); // decode a x-www-form-urlencoded string
if (fileName.isBlank()) return false;
String filenameWithoutExt = fileUtility.getFileNameWithoutExtension(fileName);
String filenameWithoutExt = fileUtility.getFileNameWithoutExtension(fileName); // get file name without extension
Path filePath = fileName.contains(File.separator) ? Paths.get(fileName) : Paths.get(getFileLocation(fileName));
Path filePathWithoutExt = fileName.contains(File.separator) ? Paths.get(filenameWithoutExt) : Paths.get(getStorageLocation() + filenameWithoutExt);
Path filePath = fileName.contains(File.separator) ? Paths.get(fileName) : Paths.get(getFileLocation(fileName)); // get the path to the file
Path filePathWithoutExt = fileName.contains(File.separator) ? Paths.get(filenameWithoutExt) : Paths.get(getStorageLocation() + filenameWithoutExt); // get the path to the file without extension
boolean fileDeleted = FileSystemUtils.deleteRecursively(filePath.toFile());
boolean fileWithoutExtDeleted = FileSystemUtils.deleteRecursively(filePathWithoutExt.toFile());
boolean fileDeleted = FileSystemUtils.deleteRecursively(filePath.toFile()); // delete the specified file; for directories, recursively delete any nested directories or files as well
boolean fileWithoutExtDeleted = FileSystemUtils.deleteRecursively(filePathWithoutExt.toFile()); // delete the specified file without extension; for directories, recursively delete any nested directories or files as well
return fileDeleted && fileWithoutExtDeleted;
}
// delete file history
public boolean deleteFileHistory(String fileName) {
fileName = URLDecoder.decode(fileName, StandardCharsets.UTF_8);
fileName = URLDecoder.decode(fileName, StandardCharsets.UTF_8); // decode a x-www-form-urlencoded string
if (fileName.isBlank()) return false;
Path fileHistoryPath = Paths.get(getStorageLocation() + getHistoryDir(fileName));
Path fileHistoryPathWithoutExt = Paths.get(getStorageLocation() + getHistoryDir(fileUtility.getFileNameWithoutExtension(fileName)));
Path fileHistoryPath = Paths.get(getStorageLocation() + getHistoryDir(fileName)); // get the path to the history file
Path fileHistoryPathWithoutExt = Paths.get(getStorageLocation() + getHistoryDir(fileUtility.getFileNameWithoutExtension(fileName))); // get the path to the history file without extension
boolean historyDeleted = FileSystemUtils.deleteRecursively(fileHistoryPath.toFile());
boolean historyWithoutExtDeleted = FileSystemUtils.deleteRecursively(fileHistoryPathWithoutExt.toFile());
boolean historyDeleted = FileSystemUtils.deleteRecursively(fileHistoryPath.toFile()); // delete the specified history file; for directories, recursively delete any nested directories or files as well
boolean historyWithoutExtDeleted = FileSystemUtils.deleteRecursively(fileHistoryPathWithoutExt.toFile()); // delete the specified history file without extension; for directories, recursively delete any nested directories or files as well
return historyDeleted || historyWithoutExtDeleted;
}
// update a file
public String updateFile(String fileName, byte[] bytes) {
Path path = fileUtility.generateFilepath(getStorageLocation(), fileName);
Path path = fileUtility.generateFilepath(getStorageLocation(), fileName); // generate the path to the specified file
try {
Files.write(path, bytes);
Files.write(path, bytes); // write new information in the bytes format to the file
return path.getFileName().toString();
} catch (IOException e) {
e.printStackTrace();
@ -169,6 +176,7 @@ public class LocalFileStorage implements FileStorageMutator, FileStoragePathBuil
return "";
}
// move a file to the specified destination
public boolean moveFile(Path source, Path destination){
try {
Files.move(source, destination,
@ -180,6 +188,7 @@ public class LocalFileStorage implements FileStorageMutator, FileStoragePathBuil
return false;
}
// write the payload to the file
public boolean writeToFile(String pathName, String payload){
try (FileWriter fw = new FileWriter(pathName)) {
fw.write(payload);
@ -190,18 +199,19 @@ public class LocalFileStorage implements FileStorageMutator, FileStoragePathBuil
return false;
}
// get the path where all the forcely saved file versions are saved or create it
public String getForcesavePath(String fileName, Boolean create) {
String directory = getStorageLocation();
Path path = Paths.get(directory);
Path path = Paths.get(directory); // get the storage directory
if (!Files.exists(path)) return "";
directory = getFileLocation(fileName) + historyPostfix + File.separator;
path = Paths.get(directory);
path = Paths.get(directory); // get the history file directory
if (!create && !Files.exists(path)) return "";
createDirectory(path);
createDirectory(path); // create a new directory where all the forcely saved file versions will be saved
directory = directory + fileName;
path = Paths.get(directory);
@ -212,14 +222,15 @@ public class LocalFileStorage implements FileStorageMutator, FileStoragePathBuil
return directory;
}
// load file as a resource
public Resource loadFileAsResource(String fileName){
String fileLocation = getForcesavePath(fileName, false);
if (fileLocation.isBlank()){
fileLocation = getFileLocation(fileName);
String fileLocation = getForcesavePath(fileName, false); // get the path where all the forcely saved file versions are saved
if (fileLocation.isBlank()){ // if file location is empty
fileLocation = getFileLocation(fileName); // get it by the file name
}
try {
Path filePath = Paths.get(fileLocation);
Resource resource = new UrlResource(filePath.toUri());
Path filePath = Paths.get(fileLocation); // get the path to the file location
Resource resource = new UrlResource(filePath.toUri()); // convert the file path to URL
if(resource.exists()) return resource;
} catch (MalformedURLException e) {
e.printStackTrace();
@ -227,6 +238,7 @@ public class LocalFileStorage implements FileStorageMutator, FileStoragePathBuil
return null;
}
// get a collection of all the stored files
public File[] getStoredFiles()
{
File file = new File(getStorageLocation());
@ -234,18 +246,19 @@ public class LocalFileStorage implements FileStorageMutator, FileStoragePathBuil
}
@SneakyThrows
public void createMeta(String fileName, String uid, String uname) {
String histDir = getHistoryDir(getFileLocation(fileName));
public void createMeta(String fileName, String uid, String uname) { // create the file meta information
String histDir = getHistoryDir(getFileLocation(fileName)); // get the history directory
Path path = Paths.get(histDir);
createDirectory(path);
Path path = Paths.get(histDir); // get the path to the history directory
createDirectory(path); // create the history directory
// create the json object with the file metadata
JSONObject json = new JSONObject();
json.put("created", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
json.put("id", uid);
json.put("name", uname);
json.put("created", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); // put the file creation date to the json object
json.put("id", uid); // put the user ID to the json object
json.put("name", uname); // put the user name to the json object
File meta = new File(histDir + File.separator + "createdInfo.json");
File meta = new File(histDir + File.separator + "createdInfo.json"); // create the createdInfo.json file with the file meta information
try (FileWriter writer = new FileWriter(meta)) {
json.writeJSONString(writer);
} catch (IOException ex){
@ -253,12 +266,13 @@ public class LocalFileStorage implements FileStorageMutator, FileStoragePathBuil
}
}
// create or update a file
public boolean createOrUpdateFile(Path path, InputStream stream) {
if (!Files.exists(path)){
return createFile(path, stream);
if (!Files.exists(path)){ // if the specified file does not exist
return createFile(path, stream); // create it in the specified directory
} else {
try {
Files.write(path, stream.readAllBytes());
Files.write(path, stream.readAllBytes()); // otherwise, write new information in the bytes format to the file
return true;
} catch (IOException e) {
e.printStackTrace();
@ -267,6 +281,7 @@ public class LocalFileStorage implements FileStorageMutator, FileStoragePathBuil
return false;
}
// get the server URL
public String getServerUrl(Boolean forDocumentServer) {
if (forDocumentServer && !docserviceUrlExample.equals("")) {
return docserviceUrlExample;
@ -275,27 +290,29 @@ public class LocalFileStorage implements FileStorageMutator, FileStoragePathBuil
}
}
// get the history directory
public String getHistoryDir(String path)
{
return path + historyPostfix;
}
// get the file version
public int getFileVersion(String historyPath, Boolean ifIndexPage)
{
Path path;
if (ifIndexPage) {
path = Paths.get(getStorageLocation() + getHistoryDir(historyPath));
if (ifIndexPage) { // if the start page is opened
path = Paths.get(getStorageLocation() + getHistoryDir(historyPath)); // get the storage directory and add the history directory to it
} else {
path = Paths.get(historyPath);
if (!Files.exists(path)) return 1;
path = Paths.get(historyPath); // otherwise, get the path to the history directory
if (!Files.exists(path)) return 1; // if the history directory does not exist, then the file version is 1
}
try (Stream<Path> stream = Files.walk(path, 1)) {
try (Stream<Path> stream = Files.walk(path, 1)) { // run through all the files in the history directory
return stream
.filter(file -> Files.isDirectory(file))
.map(Path::getFileName)
.map(Path::toString)
.collect(Collectors.toSet()).size();
.filter(file -> Files.isDirectory(file)) // take only directories from the history folder
.map(Path::getFileName) // get file names
.map(Path::toString) // and convert them into strings
.collect(Collectors.toSet()).size(); // convert stream into set and get its size which specifies the file version
} catch (IOException e) {
e.printStackTrace();
return 0;

View File

@ -7,7 +7,7 @@ import java.util.stream.Collectors;
@Component
public class Misc {
public String convertUserDescriptions(String username, List<String> description){
public String convertUserDescriptions(String username, List<String> description){ // cenvert user descriptions to the specified format
String result = "<div class=\"user-descr\"><b>"+username+"</b><br/><ul>"+description.
stream().map(text -> "<li>"+text+"</li>")
.collect(Collectors.joining()) + "</ul></div>";

View File

@ -46,48 +46,62 @@ public class DefaultFileUtility implements FileUtility {
@Value("${files.docservice.convert-docs}")
private String docserviceConvertDocs;
@Value("${files.docservice.fillforms-docs}")
private String docserviceFillDocs;
// document extensions
private List<String> ExtsDocument = Arrays.asList(
".doc", ".docx", ".docm",
".dot", ".dotx", ".dotm",
".odt", ".fodt", ".ott", ".rtf", ".txt",
".html", ".htm", ".mht", ".xml",
".pdf", ".djvu", ".fb2", ".epub", ".xps");
".pdf", ".djvu", ".fb2", ".epub", ".xps", ".oform");
// spreadsheet extensions
private List<String> ExtsSpreadsheet = Arrays.asList(
".xls", ".xlsx", ".xlsm",
".xlt", ".xltx", ".xltm",
".ods", ".fods", ".ots", ".csv");
// presentation extensions
private List<String> ExtsPresentation = Arrays.asList(
".pps", ".ppsx", ".ppsm",
".ppt", ".pptx", ".pptm",
".pot", ".potx", ".potm",
".odp", ".fodp", ".otp");
// get the document type
public DocumentType getDocumentType(String fileName)
{
String ext = getFileExtension(fileName).toLowerCase();
String ext = getFileExtension(fileName).toLowerCase(); // get file extension from its name
// word type for document extensions
if (ExtsDocument.contains(ext))
return DocumentType.word;
// cell type for spreadsheet extensions
if (ExtsSpreadsheet.contains(ext))
return DocumentType.cell;
// slide type for presentation extensions
if (ExtsPresentation.contains(ext))
return DocumentType.slide;
// default file type is word
return DocumentType.word;
}
// get file name from its URL
public String getFileName(String url)
{
if (url == null) return "";
// get file name from the last part of URL
String fileName = url.substring(url.lastIndexOf('/') + 1);
fileName = fileName.split("\\?")[0];
return fileName;
}
// get file name without extension
public String getFileNameWithoutExtension(String url)
{
String fileName = getFileName(url);
@ -96,6 +110,7 @@ public class DefaultFileUtility implements FileUtility {
return fileNameWithoutExt;
}
// get file extension from URL
public String getFileExtension(String url)
{
String fileName = getFileName(url);
@ -104,59 +119,76 @@ public class DefaultFileUtility implements FileUtility {
return fileExt.toLowerCase();
}
// get an editor internal extension
public String getInternalExtension(DocumentType type)
{
// .docx for word file type
if (type.equals(DocumentType.word))
return ".docx";
// .xlsx for cell file type
if (type.equals(DocumentType.cell))
return ".xlsx";
// .pptx for slide file type
if (type.equals(DocumentType.slide))
return ".pptx";
// the default file type is .docx
return ".docx";
}
public List<String> getFillExts()
{
return Arrays.asList(docserviceFillDocs.split("\\|"));
}
// get file extensions that can be viewed
public List<String> getViewedExts()
{
return Arrays.asList(docserviceViewedDocs.split("\\|"));
}
// get file extensions that can be edited
public List<String> getEditedExts()
{
return Arrays.asList(docserviceEditedDocs.split("\\|"));
}
// get file extensions that can be converted
public List<String> getConvertExts()
{
return Arrays.asList(docserviceConvertDocs.split("\\|"));
}
// get all the supported file extensions
public List<String> getFileExts() {
List<String> res = new ArrayList<>();
res.addAll(getViewedExts());
res.addAll(getEditedExts());
res.addAll(getConvertExts());
res.addAll(getFillExts());
return res;
}
// generate the file path from file directory and name
public Path generateFilepath(String directory, String fullFileName){
String fileName = getFileNameWithoutExtension(fullFileName);
String fileExtension = getFileExtension(fullFileName);
Path path = Paths.get(directory+fullFileName);
String fileName = getFileNameWithoutExtension(fullFileName); // get file name without extension
String fileExtension = getFileExtension(fullFileName); // get file extension
Path path = Paths.get(directory+fullFileName); // get the path to the files with the specified name
for(int i = 1; Files.exists(path); i++){
fileName = getFileNameWithoutExtension(fullFileName) + "("+i+")";
path = Paths.get(directory+fileName+fileExtension);
for(int i = 1; Files.exists(path); i++){ // run through all the files with the specified name
fileName = getFileNameWithoutExtension(fullFileName) + "("+i+")"; // get a name of each file without extension and add an index to it
path = Paths.get(directory+fileName+fileExtension); // create a new path for this file with the correct name and extension
}
path = Paths.get(directory+fileName+fileExtension);
return path;
}
// get maximum file size
public long getMaxFileSize(){
long size = Long.parseLong(filesizeMax);
return size > 0 ? size : 5 * 1024 * 1024;

View File

@ -23,16 +23,18 @@ import com.onlyoffice.integration.documentserver.models.enums.DocumentType;
import java.nio.file.Path;
import java.util.List;
// specify the file utility functions
public interface FileUtility {
DocumentType getDocumentType(String fileName);
String getFileName(String url);
String getFileNameWithoutExtension(String url);
String getFileExtension(String url);
String getInternalExtension(DocumentType type);
List<String> getFileExts();
List<String> getViewedExts();
List<String> getEditedExts();
List<String> getConvertExts();
Path generateFilepath(String directory, String fullFileName);
long getMaxFileSize();
DocumentType getDocumentType(String fileName); // get the document type
String getFileName(String url); // get file name from its URL
String getFileNameWithoutExtension(String url); // get file name without extension
String getFileExtension(String url); // get file extension from URL
String getInternalExtension(DocumentType type); // get an editor internal extension
List<String> getFileExts(); // get all the supported file extensions
List<String> getFillExts(); // get file extensions that can be filled
List<String> getViewedExts(); // get file extensions that can be viewed
List<String> getEditedExts(); // get file extensions that can be edited
List<String> getConvertExts(); // get file extensions that can be converted
Path generateFilepath(String directory, String fullFileName); // generate the file path from file directory and name
long getMaxFileSize(); // get maximum file size
}

View File

@ -62,20 +62,21 @@ public class DefaultServiceConverter implements ServiceConverter
@PostConstruct
public void init(){
int timeout = Integer.parseInt(docserviceTimeout);
int timeout = Integer.parseInt(docserviceTimeout); // parse the dcoument service timeout value
if (timeout > 0) convertTimeout = timeout;
}
@SneakyThrows
private String postToServer(Convert body, String headerToken){
String bodyString = objectMapper.writeValueAsString(body);
private String postToServer(Convert body, String headerToken){ // send the POST request to the server
String bodyString = objectMapper.writeValueAsString(body); // write the body request to the object mapper in the string format
URL url = null;
java.net.HttpURLConnection connection = null;
InputStream response = null;
String jsonString = null;
byte[] bodyByte = bodyString.getBytes(StandardCharsets.UTF_8);
byte[] bodyByte = bodyString.getBytes(StandardCharsets.UTF_8); // convert body string into bytes
try{
// set the request parameters
url = new URL(docServiceUrl+docServiceUrlConverter);
connection = (java.net.HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
@ -85,33 +86,38 @@ public class DefaultServiceConverter implements ServiceConverter
connection.setRequestProperty("Accept", "application/json");
connection.setConnectTimeout(convertTimeout);
// check if the token is enabled
if (jwtManager.tokenEnabled())
{
// set the JWT header to the request
connection.setRequestProperty(documentJwtHeader.isBlank() ?
"Authorization" : documentJwtHeader, "Bearer " + headerToken);
}
connection.connect();
try (OutputStream os = connection.getOutputStream()) {
os.write(bodyByte);
os.flush();
os.write(bodyByte); // write bytes to the output stream
os.flush(); // force write data to the output stream that can be cached in the current thread
}
response = connection.getInputStream();
jsonString = convertStreamToString(response);
response = connection.getInputStream(); // get the input stream
jsonString = convertStreamToString(response); // convert the response stream into a string
} finally {
connection.disconnect();
return jsonString;
}
}
// get the URL to the converted file
public String getConvertedUri(String documentUri, String fromExtension,
String toExtension, String documentRevisionId,
String filePass, Boolean isAsync, String lang)
{
// check if the fromExtension parameter is defined; if not, get it from the document url
fromExtension = fromExtension == null || fromExtension.isEmpty() ?
fileUtility.getFileExtension(documentUri) : fromExtension;
// check if the file name parameter is defined; if not, get random uuid for this file
String title = fileUtility.getFileName(documentUri);
title = title == null || title.isEmpty() ? UUID.randomUUID().toString() : title;
@ -119,6 +125,7 @@ public class DefaultServiceConverter implements ServiceConverter
documentRevisionId = generateRevisionId(documentRevisionId); // create document token
// write all the necessary parameters to the body object
Convert body = new Convert();
body.setLang(lang);
body.setUrl(documentUri);
@ -158,22 +165,24 @@ public class DefaultServiceConverter implements ServiceConverter
return getResponseUri(jsonString);
}
// generate document key
public String generateRevisionId(String expectedKey)
{
if (expectedKey.length() > 20)
expectedKey = Integer.toString(expectedKey.hashCode());
if (expectedKey.length() > 20) // if the expected key length is greater than 20
expectedKey = Integer.toString(expectedKey.hashCode()); // the expected key is hashed and a fixed length value is stored in the string format
String key = expectedKey.replace("[^0-9-.a-zA-Z_=]", "_");
return key.substring(0, Math.min(key.length(), 20));
return key.substring(0, Math.min(key.length(), 20)); // the resulting key length is 20 or less
}
//TODO: Replace with a registry (callbacks package for reference)
private void processConvertServiceResponceError(int errorCode)
private void processConvertServiceResponceError(int errorCode) // create an error message for an error code
{
String errorMessage = "";
String errorMessageTemplate = "Error occurred in the ConvertService: ";
// add the error message to the error message template depending on the error code
switch (errorCode)
{
case -8:
@ -200,10 +209,10 @@ public class DefaultServiceConverter implements ServiceConverter
case -1:
errorMessage = errorMessageTemplate + "Error convertation unknown";
break;
case 0:
case 0: // if the error code is equal to 0, the error message is empty
break;
default:
errorMessage = "ErrorCode = " + errorCode;
errorMessage = "ErrorCode = " + errorCode; // default value for the error message
break;
}
@ -211,44 +220,45 @@ public class DefaultServiceConverter implements ServiceConverter
}
@SneakyThrows
private String getResponseUri(String jsonString)
private String getResponseUri(String jsonString) // get the response URL
{
JSONObject jsonObj = convertStringToJSON(jsonString);
Object error = jsonObj.get("error");
if (error != null)
processConvertServiceResponceError(Math.toIntExact((long)error));
if (error != null) // if an error occurs
processConvertServiceResponceError(Math.toIntExact((long)error)); // then get an error message
// check if the conversion is completed and save the result to a variable
Boolean isEndConvert = (Boolean) jsonObj.get("endConvert");
Long resultPercent = 0l;
String responseUri = null;
if (isEndConvert)
if (isEndConvert) // if the conversion is completed
{
resultPercent = 100l;
responseUri = (String) jsonObj.get("fileUrl");
responseUri = (String) jsonObj.get("fileUrl"); // get the file URL
}
else
else // if the conversion isn't completed
{
resultPercent = (Long) jsonObj.get("percent");
resultPercent = resultPercent >= 100l ? 99l : resultPercent;
resultPercent = resultPercent >= 100l ? 99l : resultPercent; // get the percentage value of the conversion process
}
return resultPercent >= 100l ? responseUri : "";
}
@SneakyThrows
public String convertStreamToString(InputStream stream)
public String convertStreamToString(InputStream stream) // convert stream to string
{
InputStreamReader inputStreamReader = new InputStreamReader(stream);
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String line = bufferedReader.readLine();
InputStreamReader inputStreamReader = new InputStreamReader(stream); // create an object to get incoming stream
StringBuilder stringBuilder = new StringBuilder(); // create a string builder object
BufferedReader bufferedReader = new BufferedReader(inputStreamReader); // create an object to read incoming streams
String line = bufferedReader.readLine(); // get incoming streams by lines
while (line != null)
{
stringBuilder.append(line);
stringBuilder.append(line); // concatenate strings using the string builder
line = bufferedReader.readLine();
}
@ -258,10 +268,10 @@ public class DefaultServiceConverter implements ServiceConverter
}
@SneakyThrows
public JSONObject convertStringToJSON(String jsonString)
public JSONObject convertStringToJSON(String jsonString) // convert string to json
{
Object obj = parser.parse(jsonString);
JSONObject jsonObj = (JSONObject) obj;
Object obj = parser.parse(jsonString); // parse json string
JSONObject jsonObj = (JSONObject) obj; // and turn it into a json object
return jsonObj;
}

View File

@ -24,11 +24,13 @@ import org.json.simple.parser.ParseException;
import java.io.IOException;
import java.io.InputStream;
// specify the converter service functions
public interface ServiceConverter {
String getConvertedUri(String documentUri, String fromExtension,
String getConvertedUri(String documentUri, String fromExtension, // get the URL to the converted file
String toExtension, String documentRevisionId,
String filePass, Boolean isAsync, String lang);
String generateRevisionId(String expectedKey);
String convertStreamToString(InputStream stream);
JSONObject convertStringToJSON(String jsonString);
String generateRevisionId(String expectedKey); // generate document key
String convertStreamToString(InputStream stream); // convert stream to string
JSONObject convertStringToJSON(String jsonString); // convert string to json
}

View File

@ -37,17 +37,17 @@ public abstract class AbstractMapper<E extends AbstractEntity, M extends Abstrac
}
@Override
public M toModel(E entity) {
return Objects.isNull(entity)
public M toModel(E entity) { // convert the entity to the model
return Objects.isNull(entity) // check if an entity is not empty
? null
: mapper.map(entity, modelClass);
: mapper.map(entity, modelClass); // and add it to the model mapper
}
Converter<E, M> modelConverter() {
Converter<E, M> modelConverter() { // specify the model converter
return context -> {
E source = context.getSource();
M destination = context.getDestination();
handleSpecificFields(source, destination);
E source = context.getSource(); // get the source entity
M destination = context.getDestination(); // get the destination model
handleSpecificFields(source, destination); // map the entity to the model
return context.getDestination();
};
}

View File

@ -21,6 +21,7 @@ package com.onlyoffice.integration.mappers;
import com.onlyoffice.integration.entities.AbstractEntity;
import com.onlyoffice.integration.documentserver.models.AbstractModel;
// specify the model mapper functions
public interface Mapper<E extends AbstractEntity, M extends AbstractModel> {
M toModel(E entity);
M toModel(E entity); // convert the entity to the model
}

View File

@ -39,15 +39,15 @@ public class PermissionsMapper extends AbstractMapper<Permission, com.onlyoffice
}
@PostConstruct
public void configure() {
mapper.createTypeMap(Permission.class, com.onlyoffice.integration.documentserver.models.filemodel.Permission.class)
.setPostConverter(modelConverter());
public void configure() { // configure the permission mapper
mapper.createTypeMap(Permission.class, com.onlyoffice.integration.documentserver.models.filemodel.Permission.class) // create the type map
.setPostConverter(modelConverter()); // and apply the post converter to it
}
@Override
void handleSpecificFields(Permission source, com.onlyoffice.integration.documentserver.models.filemodel.Permission destination) {
destination.setReviewGroups(source.getReviewGroups().stream().map(g -> g.getName()).collect(Collectors.toList()));
destination.setCommentGroups(
void handleSpecificFields(Permission source, com.onlyoffice.integration.documentserver.models.filemodel.Permission destination) { // handle specific permission fields
destination.setReviewGroups(source.getReviewGroups().stream().map(g -> g.getName()).collect(Collectors.toList())); // set the reviewGroups parameter
destination.setCommentGroups( // set the commentGroups parameter
new CommentGroup(
source.getCommentsViewGroups().stream().map(g -> g.getName()).collect(Collectors.toList()),
source.getCommentsEditGroups().stream().map(g -> g.getName()).collect(Collectors.toList()),

View File

@ -37,13 +37,13 @@ public class UsersMapper extends AbstractMapper<User, com.onlyoffice.integration
}
@PostConstruct
public void configure() {
mapper.createTypeMap(User.class, com.onlyoffice.integration.documentserver.models.filemodel.User.class)
.setPostConverter(modelConverter());
public void configure() { // configure the users mapper
mapper.createTypeMap(User.class, com.onlyoffice.integration.documentserver.models.filemodel.User.class) // create the type map
.setPostConverter(modelConverter()); // and apply the post converter to it
}
@Override
public void handleSpecificFields(User source, com.onlyoffice.integration.documentserver.models.filemodel.User destination) {
destination.setGroup(source.getGroup() != null ? source.getGroup().getName() : null);
public void handleSpecificFields(User source, com.onlyoffice.integration.documentserver.models.filemodel.User destination) { // handle specific users fields
destination.setGroup(source.getGroup() != null ? source.getGroup().getName() : null); // set the Group parameter
}
}

View File

@ -32,21 +32,23 @@ public class GroupServices {
@Autowired
private GroupRepository groupRepository;
// create a new group with the specified name
public Group createGroup(String name){
if(name == null) return null;
Optional<Group> group = groupRepository.findGroupByName(name);
if(group.isPresent()) return group.get();
if(name == null) return null; // check if a name is specified
Optional<Group> group = groupRepository.findGroupByName(name); // check if group with such a name already exists
if(group.isPresent()) return group.get(); // if it exists, return it
Group newGroup = new Group();
newGroup.setName(name);
newGroup.setName(name); // otherwise, create a new group with the specified name
groupRepository.save(newGroup);
groupRepository.save(newGroup); // save a new group
return newGroup;
}
// create a list of groups from the reviewGroups permission parameter
public List<Group> createGroups(List<String> reviewGroups){
if(reviewGroups == null) return null;
return reviewGroups.stream()
if(reviewGroups == null) return null; // check if the reviewGroups permission exists
return reviewGroups.stream() // convert this parameter to a list of groups whose changes the user can accept/reject
.map(group -> createGroup(group))
.collect(Collectors.toList());
}

View File

@ -32,24 +32,26 @@ public class PermissionServices {
@Autowired
private PermissionRepository permissionRepository;
// create permissions with the specified parameters
public Permission createPermission(List<Group> reviewGroups,
List<Group> commentViewGroups,
List<Group> commentEditGroups,
List<Group> commentRemoveGroups){
Permission permission = new Permission();
permission.setReviewGroups(reviewGroups);
permission.setCommentsViewGroups(commentViewGroups);
permission.setCommentsEditGroups(commentEditGroups);
permission.setCommentsRemoveGroups(commentRemoveGroups);
permission.setReviewGroups(reviewGroups); // define the groups whose changes the user can accept/reject
permission.setCommentsViewGroups(commentViewGroups); // defines the groups whose comments the user can view
permission.setCommentsEditGroups(commentEditGroups); // defines the groups whose comments the user can edit
permission.setCommentsRemoveGroups(commentRemoveGroups); // defines the groups whose comments the user can remove
permissionRepository.save(permission);
permissionRepository.save(permission); // save new permissions
return permission;
}
// update permissions
public Permission updatePermission(Permission newPermission){
permissionRepository.save(newPermission);
permissionRepository.save(newPermission); // save new permissions
return newPermission;
}

View File

@ -40,14 +40,17 @@ public class UserServices {
@Autowired
private PermissionServices permissionService;
// get a list of all users
public List<User> findAll(){
return userRepository.findAll();
}
// get a user by their ID
public Optional<User> findUserById(Integer id){
return userRepository.findById(id);
}
// create a user with the specified parameters
public User createUser(String name, String email,
List<String> description, String group,
List<String> reviewGroups,
@ -55,22 +58,22 @@ public class UserServices {
List<String> editGroups,
List<String> removeGroups, Boolean favoriteDoc){
User newUser = new User();
newUser.setName(name);
newUser.setEmail(email);
newUser.setGroup(groupServices.createGroup(group));
newUser.setDescriptions(description);
newUser.setFavorite(favoriteDoc);
newUser.setName(name); // set the user name
newUser.setEmail(email); // set the user email
newUser.setGroup(groupServices.createGroup(group)); // set the user group
newUser.setDescriptions(description); // set the user description
newUser.setFavorite(favoriteDoc); // specify if the user has the favorite documents or not
List<Group> groupsReview = groupServices.createGroups(reviewGroups);
List<Group> commentGroupsView = groupServices.createGroups(viewGroups);
List<Group> commentGroupsEdit = groupServices.createGroups(editGroups);
List<Group> commentGroupsRemove = groupServices.createGroups(removeGroups);
List<Group> groupsReview = groupServices.createGroups(reviewGroups); // define the groups whose changes the user can accept/reject
List<Group> commentGroupsView = groupServices.createGroups(viewGroups); // defines the groups whose comments the user can view
List<Group> commentGroupsEdit = groupServices.createGroups(editGroups); // defines the groups whose comments the user can edit
List<Group> commentGroupsRemove = groupServices.createGroups(removeGroups); // defines the groups whose comments the user can remove
Permission permission = permissionService
.createPermission(groupsReview, commentGroupsView, commentGroupsEdit, commentGroupsRemove);
.createPermission(groupsReview, commentGroupsView, commentGroupsEdit, commentGroupsRemove); // specify permissions for the current user
newUser.setPermissions(permission);
userRepository.save(newUser);
userRepository.save(newUser); // save a new user
return newUser;
}

View File

@ -20,6 +20,7 @@ package com.onlyoffice.integration.services.configurers.implementations;
import com.onlyoffice.integration.documentserver.models.enums.Action;
import com.onlyoffice.integration.documentserver.models.configurations.Customization;
import com.onlyoffice.integration.entities.User;
import com.onlyoffice.integration.services.configurers.CustomizationConfigurer;
import com.onlyoffice.integration.services.configurers.wrappers.DefaultCustomizationWrapper;
import org.springframework.context.annotation.Primary;
@ -29,9 +30,9 @@ import org.springframework.stereotype.Service;
@Primary
public class DefaultCustomizationConfigurer implements CustomizationConfigurer<DefaultCustomizationWrapper> {
@Override
public void configure(Customization customization, DefaultCustomizationWrapper wrapper){
Action action = wrapper.getAction();
Boolean canEdit = wrapper.getCanEdit();
customization.setSubmitForm(canEdit && (action.equals(Action.edit) || action.equals(Action.fillForms)));
public void configure(Customization customization, DefaultCustomizationWrapper wrapper) { // define the customization configurer
Action action = wrapper.getAction(); // get the action parameter from the customization wrapper
User user = wrapper.getUser();
customization.setSubmitForm(action.equals(Action.fillForms) && user.getId() == 1 && false); // set the submitForm parameter to the customization config
}
}

View File

@ -48,22 +48,22 @@ public class DefaultDocumentConfigurer implements DocumentConfigurer<DefaultDocu
@Autowired
private ServiceConverter serviceConverter;
public void configure(Document document, DefaultDocumentWrapper wrapper){
String fileName = wrapper.getFileName();
Permission permission = wrapper.getPermission();
public void configure(Document document, DefaultDocumentWrapper wrapper){ // define the document configurer
String fileName = wrapper.getFileName(); // get the fileName parameter from the document wrapper
Permission permission = wrapper.getPermission(); // get the permission parameter from the document wrapper
document.setTitle(fileName);
document.setUrl(documentManager.getDownloadUrl(fileName));
document.setUrlUser(documentManager.getFileUri(fileName, false));
document.setFileType(fileUtility.getInternalExtension(fileUtility.getDocumentType(fileName)).replace(".",""));
document.getInfo().setFavorite(wrapper.getFavorite());
document.setTitle(fileName); // set the title to the document config
document.setUrl(documentManager.getDownloadUrl(fileName)); // set the URL to download a file to the document config
document.setUrlUser(documentManager.getFileUri(fileName, false)); // set the file URL to the document config
document.setFileType(fileUtility.getFileExtension(fileName).replace(".","")); // set the file type to the document config
document.getInfo().setFavorite(wrapper.getFavorite()); // set the favorite parameter to the document config
String key = serviceConverter.
String key = serviceConverter. // get the document key
generateRevisionId(storagePathBuilder.getStorageLocation()
+ "/" + fileName + "/"
+ new File(storagePathBuilder.getFileLocation(fileName)).lastModified());
document.setKey(key);
document.setPermissions(permission);
document.setKey(key); // set the key to the document config
document.setPermissions(permission); // set the permission parameters to the document config
}
}

View File

@ -68,23 +68,24 @@ public class DefaultEditorConfigConfigurer implements EditorConfigConfigurer<Def
private FileUtility fileUtility;
@SneakyThrows
public void configure(EditorConfig config, DefaultFileWrapper wrapper){
if (wrapper.getActionData() != null) {
config.setActionLink(objectMapper.readValue(wrapper.getActionData(), (JavaType) new TypeToken<HashMap<String, Object>>() { }.getType()));
public void configure(EditorConfig config, DefaultFileWrapper wrapper){ // define the editorConfig configurer
if (wrapper.getActionData() != null) { // check if the actionData is not empty in the editorConfig wrapper
config.setActionLink(objectMapper.readValue(wrapper.getActionData(), (JavaType) new TypeToken<HashMap<String, Object>>() { }.getType())); // set actionLink to the editorConfig
}
String fileName = wrapper.getFileName();
boolean userIsAnon = wrapper.getUser().getName().equals("Anonymous");
String fileName = wrapper.getFileName(); // set the fileName parameter from the editorConfig wrapper
String fileExt = fileUtility.getFileExtension(fileName);
boolean userIsAnon = wrapper.getUser().getName().equals("Anonymous"); // check if the user from the editorConfig wrapper is anonymous or not
config.setTemplates(userIsAnon ? null : templateManager.createTemplates(fileName));
config.setCallbackUrl(documentManager.getCallback(fileName));
config.setCreateUrl(userIsAnon ? null : documentManager.getCreateUrl(fileName, false));
config.setLang(wrapper.getLang());
Boolean canEdit = fileUtility.getEditedExts().contains(fileUtility.getFileExtension(fileName));
Action action = wrapper.getAction();
config.setTemplates(userIsAnon ? null : templateManager.createTemplates(fileName)); // set a template to the editorConfig if the user is not anonymous
config.setCallbackUrl(documentManager.getCallback(fileName)); // set the callback URL to the editorConfig
config.setCreateUrl(userIsAnon ? null : documentManager.getCreateUrl(fileName, false)); // set the document URL where it will be created to the editorConfig if the user is not anonymous
config.setLang(wrapper.getLang()); // set the language to the editorConfig
Boolean canEdit = wrapper.getCanEdit(); // check if the file of the specified type can be edited or not
Action action = wrapper.getAction(); // get the action parameter from the editorConfig wrapper
defaultCustomizationConfigurer.configure(config.getCustomization(), DefaultCustomizationWrapper.builder()
defaultCustomizationConfigurer.configure(config.getCustomization(), DefaultCustomizationWrapper.builder() // define the customization configurer
.action(action)
.canEdit(canEdit)
.user(wrapper.getUser())
.build());
config.setMode(canEdit && !action.equals(Action.view) ? Mode.edit : Mode.view);
config.setUser(mapper.toModel(wrapper.getUser()));

View File

@ -35,13 +35,13 @@ public class DefaultEmbeddedConfigurer implements EmbeddedConfigurer<DefaultEmbe
@Autowired
private DocumentManager documentManager;
public void configure(Embedded embedded, DefaultEmbeddedWrapper wrapper){
if(wrapper.getType().equals(Type.embedded)) {
String url = documentManager.getFileUri(wrapper.getFileName(), false);
embedded.setEmbedUrl(url);
embedded.setSaveUrl(url);
embedded.setShareUrl(url);
embedded.setToolbarDocked(ToolbarDocked.top);
public void configure(Embedded embedded, DefaultEmbeddedWrapper wrapper){ // define the embedded configurer
if(wrapper.getType().equals(Type.embedded)) { // check if the type from the embedded wrapper is embedded
String url = documentManager.getFileUri(wrapper.getFileName(), false); // get file URL of the specified file
embedded.setEmbedUrl(url); // set the embedURL parameter to the embedded config (the absolute URL to the document serving as a source file for the document embedded into the web page)
embedded.setSaveUrl(url); // set the saveURL parameter to the embedded config (the absolute URL that will allow the document to be saved onto the user personal computer)
embedded.setShareUrl(url); // set the shareURL parameter to the embedded config (the absolute URL that will allow other users to share this document)
embedded.setToolbarDocked(ToolbarDocked.top); // set the top toolbarDocked parameter to the embedded config (the place for the embedded viewer toolbar, can be either top or bottom)
};
}
}

View File

@ -56,32 +56,34 @@ public class DefaultFileConfigurer implements FileConfigurer<DefaultFileWrapper>
@Autowired
private DefaultEditorConfigConfigurer defaultEditorConfigConfigurer;
public void configure(FileModel fileModel, DefaultFileWrapper wrapper){
if (fileModel != null){
String fileName = wrapper.getFileName();
Action action = wrapper.getAction();
public void configure(FileModel fileModel, DefaultFileWrapper wrapper){ // define the file configurer
if (fileModel != null){ // check if the file model is specified
String fileName = wrapper.getFileName(); // get the fileName parameter from the file wrapper
Action action = wrapper.getAction(); // get the action parameter from the file wrapper
DocumentType documentType = fileUtility.getDocumentType(fileName);
fileModel.setDocumentType(documentType);
fileModel.setType(wrapper.getType());
DocumentType documentType = fileUtility.getDocumentType(fileName); // get the document type of the specified file
fileModel.setDocumentType(documentType); // set the document type to the file model
fileModel.setType(wrapper.getType()); // set the platform type to the file model
Permission userPermissions = mapper.toModel(wrapper.getUser().getPermissions());
userPermissions.setComment(
!action.equals(Action.view)
&& !action.equals(Action.fillForms)
&& !action.equals(Action.embedded)
&& !action.equals(Action.blockcontent)
);
Permission userPermissions = mapper.toModel(wrapper.getUser().getPermissions()); // convert the permission entity to the model
DefaultDocumentWrapper documentWrapper = DefaultDocumentWrapper
String fileExt = fileUtility.getFileExtension(wrapper.getFileName());
Boolean canEdit = fileUtility.getEditedExts().contains(fileExt);
if ((!canEdit && action.equals(Action.edit) || action.equals(Action.fillForms)) && fileUtility.getFillExts().contains(fileExt)) {
canEdit = true;
wrapper.setAction(Action.fillForms);
}
wrapper.setCanEdit(canEdit);
DefaultDocumentWrapper documentWrapper = DefaultDocumentWrapper // define the document wrapper
.builder()
.fileName(fileName)
.permission(userPermissions)
.permission(updatePermissions(userPermissions, action, canEdit))
.favorite(wrapper.getUser().getFavorite())
.build();
defaultDocumentConfigurer.configure(fileModel.getDocument(), documentWrapper);
defaultEditorConfigConfigurer.configure(fileModel.getEditorConfig(), wrapper);
defaultDocumentConfigurer.configure(fileModel.getDocument(), documentWrapper); // define the document configurer
defaultEditorConfigConfigurer.configure(fileModel.getEditorConfig(), wrapper); // define the editorConfig configurer
Map<String, Object> map = new HashMap<>();
map.put("type", fileModel.getType());
@ -89,14 +91,41 @@ public class DefaultFileConfigurer implements FileConfigurer<DefaultFileWrapper>
map.put("document", fileModel.getDocument());
map.put("editorConfig", fileModel.getEditorConfig());
fileModel.setToken(jwtManager.createToken(map));
fileModel.setToken(jwtManager.createToken(map)); // create a token and set it to the file model
}
}
@Override
public FileModel getFileModel(DefaultFileWrapper wrapper) {
public FileModel getFileModel(DefaultFileWrapper wrapper) { // get file model
FileModel fileModel = fileModelObjectFactory.getObject();
configure(fileModel, wrapper);
configure(fileModel, wrapper); // and configure it
return fileModel;
}
private Permission updatePermissions(Permission userPermissions, Action action, Boolean canEdit) {
userPermissions.setComment(
!action.equals(Action.view)
&& !action.equals(Action.fillForms)
&& !action.equals(Action.embedded)
&& !action.equals(Action.blockcontent)
);
userPermissions.setFillForms(
!action.equals(Action.view)
&& !action.equals(Action.comment)
&& !action.equals(Action.embedded)
&& !action.equals(Action.blockcontent)
);
userPermissions.setReview(canEdit &&
(action.equals(Action.review) || action.equals(Action.edit)));
userPermissions.setEdit(canEdit &&
(action.equals(Action.view)
|| action.equals(Action.edit)
|| action.equals(Action.filter)
|| action.equals(Action.blockcontent)));
return userPermissions;
}
}

View File

@ -19,6 +19,7 @@
package com.onlyoffice.integration.services.configurers.wrappers;
import com.onlyoffice.integration.documentserver.models.enums.Action;
import com.onlyoffice.integration.entities.User;
import lombok.Builder;
import lombok.Getter;
@ -26,5 +27,5 @@ import lombok.Getter;
@Builder
public class DefaultCustomizationWrapper {
private Action action;
private Boolean canEdit;
private User user;
}

View File

@ -24,9 +24,11 @@ import com.onlyoffice.integration.documentserver.models.enums.Language;
import com.onlyoffice.integration.documentserver.models.enums.Type;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter
@Builder
@Setter
public class DefaultFileWrapper {
private String fileName;
private Type type;
@ -34,4 +36,5 @@ public class DefaultFileWrapper {
private Language lang;
private Action action;
private String actionData;
private Boolean canEdit;
}

View File

@ -1,4 +1,4 @@
server.version=1.0.0
server.version=1.1.0
server.address=127.0.0.1
server.port=4000
@ -8,8 +8,9 @@ filesize-max=5242880
files.storage=
files.storage.folder=documents
files.docservice.fillforms-docs=.oform|.docx
files.docservice.viewed-docs=.pdf|.djvu|.xps|.oxps
files.docservice.edited-docs=.docx|.xlsx|.csv|.pptx|.txt
files.docservice.edited-docs=.docx|.xlsx|.csv|.pptx|.txt|.docxf
files.docservice.convert-docs=.docm|.dotx|.dotm|.dot|.doc|.odt|.fodt|.ott|.xlsm|.xltx|.xltm|.xlt|.xls|.ods|.fods|.ots|.pptm|.ppt|.ppsx|.ppsm|.pps|.potx|.potm|.pot|.odp|.fodp|.otp|.rtf|.mht|.html|.htm|.xml|.epub|.fb2
files.docservice.timeout=120000
files.docservice.history.postfix=-hist

View File

@ -0,0 +1,6 @@
<svg width="30" height="40" viewBox="0 0 30 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 3C0 1.34315 1.34315 0 3 0H22.9167L30 7.08333V37C30 38.6569 28.6569 40 27 40H3C1.34315 40 0 38.6569 0 37V3Z" fill="#27ABA3"/>
<path d="M22.9165 0L29.9998 7.08333H25.9165C24.2597 7.08333 22.9165 5.74019 22.9165 4.08333V0Z" fill="#008078"/>
<rect x="6.5" y="15.5" width="17" height="5" stroke="white"/>
<rect x="6.5" y="23.5" width="17" height="5" stroke="white"/>
</svg>

After

Width:  |  Height:  |  Size: 477 B

View File

@ -0,0 +1,6 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 5C5 3.89543 5.89543 3 7 3H16C17.1046 3 18 3.89543 18 5V8H17V6H6V18H17V16H18V19C18 20.1046 17.1046 21 16 21H7C5.89543 21 5 20.1046 5 19V5ZM13 4H10V5H13V4ZM12 19.5C12 19.7761 11.7761 20 11.5 20C11.2239 20 11 19.7761 11 19.5C11 19.2239 11.2239 19 11.5 19C11.7761 19 12 19.2239 12 19.5Z" fill="#444444"/>
<rect x="7" y="9" width="1" height="6" fill="#444444"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 8V9L11 9V15H12V16H9V15H10V9H9V8H12Z" fill="#444444"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M15 10H18V14H15H14H13V15H14H18C18.5523 15 19 14.5523 19 14V10C19 9.44772 18.5523 9 18 9H14H13V10H14H15Z" fill="#444444"/>
</svg>

After

Width:  |  Height:  |  Size: 790 B

View File

@ -158,6 +158,10 @@ label .checkbox {
background-image: url("img/file_pptx.svg");
}
.try-editor.form {
background-image: url("img/file_docxf.svg");
}
.create-sample {
color: #666666;
line-height: 24px;

View File

@ -20,10 +20,12 @@ var ConverExtList;
var EditedExtList;
var UrlConverter;
var UrlEditor;
var FillExtList;
if (typeof jQuery !== "undefined") {
jQuery.post('/config',
function(data) {
FillExtList = data.FillExtList;
ConverExtList = data.ConverExtList;
EditedExtList = data.EditedExtList;
UrlConverter = data.UrlConverter;

View File

@ -167,7 +167,7 @@ if (typeof jQuery !== "undefined") {
var posExt = fileName.lastIndexOf(".");
posExt = 0 <= posExt ? fileName.substring(posExt).trim().toLowerCase() : "";
if (EditedExtList.indexOf(posExt) !== -1) {
if (EditedExtList.indexOf(posExt) !== -1 || FillExtList.indexOf(posExt) !== -1) {
jq("#beginEdit").removeClass("disable");
}
};

View File

@ -31,32 +31,40 @@
<script th:inline="javascript">
var docEditor;
var innerAlert = function (message) {
var innerAlert = function (message, inEditor) {
if (console && console.log)
console.log(message);
if (inEditor && docEditor)
docEditor.showMessage(message);
};
// the application is loaded into the browser
var onAppReady = function () {
innerAlert("Document editor ready");
};
// the document is modified
var onDocumentStateChange = function (event) {
var title = document.title.replace(/\*$/g, "");
document.title = title + (event.data ? "*" : "");
};
// the user is trying to switch the document from the viewing into the editing mode
var onRequestEditRights = function () {
location.href = location.href.replace(RegExp("\&?action=view\&?", "i"), "");
};
// an error or some other specific event occurs
var onError = function (event) {
if (event) innerAlert(event.data);
};
// the document is opened for editing with the old document.key value
var onOutdatedVersion = function (event) {
location.reload(true);
};
// replace the link to the document which contains a bookmark
var replaceActionLink = function(href, linkParam) {
var link;
var actionIndex = href.indexOf("&actionLink=");
@ -73,12 +81,14 @@
return link;
}
// the user is trying to get link for opening the document which contains a bookmark, scrolling to the bookmark position
var onMakeActionLink = function (event) {
var actionData = event.data;
var linkParam = JSON.stringify(actionData);
docEditor.setActionLink(replaceActionLink(location.href, linkParam));
};
// the meta information of the document is changed via the meta command
var onMetaChange = function (event) {
var favorite = !!event.data.favorite;
var title = document.title.replace(/^\☆/g, "");
@ -89,15 +99,18 @@
var dataInsertImage = [[${dataInsertImage}]];
// the user is trying to insert an image by clicking the Image from Storage button
var onRequestInsertImage = function(event) {
const temp = Object.assign({}, {"c": event.data.c}, dataInsertImage);
docEditor.insertImage(temp);
};
// the user is trying to select document for comparing by clicking the Document from Storage button
var onRequestCompareFile = function() {
docEditor.setRevisedFile([[${dataCompareFile}]]);
};
// the user is trying to select recipients data by clicking the Mail merge button
var onRequestMailMergeRecipients = function (event) {
docEditor.setMailMergeRecipients([[${dataMailMergeRecipients}]]);
};
@ -108,6 +121,23 @@
config.editorConfig.user.name = "";
}
var onRequestSaveAs = function (event) { // the user is trying to save file by clicking Save Copy as... button
var title = event.data.title;
var url = event.data.url;
var data = {
title: title,
url: url
};
let xhr = new XMLHttpRequest();
xhr.open("POST", "saveas");
xhr.setRequestHeader( 'Content-Type', 'application/json');
xhr.send(JSON.stringify(data));
xhr.onload = function () {
innerAlert(xhr.responseText);
innerAlert(JSON.parse(xhr.responseText).file, true);
}
};
config.width = "100%";
config.height = "100%";
config.events = {
@ -129,36 +159,48 @@
var usersForMentions = [[${usersForMentions}]];
if (hist && historyData) {
// the user is trying to show the document version history
config.events['onRequestHistory'] = function () {
docEditor.refreshHistory(JSON.parse(hist));
docEditor.refreshHistory(JSON.parse(hist)); // show the document version history
};
// the user is trying to click the specific document version in the document version history
config.events['onRequestHistoryData'] = function (event) {
var ver = event.data;
var histData = historyData;
docEditor.setHistoryData(JSON.parse(histData)[ver - 1]);
docEditor.setHistoryData(JSON.parse(histData)[ver - 1]); // send the link to the document for viewing the version history
};
// the user is trying to go back to the document from viewing the document version history
config.events['onRequestHistoryClose'] = function () {
document.location.reload();
};
}
if(usersForMentions){
// add mentions for not anonymous users
config.events['onRequestUsers'] = function () {
docEditor.setUsers({
docEditor.setUsers({ // set a list of users to mention in the comments
"users": usersForMentions
});
};
// the user is mentioned in a comment
config.events['onRequestSendNotify'] = function (event) {
var actionLink = JSON.stringify(event.data.actionLink);
var replaceLink =replaceActionLink(location.href, actionLink);
docEditor.setActionLink(replaceLink);
console.log("onRequestSendNotify:");
console.log(event.data);
console.log("Link to comment: " + replaceLink);
event.data.actionLink = replaceActionLink(location.href, event.data.actionLink);
var data = JSON.stringify(event.data);
innerAlert("onRequestSendNotify: " + data);
};
}
if (config.editorConfig.createUrl) {
config.events.onRequestSaveAs = onRequestSaveAs;
};
var сonnectEditor = function () {
if ((config.document.fileType === "docxf" || config.document.fileType === "oform")
&& DocsAPI.DocEditor.version().split(".")[0] < 7) {
innerAlert("Please update ONLYOFFICE Docs to version 7.0 to work on fillable forms online.");
return;
}
docEditor = new DocsAPI.DocEditor("iframeEditor", config);
};

View File

@ -54,6 +54,9 @@
<li>
<a class="try-editor slide" data-type="pptx">Presentation</a>
</li>
<li>
<a class="try-editor form" data-type="docxf">Form template</a>
</li>
</ul>
<label class="create-sample">
<input id="createSample" class="checkbox" type="checkbox" />With sample content
@ -191,27 +194,43 @@
</div>
<div th:if="${docTypes[iState.index]} eq 'word'">
<td class="contentCells contentCells-icon">
<a th:href="@{/editor(fileName=${files[iState.index].getName()},type='desktop',action='fillForms')}" target="_blank">
<img src="css/img/fill-forms.svg" alt="Open in editor for filling in forms" title="Open in editor for filling in forms"/>
</a>
</td>
</div>
<div th:if="not (${docTypes[iState.index]} eq 'word')">
<td class="contentCells contentCells-icon "></td>
</div>
<div th:if="${docTypes[iState.index]} eq 'word'">
<td class="contentCells contentCells-shift contentCells-icon firstContentCellShift">
<a th:href="@{/editor(fileName=${files[iState.index].getName()},type='desktop',action='blockcontent')}" target="_blank">
<img src="css/img/block-content.svg" alt="Open in editor without content control modification" title="Open in editor without content control modification"/>
</a>
</td>
</div>
<div th:if="not (${docTypes[iState.index]} eq 'word')">
<td class="contentCells contentCells-shift contentCells-icon firstContentCellShift "></td>
<td class="contentCells contentCells-icon"></td>
</div>
<div th:if="${docTypes[iState.index]} eq 'slide'">
<td class="contentCells contentCells-icon "></td>
</div>
<div th:if="${isFillFormDoc[iState.index]}">
<td class="contentCells contentCells-shift contentCells-icon firstContentCellShift">
<a th:href="@{/editor(fileName=${files[iState.index].getName()},type='desktop',action='fillForms')}" target="_blank">
<img src="css/img/fill-forms.svg" alt="Open in editor for filling in forms" title="Open in editor for filling in forms"/>
</a>
</td>
</div>
<div th:if="${not isFillFormDoc[iState.index]}">
<td class="contentCells contentCells-shift contentCells-icon firstContentCellShift"></td>
</div>
</th:block>
<th:block th:if="${isFillFormDoc[iState.index] and not filesEditable[iState.index]}">
<td class="contentCells contentCells-icon "></td>
<td class="contentCells contentCells-icon">
<a th:href="@{/editor(fileName=${files[iState.index].getName()},type='mobile',action='fillForms')}" target="_blank">
<img src="css/img/mobile-fill-forms.svg" alt="Open in editor for filling in forms for mobile devices" title="Open in editor for filling in forms for mobile devices" />
</a>
</td>
<td class="contentCells contentCells-icon "></td>
<td class="contentCells contentCells-icon "></td>
<td class="contentCells contentCells-icon "></td>
<td class="contentCells contentCells-shift contentCells-icon firstContentCellShift">
<a th:href="@{/editor(fileName=${files[iState.index].getName()},type='desktop',action='fillForms')}" target="_blank">
<img src="css/img/fill-forms.svg" alt="Open in editor for filling in forms" title="Open in editor for filling in forms"/>
</a>
</td>
</th:block>
<th:block th:if="${not filesEditable[iState.index]}">
<td class="contentCells contentCells-shift contentCells-icon contentCellsEmpty" colspan="6"></td>

View File

@ -40,6 +40,7 @@ import entities.FileType;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.primeframework.jwt.Verifier;
import org.primeframework.jwt.domain.JWT;
import org.primeframework.jwt.hmac.HMACVerifier;
@ -89,6 +90,47 @@ public class IndexServlet extends HttpServlet
case "files":
Files(request, response, writer);
break;
case "saveas":
SaveAs(request, response, writer);
break;
}
}
private static void SaveAs(HttpServletRequest request, HttpServletResponse response, PrintWriter writer) {
response.setContentType("text/plain");
try {
Scanner scanner = new Scanner(request.getInputStream());
scanner.useDelimiter("\\A");
String bodyString = scanner.hasNext() ? scanner.next() : "";
scanner.close();
JSONParser parser = new JSONParser();
JSONObject body = (JSONObject) parser.parse(bodyString);
CookieManager cm = new CookieManager(request);
User user = Users.getUser(cm.getCookie("uid"));
String title = (String) body.get("title");
String saveAsFileUrl = (String) body.get("url");
int filesizeMax = Integer.parseInt(ConfigManager.GetProperty("filesize-max"));
URL url = new URL(saveAsFileUrl);
java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection();
InputStream stream = connection.getInputStream();
if (filesizeMax < stream.available() || stream.available() <= 0) {
writer.write( "{\"error\":\"File size is incorrect\"}");
}
String fileName = DocumentManager.GetCorrectName(title, null);
DocumentManager.CreateFile(Paths.get(DocumentManager.StoragePath(fileName, null)), stream);
DocumentManager.CreateMeta(fileName, user.id, user.name, null);
writer.write( "{\"file\": \"" + fileName + "\"}");
} catch (Exception e) {
e.printStackTrace();
writer.write("{ \"error\" : 1, \"message\" : \"" + e.getMessage() + "\"}");
}
}

View File

@ -104,9 +104,15 @@ public class FileModel
if (_type != null) type = _type;
// check if the file with such an extension can be edited
Boolean canEdit = DocumentManager.GetEditedExts().contains(FileUtility.GetFileExtension(document.title));
String fileExt = FileUtility.GetFileExtension(document.title);
Boolean canEdit = DocumentManager.GetEditedExts().contains(fileExt);
// check if the Submit form button is displayed or not
editorConfig.customization.submitForm = canEdit && (mode.equals("edit") || mode.equals("fillForms"));
editorConfig.customization.submitForm = mode.equals("fillForms") && user.id.equals("uid-1") && false;
if ((!canEdit && mode.equals("edit") || mode.equals("fillForms")) && DocumentManager.GetFillExts().contains(fileExt)) {
canEdit = true;
mode = "fillForms";
}
// set the mode parameter: change it to view if the document can't be edited
editorConfig.mode = canEdit && !mode.equals("view") ? "edit" : "view";

View File

@ -18,16 +18,14 @@
package helpers;
import java.io.File;
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.io.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.InetAddress;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.*;
import javax.servlet.http.HttpServletRequest;
@ -76,10 +74,16 @@ public class DocumentManager
res.addAll(GetViewedExts());
res.addAll(GetEditedExts());
res.addAll(GetConvertExts());
res.addAll(GetFillExts());
return res;
}
public static List<String> GetFillExts() {
String exts = ConfigManager.GetProperty("files.docservice.fill-docs");
return Arrays.asList(exts.split("\\|"));
}
// get file extensions that can be viewed
public static List<String> GetViewedExts()
{
@ -279,19 +283,7 @@ public class DocumentManager
InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(demoPath + demoName); // get the input file stream
File file = new File(StoragePath(fileName, null));
try (FileOutputStream out = new FileOutputStream(file))
{
int read;
final byte[] bytes = new byte[1024];
while ((read = stream.read(bytes)) != -1)
{
out.write(bytes, 0, read); // write bytes to the output stream
}
// force write data to the output stream that can be cached in the current thread
out.flush();
}
CreateFile(Paths.get(StoragePath(fileName, null)), stream);
// create meta information of the demo file
CreateMeta(fileName, user.id, user.name, null);
@ -299,6 +291,28 @@ public class DocumentManager
return fileName;
}
public static boolean CreateFile(Path path, InputStream stream) {
if (Files.exists(path)){
return true;
}
try {
File file = Files.createFile(path).toFile();
try (FileOutputStream out = new FileOutputStream(file))
{
int read;
final byte[] bytes = new byte[1024];
while ((read = stream.read(bytes)) != -1)
{
out.write(bytes, 0, read);
}
out.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
// get file url
public static String GetFileUri(String fileName, Boolean forDocumentServer)
{

View File

@ -57,7 +57,7 @@ public class FileUtility
".dot", ".dotx", ".dotm",
".odt", ".fodt", ".ott", ".rtf", ".txt",
".html", ".htm", ".mht", ".xml",
".pdf", ".djvu", ".fb2", ".epub", ".xps", ".oxps"
".pdf", ".djvu", ".fb2", ".epub", ".xps", ".oxps", ".oform"
);
// spreadsheet extensions

View File

@ -1,10 +1,11 @@
version=1.0.0
version=1.1.0
filesize-max=5242880
storage-folder=app_data
files.docservice.fill-docs=.oform|.docx
files.docservice.viewed-docs=.pdf|.djvu|.xps|.oxps
files.docservice.edited-docs=.docx|.xlsx|.csv|.pptx|.txt
files.docservice.edited-docs=.docx|.xlsx|.csv|.pptx|.txt|.docxf
files.docservice.convert-docs=.docm|.dotx|.dotm|.dot|.doc|.odt|.fodt|.ott|.xlsm|.xltx|.xltm|.xlt|.xls|.ods|.fods|.ots|.pptm|.ppt|.ppsx|.ppsm|.pps|.potx|.potm|.pot|.odp|.fodp|.otp|.rtf|.mht|.html|.htm|.xml|.epub|.fb2
files.docservice.timeout=120000

View File

@ -0,0 +1,6 @@
<svg width="30" height="40" viewBox="0 0 30 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 3C0 1.34315 1.34315 0 3 0H22.9167L30 7.08333V37C30 38.6569 28.6569 40 27 40H3C1.34315 40 0 38.6569 0 37V3Z" fill="#27ABA3"/>
<path d="M22.9165 0L29.9998 7.08333H25.9165C24.2597 7.08333 22.9165 5.74019 22.9165 4.08333V0Z" fill="#008078"/>
<rect x="6.5" y="15.5" width="17" height="5" stroke="white"/>
<rect x="6.5" y="23.5" width="17" height="5" stroke="white"/>
</svg>

After

Width:  |  Height:  |  Size: 477 B

View File

@ -0,0 +1,6 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 5C5 3.89543 5.89543 3 7 3H16C17.1046 3 18 3.89543 18 5V8H17V6H6V18H17V16H18V19C18 20.1046 17.1046 21 16 21H7C5.89543 21 5 20.1046 5 19V5ZM13 4H10V5H13V4ZM12 19.5C12 19.7761 11.7761 20 11.5 20C11.2239 20 11 19.7761 11 19.5C11 19.2239 11.2239 19 11.5 19C11.7761 19 12 19.2239 12 19.5Z" fill="#444444"/>
<rect x="7" y="9" width="1" height="6" fill="#444444"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 8V9L11 9V15H12V16H9V15H10V9H9V8H12Z" fill="#444444"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M15 10H18V14H15H14H13V15H14H18C18.5523 15 19 14.5523 19 14V10C19 9.44772 18.5523 9 18 9H14H13V10H14H15Z" fill="#444444"/>
</svg>

After

Width:  |  Height:  |  Size: 790 B

View File

@ -48,7 +48,6 @@
.left-panel {
margin-left: 48px;
width: 20%;
}
}
@ -161,17 +160,8 @@
width: 45%;
}
}
@media (max-width: 670px) and (min-width: 620px){
.main-panel{
width: 90%;
}
}
@media (max-width: 681px) and (min-width: 593px) {
.left-panel {
width: 10%;
}
.main-panel {
left: 2%;
padding: 48px 0 24px;
@ -264,9 +254,6 @@
}
@media (max-width: 890px) and (min-width: 769px ) {
.left-panel{
width: 20%;
}
.contentCells-shift {
padding-right: 28px;
}
@ -328,6 +315,9 @@
.tableRow td:first-child {
max-width: 100%;
}
.left-panel {
width: min-content;
}
}
@ -369,10 +359,6 @@
margin: 0;
}
.left-panel {
width: 10%;
}
.help-block {
margin: 16px 10px 6px;
}

View File

@ -157,6 +157,10 @@ label .checkbox {
background-image: url("img/file_pptx.svg");
}
.try-editor.form {
background-image: url("img/file_docxf.svg");
}
.create-sample {
color: #666666;
line-height: 24px;

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