Merge branch 'release/v1.1.0' of github.com:ONLYOFFICE/AppServer into release/v1.1.0

This commit is contained in:
Ilya Oleshko 2021-12-13 17:58:34 +03:00
commit 39f172bd22
21 changed files with 660 additions and 313 deletions

View File

@ -37,6 +37,13 @@ while [ "$1" != "" ]; do
fi
;;
-skiphc | --skiphardwarecheck )
if [ "$2" != "" ]; then
SKIP_HARDWARE_CHECK=$2
shift
fi
;;
-? | -h | --help )
echo " Usage $0 [PARAMETER] [[PARAMETER], ...]"
echo " Parameters:"
@ -60,6 +67,10 @@ if [ -z "${LOCAL_SCRIPTS}" ]; then
LOCAL_SCRIPTS="false";
fi
if [ -z "${SKIP_HARDWARE_CHECK}" ]; then
SKIP_HARDWARE_CHECK="false";
fi
if [ $(dpkg-query -W -f='${Status}' curl 2>/dev/null | grep -c "ok installed") -eq 0 ]; then
apt-get update;
apt-get install -yq curl;

View File

@ -6,6 +6,37 @@ command_exists () {
type "$1" &> /dev/null;
}
check_hardware () {
DISK_REQUIREMENTS=40960;
MEMORY_REQUIREMENTS=5500;
CORE_REQUIREMENTS=2;
AVAILABLE_DISK_SPACE=$(df -m / | tail -1 | awk '{ print $4 }');
if [ ${AVAILABLE_DISK_SPACE} -lt ${DISK_REQUIREMENTS} ]; then
echo "Minimal requirements are not met: need at least $DISK_REQUIREMENTS MB of free HDD space"
exit 1;
fi
TOTAL_MEMORY=$(free -m | grep -oP '\d+' | head -n 1);
if [ ${TOTAL_MEMORY} -lt ${MEMORY_REQUIREMENTS} ]; then
echo "Minimal requirements are not met: need at least $MEMORY_REQUIREMENTS MB of RAM"
exit 1;
fi
CPU_CORES_NUMBER=$(cat /proc/cpuinfo | grep processor | wc -l);
if [ ${CPU_CORES_NUMBER} -lt ${CORE_REQUIREMENTS} ]; then
echo "The system does not meet the minimal hardware requirements. CPU with at least $CORE_REQUIREMENTS cores is required"
exit 1;
fi
}
if [ "$SKIP_HARDWARE_CHECK" != "true" ]; then
check_hardware
fi
ARCH="$(dpkg --print-architecture)"
if [ "$ARCH" != "amd64" ]; then
echo "ONLYOFFICE ${product^^} doesn't support architecture '$ARCH'"

View File

@ -47,6 +47,13 @@ while [ "$1" != "" ]; do
fi
;;
-skiphc | --skiphardwarecheck )
if [ "$2" != "" ]; then
SKIP_HARDWARE_CHECK=$2
shift
fi
;;
-? | -h | --help )
echo " Usage $0 [PARAMETER] [[PARAMETER], ...]"
echo " Parameters:"
@ -69,6 +76,10 @@ if [ -z "${LOCAL_SCRIPTS}" ]; then
LOCAL_SCRIPTS="false";
fi
if [ -z "${SKIP_HARDWARE_CHECK}" ]; then
SKIP_HARDWARE_CHECK="false";
fi
cat > /etc/yum.repos.d/onlyoffice.repo <<END
[onlyoffice]
name=onlyoffice repo
@ -91,11 +102,13 @@ END
DOWNLOAD_URL_PREFIX="https://raw.githubusercontent.com/ONLYOFFICE/${product}/${GIT_BRANCH}/build/install/OneClickInstall/install-RedHat"
if [ "$LOCAL_SCRIPTS" = "true" ]; then
source install-RedHat/tools.sh
source install-RedHat/bootstrap.sh
source install-RedHat/check-ports.sh
source install-RedHat/install-preq.sh
source install-RedHat/install-app.sh
else
source <(curl ${DOWNLOAD_URL_PREFIX}/tools.sh)
source <(curl ${DOWNLOAD_URL_PREFIX}/bootstrap.sh)
source <(curl ${DOWNLOAD_URL_PREFIX}/check-ports.sh)
source <(curl ${DOWNLOAD_URL_PREFIX}/install-preq.sh)

View File

@ -0,0 +1,34 @@
#!/bin/bash
set -e
check_hardware () {
DISK_REQUIREMENTS=40960;
MEMORY_REQUIREMENTS=5500;
CORE_REQUIREMENTS=2;
AVAILABLE_DISK_SPACE=$(df -m / | tail -1 | awk '{ print $4 }');
if [ ${AVAILABLE_DISK_SPACE} -lt ${DISK_REQUIREMENTS} ]; then
echo "Minimal requirements are not met: need at least $DISK_REQUIREMENTS MB of free HDD space"
exit 1;
fi
TOTAL_MEMORY=$(free -m | grep -oP '\d+' | head -n 1);
if [ ${TOTAL_MEMORY} -lt ${MEMORY_REQUIREMENTS} ]; then
echo "Minimal requirements are not met: need at least $MEMORY_REQUIREMENTS MB of RAM"
exit 1;
fi
CPU_CORES_NUMBER=$(cat /proc/cpuinfo | grep processor | wc -l);
if [ ${CPU_CORES_NUMBER} -lt ${CORE_REQUIREMENTS} ]; then
echo "The system does not meet the minimal hardware requirements. CPU with at least $CORE_REQUIREMENTS cores is required"
exit 1;
fi
}
if [ "$SKIP_HARDWARE_CHECK" != "true" ]; then
check_hardware
fi

View File

@ -811,3 +811,26 @@ export function fileCopyAs(fileId, destTitle, destFolderId, enableExternalExt) {
},
});
}
export function getEditHistory(fileId, doc) {
return request({
method: "get",
url: `files/file/${fileId}/edit/history?doc=${doc}`,
});
}
export function getEditDiff(fileId, version, doc) {
return request({
method: "get",
url: `files/file/${fileId}/edit/diff?version=${version}&doc=${doc}`,
});
}
export function restoreDocumentsVersion(fileId, version, doc) {
const options = {
method: "get",
url: `files/file/${fileId}/restoreversion?version=${version}&doc=${doc}`,
};
return request(options);
}

View File

@ -25,7 +25,11 @@ export function regDesktop(
userId: user.id,
};
console.log("regDesktop", data);
console.log(
"regDesktop date=",
data,
`isEncryption=${isEncryption} keys=${keys} isEditor=${isEditor}`
);
let extendedData;
@ -80,6 +84,8 @@ export function regDesktop(
break;
}
};
console.log("Created window.cloudCryptoCommand", window.cloudCryptoCommand);
}
window.onSystemMessage = (e) => {
@ -104,6 +110,8 @@ export function regDesktop(
break;
}
};
console.log("Created window.onSystemMessage", window.onSystemMessage);
}
export function relogin() {

View File

@ -7,16 +7,12 @@ import FilesListBody from "./FilesListBody";
import Button from "@appserver/components/button";
import Text from "@appserver/components/text";
import { isArrayEqual } from "@appserver/components/utils/array";
import { FolderType } from "@appserver/common/constants";
import { getFoldersTree } from "@appserver/common/api/files";
const exceptSortedByTagsFolders = [
FolderType.Recent,
FolderType.TRASH,
FolderType.Favorites,
];
import {
exceptSortedByTagsFolders,
exceptPrivacyTrashFolders,
} from "../SelectFolderDialog/ExceptionFoldersConstants";
const exceptTrashFolder = [FolderType.TRASH];
const exceptPrivacyTrashFolders = [FolderType.Privacy, FolderType.TRASH];
class SelectFileDialogModalView extends React.Component {
constructor(props) {
super(props);
@ -59,20 +55,6 @@ class SelectFileDialogModalView extends React.Component {
console.error(err);
}
this.loadersCompletes();
break;
case "exceptTrashFolder":
try {
const foldersTree = await getFoldersTree();
[
this.folderList,
this.noTreeSwitcher,
] = SelectFolderDialog.convertFolders(foldersTree, exceptTrashFolder);
this.onSetSelectedFolder();
} catch (err) {
console.error(err);
}
this.loadersCompletes();
break;
case "exceptPrivacyTrashFolders":

View File

@ -349,7 +349,6 @@ SelectFileDialogBody.propTypes = {
"common",
"third-party",
"exceptSortedByTags",
"exceptTrashFolder",
"exceptPrivacyTrashFolders",
]),
folderId: PropTypes.string,

View File

@ -0,0 +1,10 @@
import { FolderType } from "@appserver/common/constants";
export const exceptSortedByTagsFolders = [
FolderType.Recent,
FolderType.TRASH,
FolderType.Favorites,
FolderType.Privacy,
];
export const exceptPrivacyTrashFolders = [FolderType.Privacy, FolderType.TRASH];

View File

@ -23,6 +23,10 @@ import { FolderType } from "@appserver/common/constants";
import { isArrayEqual } from "@appserver/components/utils/array";
import store from "studio/store";
import toastr from "studio/toastr";
import {
exceptSortedByTagsFolders,
exceptPrivacyTrashFolders,
} from "./ExceptionFoldersConstants";
const { auth: authStore } = store;
@ -31,14 +35,6 @@ const { desktop } = utils.device;
let pathName = "";
let folderList;
const exceptSortedByTagsFolders = [
FolderType.Recent,
FolderType.TRASH,
FolderType.Favorites,
];
const exceptTrashFolder = [FolderType.TRASH];
const exceptPrivacyTrashFolders = [FolderType.Privacy, FolderType.TRASH];
class SelectFolderModalDialog extends React.Component {
constructor(props) {
super(props);
@ -116,6 +112,7 @@ class SelectFolderModalDialog extends React.Component {
case "exceptSortedByTags":
try {
const foldersTree = await getFoldersTree();
[folderList, this.noTreeSwitcher] = SelectFolderDialog.convertFolders(
foldersTree,
exceptSortedByTagsFolders
@ -126,19 +123,6 @@ class SelectFolderModalDialog extends React.Component {
this.loadersCompletes();
}
break;
case "exceptTrashFolder":
try {
const foldersTree = await getFoldersTree();
[folderList, this.noTreeSwitcher] = SelectFolderDialog.convertFolders(
foldersTree,
exceptTrashFolder
);
this.setBaseSettings();
} catch (err) {
console.error(err);
this.loadersCompletes();
}
break;
case "exceptPrivacyTrashFolders":
try {
const foldersTree = await getFoldersTree();
@ -146,13 +130,13 @@ class SelectFolderModalDialog extends React.Component {
foldersTree,
exceptPrivacyTrashFolders
);
console.log("folderList", folderList);
this.setBaseSettings();
} catch (err) {
console.error(err);
this.loadersCompletes();
}
break;
case "common":
try {
folderList = await SelectFolderDialog.getCommonFolders();
@ -550,7 +534,6 @@ SelectFolderModalDialog.propTypes = {
"common",
"third-party",
"exceptSortedByTags",
"exceptTrashFolder",
"exceptPrivacyTrashFolders",
]),
displayType: PropTypes.oneOf(["aside", "modal"]),

View File

@ -97,7 +97,7 @@ namespace ASC.Files.Core.Data
services.TryAdd<ILinkDao, LinkDao>();
// AddSharpBoxDaoSelectorService
services.TryAdd<EditHistory>();
}
}
}

View File

@ -1202,14 +1202,12 @@ namespace ASC.Files.Core.Data
.Select(r =>
{
var item = ServiceProvider.GetService<EditHistory>();
var editHistoryAuthor = ServiceProvider.GetService<EditHistoryAuthor>();
editHistoryAuthor.Id = r.ModifiedBy;
item.ID = r.Id;
item.Version = r.Version;
item.VersionGroup = r.VersionGroup;
item.ModifiedOn = TenantUtil.DateTimeFromUtc(r.ModifiedOn);
item.ModifiedBy = editHistoryAuthor;
item.ModifiedBy = r.ModifiedBy;
item.ChangesString = r.Changes;
item.Key = documentServiceHelper.GetDocKey(item.ID, item.Version, TenantUtil.DateTimeFromUtc(r.CreateOn));

View File

@ -1,221 +1,225 @@
/*
*
* (c) Copyright Ascensio System Limited 2010-2018
*
* This program is freeware. You can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html).
* In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that
* Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights.
*
* THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR
* FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html
*
* You can contact Ascensio System SIA by email at sales@onlyoffice.com
*
* The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display
* Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3.
*
* Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains
* relevant author attributions when distributing the software. If the display of the logo in its graphic
* form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE"
* in every copy of the program you distribute.
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
*
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text.Json.Serialization;
using ASC.Common.Logging;
using ASC.Core;
using ASC.Core.Tenants;
using ASC.Core.Users;
using ASC.Files.Core.Resources;
using ASC.Web.Core.Users;
using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq;
namespace ASC.Files.Core
{
[DebuggerDisplay("{ID} v{Version}")]
public class EditHistory
{
public EditHistory(
IOptionsMonitor<ILog> options,
TenantUtil tenantUtil,
UserManager userManager,
DisplayUserSettingsHelper displayUserSettingsHelper)
{
Logger = options.CurrentValue;
TenantUtil = tenantUtil;
UserManager = userManager;
DisplayUserSettingsHelper = displayUserSettingsHelper;
}
public int ID { get; set; }
public string Key { get; set; }
public int Version { get; set; }
public int VersionGroup { get; set; }
[JsonPropertyName("user")]
public EditHistoryAuthor ModifiedBy { get; set; }
[JsonPropertyName("changeshistory")]
public string ChangesString { get; set; }
public List<EditHistoryChanges> Changes
{
get
{
var changes = new List<EditHistoryChanges>();
if (string.IsNullOrEmpty(ChangesString)) return changes;
try
{
var jObject = JObject.Parse(ChangesString);
ServerVersion = jObject.Value<string>("serverVersion");
/*
*
* (c) Copyright Ascensio System Limited 2010-2018
*
* This program is freeware. You can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html).
* In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that
* Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights.
*
* THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR
* FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html
*
* You can contact Ascensio System SIA by email at sales@onlyoffice.com
*
* The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display
* Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3.
*
* Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains
* relevant author attributions when distributing the software. If the display of the logo in its graphic
* form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE"
* in every copy of the program you distribute.
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
*
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text.Json;
using ASC.Common;
using ASC.Common.Logging;
using ASC.Core;
using ASC.Core.Tenants;
using ASC.Core.Users;
using ASC.Files.Core.Resources;
using ASC.Web.Core.Users;
using Microsoft.Extensions.Options;
namespace ASC.Files.Core
{
[Transient]
[DebuggerDisplay("{ID} v{Version}")]
public class EditHistory
{
private ILog Logger { get; }
private TenantUtil TenantUtil { get; }
private UserManager UserManager { get; }
private DisplayUserSettingsHelper DisplayUserSettingsHelper { get; }
public EditHistory(
IOptionsMonitor<ILog> options,
TenantUtil tenantUtil,
UserManager userManager,
DisplayUserSettingsHelper displayUserSettingsHelper)
{
Logger = options.CurrentValue;
TenantUtil = tenantUtil;
UserManager = userManager;
DisplayUserSettingsHelper = displayUserSettingsHelper;
}
public int ID { get; set; }
public string Key { get; set; }
public int Version { get; set; }
public int VersionGroup { get; set; }
public DateTime ModifiedOn { get; set; }
public Guid ModifiedBy { get; set; }
public string ChangesString { get; set; }
public string ServerVersion { get; set; }
public List<EditHistoryChanges> Changes
{
get
{
var changes = new List<EditHistoryChanges>();
if (string.IsNullOrEmpty(ChangesString)) return changes;
try
{
var options = new JsonSerializerOptions
{
AllowTrailingCommas = true,
PropertyNameCaseInsensitive = true
};
var jObject = JsonSerializer.Deserialize<ChangesDataList>(ChangesString, options);
ServerVersion = jObject.ServerVersion;
if (string.IsNullOrEmpty(ServerVersion))
return changes;
var jChanges = jObject.Value<JArray>("changes");
changes = jChanges.Children()
.Select(jChange =>
{
var jUser = jChange.Value<JObject>("user");
return new EditHistoryChanges(TenantUtil)
{
Date = jChange.Value<string>("created"),
Author = new EditHistoryAuthor(UserManager, DisplayUserSettingsHelper)
{
Id = new Guid(jUser.Value<string>("id") ?? Guid.Empty.ToString()),
Name = jUser.Value<string>("name"),
},
};
})
.ToList();
return changes;
}
catch (Exception ex)
{
Logger.Error("DeSerialize old scheme exception", ex);
}
return changes;
}
set { throw new NotImplementedException(); }
}
public DateTime ModifiedOn;
[JsonPropertyName("created")]
public string ModifiedOnString
{
get { return ModifiedOn.Equals(default) ? null : ModifiedOn.ToString("g"); }
set { throw new NotImplementedException(); }
}
public ILog Logger { get; }
private TenantUtil TenantUtil { get; }
private UserManager UserManager { get; }
private DisplayUserSettingsHelper DisplayUserSettingsHelper { get; }
public string ServerVersion;
}
[DebuggerDisplay("{Id} {Name}")]
public class EditHistoryAuthor
{
public EditHistoryAuthor(
UserManager userManager,
DisplayUserSettingsHelper displayUserSettingsHelper)
{
UserManager = userManager;
DisplayUserSettingsHelper = displayUserSettingsHelper;
}
public Guid Id { get; set; }
private string _name;
public string Name
{
get
{
UserInfo user;
return
Id.Equals(Guid.Empty)
|| Id.Equals(ASC.Core.Configuration.Constants.Guest.ID)
|| (user = UserManager.GetUsers(Id)).Equals(Constants.LostUser)
? string.IsNullOrEmpty(_name)
? FilesCommonResource.Guest
: _name
: user.DisplayUserName(false, DisplayUserSettingsHelper);
}
set { _name = value; }
}
private UserManager UserManager { get; }
private DisplayUserSettingsHelper DisplayUserSettingsHelper { get; }
}
[DebuggerDisplay("{Author.Name}")]
public class EditHistoryChanges
{
public EditHistoryChanges(TenantUtil tenantUtil)
{
TenantUtil = tenantUtil;
}
[JsonPropertyName("user")]
public EditHistoryAuthor Author { get; set; }
private DateTime _date;
[JsonPropertyName("created")]
public string Date
{
get { return _date.Equals(default) ? null : _date.ToString("g"); }
set
{
if (DateTime.TryParse(value, out _date))
{
_date = TenantUtil.DateTimeFromUtc(_date);
}
}
}
private TenantUtil TenantUtil { get; }
}
[DebuggerDisplay("{Version}")]
public class EditHistoryData
{
public string ChangesUrl { get; set; }
public string Key { get; set; }
public EditHistoryUrl Previous { get; set; }
public string Token { get; set; }
public string Url { get; set; }
public int Version { get; set; }
}
[DebuggerDisplay("{Key} - {Url}")]
public class EditHistoryUrl
{
public string Key { get; set; }
public string Url { get; set; }
}
changes = jObject.Changes.Select(r =>
{
var result = new EditHistoryChanges()
{
Author = new EditHistoryAuthor(UserManager, DisplayUserSettingsHelper)
{
Id = new Guid(r.User.Id ?? Guid.Empty.ToString()),
Name = r.User.Name,
}
};
if (DateTime.TryParse(r.Created, out var _date))
{
_date = TenantUtil.DateTimeFromUtc(_date);
}
result.Date = _date;
return result;
})
.ToList();
return changes;
}
catch (Exception ex)
{
Logger.Error("DeSerialize old scheme exception", ex);
}
return changes;
}
set { throw new NotImplementedException(); }
}
}
class ChangesDataList
{
public string ServerVersion { get; set; }
public ChangesData[] Changes { get; set; }
}
class ChangesData
{
public string Created { get; set; }
public ChangesUserData User { get; set; }
}
class ChangesUserData
{
public string Id { get; set; }
public string Name { get; set; }
}
[Transient]
[DebuggerDisplay("{Id} {Name}")]
public class EditHistoryAuthor
{
public EditHistoryAuthor(
UserManager userManager,
DisplayUserSettingsHelper displayUserSettingsHelper)
{
UserManager = userManager;
DisplayUserSettingsHelper = displayUserSettingsHelper;
}
public Guid Id { get; set; }
private string _name;
public string Name
{
get
{
UserInfo user;
return
Id.Equals(Guid.Empty)
|| Id.Equals(ASC.Core.Configuration.Constants.Guest.ID)
|| (user = UserManager.GetUsers(Id)).Equals(Constants.LostUser)
? string.IsNullOrEmpty(_name)
? FilesCommonResource.Guest
: _name
: user.DisplayUserName(false, DisplayUserSettingsHelper);
}
set { _name = value; }
}
private UserManager UserManager { get; }
private DisplayUserSettingsHelper DisplayUserSettingsHelper { get; }
}
[DebuggerDisplay("{Author.Name}")]
public class EditHistoryChanges
{
public EditHistoryAuthor Author { get; set; }
public DateTime Date { get; set; }
}
[DebuggerDisplay("{Version}")]
public class EditHistoryData
{
public string ChangesUrl { get; set; }
public string Key { get; set; }
public EditHistoryUrl Previous { get; set; }
public string Token { get; set; }
public string Url { get; set; }
public int Version { get; set; }
public string FileType { get; set; }
}
[DebuggerDisplay("{Key} - {Url}")]
public class EditHistoryUrl
{
public string Key { get; set; }
public string Url { get; set; }
public string FileType { get; set; }
}
}

View File

@ -1071,12 +1071,15 @@ namespace ASC.Web.Files.Services.WCFService
Key = DocumentServiceHelper.GetDocKey(file),
Url = DocumentServiceConnector.ReplaceCommunityAdress(PathProvider.GetFileStreamUrl(file, doc)),
Version = version,
FileType = GetFileExtensionWithoutDot(FileUtility.GetFileExtension(file.Title))
};
if (fileDao.ContainChanges(file.ID, file.Version))
{
string previouseKey;
string sourceFileUrl;
string previousFileExt;
if (file.Version > 1)
{
var previousFileStable = fileDao.GetFileStable(file.ID, file.Version - 1);
@ -1085,6 +1088,7 @@ namespace ASC.Web.Files.Services.WCFService
sourceFileUrl = PathProvider.GetFileStreamUrl(previousFileStable, doc);
previouseKey = DocumentServiceHelper.GetDocKey(previousFileStable);
previousFileExt = FileUtility.GetFileExtension(previousFileStable.Title);
}
else
{
@ -1105,12 +1109,14 @@ namespace ASC.Web.Files.Services.WCFService
sourceFileUrl = BaseCommonLinkUtility.GetFullAbsolutePath(sourceFileUrl);
previouseKey = DocumentServiceConnector.GenerateRevisionId(Guid.NewGuid().ToString());
previousFileExt = fileExt;
}
result.Previous = new EditHistoryUrl
{
Key = previouseKey,
Url = DocumentServiceConnector.ReplaceCommunityAdress(sourceFileUrl),
FileType = GetFileExtensionWithoutDot(previousFileExt)
};
result.ChangesUrl = PathProvider.GetFileChangesUrl(file, doc);
}
@ -1118,6 +1124,11 @@ namespace ASC.Web.Files.Services.WCFService
result.Token = DocumentServiceHelper.GetSignature(result);
return result;
string GetFileExtensionWithoutDot(string ext)
{
return ext.Substring(ext.IndexOf('.') + 1);
}
}
public List<EditHistory> RestoreVersion(T fileId, int version, string url = null, string doc = null)

View File

@ -0,0 +1,49 @@

using System.Collections.Generic;
using System.Linq;
using ASC.Api.Core;
using ASC.Core;
using ASC.Web.Core.Users;
namespace ASC.Files.Core.Model
{
public class EditHistoryWrapper
{
public int ID { get; set; }
public string Key { get; set; }
public int Version { get; set; }
public int VersionGroup { get; set; }
public EditHistoryAuthor User { get; set; }
public ApiDateTime Created { get; set; }
public string ChangesHistory { get; set; }
public List<EditHistoryChangesWrapper> Changes { get; set; }
public string ServerVersion { get; set; }
public EditHistoryWrapper(EditHistory editHistory, ApiDateTimeHelper apiDateTimeHelper, UserManager userManager, DisplayUserSettingsHelper displayUserSettingsHelper)
{
ID = editHistory.ID;
Key = editHistory.Key;
Version = editHistory.Version;
VersionGroup = editHistory.VersionGroup;
Changes = editHistory.Changes.Select(r => new EditHistoryChangesWrapper(r, apiDateTimeHelper)).ToList();
ChangesHistory = editHistory.ChangesString;
Created = apiDateTimeHelper.Get(editHistory.ModifiedOn);
User = new EditHistoryAuthor(userManager, displayUserSettingsHelper) { Id = editHistory.ModifiedBy };
ServerVersion = editHistory.ServerVersion;
}
}
public class EditHistoryChangesWrapper
{
public EditHistoryAuthor User { get; set; }
public ApiDateTime Created { get; set; }
public EditHistoryChangesWrapper(EditHistoryChanges historyChanges, ApiDateTimeHelper apiDateTimeHelper)
{
User = historyChanges.Author;
Created = apiDateTimeHelper.Get(historyChanges.Date);
}
}
}

View File

@ -1517,6 +1517,48 @@ namespace ASC.Api.Documents
return FilesControllerHelperInt.LockFile(fileId, model.LockFile);
}
[AllowAnonymous]
[Read("file/{fileId}/edit/history")]
public List<EditHistoryWrapper> GetEditHistory(string fileId, string doc = null)
{
return FilesControllerHelperString.GetEditHistory(fileId, doc);
}
[AllowAnonymous]
[Read("file/{fileId:int}/edit/history")]
public List<EditHistoryWrapper> GetEditHistory(int fileId, string doc = null)
{
return FilesControllerHelperInt.GetEditHistory(fileId, doc);
}
[AllowAnonymous]
[Read("file/{fileId}/edit/diff")]
public EditHistoryData GetEditDiffUrl(string fileId, int version = 0, string doc = null)
{
return FilesControllerHelperString.GetEditDiffUrl(fileId, version, doc);
}
[AllowAnonymous]
[Read("file/{fileId:int}/edit/diff")]
public EditHistoryData GetEditDiffUrl(int fileId, int version = 0, string doc = null)
{
return FilesControllerHelperInt.GetEditDiffUrl(fileId, version, doc);
}
[AllowAnonymous]
[Read("file/{fileId}/restoreversion")]
public List<EditHistoryWrapper> RestoreVersion(string fileId, int version = 0, string url = null, string doc = null)
{
return FilesControllerHelperString.RestoreVersion(fileId, version, url, doc);
}
[AllowAnonymous]
[Read("file/{fileId:int}/restoreversion")]
public List<EditHistoryWrapper> RestoreVersion(int fileId, int version = 0, string url = null, string doc = null)
{
return FilesControllerHelperInt.RestoreVersion(fileId, version, url, doc);
}
[Update("file/{fileId}/comment")]
public object UpdateCommentFromBody(string fileId, [FromBody] UpdateCommentModel model)
{

View File

@ -21,6 +21,7 @@ using ASC.Files.Core;
using ASC.Files.Core.Model;
using ASC.Files.Model;
using ASC.Web.Core.Files;
using ASC.Web.Core.Users;
using ASC.Web.Files.Classes;
using ASC.Web.Files.Core.Entries;
using ASC.Web.Files.Services.DocumentService;
@ -66,6 +67,9 @@ namespace ASC.Files.Helpers
private EncryptionKeyPairHelper EncryptionKeyPairHelper { get; }
private IHttpContextAccessor HttpContextAccessor { get; }
private FileConverter FileConverter { get; }
private ApiDateTimeHelper ApiDateTimeHelper { get; }
private UserManager UserManager { get; }
private DisplayUserSettingsHelper DisplayUserSettingsHelper { get; }
private ILog Logger { get; set; }
/// <summary>
@ -94,7 +98,10 @@ namespace ASC.Files.Helpers
SettingsManager settingsManager,
EncryptionKeyPairHelper encryptionKeyPairHelper,
IHttpContextAccessor httpContextAccessor,
FileConverter fileConverter)
FileConverter fileConverter,
ApiDateTimeHelper apiDateTimeHelper,
UserManager userManager,
DisplayUserSettingsHelper displayUserSettingsHelper)
{
ApiContext = context;
FileStorageService = fileStorageService;
@ -115,6 +122,9 @@ namespace ASC.Files.Helpers
DocumentServiceTracker = documentServiceTracker;
SettingsManager = settingsManager;
EncryptionKeyPairHelper = encryptionKeyPairHelper;
ApiDateTimeHelper = apiDateTimeHelper;
UserManager = userManager;
DisplayUserSettingsHelper = displayUserSettingsHelper;
HttpContextAccessor = httpContextAccessor;
FileConverter = fileConverter;
Logger = optionMonitor.Get("ASC.Files");
@ -571,6 +581,23 @@ namespace ASC.Files.Helpers
return FileStorageService.GetPresignedUri(fileId);
}
public List<EditHistoryWrapper> GetEditHistory(T fileId, string doc = null)
{
var result = FileStorageService.GetEditHistory(fileId, doc);
return result.Select(r => new EditHistoryWrapper(r, ApiDateTimeHelper, UserManager, DisplayUserSettingsHelper)).ToList();
}
public EditHistoryData GetEditDiffUrl(T fileId, int version = 0, string doc = null)
{
return FileStorageService.GetEditDiffUrl(fileId, version, doc);
}
public List<EditHistoryWrapper> RestoreVersion(T fileId, int version = 0, string url = null, string doc = null)
{
var result = FileStorageService.RestoreVersion(fileId, version, url, doc);
return result.Select(r => new EditHistoryWrapper(r, ApiDateTimeHelper, UserManager, DisplayUserSettingsHelper)).ToList();
}
public string UpdateComment(T fileId, int version, string comment)
{
return FileStorageService.UpdateComment(fileId, version, comment);

View File

@ -166,7 +166,9 @@ namespace ASC.Api.Settings
private ILog Log { get; set; }
private TelegramHelper TelegramHelper { get; }
private PaymentManager PaymentManager { get; }
public Constants Constants { get; }
private Constants Constants { get; }
private InstanceCrypto InstanceCrypto { get; }
private Signature Signature { get; }
public SettingsController(
IOptionsMonitor<ILog> option,
@ -228,7 +230,9 @@ namespace ASC.Api.Settings
EncryptionWorker encryptionWorker,
PasswordHasher passwordHasher,
PaymentManager paymentManager,
Constants constants)
Constants constants,
InstanceCrypto instanceCrypto,
Signature signature)
{
Log = option.Get("ASC.Api");
WebHostEnvironment = webHostEnvironment;
@ -290,6 +294,8 @@ namespace ASC.Api.Settings
TelegramHelper = telegramHelper;
PaymentManager = paymentManager;
Constants = constants;
InstanceCrypto = instanceCrypto;
Signature = signature;
}
[Read("", Check = false)]
@ -1598,7 +1604,7 @@ namespace ASC.Api.Settings
if (currentUser.IsVisitor(UserManager) || currentUser.IsOutsider(UserManager))
throw new NotSupportedException("Not available.");
return SettingsManager.LoadForCurrentUser<TfaAppUserSettings>().CodesSetting.Select(r => new { r.IsUsed, r.Code }).ToList();
return SettingsManager.LoadForCurrentUser<TfaAppUserSettings>().CodesSetting.Select(r => new { r.IsUsed, Code = r.GetEncryptedCode(InstanceCrypto, Signature) }).ToList();
}
[Update("tfaappnewcodes")]
@ -1612,7 +1618,7 @@ namespace ASC.Api.Settings
if (currentUser.IsVisitor(UserManager) || currentUser.IsOutsider(UserManager))
throw new NotSupportedException("Not available.");
var codes = TfaManager.GenerateBackupCodes().Select(r => new { r.IsUsed, r.Code }).ToList();
var codes = TfaManager.GenerateBackupCodes().Select(r => new { r.IsUsed, Code = r.GetEncryptedCode(InstanceCrypto, Signature) }).ToList();
MessageService.Send(MessageAction.UserConnectedTfaApp, MessageTarget.Create(currentUser.ID), currentUser.DisplayUserName(false, DisplayUserSettingsHelper));
return codes;
}

View File

@ -29,7 +29,9 @@ using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
using ASC.Common.Utils;
using ASC.Core.Common.Settings;
using ASC.Security.Cryptography;
namespace ASC.Web.Studio.Core.TFA
{
@ -65,7 +67,7 @@ namespace ASC.Web.Studio.Core.TFA
var from = new DateTime(2018, 07, 07, 0, 0, 0, DateTimeKind.Utc);
settings.SaltSetting = salt = (long)(DateTime.UtcNow - from).TotalMilliseconds;
settingsManager.SaveForUser<TfaAppUserSettings>(settings, userId);
settingsManager.SaveForUser(settings, userId);
}
return salt;
}
@ -75,10 +77,10 @@ namespace ASC.Web.Studio.Core.TFA
return settingsManager.LoadForUser<TfaAppUserSettings>(userId).CodesSetting;
}
public static void DisableCodeForUser(SettingsManager settingsManager, Guid userId, string code)
public static void DisableCodeForUser(SettingsManager settingsManager, InstanceCrypto instanceCrypto, Signature signature, Guid userId, string code)
{
var settings = settingsManager.LoadForUser<TfaAppUserSettings>(userId);
var query = settings.CodesSetting.Where(x => x.Code == code).ToList();
var query = settings.CodesSetting.Where(x => x.GetEncryptedCode(instanceCrypto, signature) == code).ToList();
if (query.Any())
query.First().IsUsed = true;

View File

@ -51,35 +51,26 @@ namespace ASC.Web.Studio.Core.TFA
[Serializable]
public class BackupCode
{
private string code;
private InstanceCrypto InstanceCrypto { get; }
private Signature Signature { get; }
public string Code
{
get
{
try
{
return InstanceCrypto.Decrypt(code);
}
catch
{
//support old scheme stored in the DB
return Signature.Read<string>(code);
}
}
set { code = InstanceCrypto.Encrypt(value); }
}
public bool IsUsed { get; set; }
public BackupCode(InstanceCrypto instanceCrypto, Signature signature, string code)
public string Code { get; set; }
public string GetEncryptedCode(InstanceCrypto InstanceCrypto, Signature Signature)
{
InstanceCrypto = instanceCrypto;
Signature = signature;
Code = code;
IsUsed = false;
try
{
return InstanceCrypto.Decrypt(Code);
}
catch
{
//support old scheme stored in the DB
return Signature.Read<string>(Code);
}
}
public void SetEncryptedCode(InstanceCrypto InstanceCrypto, string code)
{
Code = InstanceCrypto.Encrypt(code);
}
}
@ -145,9 +136,9 @@ namespace ASC.Web.Studio.Core.TFA
if (!Tfa.ValidateTwoFactorPIN(GenerateAccessToken(user), code))
{
if (checkBackup && TfaAppUserSettings.BackupCodesForUser(SettingsManager, user.ID).Any(x => x.Code == code && !x.IsUsed))
if (checkBackup && TfaAppUserSettings.BackupCodesForUser(SettingsManager, user.ID).Any(x => x.GetEncryptedCode(InstanceCrypto, Signature) == code && !x.IsUsed))
{
TfaAppUserSettings.DisableCodeForUser(SettingsManager, user.ID, code);
TfaAppUserSettings.DisableCodeForUser(SettingsManager, InstanceCrypto, Signature, user.ID, code);
}
else
{
@ -195,7 +186,9 @@ namespace ASC.Web.Studio.Core.TFA
result.Append(alphabet[b % (alphabet.Length)]);
}
list.Add(new BackupCode(InstanceCrypto, Signature, result.ToString()));
var code = new BackupCode();
code.SetEncryptedCode(InstanceCrypto, result.ToString());
list.Add(code);
}
}
var settings = SettingsManager.LoadForCurrentUser<TfaAppUserSettings>();

View File

@ -26,6 +26,9 @@ import {
getPresignedUri,
convertFile,
checkFillFormDraft,
getEditHistory,
getEditDiff,
restoreDocumentsVersion,
} from "@appserver/common/api/files";
import FilesFilter from "@appserver/common/api/files/filter";
@ -33,12 +36,12 @@ import throttle from "lodash/throttle";
import { isIOS, deviceType } from "react-device-detect";
import { homepage } from "../package.json";
import { AppServerConfig, FolderType } from "@appserver/common/constants";
import { AppServerConfig } from "@appserver/common/constants";
import SharingDialog from "files/SharingDialog";
import { getDefaultFileName, SaveAs, canConvert } from "files/utils";
import SelectFileDialog from "files/SelectFileDialog";
import SelectFolderDialog from "files/SelectFolderDialog";
import { StyledSelectFolder, StyledSelectFile } from "./StyledEditor";
import { StyledSelectFolder } from "./StyledEditor";
import i18n from "./i18n";
import Text from "@appserver/components/text";
import TextInput from "@appserver/components/text-input";
@ -67,7 +70,9 @@ let isSharingAccess;
let user = null;
let personal;
let url = window.location.href;
let config;
const filesUrl = url.substring(0, url.indexOf("/doceditor"));
const doc = url.indexOf("doc=") !== -1 ? url.split("doc=")[1] : null;
toast.configure();
@ -142,7 +147,7 @@ const Editor = () => {
docEditor.setFavorite(favorite);
};
const initDesktop = (config) => {
const initDesktop = () => {
const isEncryption = config?.editorConfig["encryptionKeys"] !== undefined;
regDesktop(
@ -237,7 +242,7 @@ const Editor = () => {
setIsAuthenticated(successAuth);
}
const config = await openEdit(fileId, version, doc, view);
config = await openEdit(fileId, version, doc, view);
if (
!view &&
@ -269,7 +274,7 @@ const Editor = () => {
setIsLoading(false);
loadScript(docApiUrl, "scripDocServiceAddress", () => onLoad(config));
loadScript(docApiUrl, "scripDocServiceAddress", () => onLoad());
} catch (error) {
console.log(error);
toastr.error(
@ -332,7 +337,7 @@ const Editor = () => {
document.title = title;
};
const onLoad = (config) => {
const onLoad = () => {
try {
if (!window.DocsAPI) throw new Error("DocsAPI is not defined");
@ -408,7 +413,8 @@ const Editor = () => {
onRequestSaveAs,
onRequestInsertImage,
onRequestMailMergeRecipients,
onRequestCompareFile;
onRequestCompareFile,
onRequestRestore;
if (isSharingAccess) {
onRequestSharingSettings = onSDKRequestSharingSettings;
@ -425,6 +431,9 @@ const Editor = () => {
onRequestCompareFile = onSDKRequestCompareFile;
}
if (!!config.document.permissions.changeHistory) {
onRequestRestore = onSDKRequestRestore;
}
const events = {
events: {
onAppReady: onSDKAppReady,
@ -442,6 +451,10 @@ const Editor = () => {
onRequestMailMergeRecipients,
onRequestCompareFile,
onRequestEditRights: onSDKRequestEditRights,
onRequestHistory: onSDKRequestHistory,
onRequestHistoryClose: onSDKRequestHistoryClose,
onRequestHistoryData: onSDKRequestHistoryData,
onRequestRestore,
},
};
@ -454,6 +467,114 @@ const Editor = () => {
}
};
const onSDKRequestHistoryData = async (event) => {
const version = event.data;
try {
const versionDifference = await getEditDiff(fileId, version, doc);
const changesUrl = versionDifference.changesUrl;
const previous = versionDifference.previous;
const token = versionDifference.token;
docEditor.setHistoryData({
...(changesUrl && { changesUrl }),
key: versionDifference.key,
fileType: versionDifference.fileType,
...(previous && {
previous: {
fileType: previous.fileType,
key: previous.key,
url: previous.url,
},
}),
...(token && { token }),
url: versionDifference.url,
version,
});
} catch (e) {
docEditor.setHistoryData({
error: `${e}`, //TODO: maybe need to display something else.
version,
});
}
};
const onSDKRequestHistoryClose = () => {
document.location.reload();
};
const getDocumentHistory = (fileHistory, historyLength) => {
let result = [];
for (let i = 0; i < historyLength; i++) {
const changes = fileHistory[i].changes;
const serverVersion = fileHistory[i].serverVersion;
const version = fileHistory[i].version;
const versionGroup = fileHistory[i].versionGroup;
let obj = {
...(changes.length !== 0 && { changes }),
created: `${new Date(fileHistory[i].created).toLocaleString(
config.editorConfig.lang
)}`,
...(serverVersion && { serverVersion }),
key: fileHistory[i].key,
user: {
id: fileHistory[i].user.id,
name: fileHistory[i].user.name,
},
version,
versionGroup,
};
result.push(obj);
}
return result;
};
const getCurrentDocumentVersion = (fileHistory, historyLength) => {
return url.indexOf("&version=") !== -1
? +url.split("&version=")[1]
: fileHistory[historyLength - 1].version;
};
const onSDKRequestHistory = async () => {
try {
const fileHistory = await getEditHistory(fileId, doc);
const historyLength = fileHistory.length;
docEditor.refreshHistory({
currentVersion: getCurrentDocumentVersion(fileHistory, historyLength),
history: getDocumentHistory(fileHistory, historyLength),
});
} catch (e) {
docEditor.refreshHistory({
error: `${e}`, //TODO: maybe need to display something else.
});
}
};
const onSDKRequestRestore = async (event) => {
const restoreVersion = event.data.version;
try {
const updateVersions = await restoreDocumentsVersion(
fileId,
restoreVersion,
doc
);
const historyLength = updateVersions.length;
docEditor.refreshHistory({
currentVersion: getCurrentDocumentVersion(
updateVersions,
historyLength
),
history: getDocumentHistory(updateVersions, historyLength),
});
} catch (e) {
docEditor.refreshHistory({
error: `${e}`, //TODO: maybe need to display something else.
});
}
};
const onSDKAppReady = () => {
console.log("ONLYOFFICE Document Editor is ready");
@ -738,7 +859,7 @@ const Editor = () => {
onSelectFile={onSelectFile}
isPanelVisible={isFileDialogVisible}
onClose={onCloseFileDialog}
foldersType="exceptTrashFolder"
foldersType="exceptPrivacyTrashFolders"
{...fileTypeDetection()}
titleFilesList={selectFilesListTitle()}
headerName={i18n.t("SelectFileTitle")}