mirror of
https://github.com/ONLYOFFICE/document-server-integration.git
synced 2026-04-07 14:06:11 +08:00
Compare commits
72 Commits
v99.99.99.
...
feature/no
| Author | SHA1 | Date | |
|---|---|---|---|
| e198dd7e6c | |||
| 3375315cc0 | |||
| a070b6e82b | |||
| 43d260cd7a | |||
| 14bb9fc8a4 | |||
| 57ca15153f | |||
| bbde4dc87b | |||
| 5b46195f72 | |||
| 81c94c9fbd | |||
| cc3c640868 | |||
| 20e6664a6b | |||
| 61af52a534 | |||
| 6236f90a50 | |||
| 5118352714 | |||
| aa5a660001 | |||
| bf1f987333 | |||
| f1f834fb0c | |||
| 53c3e97b5c | |||
| eda1876f59 | |||
| 64eab6c4a2 | |||
| acb9a0e4c9 | |||
| 909f638a92 | |||
| 6d28e3f8d8 | |||
| 1b8f60d5dd | |||
| 0414319893 | |||
| 12ad0d9e7e | |||
| f3b35a878c | |||
| c712f596e1 | |||
| ad39fb7c19 | |||
| 7d2eb086ce | |||
| 0a61708f67 | |||
| 389198aec6 | |||
| 0d302ee8f6 | |||
| d3a548bf3f | |||
| fadae60e89 | |||
| 426b15b8f1 | |||
| 601146c847 | |||
| 0bfb036be6 | |||
| 88b36049d2 | |||
| e389cf41b8 | |||
| b9113f93f6 | |||
| af778a8636 | |||
| 47f4f022aa | |||
| b2202111ac | |||
| 1da215b1eb | |||
| 8c8d14b48c | |||
| 5217a64e83 | |||
| 456789191d | |||
| e1e9efa305 | |||
| abab30176f | |||
| 1abcf78b85 | |||
| 5258fd0674 | |||
| ca274bc465 | |||
| b1d66e16a5 | |||
| 359cda0f67 | |||
| 24a3441b1e | |||
| a7fca1a53b | |||
| 5b6a2ba318 | |||
| bb4aed6efb | |||
| fbe24234d2 | |||
| ac894171fb | |||
| fd21292dbd | |||
| d43294bd8a | |||
| 81f5bdd528 | |||
| c1da8e14c3 | |||
| 767b5588ab | |||
| 8858c3c256 | |||
| 89f1c18d06 | |||
| 81ad7f7a64 | |||
| 672aae3791 | |||
| 999e147539 | |||
| dcf2fb43e0 |
@ -1,11 +1,11 @@
|
||||
# Change Log
|
||||
|
||||
- fill permission in embedded mode
|
||||
- delete all files
|
||||
- handling conversion -9 error
|
||||
- nodejs: wopi formsubmit icon
|
||||
- php: handling conversion -9 error
|
||||
- nodejs: tabs menu
|
||||
- nodejs: delete all files
|
||||
- change insert image
|
||||
- nodejs: handling conversion -9 error
|
||||
- different goback for users
|
||||
- nodejs: converting function on index page
|
||||
- nodejs: close editor
|
||||
|
||||
@ -502,6 +502,17 @@
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.buttonsMobile.indent {
|
||||
margin-bottom: 0;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
.button.file-type:hover,
|
||||
.button.file-type {
|
||||
height: 28px;
|
||||
width: 100px;
|
||||
margin-bottom: 10px !important;
|
||||
font-size: 9px;
|
||||
}
|
||||
.button.gray{
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@ -230,6 +230,33 @@ label .checkbox {
|
||||
color: #FF6F3D;
|
||||
}
|
||||
|
||||
.button.file-type {
|
||||
font-size: 11px;
|
||||
color: #FFFFFF;
|
||||
padding: 8px 8px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.button.file-type.disable {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.button.file-type.pale {
|
||||
opacity: 30%;
|
||||
}
|
||||
|
||||
.button.file-type.document {
|
||||
background: #446995;
|
||||
}
|
||||
|
||||
.button.file-type.spreadsheet {
|
||||
background: #40865C;
|
||||
}
|
||||
|
||||
.button.file-type.presentation {
|
||||
background: #AA5252;
|
||||
}
|
||||
|
||||
.upload-panel {
|
||||
float: left;
|
||||
padding: 24px 0;
|
||||
@ -592,6 +619,29 @@ footer table tr td:first-child {
|
||||
width: 4%;
|
||||
}
|
||||
|
||||
.storedHeader {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.storedHeaderClearAll {
|
||||
padding-right: 52px;
|
||||
}
|
||||
|
||||
.clear-all {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
padding: 2px;
|
||||
outline: 1px solid #E5E5E5;
|
||||
text-align: center;
|
||||
cursor:pointer;
|
||||
text-transform: uppercase;
|
||||
background-color: #F5F5F5;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.select-user {
|
||||
color: #444444;
|
||||
font-family: Open Sans;
|
||||
@ -741,6 +791,16 @@ html {
|
||||
margin-left: 25px;
|
||||
}
|
||||
|
||||
.buttonsMobile.indent{
|
||||
padding-left: 35px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.invisible {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
background: #FFFFFF;
|
||||
border-radius: 5px;
|
||||
|
||||
@ -229,6 +229,10 @@ namespace OnlineEditorsExampleMVC.Helpers
|
||||
|
||||
switch (errorCode)
|
||||
{
|
||||
case -9:
|
||||
// public const int c_nErrorConversionOutputFormatError = -9;
|
||||
errorMessage = String.Format(errorMessageTemplate, "Error conversion output format");
|
||||
break;
|
||||
case -8:
|
||||
// public const int c_nErrorFileVKey = -8;
|
||||
errorMessage = String.Format(errorMessageTemplate, "Error document VKey");
|
||||
|
||||
@ -157,7 +157,7 @@ namespace OnlineEditorsExampleMVC.Models
|
||||
{ "download", !user.deniedPermissions.Contains("download") },
|
||||
{ "edit", canEdit && (editorsMode == "edit" || editorsMode == "view" || editorsMode == "filter" || editorsMode == "blockcontent") },
|
||||
{ "print", !user.deniedPermissions.Contains("print") },
|
||||
{ "fillForms", editorsMode != "view" && editorsMode != "comment" && editorsMode != "embedded" && editorsMode != "blockcontent" },
|
||||
{ "fillForms", editorsMode != "view" && editorsMode != "comment" && editorsMode != "blockcontent" },
|
||||
{ "modifyFilter", editorsMode != "filter" },
|
||||
{ "modifyContentControl", editorsMode != "blockcontent" },
|
||||
{ "review", canEdit && (editorsMode == "edit" || editorsMode == "review") },
|
||||
|
||||
66
web/documentserver-example/csharp-mvc/Scripts/formats.js
Normal file
66
web/documentserver-example/csharp-mvc/Scripts/formats.js
Normal file
@ -0,0 +1,66 @@
|
||||
/**
|
||||
*
|
||||
* (c) Copyright Ascensio System SIA 2024
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
class Format {
|
||||
constructor(name, type, actions, convert, mime) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.actions = actions;
|
||||
this.convert = convert;
|
||||
this.mime = mime;
|
||||
}
|
||||
|
||||
isAutoConvertible() {
|
||||
return this.actions.includes('auto-convert');
|
||||
}
|
||||
|
||||
isEditable() {
|
||||
return this.actions.includes('edit') || this.actions.includes('lossy-edit');
|
||||
}
|
||||
|
||||
isFillable() {
|
||||
return this.actions.includes('fill');
|
||||
}
|
||||
}
|
||||
|
||||
class FormatManager {
|
||||
formats = [];
|
||||
|
||||
constructor(formats) {
|
||||
if(Array.isArray(formats)) this.formats = formats;
|
||||
}
|
||||
|
||||
findByExtension(extension) {
|
||||
return this.formats.find(format => format.name == extension);
|
||||
}
|
||||
|
||||
isAutoConvertible(extension) {
|
||||
let format = this.findByExtension(extension);
|
||||
return format !== undefined && format.isAutoConvertible();
|
||||
}
|
||||
|
||||
isEditable(extension) {
|
||||
let format = this.findByExtension(extension);
|
||||
return format !== undefined && format.isEditable();
|
||||
}
|
||||
|
||||
isFillable(extension) {
|
||||
let format = this.findByExtension(extension);
|
||||
return format !== undefined && format.isFillable();
|
||||
}
|
||||
}
|
||||
@ -17,6 +17,27 @@
|
||||
*/
|
||||
|
||||
var directUrl;
|
||||
var formatManager;
|
||||
|
||||
window.onload = function () {
|
||||
fetch("webeditor.ashx?type=formats")
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
if (data.formats) {
|
||||
let formats = [];
|
||||
data.formats.forEach(format => {
|
||||
formats.push(new Format(
|
||||
format.Name,
|
||||
format.Type,
|
||||
format.Actions,
|
||||
format.Convert,
|
||||
format.Mime
|
||||
));
|
||||
});
|
||||
formatManager = new FormatManager(formats);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (typeof jQuery != "undefined") {
|
||||
jq = jQuery.noConflict();
|
||||
@ -87,7 +108,7 @@ if (typeof jQuery != "undefined") {
|
||||
});
|
||||
|
||||
var timer = null;
|
||||
var checkConvert = function (filePass) {
|
||||
var checkConvert = function (filePass, fileType) {
|
||||
filePass = filePass ? filePass : null;
|
||||
if (timer != null) {
|
||||
clearTimeout(timer);
|
||||
@ -103,7 +124,7 @@ if (typeof jQuery != "undefined") {
|
||||
var posExt = fileName.lastIndexOf('.');
|
||||
posExt = 0 <= posExt ? fileName.substring(posExt).trim().toLowerCase() : '';
|
||||
|
||||
if (ConverExtList.indexOf(posExt) == -1) {
|
||||
if (!formatManager.isAutoConvertible(posExt)) {
|
||||
jq("#step2").addClass("done").removeClass("current");
|
||||
loadScripts();
|
||||
return;
|
||||
@ -116,7 +137,7 @@ if (typeof jQuery != "undefined") {
|
||||
contentType: "text/xml",
|
||||
type: "post",
|
||||
dataType: "json",
|
||||
data: JSON.stringify({ filename: fileName, filePass: filePass }),
|
||||
data: JSON.stringify({ filename: fileName, filePass: filePass, fileExt: fileType }),
|
||||
url: UrlConverter,
|
||||
complete: function (data) {
|
||||
var responseText = data.responseText;
|
||||
@ -132,6 +153,12 @@ if (typeof jQuery != "undefined") {
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
if (response.error.includes("Error conversion output format")) {
|
||||
jq("#select-file-type").removeClass("invisible");
|
||||
jq("#step2").removeClass("current");
|
||||
jq("#hiddenFileName").attr("placeholder", filePass);
|
||||
return;
|
||||
}
|
||||
jq(".current").removeClass("current");
|
||||
jq(".step:not(.done)").addClass("error");
|
||||
jq("#mainProgress .error-message").show().find("span").text(response.error);
|
||||
@ -143,7 +170,7 @@ if (typeof jQuery != "undefined") {
|
||||
jq("#hiddenFileName").val(response.filename);
|
||||
|
||||
if (response.step && response.step < 100) {
|
||||
checkConvert(filePass);
|
||||
checkConvert(filePass, fileType);
|
||||
} else {
|
||||
jq("#step2").addClass("done").removeClass("current");
|
||||
loadScripts();
|
||||
@ -178,10 +205,10 @@ if (typeof jQuery != "undefined") {
|
||||
jq("#beginView, #beginEmbedded").removeClass("disable");
|
||||
|
||||
var fileName = jq("#hiddenFileName").val();
|
||||
var posExt = fileName.lastIndexOf('.');
|
||||
var posExt = fileName.lastIndexOf('.') + 1;
|
||||
posExt = 0 <= posExt ? fileName.substring(posExt).trim().toLowerCase() : '';
|
||||
|
||||
if (EditedExtList.indexOf(posExt) != -1 || FillExtList.indexOf(posExt) != -1) {
|
||||
if (formatManager.isEditable(posExt) || formatManager.isFillable(posExt)) {
|
||||
jq("#beginEdit").removeClass("disable");
|
||||
}
|
||||
};
|
||||
@ -213,6 +240,15 @@ if (typeof jQuery != "undefined") {
|
||||
});
|
||||
};
|
||||
|
||||
jq(document).on("click", ".file-type:not(.disable)", function () {
|
||||
const currentElement = jq(this);
|
||||
var fileType = currentElement.attr("data");
|
||||
var filePass = jq("#hiddenFileName").attr("placeholder");
|
||||
jq('.file-type').addClass(["disable", "pale"]);
|
||||
currentElement.removeClass("pale");
|
||||
checkConvert(filePass, fileType);
|
||||
});
|
||||
|
||||
jq(document).on("click", "#enterPass", function () {
|
||||
var filePass = jq("#filePass").val();
|
||||
if (filePass) {
|
||||
@ -294,6 +330,23 @@ if (typeof jQuery != "undefined") {
|
||||
});
|
||||
});
|
||||
|
||||
jq(document).on("click", ".clear-all", function () {
|
||||
if (confirm("Delete all the files?")) {
|
||||
var requestAddress = "webeditor.ashx"
|
||||
+ "?type=remove";
|
||||
jq.ajax({
|
||||
async: true,
|
||||
contentType: "text/xml",
|
||||
url: requestAddress,
|
||||
complete: function (data) {
|
||||
if (JSON.parse(data.responseText).success) {
|
||||
window.location.reload(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function showUserTooltip (isMobile) {
|
||||
if ( jq("div#portal-info").is(":hidden") ) {
|
||||
jq("div#portal-info").show();
|
||||
|
||||
@ -151,7 +151,14 @@
|
||||
if (storedFiles.Any())
|
||||
{ %>
|
||||
<div class="stored-list">
|
||||
<span class="header-list">Your documents</span>
|
||||
<div class="storedHeader">
|
||||
<div class="storedHeaderText">
|
||||
<span class="header-list">Your documents</span>
|
||||
</div>
|
||||
<div class="storedHeaderClearAll">
|
||||
<div class="clear-all">Clear all</div>
|
||||
</div>
|
||||
</div>
|
||||
<table class="tableHeader" cellspacing="0" cellpadding="0" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
@ -295,6 +302,15 @@
|
||||
<div class="describeUpload">After these steps are completed, you can work with your document.</div>
|
||||
<span id="step1" class="step">1. Loading the file.</span>
|
||||
<span class="step-descr">The loading speed depends on file size and additional elements it contains.</span>
|
||||
<div id="select-file-type" class="invisible">
|
||||
<br />
|
||||
<span class="step">Please select the current document type</span>
|
||||
<div class="buttonsMobile indent">
|
||||
<div class="button file-type document" data="docx">Document</div>
|
||||
<div class="button file-type spreadsheet" data="xlsx">Spreadsheet</div>
|
||||
<div class="button file-type presentation" data="pptx">Presentation</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<span id="step2" class="step">2. Conversion.</span>
|
||||
<span class="step-descr">The file is converted to OOXML so that you can edit it.</span>
|
||||
@ -357,9 +373,6 @@
|
||||
<%: 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") %>';
|
||||
var UrlEditor = '<%= Url.Action("editor", "Home") %>';
|
||||
</script>
|
||||
|
||||
@ -87,6 +87,9 @@ namespace OnlineEditorsExampleMVC
|
||||
case "reference":
|
||||
Reference(context);
|
||||
break;
|
||||
case "formats":
|
||||
Formats(context);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -240,7 +243,13 @@ namespace OnlineEditorsExampleMVC
|
||||
var fileUri = DocManagerHelper.GetDownloadUrl(fileName);
|
||||
|
||||
var extension = (Path.GetExtension(fileName).ToLower() ?? "").Trim('.');
|
||||
var internalExtension = "ooxml";
|
||||
string conversionExtension = "ooxml";
|
||||
object fileExt;
|
||||
|
||||
if (body.TryGetValue("fileExt", out fileExt) && !String.IsNullOrEmpty(fileExt.ToString()))
|
||||
{
|
||||
conversionExtension = fileExt.ToString();
|
||||
}
|
||||
|
||||
// check if the file with such an extension can be converted
|
||||
if (DocManagerHelper.ConvertExts.Contains("." + extension))
|
||||
@ -258,7 +267,7 @@ namespace OnlineEditorsExampleMVC
|
||||
|
||||
// get the url and file type of the converted file
|
||||
Dictionary<string, string> newFileData;
|
||||
var result = ServiceConverter.GetConvertedData(downloadUri.ToString(), extension, internalExtension, key, true, out newFileData, filePass, lang);
|
||||
var result = ServiceConverter.GetConvertedData(downloadUri.ToString(), extension, conversionExtension, key, true, out newFileData, filePass, lang);
|
||||
if (result != 100)
|
||||
{
|
||||
context.Response.Write("{ \"step\" : \"" + result + "\", \"filename\" : \"" + fileName + "\"}");
|
||||
@ -393,8 +402,17 @@ namespace OnlineEditorsExampleMVC
|
||||
context.Response.ContentType = "text/plain";
|
||||
try
|
||||
{
|
||||
var fileName = Path.GetFileName(context.Request["fileName"]);
|
||||
Remove(fileName); // remove a file and its history if it exists
|
||||
string fileName = context.Request["fileName"];
|
||||
|
||||
if (!String.IsNullOrEmpty(fileName))
|
||||
{
|
||||
fileName = Path.GetFileName(context.Request["fileName"]);
|
||||
Remove(fileName); // remove a file and its history if it exists
|
||||
}
|
||||
else
|
||||
{
|
||||
RemoveUserDirectory(); // remove the user's directory
|
||||
}
|
||||
|
||||
context.Response.Write("{ \"success\": true }");
|
||||
}
|
||||
@ -414,6 +432,14 @@ namespace OnlineEditorsExampleMVC
|
||||
if (Directory.Exists(histDir)) Directory.Delete(histDir, true);
|
||||
}
|
||||
|
||||
// remove the user's directory
|
||||
private static void RemoveUserDirectory()
|
||||
{
|
||||
var path = DocManagerHelper.StoragePath("", null); // get the path to the user directory
|
||||
|
||||
if (Directory.Exists(path)) Directory.Delete(path, true);
|
||||
}
|
||||
|
||||
// get files information
|
||||
private static void Files(HttpContext context)
|
||||
{
|
||||
@ -951,6 +977,25 @@ namespace OnlineEditorsExampleMVC
|
||||
return history;
|
||||
}
|
||||
|
||||
// return all the supported formats
|
||||
private static void Formats(HttpContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
Dictionary<string, object> data = new Dictionary<string, object>
|
||||
{
|
||||
{ "formats", FormatManager.All() }
|
||||
};
|
||||
context.Response.ContentType = "application/json";
|
||||
var jss = new JavaScriptSerializer();
|
||||
|
||||
context.Response.Write(jss.Serialize(data));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
context.Response.Write("{ \"error\": \"" + e.Message + "\"}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -502,6 +502,17 @@
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.buttonsMobile.indent {
|
||||
margin-bottom: 0;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
.button.file-type:hover,
|
||||
.button.file-type {
|
||||
height: 28px;
|
||||
width: 100px;
|
||||
margin-bottom: 10px !important;
|
||||
font-size: 9px;
|
||||
}
|
||||
.button.gray{
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@ -230,6 +230,33 @@ label .checkbox {
|
||||
color: #FF6F3D;
|
||||
}
|
||||
|
||||
.button.file-type {
|
||||
font-size: 11px;
|
||||
color: #FFFFFF;
|
||||
padding: 8px 8px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.button.file-type.disable {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.button.file-type.pale {
|
||||
opacity: 30%;
|
||||
}
|
||||
|
||||
.button.file-type.document {
|
||||
background: #446995;
|
||||
}
|
||||
|
||||
.button.file-type.spreadsheet {
|
||||
background: #40865C;
|
||||
}
|
||||
|
||||
.button.file-type.presentation {
|
||||
background: #AA5252;
|
||||
}
|
||||
|
||||
.upload-panel {
|
||||
float: left;
|
||||
padding: 24px 0;
|
||||
@ -596,6 +623,29 @@ footer a:hover {
|
||||
width: 4%;
|
||||
}
|
||||
|
||||
.storedHeader {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.storedHeaderClearAll {
|
||||
padding-right: 52px;
|
||||
}
|
||||
|
||||
.clear-all {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
padding: 2px;
|
||||
outline: 1px solid #E5E5E5;
|
||||
text-align: center;
|
||||
cursor:pointer;
|
||||
text-transform: uppercase;
|
||||
background-color: #F5F5F5;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.select-user {
|
||||
color: #444444;
|
||||
font-family: Open Sans;
|
||||
@ -745,6 +795,16 @@ html {
|
||||
margin-left: 25px;
|
||||
}
|
||||
|
||||
.buttonsMobile.indent{
|
||||
padding-left: 35px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.invisible {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
background: #FFFFFF;
|
||||
border-radius: 5px;
|
||||
|
||||
@ -153,7 +153,14 @@
|
||||
if (storedFiles.Any())
|
||||
{ %>
|
||||
<div class="stored-list">
|
||||
<span class="header-list">Your documents</span>
|
||||
<div class="storedHeader">
|
||||
<div class="storedHeaderText">
|
||||
<span class="header-list">Your documents</span>
|
||||
</div>
|
||||
<div class="storedHeaderClearAll">
|
||||
<div class="clear-all">Clear all</div>
|
||||
</div>
|
||||
</div>
|
||||
<table class="tableHeader" cellspacing="0" cellpadding="0" width="100%">
|
||||
<thead>
|
||||
<tr >
|
||||
@ -297,6 +304,15 @@
|
||||
<div class="describeUpload">After these steps are completed, you can work with your document.</div>
|
||||
<span id="step1" class="step">1. Loading the file.</span>
|
||||
<span class="step-descr">The loading speed depends on file size and additional elements it contains.</span>
|
||||
<div id="select-file-type" class="invisible">
|
||||
<br />
|
||||
<span class="step">Please select the current document type</span>
|
||||
<div class="buttonsMobile indent">
|
||||
<div class="button file-type document" data="docx">Document</div>
|
||||
<div class="button file-type spreadsheet" data="xlsx">Spreadsheet</div>
|
||||
<div class="button file-type presentation" data="pptx">Presentation</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<span id="step2" class="step">2. Conversion.</span>
|
||||
<span class="step-descr">The file is converted to OOXML so that you can edit it.</span>
|
||||
@ -364,12 +380,8 @@
|
||||
<script language="javascript" type="text/javascript" src="script/jquery.iframe-transport.js"></script>
|
||||
<script language="javascript" type="text/javascript" src="script/jquery.fileupload.js"></script>
|
||||
<script language="javascript" type="text/javascript" src="script/jquery.dropdownToggle.js"></script>
|
||||
<script language="javascript" type="text/javascript" src="script/formats.js"></script>
|
||||
<script language="javascript" type="text/javascript" src="script/jscript.js"></script>
|
||||
<script language="javascript" type="text/javascript">
|
||||
var FillFormExtList = '<%= string.Join(",", FillFormsExts.ToArray()) %>';
|
||||
var ConverExtList = '<%= string.Join(",", ConvertExts.ToArray()) %>';
|
||||
var EditedExtList = '<%= string.Join(",", EditedExts.ToArray()) %>';
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -437,7 +437,14 @@ namespace OnlineEditorsExample
|
||||
var lang = context.Request.Cookies.GetOrDefault("ulang", null);
|
||||
|
||||
var extension = (Path.GetExtension(_fileName).ToLower() ?? "").Trim('.');
|
||||
var internalExtension = "ooxml";
|
||||
string conversionExtension = "ooxml"; // set the default conversion extension as ooxml
|
||||
object fileExt;
|
||||
|
||||
// change the conversion extension if it was provided in the request body
|
||||
if (body.TryGetValue("fileExt", out fileExt) && !String.IsNullOrEmpty(fileExt.ToString()))
|
||||
{
|
||||
conversionExtension = fileExt.ToString();
|
||||
}
|
||||
|
||||
// check if the file with such an extension can be converted
|
||||
if (ConvertExts.Contains("." + extension))
|
||||
@ -454,7 +461,7 @@ namespace OnlineEditorsExample
|
||||
|
||||
// get the url and file type of the converted file
|
||||
Dictionary<string, string> newFileData;
|
||||
var result = ServiceConverter.GetConvertedData(fileUrl.ToString() , extension, internalExtension, key, true, out newFileData, filePass, lang);
|
||||
var result = ServiceConverter.GetConvertedData(fileUrl.ToString() , extension, conversionExtension, key, true, out newFileData, filePass, lang);
|
||||
if (result != 100)
|
||||
{
|
||||
return "{ \"step\" : \"" + result + "\", \"filename\" : \"" + _fileName + "\"}";
|
||||
|
||||
@ -225,7 +225,7 @@ namespace OnlineEditorsExample
|
||||
{ "download", !user.deniedPermissions.Contains("download") },
|
||||
{ "edit", canEdit && (editorsMode == "edit" || editorsMode =="view" || editorsMode == "filter" || editorsMode == "blockcontent") },
|
||||
{ "print", !user.deniedPermissions.Contains("print") },
|
||||
{ "fillForms", editorsMode != "view" && editorsMode != "comment" && editorsMode != "embedded" && editorsMode != "blockcontent" },
|
||||
{ "fillForms", editorsMode != "view" && editorsMode != "comment" && editorsMode != "blockcontent" },
|
||||
{ "modifyFilter", editorsMode != "filter" },
|
||||
{ "modifyContentControl", editorsMode != "blockcontent" },
|
||||
{ "review", canEdit && (editorsMode == "edit" || editorsMode == "review") },
|
||||
|
||||
@ -231,6 +231,10 @@ namespace ASC.Api.DocumentConverter
|
||||
|
||||
switch (errorCode)
|
||||
{
|
||||
case -9:
|
||||
// public const int c_nErrorConversionOutputFormatError = -9;
|
||||
errorMessage = String.Format(errorMessageTemplate, "Error conversion output format");
|
||||
break;
|
||||
case -8:
|
||||
// public const int c_nErrorFileVKey = -8;
|
||||
errorMessage = String.Format(errorMessageTemplate, "Error document VKey");
|
||||
|
||||
@ -87,6 +87,9 @@ namespace OnlineEditorsExample
|
||||
case "reference":
|
||||
Reference(context);
|
||||
break;
|
||||
case "formats":
|
||||
Formats(context);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -222,12 +225,22 @@ namespace OnlineEditorsExample
|
||||
context.Response.ContentType = "text/plain";
|
||||
try
|
||||
{
|
||||
var fileName = Path.GetFileName(context.Request["fileName"]);
|
||||
var path = _Default.StoragePath(fileName, HttpUtility.UrlEncode(_Default.CurUserHostAddress(HttpContext.Current.Request.UserHostAddress)));
|
||||
var histDir = _Default.HistoryDir(path);
|
||||
string fileName = context.Request["fileName"];
|
||||
string userAddress = HttpUtility.UrlEncode(_Default.CurUserHostAddress(HttpContext.Current.Request.UserHostAddress));
|
||||
|
||||
if (File.Exists(path)) File.Delete(path); // delete file
|
||||
if (Directory.Exists(histDir)) Directory.Delete(histDir, true); // delete file history
|
||||
if (!String.IsNullOrEmpty(fileName))
|
||||
{
|
||||
fileName = Path.GetFileName(fileName);
|
||||
var path = _Default.StoragePath(fileName, userAddress);
|
||||
var histDir = _Default.HistoryDir(path);
|
||||
|
||||
if (File.Exists(path)) File.Delete(path); // delete file
|
||||
if (Directory.Exists(histDir)) Directory.Delete(histDir, true); // delete file history
|
||||
} else
|
||||
{
|
||||
string userDir = _Default.StoragePath("", userAddress);
|
||||
if (Directory.Exists(userDir)) Directory.Delete(userDir, true); // delete the user's directory
|
||||
}
|
||||
|
||||
context.Response.Write("{ \"success\": true }");
|
||||
}
|
||||
@ -778,5 +791,25 @@ namespace OnlineEditorsExample
|
||||
+ userAddress;
|
||||
return fileUrl.ToString();
|
||||
}
|
||||
|
||||
// return all the supported formats
|
||||
private static void Formats(HttpContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
Dictionary<string, object> data = new Dictionary<string, object>
|
||||
{
|
||||
{ "formats", FormatManager.All() }
|
||||
};
|
||||
context.Response.ContentType = "application/json";
|
||||
var jss = new JavaScriptSerializer();
|
||||
|
||||
context.Response.Write(jss.Serialize(data));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
context.Response.Write("{ \"error\": \"" + e.Message + "\"}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
66
web/documentserver-example/csharp/script/formats.js
Normal file
66
web/documentserver-example/csharp/script/formats.js
Normal file
@ -0,0 +1,66 @@
|
||||
/**
|
||||
*
|
||||
* (c) Copyright Ascensio System SIA 2024
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
class Format {
|
||||
constructor(name, type, actions, convert, mime) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.actions = actions;
|
||||
this.convert = convert;
|
||||
this.mime = mime;
|
||||
}
|
||||
|
||||
isAutoConvertible() {
|
||||
return this.actions.includes('auto-convert');
|
||||
}
|
||||
|
||||
isEditable() {
|
||||
return this.actions.includes('edit') || this.actions.includes('lossy-edit');
|
||||
}
|
||||
|
||||
isFillable() {
|
||||
return this.actions.includes('fill');
|
||||
}
|
||||
}
|
||||
|
||||
class FormatManager {
|
||||
formats = [];
|
||||
|
||||
constructor(formats) {
|
||||
if(Array.isArray(formats)) this.formats = formats;
|
||||
}
|
||||
|
||||
findByExtension(extension) {
|
||||
return this.formats.find(format => format.name == extension);
|
||||
}
|
||||
|
||||
isAutoConvertible(extension) {
|
||||
let format = this.findByExtension(extension);
|
||||
return format !== undefined && format.isAutoConvertible();
|
||||
}
|
||||
|
||||
isEditable(extension) {
|
||||
let format = this.findByExtension(extension);
|
||||
return format !== undefined && format.isEditable();
|
||||
}
|
||||
|
||||
isFillable(extension) {
|
||||
let format = this.findByExtension(extension);
|
||||
return format !== undefined && format.isFillable();
|
||||
}
|
||||
}
|
||||
@ -17,6 +17,27 @@
|
||||
*/
|
||||
|
||||
var directUrl;
|
||||
var formatManager;
|
||||
|
||||
window.onload = function () {
|
||||
fetch("webeditor.ashx?type=formats")
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
if (data.formats) {
|
||||
let formats = [];
|
||||
data.formats.forEach(format => {
|
||||
formats.push(new Format(
|
||||
format.Name,
|
||||
format.Type,
|
||||
format.Actions,
|
||||
format.Convert,
|
||||
format.Mime
|
||||
));
|
||||
});
|
||||
formatManager = new FormatManager(formats);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (typeof jQuery != "undefined") {
|
||||
jq = jQuery.noConflict();
|
||||
@ -87,7 +108,7 @@ if (typeof jQuery != "undefined") {
|
||||
});
|
||||
|
||||
var timer = null;
|
||||
var checkConvert = function (filePass) {
|
||||
var checkConvert = function (filePass, fileType) {
|
||||
filePass = filePass ? filePass : null;
|
||||
if (timer != null) {
|
||||
clearTimeout(timer);
|
||||
@ -103,7 +124,7 @@ if (typeof jQuery != "undefined") {
|
||||
var posExt = fileName.lastIndexOf('.');
|
||||
posExt = 0 <= posExt ? fileName.substring(posExt).trim().toLowerCase() : '';
|
||||
|
||||
if (ConverExtList.indexOf(posExt) == -1) {
|
||||
if (!formatManager.isAutoConvertible(posExt)) {
|
||||
jq("#step2").addClass("done").removeClass("current");
|
||||
loadScripts();
|
||||
return;
|
||||
@ -116,7 +137,7 @@ if (typeof jQuery != "undefined") {
|
||||
contentType: "text/xml",
|
||||
type: "post",
|
||||
dataType: "json",
|
||||
data: JSON.stringify({ filename: fileName, filePass: filePass }),
|
||||
data: JSON.stringify({ filename: fileName, filePass: filePass, fileExt: fileType }),
|
||||
url: requestAddress,
|
||||
complete: function (data) {
|
||||
var responseText = data.responseText;
|
||||
@ -132,6 +153,12 @@ if (typeof jQuery != "undefined") {
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
if (response.error.includes("Error conversion output format")) {
|
||||
jq("#select-file-type").removeClass("invisible");
|
||||
jq("#step2").removeClass("current");
|
||||
jq("#hiddenFileName").attr("placeholder", filePass);
|
||||
return;
|
||||
}
|
||||
jq(".current").removeClass("current");
|
||||
jq(".step:not(.done)").addClass("error");
|
||||
jq("#mainProgress .error-message").show().find("span").text(response.error);
|
||||
@ -143,7 +170,7 @@ if (typeof jQuery != "undefined") {
|
||||
jq("#hiddenFileName").val(response.filename);
|
||||
|
||||
if (response.step && response.step < 100) {
|
||||
checkConvert(filePass);
|
||||
checkConvert(filePass, fileType);
|
||||
} else {
|
||||
jq("#step2").addClass("done").removeClass("current");
|
||||
loadScripts();
|
||||
@ -178,10 +205,10 @@ if (typeof jQuery != "undefined") {
|
||||
jq("#beginView, #beginEmbedded").removeClass("disable");
|
||||
|
||||
var fileName = jq("#hiddenFileName").val();
|
||||
var posExt = fileName.lastIndexOf('.');
|
||||
var posExt = fileName.lastIndexOf('.') + 1;
|
||||
posExt = 0 <= posExt ? fileName.substring(posExt).trim().toLowerCase() : '';
|
||||
|
||||
if (EditedExtList.indexOf(posExt) != -1 || FillFormExtList.indexOf(posExt) != -1) {
|
||||
if (formatManager.isEditable(posExt) || formatManager.isFillable(posExt)) {
|
||||
jq("#beginEdit").removeClass("disable");
|
||||
}
|
||||
};
|
||||
@ -213,6 +240,15 @@ if (typeof jQuery != "undefined") {
|
||||
});
|
||||
};
|
||||
|
||||
jq(document).on("click", ".file-type:not(.disable)", function () {
|
||||
const currentElement = jq(this);
|
||||
var fileType = currentElement.attr("data");
|
||||
var filePass = jq("#hiddenFileName").attr("placeholder");
|
||||
jq('.file-type').addClass(["disable", "pale"]);
|
||||
currentElement.removeClass("pale");
|
||||
checkConvert(filePass, fileType);
|
||||
});
|
||||
|
||||
jq(document).on("click", "#enterPass", function () {
|
||||
var filePass = jq("#filePass").val();
|
||||
if (filePass) {
|
||||
@ -293,6 +329,24 @@ if (typeof jQuery != "undefined") {
|
||||
});
|
||||
});
|
||||
|
||||
jq(document).on("click", ".clear-all", function () {
|
||||
if (confirm("Delete all the files?")) {
|
||||
var requestAddress = "webeditor.ashx"
|
||||
+ "?type=remove";
|
||||
|
||||
jq.ajax({
|
||||
async: true,
|
||||
contentType: "text/xml",
|
||||
url: requestAddress,
|
||||
complete: function (data) {
|
||||
if (JSON.parse(data.responseText).success) {
|
||||
window.location.reload(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function showUserTooltip (isMobile) {
|
||||
if ( jq("div#portal-info").is(":hidden") ) {
|
||||
jq("div#portal-info").show();
|
||||
|
||||
@ -248,15 +248,15 @@ public class FileController {
|
||||
// get document type (word, cell or slide)
|
||||
DocumentType type = fileUtility.getDocumentType(fileName);
|
||||
|
||||
// convert to .ooxml
|
||||
String internalFileExt = "ooxml";
|
||||
// get an auto-conversion extension from the request body or set it to the ooxml extension
|
||||
String conversionExtension = body.getFileExt() != null ? body.getFileExt() : "ooxml";
|
||||
|
||||
try {
|
||||
// check if the file with such an extension can be converted
|
||||
if (fileUtility.getConvertExts().contains(fileExt)) {
|
||||
String key = serviceConverter.generateRevisionId(fileUri); // generate document key
|
||||
ConvertedData response = serviceConverter // get the URL to the converted file
|
||||
.getConvertedData(fileUri, fileExt, internalFileExt, key, filePass, true, lang);
|
||||
.getConvertedData(fileUri, fileExt, conversionExtension, key, filePass, true, lang);
|
||||
|
||||
String newFileUri = response.getUri();
|
||||
String newFileType = "." + response.getFileType();
|
||||
@ -291,24 +291,33 @@ public class FileController {
|
||||
return createUserMetadata(uid, fileName);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
||||
// if the operation of file converting is unsuccessful, an error occurs
|
||||
return "{ \"error\": \"" + e.getMessage() + "\"}";
|
||||
}
|
||||
// if the operation of file converting is unsuccessful, an error occurs
|
||||
return "{ \"error\": \"" + "The file can't be converted.\"}";
|
||||
}
|
||||
|
||||
@PostMapping("/delete")
|
||||
@ResponseBody
|
||||
public String delete(@RequestBody final Converter body) { // delete a file
|
||||
try {
|
||||
String fullFileName = fileUtility.getFileName(body.getFileName()); // get full file name
|
||||
String filename = body.getFileName();
|
||||
boolean success = false;
|
||||
|
||||
// delete a file from the storage and return the status of this operation (true or false)
|
||||
boolean fileSuccess = storageMutator.deleteFile(fullFileName);
|
||||
if (filename != null) {
|
||||
String fullFileName = fileUtility.getFileName(filename); // get full file name
|
||||
|
||||
// delete file history and return the status of this operation (true or false)
|
||||
boolean historySuccess = storageMutator.deleteFileHistory(fullFileName);
|
||||
// delete a file from the storage and return the status of this operation (true or false)
|
||||
boolean fileSuccess = storageMutator.deleteFile(fullFileName);
|
||||
|
||||
return "{ \"success\": \"" + (fileSuccess && historySuccess) + "\"}";
|
||||
// delete file history and return the status of this operation (true or false)
|
||||
boolean historySuccess = storageMutator.deleteFileHistory(fullFileName);
|
||||
success = fileSuccess && historySuccess;
|
||||
} else {
|
||||
// delete the user's folder and return the boolean status
|
||||
success = storageMutator.deleteUserFolder();
|
||||
}
|
||||
return "{ \"success\": \"" + (success) + "\"}";
|
||||
} catch (Exception e) {
|
||||
// if the operation of file deleting is unsuccessful, an error occurs
|
||||
return "{ \"error\": \"" + e.getMessage() + "\"}";
|
||||
|
||||
@ -22,8 +22,10 @@ import com.onlyoffice.integration.documentserver.storage.FileStorageMutator;
|
||||
import com.onlyoffice.integration.documentserver.storage.FileStoragePathBuilder;
|
||||
import com.onlyoffice.integration.documentserver.util.Misc;
|
||||
import com.onlyoffice.integration.documentserver.util.file.FileUtility;
|
||||
import com.onlyoffice.integration.documentserver.util.service.FormatService;
|
||||
import com.onlyoffice.integration.entities.User;
|
||||
import com.onlyoffice.integration.services.UserServices;
|
||||
import com.onlyoffice.integration.dto.FormatsList;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Controller;
|
||||
@ -33,6 +35,7 @@ import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@ -61,6 +64,9 @@ public class IndexController {
|
||||
@Autowired
|
||||
private UserServices userService;
|
||||
|
||||
@Autowired
|
||||
private FormatService formatService;
|
||||
|
||||
@Value("${files.docservice.url.site}")
|
||||
private String docserviceSite;
|
||||
|
||||
@ -136,16 +142,16 @@ public class IndexController {
|
||||
@ResponseBody
|
||||
public HashMap<String, String> configParameters() { // get configuration parameters
|
||||
HashMap<String, String> configuration = new HashMap<>();
|
||||
|
||||
configuration.put("FillExtList", String.join(",", fileUtility
|
||||
.getFillExts())); // put a list of the extensions that can be filled to config
|
||||
configuration.put("ConverExtList", String.join(",", fileUtility
|
||||
.getConvertExts())); // put a list of the extensions that can be converted to config
|
||||
configuration.put("EditedExtList", String.join(",", fileUtility
|
||||
.getEditedExts())); // put a list of the extensions that can be edited to config
|
||||
configuration.put("UrlConverter", urlConverter);
|
||||
configuration.put("UrlEditor", urlEditor);
|
||||
|
||||
return configuration;
|
||||
}
|
||||
|
||||
@GetMapping("/formats")
|
||||
@ResponseBody
|
||||
public ResponseEntity<FormatsList> formats() { // return all the supported formats
|
||||
FormatsList list = new FormatsList(formatService.getFormats());
|
||||
return ResponseEntity.ok(list);
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,7 +27,8 @@ public enum ConvertErrorType {
|
||||
UNEXPECTED_GUID_ERROR(-5, "Error unexpected guid"),
|
||||
DATABASE_ERROR(-6, "Error database"),
|
||||
DOCUMENT_REQUEST_ERROR(-7, "Error document request"),
|
||||
DOCUMENT_VKEY_ERROR(-8, "Error document VKey");
|
||||
DOCUMENT_VKEY_ERROR(-8, "Error document VKey"),
|
||||
DOCUMENT_CONVERSION_OUTPUT_ERROR(-9, "Error conversion output format");
|
||||
|
||||
private final int code;
|
||||
private final String label;
|
||||
|
||||
@ -31,6 +31,7 @@ public interface FileStorageMutator {
|
||||
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
|
||||
boolean deleteUserFolder(); // delete the user's folder recursively
|
||||
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
|
||||
|
||||
@ -202,6 +202,11 @@ public class LocalFileStorage implements FileStorageMutator, FileStoragePathBuil
|
||||
return historyDeleted || historyWithoutExtDeleted;
|
||||
}
|
||||
|
||||
// delete the user's folder recursively
|
||||
public boolean deleteUserFolder() {
|
||||
return FileSystemUtils.deleteRecursively(new File(getStorageLocation()));
|
||||
}
|
||||
|
||||
// update a file
|
||||
public String updateFile(final String fileName, final byte[] bytes) {
|
||||
Path path = fileUtility
|
||||
|
||||
@ -31,6 +31,8 @@ public class Converter {
|
||||
private String fileName;
|
||||
@JsonProperty("filePass")
|
||||
private String filePass;
|
||||
@JsonProperty("fileExt")
|
||||
private String fileExt;
|
||||
@JsonProperty("lang")
|
||||
private String lang;
|
||||
}
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
/**
|
||||
*
|
||||
* (c) Copyright Ascensio System SIA 2024
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.onlyoffice.integration.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import com.onlyoffice.integration.documentserver.models.Format;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
public class FormatsList {
|
||||
private List<Format> formats;
|
||||
}
|
||||
@ -120,7 +120,6 @@ public class DefaultFileConfigurer implements FileConfigurer<DefaultFileWrapper>
|
||||
userPermissions.setFillForms(
|
||||
!action.equals(Action.view)
|
||||
&& !action.equals(Action.comment)
|
||||
&& !action.equals(Action.embedded)
|
||||
&& !action.equals(Action.blockcontent)
|
||||
);
|
||||
|
||||
|
||||
@ -489,6 +489,17 @@
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.buttonsMobile.indent {
|
||||
margin-bottom: 0;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
.button.file-type:hover,
|
||||
.button.file-type {
|
||||
height: 28px;
|
||||
width: 100px;
|
||||
margin-bottom: 10px !important;
|
||||
font-size: 9px;
|
||||
}
|
||||
.button.gray{
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@ -230,6 +230,33 @@ label .checkbox {
|
||||
color: #FF6F3D;
|
||||
}
|
||||
|
||||
.button.file-type {
|
||||
font-size: 11px;
|
||||
color: #FFFFFF;
|
||||
padding: 8px 8px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.button.file-type.disable {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.button.file-type.pale {
|
||||
opacity: 30%;
|
||||
}
|
||||
|
||||
.button.file-type.document {
|
||||
background: #446995;
|
||||
}
|
||||
|
||||
.button.file-type.spreadsheet {
|
||||
background: #40865C;
|
||||
}
|
||||
|
||||
.button.file-type.presentation {
|
||||
background: #AA5252;
|
||||
}
|
||||
|
||||
.upload-panel {
|
||||
float: left;
|
||||
padding: 24px 0;
|
||||
@ -595,6 +622,29 @@ footer table tr td:first-child {
|
||||
width: 4%;
|
||||
}
|
||||
|
||||
.storedHeader {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.storedHeaderClearAll {
|
||||
padding-right: 52px;
|
||||
}
|
||||
|
||||
.clear-all {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
padding: 2px;
|
||||
outline: 1px solid #E5E5E5;
|
||||
text-align: center;
|
||||
cursor:pointer;
|
||||
text-transform: uppercase;
|
||||
background-color: #F5F5F5;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.select-user {
|
||||
color: #444444;
|
||||
font-family: Open Sans;
|
||||
@ -752,6 +802,16 @@ html {
|
||||
margin-left: 25px;
|
||||
}
|
||||
|
||||
.buttonsMobile.indent{
|
||||
padding-left: 35px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.invisible {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
background: #FFFFFF;
|
||||
border-radius: 5px;
|
||||
|
||||
@ -16,18 +16,12 @@
|
||||
*
|
||||
*/
|
||||
|
||||
var ConverExtList;
|
||||
var EditedExtList;
|
||||
var UrlConverter;
|
||||
var UrlEditor;
|
||||
var FillExtList;
|
||||
|
||||
if (typeof jQuery !== "undefined") {
|
||||
jQuery.post('/config',
|
||||
function(data) {
|
||||
FillExtList = data.FillExtList.split(',');
|
||||
ConverExtList = data.ConverExtList.split(',');
|
||||
EditedExtList = data.EditedExtList.split(',');
|
||||
UrlConverter = data.UrlConverter;
|
||||
UrlEditor = data.UrlEditor;
|
||||
});
|
||||
|
||||
@ -0,0 +1,66 @@
|
||||
/**
|
||||
*
|
||||
* (c) Copyright Ascensio System SIA 2024
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
class Format {
|
||||
constructor(name, type, actions, convert, mime) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.actions = actions;
|
||||
this.convert = convert;
|
||||
this.mime = mime;
|
||||
}
|
||||
|
||||
isAutoConvertible() {
|
||||
return this.actions.includes('auto-convert');
|
||||
}
|
||||
|
||||
isEditable() {
|
||||
return this.actions.includes('edit') || this.actions.includes('lossy-edit');
|
||||
}
|
||||
|
||||
isFillable() {
|
||||
return this.actions.includes('fill');
|
||||
}
|
||||
}
|
||||
|
||||
class FormatManager {
|
||||
formats = [];
|
||||
|
||||
constructor(formats) {
|
||||
if(Array.isArray(formats)) this.formats = formats;
|
||||
}
|
||||
|
||||
findByExtension(extension) {
|
||||
return this.formats.find(format => format.name == extension);
|
||||
}
|
||||
|
||||
isAutoConvertible(extension) {
|
||||
let format = this.findByExtension(extension);
|
||||
return format !== undefined && format.isAutoConvertible();
|
||||
}
|
||||
|
||||
isEditable(extension) {
|
||||
let format = this.findByExtension(extension);
|
||||
return format !== undefined && format.isEditable();
|
||||
}
|
||||
|
||||
isFillable(extension) {
|
||||
let format = this.findByExtension(extension);
|
||||
return format !== undefined && format.isFillable();
|
||||
}
|
||||
}
|
||||
@ -17,6 +17,27 @@
|
||||
*/
|
||||
|
||||
var directUrl;
|
||||
var formatManager;
|
||||
|
||||
window.onload = function () {
|
||||
fetch('/formats')
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
if (data.formats) {
|
||||
let formats = [];
|
||||
data.formats.forEach(format => {
|
||||
formats.push(new Format(
|
||||
format.name,
|
||||
format.type,
|
||||
format.actions,
|
||||
format.convert,
|
||||
format.mime
|
||||
));
|
||||
});
|
||||
formatManager = new FormatManager(formats);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (typeof jQuery !== "undefined") {
|
||||
jq = jQuery.noConflict();
|
||||
@ -87,7 +108,7 @@ if (typeof jQuery !== "undefined") {
|
||||
});
|
||||
|
||||
var timer = null;
|
||||
var checkConvert = function (filePass) {
|
||||
var checkConvert = function (filePass, fileType) {
|
||||
filePass = filePass ? filePass : null;
|
||||
if (timer !== null) {
|
||||
clearTimeout(timer);
|
||||
@ -103,7 +124,7 @@ if (typeof jQuery !== "undefined") {
|
||||
var posExt = fileName.lastIndexOf(".") + 1;
|
||||
posExt = 0 <= posExt ? fileName.substring(posExt).trim().toLowerCase() : "";
|
||||
|
||||
if (ConverExtList.includes(posExt) === -1) {
|
||||
if (!formatManager.isAutoConvertible(posExt)) {
|
||||
jq("#step2").addClass("done").removeClass("current");
|
||||
loadScripts();
|
||||
return;
|
||||
@ -115,7 +136,7 @@ if (typeof jQuery !== "undefined") {
|
||||
contentType: "application/json",
|
||||
type: "post",
|
||||
dataType: "json",
|
||||
data: JSON.stringify({filename: fileName, filePass: filePass}),
|
||||
data: JSON.stringify({filename: fileName, filePass: filePass, fileExt: fileType}),
|
||||
url: UrlConverter,
|
||||
complete: function (data) {
|
||||
var responseText = data.responseText;
|
||||
@ -131,6 +152,12 @@ if (typeof jQuery !== "undefined") {
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
if (response.error.includes("Error conversion output format")){
|
||||
jq("#select-file-type").removeClass("invisible");
|
||||
jq("#step2").removeClass("current");
|
||||
jq("#hiddenFileName").attr("placeholder",filePass);
|
||||
return;
|
||||
}
|
||||
jq(".current").removeClass("current");
|
||||
jq(".step:not(.done)").addClass("error");
|
||||
jq("#mainProgress .error-message").show().find("span").text(response.error);
|
||||
@ -142,7 +169,7 @@ if (typeof jQuery !== "undefined") {
|
||||
jq("#hiddenFileName").val(response.filename);
|
||||
|
||||
if (response.step && response.step < 100) {
|
||||
checkConvert(filePass);
|
||||
checkConvert(filePass, fileType);
|
||||
} else {
|
||||
jq("#step2").addClass("done").removeClass("current");
|
||||
loadScripts();
|
||||
@ -180,7 +207,7 @@ if (typeof jQuery !== "undefined") {
|
||||
var posExt = fileName.lastIndexOf(".") + 1;
|
||||
posExt = 0 <= posExt ? fileName.substring(posExt).trim().toLowerCase() : "";
|
||||
|
||||
if (EditedExtList.includes(posExt) !== -1 || FillExtList.includes(posExt) !== -1) {
|
||||
if (formatManager.isEditable(posExt) || formatManager.isFillable(posExt)) {
|
||||
jq("#beginEdit").removeClass("disable");
|
||||
}
|
||||
};
|
||||
@ -218,6 +245,15 @@ if (typeof jQuery !== "undefined") {
|
||||
}
|
||||
};
|
||||
|
||||
jq(document).on("click", ".file-type:not(.disable)", function () {
|
||||
const currentElement = jq(this);
|
||||
var fileType = currentElement.attr("data");
|
||||
var filePass = jq("#hiddenFileName").attr("placeholder");
|
||||
jq('.file-type').addClass(["disable", "pale"]);
|
||||
currentElement.removeClass("pale");
|
||||
checkConvert(filePass, fileType);
|
||||
});
|
||||
|
||||
jq(document).on("click", "#enterPass", function () {
|
||||
var pass = jq("#filePass").val();
|
||||
if (pass) {
|
||||
@ -299,6 +335,24 @@ if (typeof jQuery !== "undefined") {
|
||||
});
|
||||
});
|
||||
|
||||
jq(document).on("click", ".clear-all", function () {
|
||||
if (confirm("Delete all the files?")) {
|
||||
jq.ajax({
|
||||
async: true,
|
||||
contentType: "application/json",
|
||||
type: "post",
|
||||
dataType: "json",
|
||||
data: JSON.stringify({filename: null, filePass: null}),
|
||||
url: "/delete",
|
||||
complete: function (data) {
|
||||
if (JSON.parse(data.responseText).success) {
|
||||
window.location.reload(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function showUserTooltip (isMobile) {
|
||||
if ( jq("div#portal-info").is(":hidden") ) {
|
||||
jq("div#portal-info").show();
|
||||
|
||||
@ -123,7 +123,14 @@
|
||||
</div>
|
||||
<th:block th:if="${not #lists.isEmpty(files)}">
|
||||
<div class="stored-list">
|
||||
<span class="header-list">Your documents</span>
|
||||
<div class="storedHeader">
|
||||
<div class="storedHeaderText">
|
||||
<span class="header-list">Your documents</span>
|
||||
</div>
|
||||
<div class="storedHeaderClearAll">
|
||||
<div class="clear-all">Clear all</div>
|
||||
</div>
|
||||
</div>
|
||||
<table class="tableHeader" cellspacing="0" cellpadding="0" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
@ -275,6 +282,15 @@
|
||||
<div class="describeUpload">After these steps are completed, you can work with your document.</div>
|
||||
<span id="step1" class="step">1. Loading the file.</span>
|
||||
<span class="step-descr">The loading speed depends on file size and additional elements it contains.</span>
|
||||
<div id="select-file-type" class="invisible">
|
||||
<br />
|
||||
<span class="step">Please select the current document type</span>
|
||||
<div class="buttonsMobile indent">
|
||||
<div class="button file-type document" data="docx">Document</div>
|
||||
<div class="button file-type spreadsheet" data="xlsx">Spreadsheet</div>
|
||||
<div class="button file-type presentation" data="pptx">Presentation</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<span id="step2" class="step">2. Conversion.</span>
|
||||
<span class="step-descr">The file is converted to OOXML so that you can edit it.</span>
|
||||
@ -341,6 +357,7 @@
|
||||
<script type="text/javascript" src="scripts/jquery.iframe-transport.js"></script>
|
||||
<script type="text/javascript" src="scripts/jquery.fileupload.js"></script>
|
||||
<script type="text/javascript" src="scripts/jquery.dropdownToggle.js"></script>
|
||||
<script type="text/javascript" src="scripts/formats.js"></script>
|
||||
<script type="text/javascript" src="scripts/jscript.js"></script>
|
||||
<script type="text/javascript" src="scripts/converter.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
@ -29,6 +29,7 @@ import helpers.FileUtility;
|
||||
import helpers.ServiceConverter;
|
||||
import helpers.TrackManager;
|
||||
import helpers.Users;
|
||||
import format.FormatManager;
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
@ -139,6 +140,9 @@ public class IndexServlet extends HttpServlet {
|
||||
case "historydata":
|
||||
historyData(request, response, writer);
|
||||
break;
|
||||
case "formats":
|
||||
formats(request, response, writer);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -283,7 +287,8 @@ public class IndexServlet extends HttpServlet {
|
||||
String fileUri = DocumentManager.getDownloadUrl(fileName, true);
|
||||
String fileExt = FileUtility.getFileExtension(fileName);
|
||||
FileType fileType = FileUtility.getFileType(fileName);
|
||||
String internalFileExt = "ooxml";
|
||||
// get an auto-conversion extension from the request body or set it to the ooxml extension
|
||||
String conversionExtension = body.get("fileExt") != null ? (String) body.get("fileExt") : "ooxml";
|
||||
|
||||
// check if the file with such an extension can be converted
|
||||
if (DocumentManager.getConvertExts().contains(fileExt)) {
|
||||
@ -292,7 +297,7 @@ public class IndexServlet extends HttpServlet {
|
||||
|
||||
// get the url and file type to the converted file
|
||||
Map<String, String> newFileData = ServiceConverter
|
||||
.getConvertedData(fileUri, fileExt, internalFileExt, key, filePass, true, lang);
|
||||
.getConvertedData(fileUri, fileExt, conversionExtension, key, filePass, true, lang);
|
||||
String newFileUri = newFileData.get("fileUrl");
|
||||
String newFileType = "." + newFileData.get("fileType");
|
||||
|
||||
@ -416,17 +421,23 @@ public class IndexServlet extends HttpServlet {
|
||||
final HttpServletResponse response,
|
||||
final PrintWriter writer) {
|
||||
try {
|
||||
String fileName = FileUtility.getFileName(request.getParameter("filename"));
|
||||
String path = DocumentManager.storagePath(fileName, null);
|
||||
String fileName = request.getParameter("filename");
|
||||
if (fileName != null && !fileName.isEmpty()) {
|
||||
fileName = FileUtility.getFileName(fileName);
|
||||
String path = DocumentManager.storagePath(fileName, null);
|
||||
|
||||
// delete file
|
||||
File f = new File(path);
|
||||
delete(f);
|
||||
|
||||
// delete file history
|
||||
File hist = new File(DocumentManager.historyDir(path));
|
||||
delete(hist);
|
||||
// delete file
|
||||
File f = new File(path);
|
||||
delete(f);
|
||||
|
||||
// delete file history
|
||||
File hist = new File(DocumentManager.historyDir(path));
|
||||
delete(hist);
|
||||
} else {
|
||||
// delete the user's folder and all the containing files
|
||||
File userFolder = new File(DocumentManager.storagePath(null, null));
|
||||
delete(userFolder);
|
||||
}
|
||||
writer.write("{ \"success\": true }");
|
||||
} catch (Exception e) {
|
||||
writer.write("{ \"error\": \"" + e.getMessage() + "\"}");
|
||||
@ -1039,6 +1050,16 @@ public class IndexServlet extends HttpServlet {
|
||||
writer.write("{}");
|
||||
}
|
||||
|
||||
private static void formats(final HttpServletRequest request,
|
||||
final HttpServletResponse response,
|
||||
final PrintWriter writer) {
|
||||
Map<String, Object> data = new HashMap<String, Object>();
|
||||
data.put("formats", (new FormatManager()).getFormats());
|
||||
response.setContentType("application/json");
|
||||
Gson gson = new Gson();
|
||||
writer.write(gson.toJson(data));
|
||||
}
|
||||
|
||||
// process get request
|
||||
@Override
|
||||
protected void doGet(final HttpServletRequest request,
|
||||
|
||||
@ -297,7 +297,7 @@ public class FileModel {
|
||||
edit = canEdit && (modeParam.equals("edit") || modeParam.equals("view") || modeParam.equals("filter")
|
||||
|| modeParam.equals("blockcontent"));
|
||||
print = !user.getDeniedPermissions().contains("print");
|
||||
fillForms = !modeParam.equals("view") && !modeParam.equals("comment") && !modeParam.equals("embedded")
|
||||
fillForms = !modeParam.equals("view") && !modeParam.equals("comment")
|
||||
&& !modeParam.equals("blockcontent");
|
||||
modifyFilter = !modeParam.equals("filter");
|
||||
modifyContentControl = !modeParam.equals("blockcontent");
|
||||
|
||||
@ -27,7 +27,8 @@ public enum ConvertErrorType {
|
||||
UNEXPECTED_GUID_ERROR(-5, "Error unexpected guid"),
|
||||
DATABASE_ERROR(-6, "Error database"),
|
||||
DOCUMENT_REQUEST_ERROR(-7, "Error document request"),
|
||||
DOCUMENT_VKEY_ERROR(-8, "Error document VKey");
|
||||
DOCUMENT_VKEY_ERROR(-8, "Error document VKey"),
|
||||
CONVERSION_OUTPUT_FORMAT_ERROR(-9, "Error conversion output format");
|
||||
|
||||
private final int code;
|
||||
private final String label;
|
||||
|
||||
@ -488,6 +488,17 @@
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.buttonsMobile.indent {
|
||||
margin-bottom: 0;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
.button.file-type:hover,
|
||||
.button.file-type {
|
||||
height: 28px;
|
||||
width: 100px;
|
||||
margin-bottom: 10px !important;
|
||||
font-size: 9px;
|
||||
}
|
||||
.button.gray{
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@ -230,6 +230,33 @@ label .checkbox {
|
||||
color: #FF6F3D;
|
||||
}
|
||||
|
||||
.button.file-type {
|
||||
font-size: 11px;
|
||||
color: #FFFFFF;
|
||||
padding: 8px 8px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.button.file-type.disable {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.button.file-type.pale {
|
||||
opacity: 30%;
|
||||
}
|
||||
|
||||
.button.file-type.document {
|
||||
background: #446995;
|
||||
}
|
||||
|
||||
.button.file-type.spreadsheet {
|
||||
background: #40865C;
|
||||
}
|
||||
|
||||
.button.file-type.presentation {
|
||||
background: #AA5252;
|
||||
}
|
||||
|
||||
.upload-panel {
|
||||
float: left;
|
||||
padding: 24px 0;
|
||||
@ -595,6 +622,29 @@ footer table tr td:first-child {
|
||||
width: 4%;
|
||||
}
|
||||
|
||||
.storedHeader {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.storedHeaderClearAll {
|
||||
padding-right: 52px;
|
||||
}
|
||||
|
||||
.clear-all {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
padding: 2px;
|
||||
outline: 1px solid #E5E5E5;
|
||||
text-align: center;
|
||||
cursor:pointer;
|
||||
text-transform: uppercase;
|
||||
background-color: #F5F5F5;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.select-user {
|
||||
color: #444444;
|
||||
font-family: Open Sans;
|
||||
@ -748,6 +798,16 @@ html {
|
||||
margin-left: 25px;
|
||||
}
|
||||
|
||||
.buttonsMobile.indent{
|
||||
padding-left: 35px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.invisible {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
background: #FFFFFF;
|
||||
border-radius: 5px;
|
||||
|
||||
@ -146,7 +146,14 @@
|
||||
</div>
|
||||
<% if (files.length > 0) { %>
|
||||
<div class="stored-list">
|
||||
<span class="header-list">Your documents</span>
|
||||
<div class="storedHeader">
|
||||
<div class="storedHeaderText">
|
||||
<span class="header-list">Your documents</span>
|
||||
</div>
|
||||
<div class="storedHeaderClearAll">
|
||||
<div class="clear-all">Clear all</div>
|
||||
</div>
|
||||
</div>
|
||||
<table class="tableHeader" cellspacing="0" cellpadding="0" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
@ -299,6 +306,15 @@
|
||||
<div class="describeUpload">After these steps are completed, you can work with your document.</div>
|
||||
<span id="step1" class="step">1. Loading the file.</span>
|
||||
<span class="step-descr">The loading speed depends on file size and additional elements it contains.</span>
|
||||
<div id="select-file-type" class="invisible">
|
||||
<br />
|
||||
<span class="step">Please select the current document type</span>
|
||||
<div class="buttonsMobile indent">
|
||||
<div class="button file-type document" data="docx">Document</div>
|
||||
<div class="button file-type spreadsheet" data="xlsx">Spreadsheet</div>
|
||||
<div class="button file-type presentation" data="pptx">Presentation</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<span id="step2" class="step">2. Conversion.</span>
|
||||
<span class="step-descr">The file is converted to OOXML so that you can edit it.</span>
|
||||
@ -365,12 +381,10 @@
|
||||
<script type="text/javascript" src="scripts/jquery.iframe-transport.js"></script>
|
||||
<script type="text/javascript" src="scripts/jquery.fileupload.js"></script>
|
||||
<script type="text/javascript" src="scripts/jquery.dropdownToggle.js"></script>
|
||||
<script type="text/javascript" src="scripts/formats.js"></script>
|
||||
<script type="text/javascript" src="scripts/jscript.js"></script>
|
||||
|
||||
<script language="javascript" type="text/javascript">
|
||||
var FillExtList = "<%= String.join(",", DocumentManager.getFillExts()) %>".split(",");
|
||||
var ConverExtList = "<%= String.join(",", DocumentManager.getConvertExts()) %>".split(",");
|
||||
var EditedExtList = "<%= String.join(",", DocumentManager.getEditedExts()) %>".split(",");
|
||||
var UrlConverter = "IndexServlet?type=convert";
|
||||
var UrlEditor = "EditorServlet";
|
||||
|
||||
|
||||
@ -0,0 +1,66 @@
|
||||
/**
|
||||
*
|
||||
* (c) Copyright Ascensio System SIA 2024
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
class Format {
|
||||
constructor(name, type, actions, convert, mime) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.actions = actions;
|
||||
this.convert = convert;
|
||||
this.mime = mime;
|
||||
}
|
||||
|
||||
isAutoConvertible() {
|
||||
return this.actions.includes('auto-convert');
|
||||
}
|
||||
|
||||
isEditable() {
|
||||
return this.actions.includes('edit') || this.actions.includes('lossy-edit');
|
||||
}
|
||||
|
||||
isFillable() {
|
||||
return this.actions.includes('fill');
|
||||
}
|
||||
}
|
||||
|
||||
class FormatManager {
|
||||
formats = [];
|
||||
|
||||
constructor(formats) {
|
||||
if(Array.isArray(formats)) this.formats = formats;
|
||||
}
|
||||
|
||||
findByExtension(extension) {
|
||||
return this.formats.find(format => format.name == extension);
|
||||
}
|
||||
|
||||
isAutoConvertible(extension) {
|
||||
let format = this.findByExtension(extension);
|
||||
return format !== undefined && format.isAutoConvertible();
|
||||
}
|
||||
|
||||
isEditable(extension) {
|
||||
let format = this.findByExtension(extension);
|
||||
return format !== undefined && format.isEditable();
|
||||
}
|
||||
|
||||
isFillable(extension) {
|
||||
let format = this.findByExtension(extension);
|
||||
return format !== undefined && format.isFillable();
|
||||
}
|
||||
}
|
||||
@ -17,6 +17,27 @@
|
||||
*/
|
||||
|
||||
var directUrl;
|
||||
var formatManager;
|
||||
|
||||
window.onload = function () {
|
||||
fetch('IndexServlet?type=formats')
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
if (data.formats) {
|
||||
let formats = [];
|
||||
data.formats.forEach(format => {
|
||||
formats.push(new Format(
|
||||
format.name,
|
||||
format.type,
|
||||
format.actions,
|
||||
format.convert,
|
||||
format.mime
|
||||
));
|
||||
});
|
||||
formatManager = new FormatManager(formats);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (typeof jQuery !== "undefined") {
|
||||
jq = jQuery.noConflict();
|
||||
@ -87,7 +108,7 @@ if (typeof jQuery !== "undefined") {
|
||||
});
|
||||
|
||||
var timer = null;
|
||||
var checkConvert = function (filePass) {
|
||||
var checkConvert = function (filePass, fileType) {
|
||||
filePass = filePass ? filePass : null;
|
||||
if (timer !== null) {
|
||||
clearTimeout(timer);
|
||||
@ -103,7 +124,7 @@ if (typeof jQuery !== "undefined") {
|
||||
var posExt = fileName.lastIndexOf(".") + 1;
|
||||
posExt = 0 <= posExt ? fileName.substring(posExt).trim().toLowerCase() : "";
|
||||
|
||||
if (!ConverExtList.includes(posExt)) {
|
||||
if (!formatManager.isAutoConvertible(posExt)) {
|
||||
jq("#step2").addClass("done").removeClass("current");
|
||||
loadScripts();
|
||||
return;
|
||||
@ -115,7 +136,7 @@ if (typeof jQuery !== "undefined") {
|
||||
contentType: "text/xml",
|
||||
type: "post",
|
||||
dataType: "json",
|
||||
data: JSON.stringify({filename: fileName, filePass: filePass}),
|
||||
data: JSON.stringify({filename: fileName, filePass: filePass, fileExt: fileType}),
|
||||
url: UrlConverter,
|
||||
complete: function (data) {
|
||||
var responseText = data.responseText;
|
||||
@ -131,6 +152,12 @@ if (typeof jQuery !== "undefined") {
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
if (response.error.includes("Error conversion output format")){
|
||||
jq("#select-file-type").removeClass("invisible");
|
||||
jq("#step2").removeClass("current");
|
||||
jq("#hiddenFileName").attr("placeholder",filePass);
|
||||
return;
|
||||
}
|
||||
jq(".current").removeClass("current");
|
||||
jq(".step:not(.done)").addClass("error");
|
||||
jq("#mainProgress .error-message").show().find("span").text(response.error);
|
||||
@ -142,7 +169,7 @@ if (typeof jQuery !== "undefined") {
|
||||
jq("#hiddenFileName").val(response.filename);
|
||||
|
||||
if (response.step && response.step < 100) {
|
||||
checkConvert(filePass);
|
||||
checkConvert(filePass, fileType);
|
||||
} else {
|
||||
jq("#step2").addClass("done").removeClass("current");
|
||||
loadScripts();
|
||||
@ -180,7 +207,7 @@ if (typeof jQuery !== "undefined") {
|
||||
var posExt = fileName.lastIndexOf(".") + 1;
|
||||
posExt = 0 <= posExt ? fileName.substring(posExt).trim().toLowerCase() : "";
|
||||
|
||||
if (EditedExtList.includes(posExt) || FillExtList.includes(posExt)) {
|
||||
if (formatManager.isEditable(posExt) || formatManager.isFillable(posExt)) {
|
||||
jq("#beginEdit").removeClass("disable");
|
||||
}
|
||||
};
|
||||
@ -212,6 +239,15 @@ if (typeof jQuery !== "undefined") {
|
||||
});
|
||||
};
|
||||
|
||||
jq(document).on("click", ".file-type:not(.disable)", function () {
|
||||
const currentElement = jq(this);
|
||||
var fileType = currentElement.attr("data");
|
||||
var filePass = jq("#hiddenFileName").attr("placeholder");
|
||||
jq('.file-type').addClass(["disable", "pale"]);
|
||||
currentElement.removeClass("pale");
|
||||
checkConvert(filePass, fileType);
|
||||
});
|
||||
|
||||
jq(document).on("click", "#enterPass", function () {
|
||||
var pass = jq("#filePass").val();
|
||||
if (pass) {
|
||||
@ -290,6 +326,21 @@ if (typeof jQuery !== "undefined") {
|
||||
});
|
||||
});
|
||||
|
||||
jq(document).on("click", ".clear-all", function () {
|
||||
if (confirm("Delete all the files?")) {
|
||||
jq.ajax({
|
||||
async: true,
|
||||
contentType: "text/xml",
|
||||
url: "IndexServlet?type=remove",
|
||||
complete: function (data) {
|
||||
if (JSON.parse(data.responseText).success) {
|
||||
window.location.reload(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function showUserTooltip (isMobile) {
|
||||
if ( jq("div#portal-info").is(":hidden") ) {
|
||||
jq("div#portal-info").show();
|
||||
|
||||
@ -93,8 +93,6 @@ app.get('/', (req, res) => { // define a handler for default page
|
||||
|
||||
res.render('index', { // render index template with the parameters specified
|
||||
preloaderUrl: siteUrl + configServer.get('preloaderUrl'),
|
||||
convertExts: fileUtility.getConvertExtensions(),
|
||||
editedExts: fileUtility.getEditExtensions(),
|
||||
fillExts: fileUtility.getFillExtensions(),
|
||||
storedFiles: req.DocManager.getStoredFiles(),
|
||||
params: req.DocManager.getCustomParams(),
|
||||
@ -333,8 +331,8 @@ app.post('/convert', (req, res) => { // define a handler for converting files
|
||||
const fileUri = req.DocManager.getDownloadUrl(fileName, true);
|
||||
const fileExt = fileUtility.getFileExtension(fileName, true);
|
||||
const internalFileExt = 'ooxml';
|
||||
let convExt = req.body.fileExt ? req.body.fileExt : internalFileExt;
|
||||
if (req.body.forceConv) convExt = req.body.forceConv;
|
||||
const convExt = req.body.fileExt ? req.body.fileExt : internalFileExt;
|
||||
const { keepOriginal } = req.body;
|
||||
const response = res;
|
||||
|
||||
const writeResult = function writeResult(filename, step, error) {
|
||||
@ -389,14 +387,14 @@ app.post('/convert', (req, res) => { // define a handler for converting files
|
||||
return;
|
||||
}
|
||||
// remove file with the origin extension
|
||||
if (!('fileExt' in req.body)) fileSystem.unlinkSync(req.DocManager.storagePath(fileName));
|
||||
if (!keepOriginal) fileSystem.unlinkSync(req.DocManager.storagePath(fileName));
|
||||
|
||||
const userAddress = req.DocManager.curUserHostAddress();
|
||||
const historyPath = req.DocManager.historyPath(fileName, userAddress, true);
|
||||
// get the history path to the file with a new extension
|
||||
const correctHistoryPath = req.DocManager.historyPath(correctName, userAddress, true);
|
||||
|
||||
if (!('fileExt' in req.body)) {
|
||||
if (!keepOriginal) {
|
||||
fileSystem.renameSync(historyPath, correctHistoryPath); // change the previous history path
|
||||
|
||||
fileSystem.renameSync(
|
||||
@ -688,7 +686,7 @@ app.post('/track', async (req, res) => { // define a handler for tracking file c
|
||||
}
|
||||
} catch (ex) {
|
||||
console.log(ex);
|
||||
response.write('{"error":1}');
|
||||
response.write(`{"error":1,"message":${JSON.stringify(ex)}}`);
|
||||
response.end();
|
||||
return;
|
||||
}
|
||||
@ -700,7 +698,7 @@ app.post('/track', async (req, res) => { // define a handler for tracking file c
|
||||
// file saving process
|
||||
const processSave = async function processSave(downloadUri, body, fileName, userAddress) {
|
||||
if (!downloadUri) {
|
||||
response.write('{"error":1}');
|
||||
response.write('{"error":1,"message":"save uri is empty"}');
|
||||
response.end();
|
||||
return;
|
||||
}
|
||||
@ -811,7 +809,8 @@ app.post('/track', async (req, res) => { // define a handler for tracking file c
|
||||
}
|
||||
}
|
||||
} catch (ex) {
|
||||
response.write('{"error":1}');
|
||||
console.log(ex);
|
||||
response.write(`{"error":1,"message":${JSON.stringify(ex)}}`);
|
||||
response.end();
|
||||
return;
|
||||
}
|
||||
@ -823,7 +822,7 @@ app.post('/track', async (req, res) => { // define a handler for tracking file c
|
||||
// file force saving process
|
||||
const processForceSave = async function processForceSave(downloadUri, body, fileName, userAddress) {
|
||||
if (!downloadUri) {
|
||||
response.write('{"error":1}');
|
||||
response.write('{"error":1,"message":"forcesave uri is empty"}');
|
||||
response.end();
|
||||
return;
|
||||
}
|
||||
@ -915,7 +914,7 @@ app.post('/track', async (req, res) => { // define a handler for tracking file c
|
||||
}
|
||||
}
|
||||
if (!body) {
|
||||
res.write('{"error":1}');
|
||||
res.write('{"error":1,"message":"body is empty"}');
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
@ -1067,7 +1066,7 @@ app.get('/editor', (req, res) => { // define a handler for editing document
|
||||
chat: userid !== 'uid-0',
|
||||
coEditing: mode === 'view' && userid === 'uid-0' ? { mode: 'strict', change: false } : null,
|
||||
comment: mode !== 'view' && mode !== 'fillForms' && mode !== 'embedded' && mode !== 'blockcontent',
|
||||
fillForms: mode !== 'view' && mode !== 'comment' && mode !== 'embedded' && mode !== 'blockcontent',
|
||||
fillForms: mode !== 'view' && mode !== 'comment' && mode !== 'blockcontent',
|
||||
modifyFilter: mode !== 'filter',
|
||||
modifyContentControl: mode !== 'blockcontent',
|
||||
copy: !user.deniedPermissions.includes('copy'),
|
||||
@ -1200,7 +1199,9 @@ app.post('/historyObj', (req, res) => {
|
||||
app.get('/formats', (req, res) => {
|
||||
try {
|
||||
const formats = fileUtility.getFormats();
|
||||
res.json(formats);
|
||||
res.json({
|
||||
formats,
|
||||
});
|
||||
} catch (ex) {
|
||||
console.log(ex); // display error message in the console
|
||||
res.status(500); // write status parameter to the response
|
||||
|
||||
@ -187,13 +187,12 @@ const putFile = function putFile(wopi, req, res, userHost) {
|
||||
|
||||
const userAddress = req.DocManager.curUserHostAddress(userHost);
|
||||
const storagePath = req.DocManager.storagePath(wopi.id, userAddress);
|
||||
const fileSize = fileSystem.statSync(storagePath).size;
|
||||
|
||||
if (!lockManager.hasLock(storagePath)) {
|
||||
// ToDo: if body length is 0 bytes => handle document creation
|
||||
|
||||
if (!lockManager.hasLock(storagePath) && fileSize !== 0) {
|
||||
// file isn't locked => mismatch
|
||||
returnLockMismatch(res, '', 'File isn\'t locked');
|
||||
} else if (lockManager.getLock(storagePath) === requestLock) {
|
||||
} else if (lockManager.getLock(storagePath) === requestLock || fileSize === 0) {
|
||||
// lock matches current lock => put file
|
||||
saveFileFromBody(req, wopi.id, userAddress, true, (err, version) => {
|
||||
if (!err) {
|
||||
|
||||
@ -27,10 +27,15 @@ const siteUrl = configServer.get('siteUrl'); // the path to the editors installa
|
||||
|
||||
let cache = null;
|
||||
|
||||
const requestDiscovery = async function requestDiscovery() {
|
||||
const requestDiscovery = async function requestDiscovery(DocManager) {
|
||||
let absSiteUrl = siteUrl;
|
||||
if (absSiteUrl.indexOf('/') === 0) {
|
||||
absSiteUrl = DocManager.getServerHost() + siteUrl;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
return new Promise((resolve, reject) => {
|
||||
const uri = siteUrl + configServer.get('wopi.discovery');
|
||||
const uri = absSiteUrl + configServer.get('wopi.discovery');
|
||||
const actions = [];
|
||||
|
||||
// parse url to allow request by relative url after
|
||||
@ -81,13 +86,13 @@ const requestDiscovery = async function requestDiscovery() {
|
||||
};
|
||||
|
||||
// get the wopi discovery information
|
||||
const getDiscoveryInfo = async function getDiscoveryInfo() {
|
||||
const getDiscoveryInfo = async function getDiscoveryInfo(DocManager) {
|
||||
let actions = [];
|
||||
|
||||
if (cache) return cache;
|
||||
|
||||
try {
|
||||
actions = await requestDiscovery();
|
||||
actions = await requestDiscovery(DocManager);
|
||||
} catch (e) {
|
||||
return actions;
|
||||
}
|
||||
@ -102,8 +107,8 @@ const getDiscoveryInfo = async function getDiscoveryInfo() {
|
||||
};
|
||||
|
||||
// get actions of the specified extension
|
||||
const getActions = async function getActions(ext) {
|
||||
const actions = await getDiscoveryInfo(); // get the wopi discovery information
|
||||
const getActions = async function getActions(DocManager, ext) {
|
||||
const actions = await getDiscoveryInfo(DocManager); // get the wopi discovery information
|
||||
const filtered = [];
|
||||
|
||||
actions.forEach((action) => { // and filter it by the specified extention
|
||||
@ -116,8 +121,8 @@ const getActions = async function getActions(ext) {
|
||||
};
|
||||
|
||||
// get an action for the specified extension and name
|
||||
const getAction = async function getAction(ext, name) {
|
||||
const actions = await getDiscoveryInfo();
|
||||
const getAction = async function getAction(DocManager, ext, name) {
|
||||
const actions = await getDiscoveryInfo(DocManager);
|
||||
let act = null;
|
||||
|
||||
actions.forEach((action) => {
|
||||
@ -130,8 +135,8 @@ const getAction = async function getAction(ext, name) {
|
||||
};
|
||||
|
||||
// get the default action for the specified extension
|
||||
const getDefaultAction = async function getDefaultAction(ext) {
|
||||
const actions = await getDiscoveryInfo();
|
||||
const getDefaultAction = async function getDefaultAction(DocManager, ext) {
|
||||
const actions = await getDiscoveryInfo(DocManager);
|
||||
let act = null;
|
||||
|
||||
actions.forEach((action) => {
|
||||
|
||||
@ -46,7 +46,7 @@ exports.registerRoutes = function registerRoutes(app) {
|
||||
req.DocManager = new DocManager(req, res);
|
||||
|
||||
// get the wopi discovery information
|
||||
const actions = await utils.getDiscoveryInfo();
|
||||
const actions = await utils.getDiscoveryInfo(req.DocManager);
|
||||
const wopiEnable = actions.length !== 0;
|
||||
const docsExtEdit = []; // Supported extensions for WOPI
|
||||
|
||||
@ -65,11 +65,13 @@ exports.registerRoutes = function registerRoutes(app) {
|
||||
// run through all the files and write the corresponding information to each file
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const file of files) {
|
||||
const mobile = new RegExp(configServer.get('mobileRegEx'), 'i').test(req.get('User-Agent'));
|
||||
const ext = fileUtility.getFileExtension(file.name, true); // get an extension of each file
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
file.actions = await utils.getActions(ext); // get actions of the specified extension
|
||||
file.actions = await utils.getActions(req.DocManager, ext); // get actions of the specified extension
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
file.defaultAction = await utils.getDefaultAction(ext);// get the default action of the specified extension
|
||||
file.defaultAction = await utils.getDefaultAction(req.DocManager, ext);// get the default action for extension
|
||||
if (mobile) file.actions.forEach((act) => { if (act.name === 'mobileEdit') file.defaultAction = act; });
|
||||
}
|
||||
|
||||
// render wopiIndex template with the parameters specified
|
||||
@ -114,7 +116,7 @@ exports.registerRoutes = function registerRoutes(app) {
|
||||
const user = users.getUser(req.query.userid); // get a user by the id
|
||||
|
||||
// get an action for the specified extension and name
|
||||
const action = await utils.getAction(fileExt, req.query.action);
|
||||
const action = await utils.getAction(req.DocManager, fileExt, req.query.action);
|
||||
|
||||
if (action && req.query.action === 'editnew') {
|
||||
fileName = req.DocManager.requestEditnew(req, fileName, user);
|
||||
|
||||
4936
web/documentserver-example/nodejs/package-lock.json
generated
4936
web/documentserver-example/nodejs/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -17,17 +17,17 @@
|
||||
"url": "https://github.com/ONLYOFFICE/document-server-integration/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"body-parser": "^1.19.0",
|
||||
"config": "^3.3.2",
|
||||
"debug": "^4.2.0",
|
||||
"ejs": "^3.1.5",
|
||||
"express": "^4.17.1",
|
||||
"fast-xml-parser": "^4.3.1",
|
||||
"body-parser": "^1.20.2",
|
||||
"config": "^3.3.11",
|
||||
"debug": "^4.3.4",
|
||||
"ejs": "^3.1.9",
|
||||
"express": "^4.18.2",
|
||||
"fast-xml-parser": "^4.3.4",
|
||||
"formidable": "^1.2.2",
|
||||
"he": "^1.2.0",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"jwa": "^2.0.0",
|
||||
"log4js": "^6.3.0",
|
||||
"log4js": "^6.9.1",
|
||||
"mime": "^2.4.6",
|
||||
"serve-favicon": "^2.5.0",
|
||||
"urllib": "^2.36.1"
|
||||
@ -44,8 +44,8 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8.28.0",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-airbnb-base": "^15.0.0",
|
||||
"eslint-plugin-import": "^2.26.0"
|
||||
"eslint-plugin-import": "^2.29.1"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,3 @@
|
||||
<?xml version="1.0"?>
|
||||
<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
|
||||
|
||||
<g class="layer">
|
||||
<title>Layer 1</title>
|
||||
<path d="m9.05,22.33l0,-5.9l5.9,0l0,5.9l5.9,0l0,-10.33l2.95,0l-11.8,-10.33l-11.8,10.33l2.95,0l0,10.33l5.9,0z" fill="#FF6F3D" id="svg_1"/>
|
||||
</g>
|
||||
<svg width="20" height="17" viewBox="0 0 20 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8 17V11H12V17H17V9H20L10 0L0 9H3V17H8Z" fill="#444444"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 322 B After Width: | Height: | Size: 173 B |
@ -0,0 +1,5 @@
|
||||
<svg width="24" height="23" viewBox="0 0 24 23" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5332 22.2243L0.632544 17.5921C-0.210848 17.1877 -0.210848 16.5627 0.632544 16.1951L4.07945 14.5775L10.4966 17.5921C11.34 17.9965 12.6967 17.9965 13.5034 17.5921L19.9206 14.5775L23.3675 16.1951C24.2108 16.5995 24.2108 17.2245 23.3675 17.5921L13.4668 22.2243C12.6967 22.592 11.34 22.592 10.5332 22.2243Z" fill="#FF6F3D"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5011 16.4922L0.630617 11.8546C-0.210206 11.4497 -0.210206 10.824 0.630617 10.456L3.99391 8.87329L10.5011 11.9282C11.342 12.3331 12.6946 12.3331 13.4989 11.9282L20.0061 8.87329L23.3694 10.456C24.2102 10.8608 24.2102 11.4865 23.3694 11.8546L13.4989 16.4922C12.658 16.897 11.3054 16.897 10.5011 16.4922Z" fill="#95C038"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5011 10.8195L0.630617 6.24863C-0.210206 5.84959 -0.210206 5.23289 0.630617 4.87013L10.5011 0.299281C11.342 -0.0997605 12.6946 -0.0997605 13.4989 0.299281L23.3694 4.87013C24.2102 5.26917 24.2102 5.88587 23.3694 6.24863L13.4989 10.8195C12.658 11.1822 11.3054 11.1822 10.5011 10.8195Z" fill="#5DC0E8"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@ -0,0 +1,5 @@
|
||||
<svg width="20" height="14" viewBox="0 0 20 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="20" height="2" rx="1" fill="white"/>
|
||||
<rect y="6" width="20" height="2" rx="1" fill="white"/>
|
||||
<rect y="12" width="20" height="2" rx="1" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 278 B |
3
web/documentserver-example/nodejs/public/images/plus.svg
Normal file
3
web/documentserver-example/nodejs/public/images/plus.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 17C13 17.5523 12.5523 18 12 18C11.4477 18 11 17.5523 11 17V13H7C6.44772 13 6 12.5523 6 12C6 11.4477 6.44772 11 7 11H11V7C11 6.44772 11.4477 6 12 6C12.5523 6 13 6.44772 13 7V11H17C17.5523 11 18 11.4477 18 12C18 12.5523 17.5523 13 17 13H13V17Z" fill="#444444"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 416 B |
@ -0,0 +1,66 @@
|
||||
/**
|
||||
*
|
||||
* (c) Copyright Ascensio System SIA 2024
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
class Format {
|
||||
constructor(name, type, actions, convert, mime) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.actions = actions;
|
||||
this.convert = convert;
|
||||
this.mime = mime;
|
||||
}
|
||||
|
||||
isAutoConvertible() {
|
||||
return this.actions.includes('auto-convert');
|
||||
}
|
||||
|
||||
isEditable() {
|
||||
return this.actions.includes('edit') || this.actions.includes('lossy-edit');
|
||||
}
|
||||
|
||||
isFillable() {
|
||||
return this.actions.includes('fill');
|
||||
}
|
||||
}
|
||||
|
||||
class FormatManager {
|
||||
formats = [];
|
||||
|
||||
constructor(formats) {
|
||||
if(Array.isArray(formats)) this.formats = formats;
|
||||
}
|
||||
|
||||
findByExtension(extension) {
|
||||
return this.formats.find(format => format.name == extension);
|
||||
}
|
||||
|
||||
isAutoConvertible(extension) {
|
||||
let format = this.findByExtension(extension);
|
||||
return format !== undefined && format.isAutoConvertible();
|
||||
}
|
||||
|
||||
isEditable(extension) {
|
||||
let format = this.findByExtension(extension);
|
||||
return format !== undefined && format.isEditable();
|
||||
}
|
||||
|
||||
isFillable(extension) {
|
||||
let format = this.findByExtension(extension);
|
||||
return format !== undefined && format.isFillable();
|
||||
}
|
||||
}
|
||||
@ -19,13 +19,26 @@
|
||||
var language;
|
||||
var userid;
|
||||
var directUrl;
|
||||
var Formats;
|
||||
var formatManager;
|
||||
var leftPanelToggle = false;
|
||||
|
||||
window.onload = function () {
|
||||
fetch('formats')
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
Formats = data;
|
||||
if (data.formats) {
|
||||
let formats = [];
|
||||
data.formats.forEach(format => {
|
||||
formats.push(new Format(
|
||||
format.name,
|
||||
format.type,
|
||||
format.actions,
|
||||
format.convert,
|
||||
format.mime
|
||||
));
|
||||
});
|
||||
formatManager = new FormatManager(formats);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -109,7 +122,7 @@ if (typeof jQuery != "undefined") {
|
||||
});
|
||||
|
||||
var timer = null;
|
||||
var checkConvert = function (filePass, forceConvert) {
|
||||
var checkConvert = function (filePass, fileType) {
|
||||
filePass = filePass ? filePass : null;
|
||||
if (timer != null) {
|
||||
clearTimeout(timer);
|
||||
@ -125,22 +138,19 @@ if (typeof jQuery != "undefined") {
|
||||
var posExt = fileName.lastIndexOf('.') + 1;
|
||||
posExt = 0 <= posExt ? fileName.substring(posExt).trim().toLowerCase() : '';
|
||||
|
||||
if (ConverExtList.indexOf(posExt) == -1) {
|
||||
if (!formatManager.isAutoConvertible(posExt)) {
|
||||
jq("#step2").addClass("done").removeClass("current");
|
||||
loadScripts();
|
||||
return;
|
||||
}
|
||||
|
||||
var convData = {filename: fileName, filePass: filePass, lang: language};
|
||||
if (forceConvert) convData.forceConv = forceConvert;
|
||||
|
||||
timer = setTimeout(function () {
|
||||
jq.ajaxSetup({ cache: false });
|
||||
jq.ajax({
|
||||
async: true,
|
||||
type: "post",
|
||||
dataType: "json",
|
||||
data: convData,
|
||||
data: {filename: fileName, filePass: filePass, lang: language, fileExt: fileType},
|
||||
url: UrlConverter,
|
||||
complete: function (data) {
|
||||
var responseText = data.responseText;
|
||||
@ -161,7 +171,7 @@ if (typeof jQuery != "undefined") {
|
||||
return;
|
||||
} else {
|
||||
if (response.error.includes("-9")){
|
||||
jq("#xmlError").removeClass("invisible");
|
||||
jq("#select-file-type").removeClass("invisible");
|
||||
jq("#step2").removeClass("current");
|
||||
jq("#hiddenFileName").attr("placeholder",filePass);
|
||||
return;
|
||||
@ -177,7 +187,7 @@ if (typeof jQuery != "undefined") {
|
||||
jq("#hiddenFileName").val(response.filename);
|
||||
|
||||
if (typeof response.step != "undefined" && response.step < 100) {
|
||||
checkConvert(filePass, forceConvert);
|
||||
checkConvert(filePass, fileType);
|
||||
} else {
|
||||
jq("#step2").addClass("done").removeClass("current");
|
||||
loadScripts();
|
||||
@ -215,19 +225,16 @@ if (typeof jQuery != "undefined") {
|
||||
var posExt = fileName.lastIndexOf('.') + 1;
|
||||
posExt = 0 <= posExt ? fileName.substring(posExt).trim().toLowerCase() : '';
|
||||
|
||||
var checkEdited = EditedExtList.split(",").filter(function(ext) { return ext == posExt;});
|
||||
var checkFilled = FilledExtList.split(",").filter(function(ext) { return ext == posExt;});
|
||||
|
||||
if (checkEdited != "" || checkFilled != "") {
|
||||
if (formatManager.isEditable(posExt) || formatManager.isFillable(posExt)) {
|
||||
jq("#beginEdit").removeClass("disable");
|
||||
}
|
||||
};
|
||||
|
||||
jq(document).on("click", "#forceConvert:not(.disable)", function () {
|
||||
jq(document).on("click", ".file-type:not(.disable)", function () {
|
||||
const currentElement = jq(this);
|
||||
var fileType = currentElement.attr("data");
|
||||
var filePass = jq("#hiddenFileName").attr("placeholder");
|
||||
jq("div[id='forceConvert']").addClass("disable, pale");
|
||||
jq(".file-type").addClass(["disable", "pale"]);
|
||||
currentElement.removeClass("pale");
|
||||
checkConvert(filePass, fileType);
|
||||
});
|
||||
@ -369,10 +376,14 @@ if (typeof jQuery != "undefined") {
|
||||
jq("#convertFileName").removeClass("word slide cell");
|
||||
jq("#convertFileName").addClass(type);
|
||||
jq("#convTypes").empty();
|
||||
let convExtensions = Formats.find(format => {return format.name == fileName.split('.').pop()}).convert;
|
||||
convExtensions.forEach(ext => {
|
||||
jq("#convTypes").append(jq(`<td name="convertingTypeButton" id="wordTo${ext}" class="button hoar" data="${ext}">${ext}</td>`));
|
||||
});
|
||||
|
||||
let format = formatManager.findByExtension(fileName.split('.').pop());
|
||||
if (format) {
|
||||
format.convert.forEach(ext => {
|
||||
jq("#convTypes").append(jq(`<td name="convertingTypeButton" id="wordTo${ext}" class="button hoar" data="${ext}">${ext}</td>`));
|
||||
});
|
||||
}
|
||||
|
||||
jq("#hiddenFileName").val(fileName);
|
||||
jq("#convertStep1").addClass("done");
|
||||
jq("#convertStep2").addClass("waiting");
|
||||
@ -410,7 +421,7 @@ if (typeof jQuery != "undefined") {
|
||||
async: true,
|
||||
type: "post",
|
||||
dataType: "json",
|
||||
data: {filename: fileName, filePass: filePass, lang: language, fileExt: fileExt},
|
||||
data: {filename: fileName, filePass: filePass, lang: language, fileExt: fileExt, keepOriginal: true},
|
||||
url: UrlConverter,
|
||||
complete: function (data) {
|
||||
try {
|
||||
@ -600,3 +611,26 @@ function collectParams(startParams) {
|
||||
});
|
||||
return startChar + params.join("&");
|
||||
}
|
||||
|
||||
function toggleLeftPanel(event) {
|
||||
leftPanelToggle = !leftPanelToggle;
|
||||
event.preventDefault();
|
||||
let leftPanel = document.querySelector(".left-panel");
|
||||
if (leftPanelToggle) {
|
||||
leftPanel.classList.remove("hide");
|
||||
leftPanel.classList.add("active")
|
||||
} else {
|
||||
leftPanel.classList.remove("active");
|
||||
leftPanel.classList.add("hide");
|
||||
}
|
||||
}
|
||||
|
||||
function toggleUserDescr(event) {
|
||||
let list = event.currentTarget.querySelector("ul");
|
||||
let cursor = window.getComputedStyle(event.currentTarget).getPropertyValue("cursor");
|
||||
|
||||
if (cursor === 'pointer') {
|
||||
if (list.classList.contains("open")) list.classList.remove("open");
|
||||
else list.classList.add("open");
|
||||
}
|
||||
}
|
||||
@ -141,9 +141,6 @@
|
||||
.contentCells-wopi {
|
||||
width: 100%;
|
||||
}
|
||||
.contentCells-icon {
|
||||
width: 1%;
|
||||
}
|
||||
.contentCells-shift {
|
||||
padding-right: 30px;
|
||||
padding-left: 3px;
|
||||
@ -154,3 +151,16 @@
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@media (max-width: 592px) and (min-width: 320px) {
|
||||
.tableRow td:last-child:after{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.contentCells-wopi {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.downloadContentCells {
|
||||
margin-left: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,6 +74,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1279px) and (min-width: 1024px) {
|
||||
menu.main-nav {
|
||||
padding-left: 64px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1080px) {
|
||||
.copy {
|
||||
margin-right: 32px;
|
||||
@ -84,7 +90,8 @@
|
||||
}
|
||||
|
||||
.tableRow,
|
||||
.storedHeader {
|
||||
.storedHeader,
|
||||
menu.links {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
@ -124,11 +131,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1023px) and (min-width: 593px) {
|
||||
menu.main-nav {
|
||||
padding-left: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 769px) and (min-width: 593px) {
|
||||
.contentCells-icon{
|
||||
width: 5%;
|
||||
}
|
||||
.tableRow {
|
||||
.tableRow,
|
||||
menu.links {
|
||||
width: 55%;
|
||||
}
|
||||
|
||||
@ -144,7 +158,7 @@
|
||||
}
|
||||
|
||||
.scroll-table-body {
|
||||
top: 88px;
|
||||
top: 33px;
|
||||
}
|
||||
|
||||
footer {
|
||||
@ -185,7 +199,8 @@
|
||||
|
||||
@media (max-width: 715px) {
|
||||
.tableRow,
|
||||
.storedHeader {
|
||||
.storedHeader,
|
||||
menu.links {
|
||||
width: 45%;
|
||||
}
|
||||
}
|
||||
@ -284,7 +299,8 @@
|
||||
}
|
||||
|
||||
.tableRow,
|
||||
.storedHeader {
|
||||
.storedHeader,
|
||||
menu.links {
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
@ -324,7 +340,8 @@
|
||||
}
|
||||
|
||||
.tableRow,
|
||||
.storedHeader {
|
||||
.storedHeader,
|
||||
menu.links {
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
@ -411,7 +428,7 @@
|
||||
}
|
||||
|
||||
.scroll-table-body {
|
||||
top: 88px;
|
||||
top: 33px;
|
||||
}
|
||||
|
||||
footer table tr {
|
||||
@ -452,7 +469,8 @@
|
||||
}
|
||||
|
||||
.tableRow,
|
||||
.storedHeader {
|
||||
.storedHeader,
|
||||
menu.links {
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
@ -551,8 +569,8 @@
|
||||
margin-bottom: 0;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
.button.forceConvert:hover,
|
||||
.button.forceConvert {
|
||||
.button.file-type:hover,
|
||||
.button.file-type {
|
||||
height: 28px;
|
||||
width: 100px;
|
||||
margin-bottom: 10px !important;
|
||||
@ -593,7 +611,8 @@
|
||||
|
||||
@media (max-width: 510px) and (min-width: 470px) {
|
||||
.tableRow,
|
||||
.storedHeader {
|
||||
.storedHeader,
|
||||
menu.links {
|
||||
width: 35%;
|
||||
}
|
||||
|
||||
@ -620,7 +639,8 @@
|
||||
|
||||
@media (max-width: 470px) and (min-width: 420px) {
|
||||
.tableRow,
|
||||
.storedHeader {
|
||||
.storedHeader,
|
||||
menu.links {
|
||||
width: 30%;
|
||||
}
|
||||
.tableRow td:first-child{
|
||||
@ -656,7 +676,8 @@
|
||||
|
||||
@media (max-width: 420px) and (min-width: 320px) {
|
||||
.tableRow,
|
||||
.storedHeader {
|
||||
.storedHeader,
|
||||
menu.links {
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
@ -711,7 +732,8 @@
|
||||
}
|
||||
@media (max-width: 769px) and (min-width: 715px){
|
||||
.tableRow,
|
||||
.storedHeader {
|
||||
.storedHeader,
|
||||
menu.links {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
@ -760,3 +782,229 @@
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
@media (max-width: 592px) and (min-width: 320px) {
|
||||
header {
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
header, footer {
|
||||
position: -webkit-sticky; /* Safari */
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
.main-nav {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.responsive-nav {
|
||||
height: 24px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin: 0;
|
||||
align-items: center;
|
||||
column-gap: 16px;
|
||||
padding: 10px 16px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.left-panel {
|
||||
background-color: rgba(186, 186, 186, 0.6);
|
||||
display: none;
|
||||
align-items: start;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
overflow-y: hidden;
|
||||
height: calc(100% - 124px);
|
||||
z-index:99;
|
||||
}
|
||||
|
||||
.left-panel.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.left-panel.active {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.help-block {
|
||||
height: calc(100% - 66px);
|
||||
margin: 0;
|
||||
background-color: #F5F5F5;
|
||||
width: 192px;
|
||||
padding: 33px 40px 33px 16px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.help-block::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.help-block {
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
|
||||
.mobile-close-btn {
|
||||
display: block;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background-color: #E2E2E2;
|
||||
border-radius: 2px;
|
||||
border-color: #E2E2E2;
|
||||
color: #808080;
|
||||
cursor: pointer;
|
||||
outline: inherit;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.center {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.table-main {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.main {
|
||||
height: calc(100% - 124px);
|
||||
}
|
||||
|
||||
.main-panel {
|
||||
width: 100%;
|
||||
left: 0;
|
||||
padding: 28px 16px;
|
||||
row-gap: 12px;
|
||||
}
|
||||
|
||||
menu.links {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#portal-info {
|
||||
width: 100%;
|
||||
max-width: fit-content;
|
||||
}
|
||||
|
||||
#portal-info .portal-name {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
#portal-info span:nth-child(2) {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
#portal-info .portal-descr {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.users-list>li:first-child {
|
||||
margin-top: 12px;
|
||||
border-top: 1px solid #E5E5E5;
|
||||
}
|
||||
|
||||
.user-descr {
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
min-width: auto;
|
||||
border-bottom: 1px solid #E5E5E5;
|
||||
padding: 12px 0;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.user-descr ul {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.user-descr ul.open {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.user-descr b {
|
||||
font-size: 13px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 8px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.user-descr b::before {
|
||||
content: url("/images/plus.svg");
|
||||
display: inline-block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.storedHeader {
|
||||
width: 100%;
|
||||
padding: 12px 0;
|
||||
}
|
||||
|
||||
.scroll-table-body tr:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.tableRow {
|
||||
width: 100%;
|
||||
row-gap: 16px;
|
||||
padding: 16px 0;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.tableRow td:first-child {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tableRow td {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.firstContentCellViewers {
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
.firstContentCellViewers ~ td {
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
.downloadContentCellShift:after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.stored-edit {
|
||||
padding: 0;
|
||||
padding-left: 26px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.stored-edit span {
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.clear-all {
|
||||
font-size: 11px;
|
||||
width: 112px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.scroll-table-body {
|
||||
position: static;
|
||||
}
|
||||
|
||||
.user-block-table {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,14 +53,34 @@ a:visited {
|
||||
|
||||
header {
|
||||
background: #333333;
|
||||
height: 48px;
|
||||
margin: 0 auto;
|
||||
min-width: 1152px;
|
||||
width: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
header img {
|
||||
margin: 10px 0 22px 32px;
|
||||
.main-nav {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 192px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.main-nav, .responsive-nav {
|
||||
list-style: none;
|
||||
column-gap: 24px;
|
||||
}
|
||||
|
||||
.main-nav li, .mobile-nav li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.responsive-nav {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mobile-close-btn {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.center {
|
||||
@ -274,30 +294,30 @@ label .checkbox {
|
||||
opacity: 100%;
|
||||
}
|
||||
|
||||
.button.forceConvert {
|
||||
.button.file-type {
|
||||
font-size: 11px;
|
||||
color: #FFFFFF;
|
||||
padding: 8px 8px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.button.forceConvert.disable {
|
||||
.button.file-type.disable {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.button.forceConvert.pale {
|
||||
.button.file-type.pale {
|
||||
opacity: 30%;
|
||||
}
|
||||
|
||||
.button.forceConvert.document {
|
||||
.button.file-type.document {
|
||||
background: #446995;
|
||||
}
|
||||
|
||||
.button.forceConvert.spreadsheet {
|
||||
.button.file-type.spreadsheet {
|
||||
background: #40865C;
|
||||
}
|
||||
|
||||
.button.forceConvert.presentation {
|
||||
.button.file-type.presentation {
|
||||
background: #AA5252;
|
||||
}
|
||||
|
||||
@ -336,34 +356,51 @@ label .checkbox {
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.links-panel {
|
||||
.links {
|
||||
display: flex;
|
||||
padding: 0;
|
||||
column-gap: 30px;
|
||||
margin-bottom: 35px;
|
||||
align-items: center;
|
||||
list-style: none;
|
||||
border-bottom: 1px solid #E2E2E2;
|
||||
margin: 0;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.links-panel-current {
|
||||
position: relative;
|
||||
.links li {
|
||||
padding: 4px;
|
||||
border-bottom: 2px solid transparent;
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
.links-panel-current::after {
|
||||
content: "";
|
||||
background-color: #ff6f3d;
|
||||
position: absolute;
|
||||
left: -10%;
|
||||
bottom: -8px;
|
||||
width: 120%;
|
||||
height: 2.5px;
|
||||
.links li.active {
|
||||
border-bottom: 2px solid #FF6F3D;
|
||||
}
|
||||
|
||||
.links-panel a {
|
||||
font-size: 14px;
|
||||
.links li.active a {
|
||||
color: #FF6F3D;
|
||||
}
|
||||
|
||||
.links li.active a img {
|
||||
filter: invert(55%) sepia(67%) saturate(2727%) hue-rotate(335deg) brightness(104%) contrast(101%);
|
||||
}
|
||||
|
||||
.links a {
|
||||
display: inline-block;
|
||||
padding: 2px 0;
|
||||
line-height: 20px;
|
||||
font-size: 13px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.links-panel-home {
|
||||
width: 22px;
|
||||
.home-link {
|
||||
height: 24px;
|
||||
padding: 0 2px 8px !important;
|
||||
}
|
||||
|
||||
.home-link a {
|
||||
padding: 0;
|
||||
padding-top: 7px;
|
||||
}
|
||||
|
||||
.upload-panel,
|
||||
@ -860,7 +897,7 @@ footer table tr td:first-child {
|
||||
overflow-x: auto;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 130px;
|
||||
top: 75px;
|
||||
scrollbar-color: #D0D5DA transparent;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
@ -958,6 +995,12 @@ html {
|
||||
padding-right: 28px;
|
||||
}
|
||||
|
||||
ul.users-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.user-descr {
|
||||
display: inline-table;
|
||||
width: 30vw;
|
||||
|
||||
@ -412,7 +412,7 @@
|
||||
if (config.type !== "mobile") {
|
||||
return;
|
||||
}
|
||||
var wrapEl = document.getElementsByClassName("form");
|
||||
var wrapEl = document.getElementsByTagName("iframe");
|
||||
if (wrapEl.length) {
|
||||
wrapEl[0].style.height = screen.availHeight + "px";
|
||||
window.scrollTo(0, -1);
|
||||
|
||||
@ -32,11 +32,27 @@
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div class="center">
|
||||
<a href="./">
|
||||
<img src ="images/logo.svg" alt="ONLYOFFICE" />
|
||||
</a>
|
||||
</div>
|
||||
<nav>
|
||||
<menu class="main-nav">
|
||||
<li>
|
||||
<a href="./">
|
||||
<img src ="images/logo.svg" alt="ONLYOFFICE" />
|
||||
</a>
|
||||
</li>
|
||||
</menu>
|
||||
<menu class="responsive-nav">
|
||||
<li>
|
||||
<a href="#" onclick="toggleLeftPanel(event)">
|
||||
<img src ="images/mobile-menu.svg" alt="ONLYOFFICE" />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="./">
|
||||
<img src ="images/mobile-logo.svg" alt="ONLYOFFICE" />
|
||||
</a>
|
||||
</li>
|
||||
</menu>
|
||||
</nav>
|
||||
</header>
|
||||
<div class="center main">
|
||||
<table class="table-main">
|
||||
@ -108,9 +124,22 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<button class="mobile-close-btn" onclick="toggleLeftPanel(event)">
|
||||
<img src="images/close.svg" alt="">
|
||||
</button>
|
||||
</td>
|
||||
<td class="section">
|
||||
<div class="main-panel">
|
||||
<menu class="links">
|
||||
<li class="home-link active" >
|
||||
<a href="./">
|
||||
<img src="images/home.svg" alt="Home"/>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="wopi">Wopi</a>
|
||||
</li>
|
||||
</menu>
|
||||
<div id="portal-info" style="display: <%= storedFiles.length > 0 ? "none" : "table-cell" %>">
|
||||
<span class="portal-name">ONLYOFFICE Document Editors – Welcome!</span>
|
||||
<span class="portal-descr">
|
||||
@ -119,26 +148,22 @@
|
||||
</span>
|
||||
<span class="portal-descr">Please do NOT use this integration example on your own server without proper code modifications, it is intended for testing purposes only. In case you enabled this test example, disable it before going for production.</span>
|
||||
<span class="portal-descr">You can open the same document using different users in different Web browser sessions, so you can check out multi-user editing functions.</span>
|
||||
<% users.forEach(user => { %>
|
||||
<div class="user-descr">
|
||||
<b><%= user.name == null ? 'Anonymous' : user.name %></b>
|
||||
<ul>
|
||||
<% user.descriptions.forEach(description => { %>
|
||||
<li><%= description %></li>
|
||||
<% }) %>
|
||||
</ul>
|
||||
</div>
|
||||
<% }) %>
|
||||
<ul class="users-list">
|
||||
<% users.forEach(user => { %>
|
||||
<li class="user-descr" onclick="toggleUserDescr(event)">
|
||||
<b><%= user.name == null ? 'Anonymous' : user.name %></b>
|
||||
<ul>
|
||||
<% user.descriptions.forEach(description => { %>
|
||||
<li><%= description %></li>
|
||||
<% }) %>
|
||||
</ul>
|
||||
</li>
|
||||
<% }) %>
|
||||
</ul>
|
||||
</div>
|
||||
<%if (storedFiles.length > 0)
|
||||
{%>
|
||||
<div class="stored-list">
|
||||
<div class="links-panel">
|
||||
<a href="./" class="links-panel-current">
|
||||
<img src="images/home.svg" alt="Home" class="links-panel-home"/>
|
||||
</a>
|
||||
<a href="wopi">Wopi</a>
|
||||
</div>
|
||||
<div class="storedHeader">
|
||||
<div class="storedHeaderText">
|
||||
<span class="header-list">Your documents</span>
|
||||
@ -274,13 +299,13 @@
|
||||
<div class="describeUpload">After these steps are completed, you can work with your document.</div>
|
||||
<span id="step1" class="step">1. Loading the file.</span>
|
||||
<span class="step-descr">The loading speed depends on file size and additional elements it contains.</span>
|
||||
<div id="xmlError" class="invisible">
|
||||
<div id="select-file-type" class="invisible">
|
||||
<br />
|
||||
<span class="step">Please select the current document type</span>
|
||||
<div class="buttonsMobile indent">
|
||||
<div id="forceConvert" class="button forceConvert document" data="docx">Document</div>
|
||||
<div id="forceConvert" class="button forceConvert spreadsheet" data="xlsx">Spreadsheet</div>
|
||||
<div id="forceConvert" class="button forceConvert presentation" data="pptx">Presentation</div>
|
||||
<div class="button file-type document" data="docx">Document</div>
|
||||
<div class="button file-type spreadsheet" data="xlsx">Spreadsheet</div>
|
||||
<div class="button file-type presentation" data="pptx">Presentation</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
@ -373,12 +398,10 @@
|
||||
<script type="text/javascript" src="javascripts/jquery.iframe-transport.js"></script>
|
||||
<script type="text/javascript" src="javascripts/jquery.fileupload.js"></script>
|
||||
<script type="text/javascript" src="javascripts/jquery.dropdownToggle.js"></script>
|
||||
<script type="text/javascript" src="javascripts/formats.js"></script>
|
||||
<script type="text/javascript" src="javascripts/jscript.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
var ConverExtList = "<%= convertExts %>";
|
||||
var EditedExtList = "<%= editedExts %>";
|
||||
var FilledExtList = "<%= fillExts %>";
|
||||
var UrlConverter = "convert";
|
||||
var UrlEditor = "editor";
|
||||
</script>
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
*
|
||||
-->
|
||||
<title>ONLYOFFICE Document Editors</title>
|
||||
<link href="images/favicon.ico" rel="shortcut icon" type="image/x-icon" />
|
||||
<link href="../images/favicon.ico" rel="shortcut icon" type="image/x-icon" />
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
@ -32,7 +32,7 @@
|
||||
overflow: hidden;
|
||||
-ms-content-zooming: none;
|
||||
}
|
||||
|
||||
|
||||
#office_frame {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@ -70,6 +70,29 @@
|
||||
frameholder.appendChild(office_frame);
|
||||
|
||||
document.getElementById('office_form').submit();
|
||||
|
||||
var _onMessage = function(msg) {
|
||||
var data = msg.data;
|
||||
if (Object.prototype.toString.apply(data) !== '[object String]' || !window.JSON) {
|
||||
return;
|
||||
}
|
||||
|
||||
var cmd = JSON.parse(data);
|
||||
if (cmd) {
|
||||
if ( cmd.MessageId == 'App_LoadingStatus' ) {
|
||||
var fixSize = function() {
|
||||
document.getElementsByTagName("iframe")[0].style.height = window.innerHeight + "px";
|
||||
}
|
||||
|
||||
fixSize();
|
||||
window.addEventListener("orientationchange", fixSize);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('message', function (e) {
|
||||
_onMessage(e);
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
@ -34,11 +34,27 @@
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<div class="center">
|
||||
<a href="wopi">
|
||||
<img src="images/logo.svg" alt="ONLYOFFICE" />
|
||||
</a>
|
||||
</div>
|
||||
<nav>
|
||||
<menu class="main-nav">
|
||||
<li>
|
||||
<a href="./">
|
||||
<img src ="images/logo.svg" alt="ONLYOFFICE" />
|
||||
</a>
|
||||
</li>
|
||||
</menu>
|
||||
<menu class="responsive-nav">
|
||||
<li>
|
||||
<a href="#" onclick="toggleLeftPanel(event)">
|
||||
<img src ="images/mobile-menu.svg" alt="ONLYOFFICE" />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="./">
|
||||
<img src ="images/mobile-logo.svg" alt="ONLYOFFICE" />
|
||||
</a>
|
||||
</li>
|
||||
</menu>
|
||||
</nav>
|
||||
</header>
|
||||
<div class="center main">
|
||||
<table class="table-main">
|
||||
@ -99,18 +115,25 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<button class="mobile-close-btn" onclick="toggleLeftPanel(event)">
|
||||
<img src="images/close.svg" alt="">
|
||||
</button>
|
||||
</td>
|
||||
<td class="section">
|
||||
<div class="main-panel">
|
||||
<menu class="links">
|
||||
<li class="home-link" >
|
||||
<a href="./">
|
||||
<img src="images/home.svg" alt="Home"/>
|
||||
</a>
|
||||
</li>
|
||||
<li class="active">
|
||||
<a href="wopi">Wopi</a>
|
||||
</li>
|
||||
</menu>
|
||||
<div id="portal-info" style="display: <%= storedFiles.length > 0 ? "none" : "table-cell" %>">
|
||||
<% if (!wopiEnable)
|
||||
{ %>
|
||||
<div class="links-panel">
|
||||
<a href="./">
|
||||
<img src="images/home.svg" alt="Home" class="links-panel-home"/>
|
||||
</a>
|
||||
<a href="wopi" class="links-panel-current">Wopi</a>
|
||||
</div>
|
||||
<span class="portal-name">ONLYOFFICE Document Editors – Welcome!</span>
|
||||
<span class="portal-descr">
|
||||
Before you get started with a demo sample of ONLYOFFICE Docs, please enable the WOPI protocol.
|
||||
@ -131,12 +154,6 @@
|
||||
<% if (storedFiles.length > 0)
|
||||
{ %>
|
||||
<div class="stored-list">
|
||||
<div class="links-panel">
|
||||
<a href="./">
|
||||
<img src="images/home.svg" alt="Home" class="links-panel-home"/>
|
||||
</a>
|
||||
<a href="wopi" class="links-panel-current">Wopi</a>
|
||||
</div>
|
||||
<div class="storedHeader">
|
||||
<div class="storedHeaderText">
|
||||
<span class="header-list">Your documents</span>
|
||||
@ -274,12 +291,10 @@
|
||||
<script type="text/javascript" src="javascripts/jquery.iframe-transport.js"></script>
|
||||
<script type="text/javascript" src="javascripts/jquery.fileupload.js"></script>
|
||||
<script type="text/javascript" src="javascripts/jquery.dropdownToggle.js"></script>
|
||||
<script type="text/javascript" src="javascripts/formats.js"></script>
|
||||
<script type="text/javascript" src="javascripts/jscript.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
var ConverExtList = "<%= convertExts %>";
|
||||
var EditedExtList = "<%= editedExts %>";
|
||||
var FilledExtList = "<%= fillExts %>";
|
||||
var UrlConverter = "convert";
|
||||
var UrlEditor = "wopi-action";
|
||||
</script>
|
||||
|
||||
@ -621,6 +621,29 @@ footer table tr td:first-child {
|
||||
width: 4%;
|
||||
}
|
||||
|
||||
.storedHeader {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.storedHeaderClearAll {
|
||||
padding-right: 52px;
|
||||
}
|
||||
|
||||
.clear-all {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
padding: 2px;
|
||||
outline: 1px solid #E5E5E5;
|
||||
text-align: center;
|
||||
cursor:pointer;
|
||||
text-transform: uppercase;
|
||||
background-color: #F5F5F5;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.select-user {
|
||||
color: #444444;
|
||||
font-family: Open Sans;
|
||||
|
||||
66
web/documentserver-example/php/assets/js/formats.js
Normal file
66
web/documentserver-example/php/assets/js/formats.js
Normal file
@ -0,0 +1,66 @@
|
||||
/**
|
||||
*
|
||||
* (c) Copyright Ascensio System SIA 2024
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
class Format {
|
||||
constructor(name, type, actions, convert, mime) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.actions = actions;
|
||||
this.convert = convert;
|
||||
this.mime = mime;
|
||||
}
|
||||
|
||||
isAutoConvertible() {
|
||||
return this.actions.includes('auto-convert');
|
||||
}
|
||||
|
||||
isEditable() {
|
||||
return this.actions.includes('edit') || this.actions.includes('lossy-edit');
|
||||
}
|
||||
|
||||
isFillable() {
|
||||
return this.actions.includes('fill');
|
||||
}
|
||||
}
|
||||
|
||||
class FormatManager {
|
||||
formats = [];
|
||||
|
||||
constructor(formats) {
|
||||
if(Array.isArray(formats)) this.formats = formats;
|
||||
}
|
||||
|
||||
findByExtension(extension) {
|
||||
return this.formats.find(format => format.name == extension);
|
||||
}
|
||||
|
||||
isAutoConvertible(extension) {
|
||||
let format = this.findByExtension(extension);
|
||||
return format !== undefined && format.isAutoConvertible();
|
||||
}
|
||||
|
||||
isEditable(extension) {
|
||||
let format = this.findByExtension(extension);
|
||||
return format !== undefined && format.isEditable();
|
||||
}
|
||||
|
||||
isFillable(extension) {
|
||||
let format = this.findByExtension(extension);
|
||||
return format !== undefined && format.isFillable();
|
||||
}
|
||||
}
|
||||
@ -17,6 +17,27 @@
|
||||
*/
|
||||
|
||||
var directUrl;
|
||||
var formatManager;
|
||||
|
||||
window.onload = function () {
|
||||
fetch('formats')
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
if (data.formats) {
|
||||
let formats = [];
|
||||
JSON.parse(data.formats).forEach(format => {
|
||||
formats.push(new Format(
|
||||
format.name,
|
||||
format.type,
|
||||
format.actions,
|
||||
format.convert,
|
||||
format.mime
|
||||
));
|
||||
});
|
||||
formatManager = new FormatManager(formats);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (typeof jQuery != "undefined") {
|
||||
jq = jQuery.noConflict();
|
||||
@ -111,7 +132,7 @@ if (typeof jQuery != "undefined") {
|
||||
var posExt = fileName.lastIndexOf('.') + 1;
|
||||
posExt = 0 <= posExt ? fileName.substring(posExt).trim().toLowerCase() : '';
|
||||
|
||||
if (ConverExtList.indexOf(posExt) == -1) {
|
||||
if (!formatManager.isAutoConvertible(posExt)) {
|
||||
jq("#step2").addClass("done").removeClass("current");
|
||||
loadScripts();
|
||||
return;
|
||||
@ -206,9 +227,9 @@ if (typeof jQuery != "undefined") {
|
||||
|
||||
var fileName = jq("#hiddenFileName").val();
|
||||
var posExt = fileName.lastIndexOf('.') + 1;
|
||||
posExt = 0 <= posExt ? fileName.substring(posExt + 1).trim().toLowerCase() : '';
|
||||
posExt = 0 <= posExt ? fileName.substring(posExt).trim().toLowerCase() : '';
|
||||
|
||||
if (EditedExtList.indexOf(posExt) != -1 || FillFormsExtList.indexOf(posExt) != -1) {
|
||||
if (formatManager.isEditable(posExt) || formatManager.isFillable(posExt)) {
|
||||
jq("#beginEdit").removeClass("disable");
|
||||
}
|
||||
};
|
||||
@ -325,6 +346,22 @@ if (typeof jQuery != "undefined") {
|
||||
});
|
||||
});
|
||||
|
||||
jq(document).on("click", ".clear-all", function () {
|
||||
if (confirm("Delete all the files?")) {
|
||||
jq.ajax({
|
||||
async: true,
|
||||
contentType: "text/xml",
|
||||
type: "delete",
|
||||
url: "delete",
|
||||
complete: function (data) {
|
||||
if (JSON.parse(data.responseText).status == 'success') {
|
||||
window.location.reload(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
jq(document).on("click", "#createSample", function () {
|
||||
jq(".try-editor").each(function () {
|
||||
var href = jq(this).attr("href");
|
||||
|
||||
@ -143,6 +143,11 @@ function routers()
|
||||
echo json_encode($response);
|
||||
return;
|
||||
}
|
||||
if (str_starts_with($path, '/formats')) {
|
||||
$response = formats();
|
||||
echo json_encode($response);
|
||||
return;
|
||||
}
|
||||
|
||||
http_response_code(HTTPStatus::NotFound->value);
|
||||
}
|
||||
|
||||
@ -300,12 +300,15 @@ function convert()
|
||||
function delete()
|
||||
{
|
||||
try {
|
||||
$fileName = basename($_GET["fileName"]);
|
||||
if (isset($_GET["fileName"]) && !empty($_GET["fileName"])) {
|
||||
$fileName = basename($_GET["fileName"]);
|
||||
$filePath = getStoragePath($fileName);
|
||||
|
||||
$filePath = getStoragePath($fileName);
|
||||
|
||||
unlink($filePath); // delete a file
|
||||
delTree(getHistoryDir($filePath)); // delete all the elements from the history directory
|
||||
unlink($filePath); // delete a file
|
||||
delTree(getHistoryDir($filePath)); // delete all the elements from the history directory
|
||||
} else {
|
||||
delTree(getStoragePath('')); // delete the user's folder and all the containing files
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
sendlog("Deletion ".$e->getMessage(), "webedior-ajax.log");
|
||||
$result["error"] = "error: " . $e->getMessage();
|
||||
@ -656,3 +659,19 @@ function restore()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
function formats()
|
||||
{
|
||||
try {
|
||||
$formatManager = new FormatManager();
|
||||
$formats = $formatManager->all();
|
||||
|
||||
return [
|
||||
'formats' => json_encode($formats)
|
||||
];
|
||||
} catch (Exception $error) {
|
||||
return [
|
||||
'error' => 'Server error'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,7 +132,7 @@ final class DocEditorView extends View
|
||||
$editorsMode == "view" || $editorsMode == "filter" || $editorsMode == "blockcontent"),
|
||||
"print" => !in_array("print", $user->deniedPermissions),
|
||||
"fillForms" => $editorsMode != "view" && $editorsMode != "comment"
|
||||
&& $editorsMode != "embedded" && $editorsMode != "blockcontent",
|
||||
&& $editorsMode != "blockcontent",
|
||||
"modifyFilter" => $editorsMode != "filter",
|
||||
"modifyContentControl" => $editorsMode != "blockcontent",
|
||||
"review" => $canEdit && ($editorsMode == "edit" || $editorsMode == "review"),
|
||||
|
||||
@ -44,9 +44,6 @@ final class IndexView extends View
|
||||
"editButton" => $this->getEditButton(),
|
||||
"dataDocs" => $this->getPreloaderUrl(),
|
||||
"date" => date("Y"),
|
||||
"fillFormsExtList" => implode(",", $formatManager->fillableExtensions()),
|
||||
"converExtList" => implode(",", $formatManager->convertibleExtensions()),
|
||||
"editedExtList" => implode(",", $formatManager->editableExtensions()),
|
||||
"serverVersion" => $configManager -> getVersion(),
|
||||
];
|
||||
}
|
||||
|
||||
@ -220,11 +220,7 @@
|
||||
<script type="text/javascript" src="assets/js/jquery.iframe-transport.js"></script>
|
||||
<script type="text/javascript" src="assets/js/jquery.fileupload.js"></script>
|
||||
<script type="text/javascript" src="assets/js/jquery.dropdownToggle.js"></script>
|
||||
<script type="text/javascript" src="assets/js/formats.js"></script>
|
||||
<script type="text/javascript" src="assets/js/jscript.js"></script>
|
||||
<script type="text/javascript">
|
||||
var FillFormsExtList = '{fillFormsExtList}';
|
||||
var ConverExtList = '{converExtList}';
|
||||
var EditedExtList = '{editedExtList}';
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,5 +1,12 @@
|
||||
<div class="stored-list">
|
||||
<span class="header-list">Your documents</span>
|
||||
<div class="storedHeader">
|
||||
<div class="storedHeaderText">
|
||||
<span class="header-list">Your documents</span>
|
||||
</div>
|
||||
<div class="storedHeaderClearAll">
|
||||
<div class="clear-all">Clear all</div>
|
||||
</div>
|
||||
</div>
|
||||
<table class="tableHeader" cellspacing="0" cellpadding="0" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
@ -80,7 +80,8 @@ def routers():
|
||||
path('restore', actions.restore),
|
||||
path('saveas', actions.saveAs),
|
||||
path('track', actions.track),
|
||||
path('upload', actions.upload)
|
||||
path('upload', actions.upload),
|
||||
path('formats', actions.formats)
|
||||
]
|
||||
main += static(
|
||||
settings.STATIC_URL,
|
||||
|
||||
@ -286,6 +286,16 @@ def removeFile(filename, req):
|
||||
shutil.rmtree(histDir)
|
||||
|
||||
|
||||
# remove the user's directory and all the containing files
|
||||
def removeUserFolder(req):
|
||||
path = os.path.normpath(getRootFolder(req))
|
||||
|
||||
if not path.startswith(str(config_manager.storage_path())) or not os.path.exists(path):
|
||||
raise FileNotFoundError
|
||||
|
||||
shutil.rmtree(path)
|
||||
|
||||
|
||||
# generate file key
|
||||
def generateFileKey(filename, req):
|
||||
path = getStoragePath(filename, req)
|
||||
|
||||
@ -78,6 +78,7 @@ def processError(error):
|
||||
prefix = 'Error occurred in the ConvertService: '
|
||||
|
||||
mapping = {
|
||||
'-9': f'{prefix}Error conversion output format',
|
||||
'-8': f'{prefix}Error document VKey',
|
||||
'-7': f'{prefix}Error document request',
|
||||
'-6': f'{prefix}Error database',
|
||||
|
||||
@ -30,6 +30,8 @@ from src.configuration import ConfigurationManager
|
||||
from src.response import ErrorResponse
|
||||
from src.utils import docManager, fileUtils, serviceConverter, users, jwtManager, historyManager, trackManager
|
||||
from urllib.parse import urlparse, parse_qs
|
||||
from src.format import FormatManager
|
||||
import msgspec
|
||||
|
||||
config_manager = ConfigurationManager()
|
||||
|
||||
@ -75,13 +77,16 @@ def convert(request):
|
||||
lang = request.COOKIES.get('ulang') if request.COOKIES.get('ulang') else 'en'
|
||||
fileUri = docManager.getDownloadUrl(filename, request)
|
||||
fileExt = fileUtils.getFileExt(filename)
|
||||
newExt = 'ooxml' # convert to .ooxml
|
||||
# get an auto-conversion extension from the request body or set it to the ooxml extension
|
||||
conversionExtension = body.get('fileExt') or 'ooxml'
|
||||
|
||||
if docManager.isCanConvert(fileExt): # check if the file extension is available for converting
|
||||
key = docManager.generateFileKey(filename, request) # generate the file key
|
||||
|
||||
# get the url of the converted file
|
||||
convertedData = serviceConverter.getConvertedData(fileUri, fileExt, newExt, key, True, filePass, lang)
|
||||
convertedData = serviceConverter.getConvertedData(
|
||||
fileUri, fileExt, conversionExtension, key, True, filePass, lang
|
||||
)
|
||||
|
||||
# if the converter url is not received, the original file name is passed to the response
|
||||
if not convertedData:
|
||||
@ -276,7 +281,7 @@ def edit(request):
|
||||
'edit': canEdit & ((edMode == 'edit') | (edMode == 'view') | (edMode == 'filter') \
|
||||
| (edMode == "blockcontent")),
|
||||
'print': 'print' not in user.deniedPermissions,
|
||||
'fillForms': (edMode != 'view') & (edMode != 'comment') & (edMode != 'embedded') \
|
||||
'fillForms': (edMode != 'view') & (edMode != 'comment') \
|
||||
& (edMode != "blockcontent"),
|
||||
'modifyFilter': edMode != 'filter',
|
||||
'modifyContentControl': edMode != "blockcontent",
|
||||
@ -424,13 +429,19 @@ def track(request):
|
||||
|
||||
# remove a file
|
||||
def remove(request):
|
||||
filename = fileUtils.getFileName(request.GET['filename'])
|
||||
|
||||
response = {}
|
||||
|
||||
docManager.removeFile(filename, request)
|
||||
try:
|
||||
filename = request.GET.get('filename', '')
|
||||
if filename:
|
||||
filename = fileUtils.getFileName(filename)
|
||||
docManager.removeFile(filename, request)
|
||||
else:
|
||||
docManager.removeUserFolder(request)
|
||||
response.setdefault('success', True)
|
||||
except Exception as e:
|
||||
response.setdefault('error', str(e.args[0]))
|
||||
|
||||
response.setdefault('success', True)
|
||||
return HttpResponse(json.dumps(response), content_type='application/json')
|
||||
|
||||
|
||||
@ -658,3 +669,12 @@ def restore(request: HttpRequest) -> HttpResponse:
|
||||
message=f'{type(error)}: {error}',
|
||||
status=HTTPStatus.INTERNAL_SERVER_ERROR
|
||||
)
|
||||
|
||||
|
||||
@http.GET()
|
||||
def formats(request: HttpRequest) -> HttpResponse:
|
||||
data = {
|
||||
'formats': [msgspec.to_builtins(format) for format in FormatManager().all()]
|
||||
}
|
||||
|
||||
return HttpResponse(json.dumps(data), content_type='application/json')
|
||||
|
||||
@ -16,17 +16,13 @@
|
||||
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
from django.shortcuts import render
|
||||
|
||||
from src.configuration import ConfigurationManager
|
||||
from src.format import FormatManager
|
||||
from src.utils import users
|
||||
from src.utils import docManager
|
||||
|
||||
config_manager = ConfigurationManager()
|
||||
format_manager = FormatManager()
|
||||
|
||||
|
||||
def getDirectUrlParam(request):
|
||||
@ -41,10 +37,7 @@ def default(request): # default parameters that will be passed to the template
|
||||
'users': users.USERS,
|
||||
'languages': config_manager.languages(),
|
||||
'preloadurl': config_manager.document_server_preloader_url().geturl(),
|
||||
'editExt': json.dumps(format_manager.editable_extensions()), # file extensions that can be edited
|
||||
'convExt': json.dumps(format_manager.convertible_extensions()), # file extensions that can be converted
|
||||
'files': docManager.getStoredFiles(request), # information about stored files
|
||||
'fillExt': json.dumps(format_manager.fillable_extensions()),
|
||||
'directUrl': str(getDirectUrlParam(request)).lower,
|
||||
'serverVersion': config_manager.getVersion()
|
||||
}
|
||||
|
||||
@ -503,6 +503,17 @@
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.buttonsMobile.indent {
|
||||
margin-bottom: 0;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
.button.file-type:hover,
|
||||
.button.file-type {
|
||||
height: 28px;
|
||||
width: 100px;
|
||||
margin-bottom: 10px !important;
|
||||
font-size: 9px;
|
||||
}
|
||||
.button.gray{
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@ -230,6 +230,33 @@ label .checkbox {
|
||||
color: #FF6F3D;
|
||||
}
|
||||
|
||||
.button.file-type {
|
||||
font-size: 11px;
|
||||
color: #FFFFFF;
|
||||
padding: 8px 8px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.button.file-type.disable {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.button.file-type.pale {
|
||||
opacity: 30%;
|
||||
}
|
||||
|
||||
.button.file-type.document {
|
||||
background: #446995;
|
||||
}
|
||||
|
||||
.button.file-type.spreadsheet {
|
||||
background: #40865C;
|
||||
}
|
||||
|
||||
.button.file-type.presentation {
|
||||
background: #AA5252;
|
||||
}
|
||||
|
||||
.upload-panel {
|
||||
float: left;
|
||||
padding: 24px 0;
|
||||
@ -593,6 +620,29 @@ footer table tr td:first-child {
|
||||
width: 4%;
|
||||
}
|
||||
|
||||
.storedHeader {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.storedHeaderClearAll {
|
||||
padding-right: 52px;
|
||||
}
|
||||
|
||||
.clear-all {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
padding: 2px;
|
||||
outline: 1px solid #E5E5E5;
|
||||
text-align: center;
|
||||
cursor:pointer;
|
||||
text-transform: uppercase;
|
||||
background-color: #F5F5F5;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.select-user {
|
||||
color: #444444;
|
||||
font-family: Open Sans;
|
||||
@ -742,6 +792,16 @@ html {
|
||||
margin-left: 25px;
|
||||
}
|
||||
|
||||
.buttonsMobile.indent{
|
||||
padding-left: 35px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.invisible {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
background: #FFFFFF;
|
||||
border-radius: 5px;
|
||||
|
||||
66
web/documentserver-example/python/static/js/formats.js
Normal file
66
web/documentserver-example/python/static/js/formats.js
Normal file
@ -0,0 +1,66 @@
|
||||
/**
|
||||
*
|
||||
* (c) Copyright Ascensio System SIA 2024
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
class Format {
|
||||
constructor(name, type, actions, convert, mime) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.actions = actions;
|
||||
this.convert = convert;
|
||||
this.mime = mime;
|
||||
}
|
||||
|
||||
isAutoConvertible() {
|
||||
return this.actions.includes('auto-convert');
|
||||
}
|
||||
|
||||
isEditable() {
|
||||
return this.actions.includes('edit') || this.actions.includes('lossy-edit');
|
||||
}
|
||||
|
||||
isFillable() {
|
||||
return this.actions.includes('fill');
|
||||
}
|
||||
}
|
||||
|
||||
class FormatManager {
|
||||
formats = [];
|
||||
|
||||
constructor(formats) {
|
||||
if(Array.isArray(formats)) this.formats = formats;
|
||||
}
|
||||
|
||||
findByExtension(extension) {
|
||||
return this.formats.find(format => format.name == extension);
|
||||
}
|
||||
|
||||
isAutoConvertible(extension) {
|
||||
let format = this.findByExtension(extension);
|
||||
return format !== undefined && format.isAutoConvertible();
|
||||
}
|
||||
|
||||
isEditable(extension) {
|
||||
let format = this.findByExtension(extension);
|
||||
return format !== undefined && format.isEditable();
|
||||
}
|
||||
|
||||
isFillable(extension) {
|
||||
let format = this.findByExtension(extension);
|
||||
return format !== undefined && format.isFillable();
|
||||
}
|
||||
}
|
||||
@ -17,6 +17,27 @@
|
||||
*/
|
||||
|
||||
var directUrl;
|
||||
var formatManager;
|
||||
|
||||
window.onload = function () {
|
||||
fetch('formats')
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
if (data.formats) {
|
||||
let formats = [];
|
||||
data.formats.forEach(format => {
|
||||
formats.push(new Format(
|
||||
format.name,
|
||||
format.type,
|
||||
format.actions,
|
||||
format.convert,
|
||||
format.mime
|
||||
));
|
||||
});
|
||||
formatManager = new FormatManager(formats);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (typeof jQuery !== "undefined") {
|
||||
jq = jQuery.noConflict();
|
||||
@ -87,7 +108,7 @@ if (typeof jQuery !== "undefined") {
|
||||
});
|
||||
|
||||
var timer = null;
|
||||
var checkConvert = function (filePass) {
|
||||
var checkConvert = function (filePass, fileType) {
|
||||
filePass = filePass ? filePass : null;
|
||||
if (timer !== null) {
|
||||
clearTimeout(timer);
|
||||
@ -103,7 +124,7 @@ if (typeof jQuery !== "undefined") {
|
||||
var posExt = fileName.lastIndexOf(".");
|
||||
posExt = 0 <= posExt ? fileName.substring(posExt).trim().toLowerCase() : "";
|
||||
|
||||
if (ConverExtList.indexOf(posExt) === -1) {
|
||||
if (!formatManager.isAutoConvertible(posExt)) {
|
||||
jq("#step2").addClass("done").removeClass("current");
|
||||
loadScripts();
|
||||
return;
|
||||
@ -115,7 +136,7 @@ if (typeof jQuery !== "undefined") {
|
||||
contentType: "text/xml",
|
||||
type: "post",
|
||||
dataType: "json",
|
||||
data: JSON.stringify({ filename: fileName, filePass: filePass }),
|
||||
data: JSON.stringify({ filename: fileName, filePass: filePass, fileExt: fileType }),
|
||||
url: UrlConverter,
|
||||
complete: function (data) {
|
||||
var responseText = data.responseText;
|
||||
@ -131,6 +152,12 @@ if (typeof jQuery !== "undefined") {
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
if (response.error.includes("Error conversion output format")){
|
||||
jq("#select-file-type").removeClass("invisible");
|
||||
jq("#step2").removeClass("current");
|
||||
jq("#hiddenFileName").attr("placeholder",filePass);
|
||||
return;
|
||||
}
|
||||
jq(".current").removeClass("current");
|
||||
jq(".step:not(.done)").addClass("error");
|
||||
jq("#mainProgress .error-message").show().find("span").text(response.error);
|
||||
@ -142,7 +169,7 @@ if (typeof jQuery !== "undefined") {
|
||||
jq("#hiddenFileName").val(response.filename);
|
||||
|
||||
if (response.step && response.step < 100) {
|
||||
checkConvert(filePass);
|
||||
checkConvert(filePass, fileType);
|
||||
} else {
|
||||
jq("#step2").addClass("done").removeClass("current");
|
||||
loadScripts();
|
||||
@ -177,10 +204,10 @@ if (typeof jQuery !== "undefined") {
|
||||
jq("#beginView, #beginEmbedded").removeClass("disable");
|
||||
|
||||
var fileName = jq("#hiddenFileName").val();
|
||||
var posExt = fileName.lastIndexOf(".");
|
||||
var posExt = fileName.lastIndexOf(".") + 1;
|
||||
posExt = 0 <= posExt ? fileName.substring(posExt).trim().toLowerCase() : "";
|
||||
|
||||
if (EditedExtList.indexOf(posExt) !== -1 || FillExtList.indexOf(posExt) !== -1) {
|
||||
if (formatManager.isEditable(posExt) || formatManager.isFillable(posExt)) {
|
||||
jq("#beginEdit").removeClass("disable");
|
||||
}
|
||||
};
|
||||
@ -212,6 +239,15 @@ if (typeof jQuery !== "undefined") {
|
||||
});
|
||||
};
|
||||
|
||||
jq(document).on("click", ".file-type:not(.disable)", function () {
|
||||
const currentElement = jq(this);
|
||||
var fileType = currentElement.attr("data");
|
||||
var filePass = jq("#hiddenFileName").attr("placeholder");
|
||||
jq('.file-type').addClass(["disable", "pale"]);
|
||||
currentElement.removeClass("pale");
|
||||
checkConvert(filePass, fileType);
|
||||
});
|
||||
|
||||
jq(document).on("click", "#enterPass", function () {
|
||||
var filePass = jq("#filePass").val();
|
||||
if (filePass) {
|
||||
@ -291,6 +327,22 @@ if (typeof jQuery !== "undefined") {
|
||||
});
|
||||
});
|
||||
|
||||
jq(document).on("click", ".clear-all", function () {
|
||||
if (confirm("Delete all the files?")) {
|
||||
jq.ajax({
|
||||
async: true,
|
||||
contentType: "text/xml",
|
||||
type: "delete",
|
||||
url: "remove",
|
||||
complete: function (data) {
|
||||
if (JSON.parse(data.responseText).success) {
|
||||
window.location.reload(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function showUserTooltip (isMobile) {
|
||||
if ( jq("div#portal-info").is(":hidden") ) {
|
||||
jq("div#portal-info").show();
|
||||
|
||||
@ -139,7 +139,14 @@
|
||||
</div>
|
||||
{% if files %}
|
||||
<div class="stored-list">
|
||||
<span class="header-list">Your documents</span>
|
||||
<div class="storedHeader">
|
||||
<div class="storedHeaderText">
|
||||
<span class="header-list">Your documents</span>
|
||||
</div>
|
||||
<div class="storedHeaderClearAll">
|
||||
<div class="clear-all">Clear all</div>
|
||||
</div>
|
||||
</div>
|
||||
<table class="tableHeader" cellspacing="0" cellpadding="0" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
@ -274,6 +281,15 @@
|
||||
<div class="describeUpload">After these steps are completed, you can work with your document.</div>
|
||||
<span id="step1" class="step">1. Loading the file.</span>
|
||||
<span class="step-descr">The loading speed depends on file size and additional elements it contains.</span>
|
||||
<div id="select-file-type" class="invisible">
|
||||
<br />
|
||||
<span class="step">Please select the current document type</span>
|
||||
<div class="buttonsMobile indent">
|
||||
<div class="button file-type document" data="docx">Document</div>
|
||||
<div class="button file-type spreadsheet" data="xlsx">Spreadsheet</div>
|
||||
<div class="button file-type presentation" data="pptx">Presentation</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<span id="step2" class="step">2. Conversion.</span>
|
||||
<span class="step-descr">The file is converted to OOXML so that you can edit it.</span>
|
||||
@ -338,12 +354,10 @@
|
||||
<script type="text/javascript" src="{% static "js/jquery.blockUI.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "js/jquery.iframe-transport.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "js/jquery.fileupload.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "js/formats.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "js/jscript.js" %}"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
var FillExtList = {{ fillExt | safe }};
|
||||
var ConverExtList = {{ convExt | safe }};
|
||||
var EditedExtList = {{ editExt | safe }};
|
||||
var UrlConverter = "convert";
|
||||
var UrlEditor = "edit";
|
||||
</script>
|
||||
|
||||
@ -0,0 +1,66 @@
|
||||
/**
|
||||
*
|
||||
* (c) Copyright Ascensio System SIA 2024
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
class Format {
|
||||
constructor(name, type, actions, convert, mime) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.actions = actions;
|
||||
this.convert = convert;
|
||||
this.mime = mime;
|
||||
}
|
||||
|
||||
isAutoConvertible() {
|
||||
return this.actions.includes('auto-convert');
|
||||
}
|
||||
|
||||
isEditable() {
|
||||
return this.actions.includes('edit') || this.actions.includes('lossy-edit');
|
||||
}
|
||||
|
||||
isFillable() {
|
||||
return this.actions.includes('fill');
|
||||
}
|
||||
}
|
||||
|
||||
class FormatManager {
|
||||
formats = [];
|
||||
|
||||
constructor(formats) {
|
||||
if(Array.isArray(formats)) this.formats = formats;
|
||||
}
|
||||
|
||||
findByExtension(extension) {
|
||||
return this.formats.find(format => format.name == extension);
|
||||
}
|
||||
|
||||
isAutoConvertible(extension) {
|
||||
let format = this.findByExtension(extension);
|
||||
return format !== undefined && format.isAutoConvertible();
|
||||
}
|
||||
|
||||
isEditable(extension) {
|
||||
let format = this.findByExtension(extension);
|
||||
return format !== undefined && format.isEditable();
|
||||
}
|
||||
|
||||
isFillable(extension) {
|
||||
let format = this.findByExtension(extension);
|
||||
return format !== undefined && format.isFillable();
|
||||
}
|
||||
}
|
||||
@ -18,6 +18,27 @@
|
||||
|
||||
var directUrl;
|
||||
var userId;
|
||||
var formatManager;
|
||||
|
||||
window.onload = function () {
|
||||
fetch('formats')
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
if (data.formats) {
|
||||
let formats = [];
|
||||
data.formats.forEach(format => {
|
||||
formats.push(new Format(
|
||||
format.name,
|
||||
format.type,
|
||||
format.actions,
|
||||
format.convert,
|
||||
format.mime
|
||||
));
|
||||
});
|
||||
formatManager = new FormatManager(formats);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (typeof jQuery != "undefined") {
|
||||
jq = jQuery.noConflict();
|
||||
@ -98,7 +119,7 @@ if (typeof jQuery != "undefined") {
|
||||
});
|
||||
|
||||
var timer = null;
|
||||
var checkConvert = function (filePass) {
|
||||
var checkConvert = function (filePass, fileType) {
|
||||
filePass = filePass ? filePass : null;
|
||||
if (timer != null) {
|
||||
clearTimeout(timer);
|
||||
@ -111,10 +132,10 @@ if (typeof jQuery != "undefined") {
|
||||
jq("#filePass").val("");
|
||||
|
||||
var fileName = jq("#hiddenFileName").val();
|
||||
var posExt = fileName.lastIndexOf('.');
|
||||
var posExt = fileName.lastIndexOf('.') + 1;
|
||||
posExt = 0 <= posExt ? fileName.substring(posExt).trim().toLowerCase() : '';
|
||||
|
||||
if (ConvertExtList.indexOf(posExt) == -1) {
|
||||
if (!formatManager.isAutoConvertible(posExt)) {
|
||||
jq("#step2").addClass("done").removeClass("current");
|
||||
loadScripts();
|
||||
return;
|
||||
@ -126,7 +147,7 @@ if (typeof jQuery != "undefined") {
|
||||
contentType: "text/xml",
|
||||
type: "post",
|
||||
dataType: "json",
|
||||
data: JSON.stringify({ filename: fileName, filePass: filePass }),
|
||||
data: JSON.stringify({filename: fileName, filePass: filePass, fileExt: fileType}),
|
||||
url: UrlConverter,
|
||||
complete: function (data) {
|
||||
var responseText = data.responseText;
|
||||
@ -142,6 +163,12 @@ if (typeof jQuery != "undefined") {
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
if (response.error.includes("Error conversion output format")){
|
||||
jq("#select-file-type").removeClass("invisible");
|
||||
jq("#step2").removeClass("current");
|
||||
jq("#hiddenFileName").attr("placeholder",filePass);
|
||||
return;
|
||||
}
|
||||
jq(".current").removeClass("current");
|
||||
jq(".step:not(.done)").addClass("error");
|
||||
jq("#mainProgress .error-message").show().find("span").text(response.error);
|
||||
@ -153,7 +180,7 @@ if (typeof jQuery != "undefined") {
|
||||
jq("#hiddenFileName").val(response.filename);
|
||||
|
||||
if (response.step && response.step < 100) {
|
||||
checkConvert(filePass);
|
||||
checkConvert(filePass, fileType);
|
||||
} else {
|
||||
jq("#step2").addClass("done").removeClass("current");
|
||||
loadScripts();
|
||||
@ -188,10 +215,10 @@ if (typeof jQuery != "undefined") {
|
||||
jq("#beginView, #beginEmbedded").removeClass("disable");
|
||||
|
||||
var fileName = jq("#hiddenFileName").val();
|
||||
var posExt = fileName.lastIndexOf('.');
|
||||
var posExt = fileName.lastIndexOf('.') + 1;
|
||||
posExt = 0 <= posExt ? fileName.substring(posExt).trim().toLowerCase() : '';
|
||||
|
||||
if (EditedExtList.indexOf(posExt) != -1 || FillExtList.indexOf(posExt) != -1) {
|
||||
if (formatManager.isEditable(posExt) || formatManager.isFillable(posExt)) {
|
||||
jq("#beginEdit").removeClass("disable");
|
||||
}
|
||||
};
|
||||
@ -217,6 +244,15 @@ if (typeof jQuery != "undefined") {
|
||||
});
|
||||
};
|
||||
|
||||
jq(document).on("click", ".file-type:not(.disable)", function () {
|
||||
const currentElement = jq(this);
|
||||
var fileType = currentElement.attr("data");
|
||||
var filePass = jq("#hiddenFileName").attr("placeholder");
|
||||
jq('.file-type').addClass(["disable", "pale"]);
|
||||
currentElement.removeClass("pale");
|
||||
checkConvert(filePass, fileType);
|
||||
});
|
||||
|
||||
jq(document).on("click", "#enterPass", function () {
|
||||
var filePass = jq("#filePass").val();
|
||||
if (filePass) {
|
||||
@ -292,6 +328,7 @@ if (typeof jQuery != "undefined") {
|
||||
jq.ajax({
|
||||
async: true,
|
||||
contentType: "text/xml",
|
||||
type: "delete",
|
||||
url: requestAddress,
|
||||
complete: function (data) {
|
||||
document.location.reload();
|
||||
@ -299,6 +336,22 @@ if (typeof jQuery != "undefined") {
|
||||
});
|
||||
});
|
||||
|
||||
jq(document).on("click", ".clear-all", function () {
|
||||
if (confirm("Delete all the files?")) {
|
||||
jq.ajax({
|
||||
async: true,
|
||||
contentType: "text/xml",
|
||||
type: "delete",
|
||||
url: "remove",
|
||||
complete: function (data) {
|
||||
if (JSON.parse(data.responseText).success) {
|
||||
window.location.reload(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function showUserTooltip (isMobile) {
|
||||
if ( jq("div#portal-info").is(":hidden") ) {
|
||||
jq("div#portal-info").show();
|
||||
|
||||
@ -503,6 +503,17 @@
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.buttonsMobile.indent {
|
||||
margin-bottom: 0;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
.button.file-type:hover,
|
||||
.button.file-type {
|
||||
height: 28px;
|
||||
width: 100px;
|
||||
margin-bottom: 10px !important;
|
||||
font-size: 9px;
|
||||
}
|
||||
.button.gray{
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@ -231,6 +231,33 @@ label .checkbox {
|
||||
color: #FF6F3D;
|
||||
}
|
||||
|
||||
.button.file-type {
|
||||
font-size: 11px;
|
||||
color: #FFFFFF;
|
||||
padding: 8px 8px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.button.file-type.disable {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.button.file-type.pale {
|
||||
opacity: 30%;
|
||||
}
|
||||
|
||||
.button.file-type.document {
|
||||
background: #446995;
|
||||
}
|
||||
|
||||
.button.file-type.spreadsheet {
|
||||
background: #40865C;
|
||||
}
|
||||
|
||||
.button.file-type.presentation {
|
||||
background: #AA5252;
|
||||
}
|
||||
|
||||
.upload-panel {
|
||||
float: left;
|
||||
padding: 24px 0;
|
||||
@ -594,6 +621,29 @@ footer table tr td:first-child {
|
||||
width: 4%;
|
||||
}
|
||||
|
||||
.storedHeader {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.storedHeaderClearAll {
|
||||
padding-right: 52px;
|
||||
}
|
||||
|
||||
.clear-all {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
padding: 2px;
|
||||
outline: 1px solid #E5E5E5;
|
||||
text-align: center;
|
||||
cursor:pointer;
|
||||
text-transform: uppercase;
|
||||
background-color: #F5F5F5;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.select-user {
|
||||
color: #444444;
|
||||
font-family: Open Sans;
|
||||
@ -743,6 +793,16 @@ html {
|
||||
margin-left: 25px;
|
||||
}
|
||||
|
||||
.buttonsMobile.indent{
|
||||
padding-left: 35px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.invisible {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
background: #FFFFFF;
|
||||
border-radius: 5px;
|
||||
|
||||
@ -102,14 +102,15 @@ class HomeController < ApplicationController
|
||||
file_pass = body['filePass'] || nil
|
||||
file_uri = DocumentHelper.get_download_url(file_name)
|
||||
extension = File.extname(file_name).downcase
|
||||
internal_extension = 'ooxml'
|
||||
# get an auto-conversion extension from the request body or set it to the ooxml extension
|
||||
conversion_extension = body['fileExt'] || 'ooxml'
|
||||
|
||||
if DocumentHelper.convert_exts.include?(extension) # check if the file with such an extension can be converted
|
||||
key = ServiceConverter.generate_revision_id(file_uri) # generate document key
|
||||
percent, new_file_uri, new_file_type = ServiceConverter.get_converted_data(
|
||||
file_uri,
|
||||
extension.delete('.'),
|
||||
internal_extension.delete('.'),
|
||||
conversion_extension.delete('.'),
|
||||
key,
|
||||
true,
|
||||
file_pass,
|
||||
@ -251,24 +252,31 @@ class HomeController < ApplicationController
|
||||
|
||||
# removing a file
|
||||
def remove
|
||||
file_name = File.basename(params[:filename]) # get the file name
|
||||
unless file_name # if it doesn't exist
|
||||
render(plain: '{"success":false}') # report that the operation is unsuccessful
|
||||
return
|
||||
end
|
||||
|
||||
DocumentHelper.init(request.remote_ip, request.base_url)
|
||||
storage_path = DocumentHelper.storage_path(file_name, nil)
|
||||
hist_dir = DocumentHelper.history_dir(storage_path)
|
||||
|
||||
# if the file exists
|
||||
FileUtils.rm_f(storage_path) # delete it from the storage path
|
||||
if params[:filename].present?
|
||||
file_name = File.basename(params[:filename]) # get the file name
|
||||
unless file_name # if it doesn't exist
|
||||
render(plain: '{"success":false}') # report that the operation is unsuccessful
|
||||
return
|
||||
end
|
||||
|
||||
# if the history directory of this file exists
|
||||
FileUtils.rm_rf(hist_dir) # delete it
|
||||
storage_path = DocumentHelper.storage_path(file_name, nil)
|
||||
hist_dir = DocumentHelper.history_dir(storage_path)
|
||||
|
||||
# if the file exists
|
||||
FileUtils.rm_f(storage_path) # delete it from the storage path
|
||||
|
||||
# if the history directory of this file exists
|
||||
FileUtils.rm_rf(hist_dir) # delete it
|
||||
else
|
||||
storage_path = DocumentHelper.storage_path('', nil)
|
||||
FileUtils.rm_rf(storage_path) # remove the user's directory and all the containing files
|
||||
end
|
||||
render(plain: '{"success":true}') # report that the operation is successful
|
||||
nil
|
||||
rescue StandardError
|
||||
render(plain: '{"error": "Server error"}')
|
||||
end
|
||||
|
||||
# getting files information
|
||||
@ -539,4 +547,15 @@ class HomeController < ApplicationController
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
# return all supported formats
|
||||
def formats
|
||||
render(
|
||||
json: JSON.generate(
|
||||
{
|
||||
formats: FormatManager.new.all.map(&:serialize)
|
||||
}
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@ -146,7 +146,7 @@ class FileModel
|
||||
download: @user.denied_permissions.exclude?('download'),
|
||||
edit: can_edit && ['edit', 'view', 'filter', 'blockcontent'].include?(editors_mode),
|
||||
print: @user.denied_permissions.exclude?('print'),
|
||||
fillForms: ['view', 'comment', 'embedded', 'blockcontent'].exclude?(editors_mode),
|
||||
fillForms: ['view', 'comment', 'blockcontent'].exclude?(editors_mode),
|
||||
modifyFilter: !editors_mode.eql?('filter'),
|
||||
modifyContentControl: !editors_mode.eql?('blockcontent'),
|
||||
review: can_edit && (editors_mode.eql?('edit') || editors_mode.eql?('review')),
|
||||
@ -304,19 +304,19 @@ class FileModel
|
||||
prev = hist_data[(i - 2).to_s] # get the history data from the previous file version
|
||||
# write key and url information about previous file version with optional direct url
|
||||
data_obj['previous'] = if enable_direct_url? == true
|
||||
{ # write key and url information about previous file version with optional directUrl
|
||||
fileType: prev['fileType'],
|
||||
key: prev['key'],
|
||||
url: prev['url'],
|
||||
directUrl: prev['directUrl']
|
||||
}
|
||||
else
|
||||
{
|
||||
fileType: prev['fileType'],
|
||||
key: prev['key'],
|
||||
url: prev['url']
|
||||
}
|
||||
end
|
||||
{ # write key and url information about previous file version with optional directUrl
|
||||
fileType: prev['fileType'],
|
||||
key: prev['key'],
|
||||
url: prev['url'],
|
||||
directUrl: prev['directUrl']
|
||||
}
|
||||
else
|
||||
{
|
||||
fileType: prev['fileType'],
|
||||
key: prev['key'],
|
||||
url: prev['url']
|
||||
}
|
||||
end
|
||||
|
||||
diff_path = [hist_dir, (i - 1).to_s, 'diff.zip'].join(File::SEPARATOR)
|
||||
if File.exist?(diff_path)
|
||||
|
||||
@ -108,6 +108,8 @@ class ServiceConverter
|
||||
|
||||
# add an error message to the error message template depending on the error code
|
||||
case error_code
|
||||
when -9
|
||||
error_message = 'Error occurred in the ConvertService.ashx: Error conversion output format'
|
||||
when -8
|
||||
error_message = 'Error occurred in the ConvertService.ashx: Error document VKey'
|
||||
when -7
|
||||
@ -139,7 +141,7 @@ class ServiceConverter
|
||||
|
||||
error_element = file_result['error']
|
||||
unless error_element.nil? # if an error occurs
|
||||
process_convert_service_responce_error(Integer(error_element, 10)) # get an error message
|
||||
process_convert_service_responce_error(Integer(error_element)) # get an error message
|
||||
end
|
||||
|
||||
is_end_convert = file_result['endConvert'] # check if the conversion is completed
|
||||
|
||||
@ -123,7 +123,14 @@
|
||||
</div>
|
||||
<% if docs.length > 0 %>
|
||||
<div class="stored-list">
|
||||
<span class="header-list">Your documents</span>
|
||||
<div class="storedHeader">
|
||||
<div class="storedHeaderText">
|
||||
<span class="header-list">Your documents</span>
|
||||
</div>
|
||||
<div class="storedHeaderClearAll">
|
||||
<div class="clear-all">Clear all</div>
|
||||
</div>
|
||||
</div>
|
||||
<table class="tableHeader" cellspacing="0" cellpadding="0" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
@ -267,6 +274,15 @@
|
||||
<div class="describeUpload">After these steps are completed, you can work with your document.</div>
|
||||
<span id="step1" class="step">1. Loading the file.</span>
|
||||
<span class="step-descr">The loading speed depends on file size and additional elements it contains.</span>
|
||||
<div id="select-file-type" class="invisible">
|
||||
<br />
|
||||
<span class="step">Please select the current document type</span>
|
||||
<div class="buttonsMobile indent">
|
||||
<div class="button file-type document" data="docx">Document</div>
|
||||
<div class="button file-type spreadsheet" data="xlsx">Spreadsheet</div>
|
||||
<div class="button file-type presentation" data="pptx">Presentation</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<span id="step2" class="step">2. Conversion.</span>
|
||||
<span class="step-descr">The file is converted to OOXML so that you can edit it.</span>
|
||||
@ -329,9 +345,6 @@
|
||||
<%= javascript_include_tag "application" %>
|
||||
|
||||
<script language="javascript" type="text/javascript">
|
||||
var FillExtList = '<%= DocumentHelper.fill_forms_exts.join"," %>';
|
||||
var ConvertExtList = '<%= DocumentHelper.convert_exts.join(",") %>';
|
||||
var EditedExtList = '<%= DocumentHelper.edited_exts.join(",") %>';
|
||||
var UrlConverter = '<%= convert_path %>';
|
||||
var UrlEditor = '<%= editor_path %>';
|
||||
</script>
|
||||
|
||||
@ -39,12 +39,13 @@ class Application < Rails::Application
|
||||
match '/files', to: 'home#files', via: 'get'
|
||||
match '/index', to: 'home#index', via: 'get'
|
||||
match '/reference', to: 'home#reference', via: 'post'
|
||||
match '/remove', to: 'home#remove', via: 'get'
|
||||
match '/remove', to: 'home#remove', via: 'delete'
|
||||
match '/rename', to: 'home#rename', via: 'post'
|
||||
match '/restore', to: 'home#restore', via: 'put'
|
||||
match '/sample', to: 'home#sample', via: 'get'
|
||||
match '/saveas', to: 'home#saveas', via: 'post'
|
||||
match '/track', to: 'home#track', via: 'post'
|
||||
match '/upload', to: 'home#upload', via: 'post'
|
||||
match '/formats', to: 'home#formats', via: 'get'
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user