Merge branch 'release/rc-v1.2.0' of github.com:ONLYOFFICE/DocSpace into release/rc-v1.2.0

This commit is contained in:
Akmal Isomadinov 2022-12-26 17:46:01 +05:00
commit 0ee663afb1
16 changed files with 217 additions and 106 deletions

View File

@ -73,7 +73,7 @@ public class MigrationCreator
_moduleProvider = moduleProvider;
}
public async Task<string> Create(int tenant, string userName, string toRegion)
public string Create(int tenant, string userName, string toRegion)
{
Init(tenant, userName, toRegion);
@ -83,7 +83,7 @@ public class MigrationCreator
using (var writer = new ZipWriteOperator(_tempStream, path))
{
DoMigrationDb(id, writer);
await DoMigrationStorage(id, writer);
DoMigrationStorage(id, writer);
}
return fileName;
}
@ -209,13 +209,13 @@ public class MigrationCreator
data.Rows[0]["name"] = "";
}
private async Task DoMigrationStorage(Guid id, IDataWriteOperator writer)
private void DoMigrationStorage(Guid id, IDataWriteOperator writer)
{
Console.WriteLine($"start backup storage");
var fileGroups = await GetFilesGroup(id);
var fileGroups = GetFilesGroup(id);
foreach (var group in fileGroups)
{
Console.WriteLine($"start backup fileGroup: {group}");
Console.WriteLine($"start backup fileGroup: {group.Key}");
foreach (var file in group)
{
var storage = _storageFactory.GetStorage(_tenant, group.Key);
@ -227,7 +227,7 @@ public class MigrationCreator
writer.WriteEntry(file1.GetZipKey(), fileStream);
}, file, 5);
}
Console.WriteLine($"end backup fileGroup: {group}");
Console.WriteLine($"end backup fileGroup: {group.Key}");
}
var restoreInfoXml = new XElement(
@ -244,9 +244,9 @@ public class MigrationCreator
Console.WriteLine($"end backup storage");
}
private async Task<List<IGrouping<string, BackupFileInfo>>> GetFilesGroup(Guid id)
private List<IGrouping<string, BackupFileInfo>> GetFilesGroup(Guid id)
{
var files = (await GetFilesToProcess(id)).ToList();
var files = GetFilesToProcess(id).ToList();
var backupsContext = _dbFactory.CreateDbContext<BackupsContext>();
var exclude = backupsContext.Backups.AsQueryable().Where(b => b.TenantId == _tenant && b.StorageType == 0 && b.StoragePath != null).ToList();
@ -254,39 +254,60 @@ public class MigrationCreator
return files.GroupBy(file => file.Module).ToList();
}
protected async Task<IEnumerable<BackupFileInfo>> GetFilesToProcess(Guid id)
private IEnumerable<BackupFileInfo> GetFilesToProcess(Guid id)
{
var files = new List<BackupFileInfo>();
foreach (var module in _storageFactoryConfig.GetModuleList().Where(m => m == "files"))
{
var store = _storageFactory.GetStorage(_tenant, module);
var domains = _storageFactoryConfig.GetDomainList(module).ToArray();
foreach (var domain in domains)
{
files.AddRange(await store.ListFilesRelativeAsync(domain, "\\", "*.*", true).Select(path => new BackupFileInfo(domain, module, path, _tenant)).ToListAsync());
}
files.AddRange(await
store.ListFilesRelativeAsync(string.Empty, "\\", "*.*", true)
.Where(path => domains.All(domain => !path.Contains(domain + "/")))
.Select(path => new BackupFileInfo(string.Empty, module, path, _tenant))
.ToListAsync());
}
var filesDbContext = _dbFactory.CreateDbContext<FilesDbContext>();
files = files.Where(f => UserIsFileOwner(id, f, filesDbContext)).ToList();
var filesDbContext = _dbFactory.CreateDbContext<FilesDbContext>();
var module = _storageFactoryConfig.GetModuleList().Where(m => m == "files").Single();
var store = _storageFactory.GetStorage(_tenant, module);
var dbFiles = filesDbContext.Files.Where(q => q.CreateBy == id && q.TenantId == _tenant).ToList();
var tasks = new List<Task>(20);
foreach (var dbFile in dbFiles)
{
if (tasks.Count != 20)
{
tasks.Add(FindFiles(files, store, dbFile, module));
}
else
{
Task.WaitAll(tasks.ToArray());
tasks.Clear();
}
}
Task.WaitAll(tasks.ToArray());
return files.Distinct();
}
private bool UserIsFileOwner(Guid id, BackupFileInfo fileInfo, FilesDbContext filesDbContext)
{
var stringId = id.ToString();
return filesDbContext.Files.Any(
f => f.CreateBy == id &&
f.TenantId == _tenant &&
fileInfo.Path.Contains("\\file_" + f.Id + "\\"));
private async Task FindFiles(List<BackupFileInfo> list, IDataStore store, DbFile dbFile, string module)
{
var files = await store.ListFilesRelativeAsync(string.Empty, $"\\{GetUniqFileDirectory(dbFile.Id)}", "*.*", true)
.Select(path => new BackupFileInfo(string.Empty, module, path, _tenant))
.ToListAsync();
list.AddRange(files);
if (files.Any())
{
Console.WriteLine($"file {dbFile.Id} found");
}
else
{
Console.WriteLine($"file {dbFile.Id} not found");
}
}
private string GetUniqFileDirectory(int fileId)
{
if (fileId == 0)
{
throw new ArgumentNullException("fileIdObject");
}
return string.Format("folder_{0}/file_{1}", (fileId / 1000 + 1) * 1000, fileId);
}
}

View File

@ -89,7 +89,7 @@ builder.WebHost.ConfigureServices((hostContext, services) =>
var app = builder.Build();
Console.WriteLine("backup start");
var migrationCreator = app.Services.GetService<MigrationCreator>();
var fileName = await migrationCreator.Create(param.Tenant, param.UserName, param.ToRegion);
var fileName = migrationCreator.Create(param.Tenant, param.UserName, param.ToRegion);
Console.WriteLine("backup was success");
Console.WriteLine("restore start");
var migrationRunner = app.Services.GetService<MigrationRunner>();

View File

@ -5,6 +5,7 @@ const WhiteLabelWrapper = styled.div`
.subtitle {
margin-top: 5px;
margin-bottom: 20px;
color: ${(props) => props.theme.client.settings.common.descriptionColor};
}
.header-container {

View File

@ -19,7 +19,7 @@ import {
generateLogo,
getLogoOptions,
uploadLogo,
} from "../../../utils/generateLogo";
} from "../../../utils/whiteLabelHelper";
import isEqual from "lodash/isEqual";
const WhiteLabel = (props) => {
@ -131,7 +131,6 @@ const WhiteLabel = (props) => {
};
const onSave = async () => {
console.log(logoUrlsWhiteLabel);
let logosArr = [];
for (let i = 0; i < logoUrlsWhiteLabel.length; i++) {
@ -174,9 +173,7 @@ const WhiteLabel = (props) => {
<LoaderWhiteLabel />
) : (
<WhiteLabelWrapper>
<Text className="subtitle" color="#657077">
{t("BrandingSubtitle")}
</Text>
<Text className="subtitle">{t("BrandingSubtitle")}</Text>
<div className="header-container">
<Text fontSize="16px" fontWeight="700">

View File

@ -6,9 +6,10 @@ import SelectUsersCountContainer from "./sub-components/SelectUsersCountContaine
import TotalTariffContainer from "./sub-components/TotalTariffContainer";
import toastr from "@docspace/components/toast/toastr";
import axios from "axios";
import { combineUrl } from "@docspace/common/utils";
//import { combineUrl } from "@docspace/common/utils";
import ButtonContainer from "./sub-components/ButtonContainer";
import { Trans } from "react-i18next";
import { getPaymentLink } from "@docspace/common/api/portal";
const StyledBody = styled.div`
border-radius: 12px;
@ -99,16 +100,9 @@ const PriceCalculation = ({
CancelToken = axios.CancelToken;
source = CancelToken.source();
await axios
.put(
combineUrl(window.DocSpaceConfig?.proxy?.url, "/portal/payment/url"),
{ quantity: { admin: value } },
{
cancelToken: source.token,
}
)
.then((response) => {
setPaymentLink(response.data.response);
await getPaymentLink(value, source.token)
.then((link) => {
setPaymentLink(link);
setIsLoading(false);
})
.catch((thrown) => {

View File

@ -239,13 +239,14 @@ export function getPaymentAccount() {
return request({ method: "get", url: "/portal/payment/account" });
}
export function getPaymentLink(adminCount, currency) {
export function getPaymentLink(adminCount, cancelToken) {
return request({
method: "put",
url: `/portal/payment/url`,
data: {
quantity: { admin: adminCount },
},
cancelToken,
});
}

View File

@ -0,0 +1,33 @@
// (c) Copyright Ascensio System SIA 2010-2022
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL 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 details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.Files.Core.ApiModels.ResponseDto;
public class RoomSecurityDto
{
public IEnumerable<FileShareDto> Members { get; set; }
public string Warning { get; set; }
}

View File

@ -2486,12 +2486,13 @@ public class FileStorageService<T> //: IFileStorageService
return _fileSharing.GetSharedInfoShortFolderAsync(folderId);
}
public async Task SetAceObjectAsync(AceCollection<T> aceCollection, bool notify)
public async Task<string> SetAceObjectAsync(AceCollection<T> aceCollection, bool notify)
{
var fileDao = GetFileDao();
var folderDao = GetFolderDao();
var entries = new List<FileEntry<T>>();
string warning = null;
foreach (var fileId in aceCollection.Files)
{
@ -2507,7 +2508,9 @@ public class FileStorageService<T> //: IFileStorageService
{
try
{
var changed = await _fileSharingAceHelper.SetAceObjectAsync(aceCollection.Aces, entry, notify, aceCollection.Message, aceCollection.AdvancedSettings);
var (changed, warningMessage) = await _fileSharingAceHelper.SetAceObjectAsync(aceCollection.Aces, entry, notify, aceCollection.Message, aceCollection.AdvancedSettings);
warning ??= warningMessage;
if (changed)
{
foreach (var ace in aceCollection.Aces)
@ -2533,6 +2536,8 @@ public class FileStorageService<T> //: IFileStorageService
throw GenerateException(e);
}
}
return warning;
}
public async Task RemoveAceAsync(List<T> filesId, List<T> foldersId)
@ -2602,7 +2607,7 @@ public class FileStorageService<T> //: IFileStorageService
try
{
var changed = await _fileSharingAceHelper.SetAceObjectAsync(aces, room, false, null, null);
var (changed, _) = await _fileSharingAceHelper.SetAceObjectAsync(aces, room, false, null, null);
if (changed)
{
_filesMessageService.Send(room, GetHttpHeaders(), MessageAction.RoomInvintationUpdateAccess, room.Title, GetAccessString(share));
@ -2633,7 +2638,7 @@ public class FileStorageService<T> //: IFileStorageService
try
{
var changed = await _fileSharingAceHelper.SetAceObjectAsync(aces, file, false, null, null);
var (changed, _) = await _fileSharingAceHelper.SetAceObjectAsync(aces, file, false, null, null);
if (changed)
{
_filesMessageService.Send(file, GetHttpHeaders(), MessageAction.FileExternalLinkAccessUpdated, file.Title, GetAccessString(share));
@ -2767,16 +2772,18 @@ public class FileStorageService<T> //: IFileStorageService
try
{
var aces = new List<AceWrapper>
{
new AceWrapper
{
new AceWrapper
{
Access = FileShare.Read,
Id = recipient.Id,
SubjectGroup = false,
}
};
Access = FileShare.Read,
Id = recipient.Id,
SubjectGroup = false,
}
};
showSharingSettings |= await _fileSharingAceHelper.SetAceObjectAsync(aces, file, false, null, null);
var (changed, _) = await _fileSharingAceHelper.SetAceObjectAsync(aces, file, false, null, null);
showSharingSettings |= changed;
if (showSharingSettings)
{
foreach (var ace in aces)

View File

@ -30,14 +30,14 @@ public static class DocSpaceHelper
{
public static HashSet<FileShare> PaidRights { get; } = new HashSet<FileShare> { FileShare.RoomAdmin };
private static readonly HashSet<FileShare> _fillingFormRoomConstraints
= new HashSet<FileShare> { FileShare.RoomAdmin, FileShare.FillForms, FileShare.Read, FileShare.None };
private static readonly HashSet<FileShare> _collaborationRoomConstraints
= new HashSet<FileShare> { FileShare.RoomAdmin, FileShare.Editing, FileShare.Read, FileShare.None };
private static readonly HashSet<FileShare> _reviewRoomConstraints
= new HashSet<FileShare> { FileShare.RoomAdmin, FileShare.Review, FileShare.Comment, FileShare.Read, FileShare.None };
private static readonly HashSet<FileShare> _viewOnlyRoomConstraints
= new HashSet<FileShare> { FileShare.RoomAdmin, FileShare.Read, FileShare.None };
private static readonly List<FileShare> _fillingFormRoomRoles
= new List<FileShare> { FileShare.RoomAdmin, FileShare.FillForms, FileShare.Read, FileShare.None };
private static readonly List<FileShare> _collaborationRoomRoles
= new List<FileShare> { FileShare.RoomAdmin, FileShare.Editing, FileShare.Read, FileShare.None };
private static readonly List<FileShare> _reviewRoomRoles
= new List<FileShare> { FileShare.RoomAdmin, FileShare.Review, FileShare.Comment, FileShare.Read, FileShare.None };
private static readonly List<FileShare> _viewOnlyRoomRoles
= new List<FileShare> { FileShare.RoomAdmin, FileShare.Read, FileShare.None };
public static bool IsRoom(FolderType folderType)
{
@ -54,21 +54,29 @@ public static class DocSpaceHelper
return room != null && room.Private;
}
public static bool ValidateShare(FolderType folderType, FileShare fileShare, bool isUser)
public static bool ValidateShare(FolderType folderType, FileShare fileShare)
{
if (isUser && PaidRights.Contains(fileShare))
{
return false;
}
return folderType switch
{
FolderType.CustomRoom => true,
FolderType.FillingFormsRoom => _fillingFormRoomConstraints.Contains(fileShare),
FolderType.EditingRoom => _collaborationRoomConstraints.Contains(fileShare),
FolderType.ReviewRoom => _reviewRoomConstraints.Contains(fileShare),
FolderType.ReadOnlyRoom => _viewOnlyRoomConstraints.Contains(fileShare),
FolderType.FillingFormsRoom => _fillingFormRoomRoles.Contains(fileShare),
FolderType.EditingRoom => _collaborationRoomRoles.Contains(fileShare),
FolderType.ReviewRoom => _reviewRoomRoles.Contains(fileShare),
FolderType.ReadOnlyRoom => _viewOnlyRoomRoles.Contains(fileShare),
_ => false
};
}
public static FileShare GetHighFreeRole(FolderType folderType)
{
return folderType switch
{
FolderType.CustomRoom => FileShare.Editing,
FolderType.FillingFormsRoom => _fillingFormRoomRoles[1],
FolderType.EditingRoom => _collaborationRoomRoles[1],
FolderType.ReviewRoom => _reviewRoomRoles[1],
FolderType.ReadOnlyRoom => _viewOnlyRoomRoles[1],
_ => FileShare.None
};
}
}

View File

@ -780,6 +780,15 @@ namespace ASC.Files.Core.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to The role is only available to a paid user.
/// </summary>
public static string ErrorMessage_PaidRole {
get {
return ResourceManager.GetString("ErrorMessage_PaidRole", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You don&apos;t have permission to copy to this folder.
/// </summary>

View File

@ -357,6 +357,9 @@
<data name="ErrorMessage_InvintationLink" xml:space="preserve">
<value>The invitation link is invalid or it's validity has expired</value>
</data>
<data name="ErrorMessage_PaidRole" xml:space="preserve">
<value>The role is only available to a paid user</value>
</data>
<data name="ErrorMessage_SecurityException_CopyToFolder" xml:space="preserve">
<value>You don't have permission to copy to this folder</value>
</data>

View File

@ -46,6 +46,7 @@ public class FileSharingAceHelper<T>
private readonly StudioNotifyService _studioNotifyService;
private readonly UsersInRoomChecker _usersInRoomChecker;
private readonly UserManagerWrapper _userManagerWrapper;
private readonly CountRoomAdminChecker _countRoomAdminChecker;
private readonly ILogger _logger;
public FileSharingAceHelper(
@ -65,7 +66,8 @@ public class FileSharingAceHelper<T>
StudioNotifyService studioNotifyService,
ILoggerProvider loggerProvider,
UsersInRoomChecker usersInRoomChecker,
UserManagerWrapper userManagerWrapper)
UserManagerWrapper userManagerWrapper,
CountRoomAdminChecker countRoomAdminChecker)
{
_fileSecurity = fileSecurity;
_coreBaseSettings = coreBaseSettings;
@ -84,9 +86,10 @@ public class FileSharingAceHelper<T>
_usersInRoomChecker = usersInRoomChecker;
_logger = loggerProvider.CreateLogger("ASC.Files");
_userManagerWrapper = userManagerWrapper;
_countRoomAdminChecker = countRoomAdminChecker;
}
public async Task<bool> SetAceObjectAsync(List<AceWrapper> aceWrappers, FileEntry<T> entry, bool notify, string message, AceAdvancedSettingsWrapper advancedSettings)
public async Task<(bool, string)> SetAceObjectAsync(List<AceWrapper> aceWrappers, FileEntry<T> entry, bool notify, string message, AceAdvancedSettingsWrapper advancedSettings)
{
if (entry == null)
{
@ -108,18 +111,27 @@ public class FileSharingAceHelper<T>
var recipients = new Dictionary<Guid, FileShare>();
var usersWithoutRight = new List<Guid>();
var changed = false;
string warning = null;
var shares = (await _fileSecurity.GetSharesAsync(entry)).ToList();
var i = 1;
foreach (var w in aceWrappers.OrderByDescending(ace => ace.SubjectGroup))
{
if (entry is Folder<T> folder && DocSpaceHelper.IsRoom(folder.FolderType) &&
!DocSpaceHelper.ValidateShare(folder.FolderType, w.Access, _userManager.IsUser(w.Id)))
!DocSpaceHelper.ValidateShare(folder.FolderType, w.Access))
{
continue;
}
if (!await ProcessEmailAceAsync(w))
if (_userManager.IsUser(w.Id) && DocSpaceHelper.PaidRights.Contains(w.Access))
{
throw new InvalidOperationException(FilesCommonResource.ErrorMessage_PaidRole);
};
var (success, msg) = await ProcessEmailAceAsync(w, entry);
warning ??= msg;
if (!success)
{
continue;
}
@ -253,7 +265,7 @@ public class FileSharingAceHelper<T>
await _fileMarker.RemoveMarkAsNewAsync(entry, userId);
}
return changed;
return (changed, warning);
}
public async Task RemoveAceAsync(FileEntry<T> entry)
@ -279,28 +291,49 @@ public class FileSharingAceHelper<T>
await _fileMarker.RemoveMarkAsNewAsync(entry);
}
private async Task<bool> ProcessEmailAceAsync(AceWrapper ace)
private async Task<(bool, string)> ProcessEmailAceAsync(AceWrapper ace, FileEntry<T> entry)
{
if (string.IsNullOrEmpty(ace.Email))
{
return true;
return (true, null);
}
var type = DocSpaceHelper.PaidRights.Contains(ace.Access) ? EmployeeType.RoomAdmin : EmployeeType.User;
UserInfo user = null;
var room = entry is Folder<T> folder && DocSpaceHelper.IsRoom(folder.FolderType) ? folder : null;
if (room == null)
{
return (false, null);
}
UserInfo user;
try
{
user = await _userManagerWrapper.AddInvitedUserAsync(ace.Email, type);
if (DocSpaceHelper.PaidRights.Contains(ace.Access))
{
await _countRoomAdminChecker.CheckAppend();
user = await _userManagerWrapper.AddInvitedUserAsync(ace.Email, EmployeeType.RoomAdmin);
}
else
{
user = await _userManagerWrapper.AddInvitedUserAsync(ace.Email, EmployeeType.User);
}
}
catch
catch(TenantQuotaException e)
{
return false;
ace.Access = DocSpaceHelper.GetHighFreeRole(room.FolderType);
user = await _userManagerWrapper.AddInvitedUserAsync(ace.Email, EmployeeType.User);
ace.Id = user.Id;
return (true, e.Message);
}
catch(Exception e)
{
return (false, e.Message);
}
ace.Id = user.Id;
return true;
return (true, null);
}
}

View File

@ -317,9 +317,11 @@ public abstract class VirtualRoomsController<T> : ApiControllerBase
/// Room security info
/// </returns>
[HttpPut("rooms/{id}/share")]
public async Task<IEnumerable<FileShareDto>> SetRoomSecurityAsync(T id, RoomInvitationRequestDto inDto)
public async Task<RoomSecurityDto> SetRoomSecurityAsync(T id, RoomInvitationRequestDto inDto)
{
ErrorIfNotDocSpace();
ErrorIfNotDocSpace();
var result = new RoomSecurityDto();
if (inDto.Invitations != null && inDto.Invitations.Any())
{
@ -333,10 +335,12 @@ public abstract class VirtualRoomsController<T> : ApiControllerBase
Message = inDto.Message
};
await _fileStorageService.SetAceObjectAsync(aceCollection, inDto.Notify);
}
result.Warning = await _fileStorageService.SetAceObjectAsync(aceCollection, inDto.Notify);
}
result.Members = await GetRoomSecurityInfoAsync(id).ToListAsync();
return await GetRoomSecurityInfoAsync(id).ToListAsync();
return result;
}
/// <summary>

View File

@ -2293,7 +2293,7 @@ namespace ASC.Web.Core.PublicResources {
}
/// <summary>
/// Looks up a localized string similar to The number of managers should not exceed {0}.
/// Looks up a localized string similar to The number of admins should not exceed {0}.
/// </summary>
public static string TariffsFeature_manager_exception {
get {

View File

@ -889,7 +889,7 @@
<value>The number of rooms should not exceed {0}</value>
</data>
<data name="TariffsFeature_manager_exception" xml:space="preserve">
<value>The number of managers should not exceed {0}</value>
<value>The number of admins should not exceed {0}</value>
</data>
<data name="TariffsFeature_total_size_exception" xml:space="preserve">
<value>The used storage size should not exceed {0}</value>
@ -930,4 +930,4 @@
<data name="ConsumersTelegramInstructionV11" xml:space="preserve">
<value>To receive portal notifications via Telegram, enable Telegram bot. You can create a new bot using BotFather in Telegram Desktop. To use this bot, portal users need to enable Telegram notifications on their Profile Page. {0}Paste bots username and the token you received in the fields below.</value>
</data>
</root>
</root>