Merge branch 'feature/files' of https://github.com/ONLYOFFICE/CommunityServer-AspNetCore into feature/files

This commit is contained in:
Nikita Gopienko 2020-06-30 15:00:57 +03:00
commit 0025f3b858
26 changed files with 853 additions and 761 deletions

View File

@ -29,6 +29,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using ASC.Common.Logging; using ASC.Common.Logging;
using ASC.Common.Notify.Patterns; using ASC.Common.Notify.Patterns;
using ASC.Notify.Channels; using ASC.Notify.Channels;
@ -36,6 +37,7 @@ using ASC.Notify.Cron;
using ASC.Notify.Messages; using ASC.Notify.Messages;
using ASC.Notify.Patterns; using ASC.Notify.Patterns;
using ASC.Notify.Recipients; using ASC.Notify.Recipients;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@ -360,9 +362,10 @@ namespace ASC.Notify.Engine
PrepareRequestFillPatterns(request); PrepareRequestFillPatterns(request);
PrepareRequestFillTags(request); PrepareRequestFillTags(request);
} }
catch (Exception) catch (Exception ex)
{ {
responses.Add(new SendResponse(request.NotifyAction, null, request.Recipient, SendResult.Impossible)); responses.Add(new SendResponse(request.NotifyAction, null, request.Recipient, SendResult.Impossible));
log.Error("Prepare", ex);
} }
if (request.SenderNames != null && request.SenderNames.Length > 0) if (request.SenderNames != null && request.SenderNames.Length > 0)

View File

@ -26,10 +26,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\products\ASC.Files\Server\ASC.Files.csproj" /> <ProjectReference Include="..\..\..\products\ASC.Files\Server\ASC.Files.csproj" />
<ProjectReference Include="..\..\..\web\ASC.Web.Core\ASC.Web.Core.csproj" />
<ProjectReference Include="..\..\ASC.Common\ASC.Common.csproj" /> <ProjectReference Include="..\..\ASC.Common\ASC.Common.csproj" />
<ProjectReference Include="..\..\ASC.Core.Common\ASC.Core.Common.csproj" /> <ProjectReference Include="..\..\ASC.Core.Common\ASC.Core.Common.csproj" />
<ProjectReference Include="..\..\ASC.Data.Storage\ASC.Data.Storage.csproj" /> <ProjectReference Include="..\..\ASC.Data.Storage\ASC.Data.Storage.csproj" />
<ProjectReference Include="..\ASC.Notify\ASC.Notify.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Protobuf Include="protos\BackupProgress.proto" /> <Protobuf Include="protos\BackupProgress.proto" />

View File

@ -24,11 +24,13 @@
*/ */
using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using ASC.Common; using ASC.Common;
using ASC.Common.Utils; using ASC.Common.Utils;
using ASC.Web.Studio.Core.Notify;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
@ -37,6 +39,7 @@ namespace ASC.Data.Backup.Service
{ {
internal class BackupServiceLauncher : IHostedService internal class BackupServiceLauncher : IHostedService
{ {
public IServiceProvider ServiceProvider { get; }
private BackupCleanerService CleanerService { get; set; } private BackupCleanerService CleanerService { get; set; }
private BackupSchedulerService SchedulerService { get; set; } private BackupSchedulerService SchedulerService { get; set; }
private BackupWorker BackupWorker { get; set; } private BackupWorker BackupWorker { get; set; }
@ -44,12 +47,14 @@ namespace ASC.Data.Backup.Service
public BackupServiceNotifier BackupServiceNotifier { get; } public BackupServiceNotifier BackupServiceNotifier { get; }
public BackupServiceLauncher( public BackupServiceLauncher(
IServiceProvider serviceProvider,
BackupCleanerService cleanerService, BackupCleanerService cleanerService,
BackupSchedulerService schedulerService, BackupSchedulerService schedulerService,
BackupWorker backupWorker, BackupWorker backupWorker,
IConfiguration configuration, IConfiguration configuration,
BackupServiceNotifier backupServiceNotifier) BackupServiceNotifier backupServiceNotifier)
{ {
ServiceProvider = serviceProvider;
CleanerService = cleanerService; CleanerService = cleanerService;
SchedulerService = schedulerService; SchedulerService = schedulerService;
BackupWorker = backupWorker; BackupWorker = backupWorker;
@ -59,6 +64,8 @@ namespace ASC.Data.Backup.Service
public Task StartAsync(CancellationToken cancellationToken) public Task StartAsync(CancellationToken cancellationToken)
{ {
NotifyConfiguration.Configure(ServiceProvider);
var settings = Configuration.GetSetting<BackupSettings>("backup"); var settings = Configuration.GetSetting<BackupSettings>("backup");
BackupWorker.Start(settings); BackupWorker.Start(settings);

View File

@ -25,69 +25,128 @@
using System; using System;
using System.Linq;
using ASC.Common; using ASC.Common;
using ASC.Common.Logging; using ASC.Core;
using Microsoft.Extensions.Options; using ASC.Core.Tenants;
using ASC.Notify; using ASC.Core.Users;
using ASC.Notify.Model;
using ASC.Notify.Patterns;
using ASC.Notify.Recipients;
using ASC.Web.Core.Users;
using ASC.Web.Studio.Core.Notify;
using ASC.Web.Studio.Utility;
using Microsoft.Extensions.DependencyInjection;
namespace ASC.Data.Backup namespace ASC.Data.Backup
{ {
public class NotifyHelper public class NotifyHelper
{ {
private const string NotifyService = "ASC.Web.Studio.Core.Notify.StudioNotifyService, ASC.Web.Studio"; public IServiceProvider ServiceProvider { get; }
private const string MethodTransferStart = "MigrationPortalStart";
private const string MethodTransferCompleted = "MigrationPortalSuccess";
private const string MethodTransferError = "MigrationPortalError";
private const string MethodBackupCompleted = "SendMsgBackupCompleted";
private const string MethodRestoreStarted = "SendMsgRestoreStarted";
private const string MethodRestoreCompleted = "SendMsgRestoreCompleted";
private readonly ILog log;
private readonly NotifyService notifyService;
public NotifyHelper(IOptionsMonitor<ILog> options, NotifyService notifyService) public NotifyHelper(IServiceProvider serviceProvider)
{ {
this.notifyService = notifyService; ServiceProvider = serviceProvider;
log = options.CurrentValue;
}
public void SendAboutTransferStart(int tenantId, string targetRegion, bool notifyUsers)
{
SendNotification(MethodTransferStart, tenantId, targetRegion, notifyUsers);
} }
public void SendAboutTransferComplete(int tenantId, string targetRegion, string targetAddress, bool notifyOnlyOwner) public void SendAboutTransferStart(Tenant tenant, string targetRegion, bool notifyUsers)
{ {
SendNotification(MethodTransferCompleted, tenantId, targetRegion, targetAddress, !notifyOnlyOwner); MigrationNotify(tenant, Actions.MigrationPortalStart, targetRegion, string.Empty, notifyUsers);
} }
public void SendAboutTransferError(int tenantId, string targetRegion, string resultAddress, bool notifyOnlyOwner) public void SendAboutTransferComplete(Tenant tenant, string targetRegion, string targetAddress, bool notifyOnlyOwner)
{ {
SendNotification(MethodTransferError, tenantId, targetRegion, resultAddress, !notifyOnlyOwner); MigrationNotify(tenant, Actions.MigrationPortalSuccess, targetRegion, targetAddress, !notifyOnlyOwner);
} }
public void SendAboutBackupCompleted(int tenantId, Guid userId, string link) public void SendAboutTransferError(Tenant tenant, string targetRegion, string resultAddress, bool notifyOnlyOwner)
{ {
SendNotification(MethodBackupCompleted, tenantId, userId, link); MigrationNotify(tenant, !string.IsNullOrEmpty(targetRegion) ? Actions.MigrationPortalError : Actions.MigrationPortalServerFailure, targetRegion, resultAddress, !notifyOnlyOwner);
} }
public void SendAboutRestoreStarted(int tenantId, bool notifyAllUsers) public void SendAboutBackupCompleted(Guid userId)
{ {
SendNotification(MethodRestoreStarted, tenantId, notifyAllUsers); using var scope = ServiceProvider.CreateScope();
var userManager = scope.ServiceProvider.GetService<UserManager>();
var studioNotifyHelper = scope.ServiceProvider.GetService<StudioNotifyHelper>();
var notifySource = scope.ServiceProvider.GetService<StudioNotifySource>();
var displayUserSettingsHelper = scope.ServiceProvider.GetService<DisplayUserSettingsHelper>();
var client = WorkContext.NotifyContext.NotifyService.RegisterClient(notifySource, scope);
client.SendNoticeToAsync(
Actions.BackupCreated,
new[] { studioNotifyHelper.ToRecipient(userId) },
new[] { StudioNotifyService.EMailSenderName },
new TagValue(Tags.OwnerName, userManager.GetUsers(userId).DisplayUserName(displayUserSettingsHelper)));
} }
public void SendAboutRestoreCompleted(int tenantId, bool notifyAllUsers) public void SendAboutRestoreStarted(Tenant tenant, bool notifyAllUsers)
{ {
SendNotification(MethodRestoreCompleted, tenantId, notifyAllUsers); using var scope = ServiceProvider.CreateScope();
var userManager = scope.ServiceProvider.GetService<UserManager>();
var studioNotifyHelper = scope.ServiceProvider.GetService<StudioNotifyHelper>();
var notifySource = scope.ServiceProvider.GetService<StudioNotifySource>();
var displayUserSettingsHelper = scope.ServiceProvider.GetService<DisplayUserSettingsHelper>();
var client = WorkContext.NotifyContext.NotifyService.RegisterClient(notifySource, scope);
var owner = userManager.GetUsers(tenant.OwnerId);
var users =
notifyAllUsers
? studioNotifyHelper.RecipientFromEmail(userManager.GetUsers(EmployeeStatus.Active).Where(r => r.ActivationStatus == EmployeeActivationStatus.Activated).Select(u => u.Email).ToList(), false)
: owner.ActivationStatus == EmployeeActivationStatus.Activated ? studioNotifyHelper.RecipientFromEmail(owner.Email, false) : new IDirectRecipient[0];
client.SendNoticeToAsync(
Actions.RestoreStarted,
users,
new[] { StudioNotifyService.EMailSenderName });
} }
private void SendNotification(string method, int tenantId, params object[] args) public void SendAboutRestoreCompleted(Tenant tenant, bool notifyAllUsers)
{ {
try using var scope = ServiceProvider.CreateScope();
{ var userManager = scope.ServiceProvider.GetService<UserManager>();
notifyService.InvokeSendMethod(NotifyService, method, tenantId, args); var studioNotifyHelper = scope.ServiceProvider.GetService<StudioNotifyHelper>();
var notifySource = scope.ServiceProvider.GetService<StudioNotifySource>();
var displayUserSettingsHelper = scope.ServiceProvider.GetService<DisplayUserSettingsHelper>();
var client = WorkContext.NotifyContext.NotifyService.RegisterClient(notifySource, scope);
var owner = userManager.GetUsers(tenant.OwnerId);
var users =
notifyAllUsers
? userManager.GetUsers(EmployeeStatus.Active).Select(u => studioNotifyHelper.ToRecipient(u.ID)).ToArray()
: new[] { studioNotifyHelper.ToRecipient(owner.ID) };
client.SendNoticeToAsync(
Actions.RestoreCompleted,
users,
new[] { StudioNotifyService.EMailSenderName },
new TagValue(Tags.OwnerName, owner.DisplayUserName(displayUserSettingsHelper)));
} }
catch (Exception error)
private void MigrationNotify(Tenant tenant, INotifyAction action, string region, string url, bool notify)
{ {
log.Warn("Error while sending notification", error); using var scope = ServiceProvider.CreateScope();
var userManager = scope.ServiceProvider.GetService<UserManager>();
var studioNotifyHelper = scope.ServiceProvider.GetService<StudioNotifyHelper>();
var notifySource = scope.ServiceProvider.GetService<StudioNotifySource>();
var client = WorkContext.NotifyContext.NotifyService.RegisterClient(notifySource, scope);
var users = userManager.GetUsers()
.Where(u => notify ? u.ActivationStatus.HasFlag(EmployeeActivationStatus.Activated) : u.IsOwner(tenant))
.Select(u => studioNotifyHelper.ToRecipient(u.ID))
.ToArray();
if (users.Any())
{
client.SendNoticeToAsync(
action,
users,
new[] { StudioNotifyService.EMailSenderName },
new TagValue(Tags.RegionName, TransferResourceHelper.GetRegionDescription(region)),
new TagValue(Tags.PortalUrl, url));
} }
} }
} }
@ -95,9 +154,14 @@ namespace ASC.Data.Backup
{ {
public static DIHelper AddNotifyHelperService(this DIHelper services) public static DIHelper AddNotifyHelperService(this DIHelper services)
{ {
services.TryAddScoped<NotifyHelper>(); services.TryAddSingleton<NotifyHelper>();
return services return services
.AddNotifyService(); .AddNotifyConfiguration()
.AddStudioNotifySourceService()
.AddUserManagerService()
.AddStudioNotifyHelperService()
.AddDisplayUserSettingsService();
} }
} }
} }

View File

@ -376,7 +376,8 @@ namespace ASC.Data.Backup.Service
var backupWorker = scope.ServiceProvider.GetService<BackupWorker>(); var backupWorker = scope.ServiceProvider.GetService<BackupWorker>();
var backupName = string.Format("{0}_{1:yyyy-MM-dd_HH-mm-ss}.{2}", tenantManager.GetTenant(TenantId).TenantAlias, DateTime.UtcNow, ArchiveFormat); var tenant = tenantManager.GetTenant(TenantId);
var backupName = string.Format("{0}_{1:yyyy-MM-dd_HH-mm-ss}.{2}", tenant.TenantAlias, DateTime.UtcNow, ArchiveFormat);
var tempFile = Path.Combine(TempFolder, backupName); var tempFile = Path.Combine(TempFolder, backupName);
var storagePath = tempFile; var storagePath = tempFile;
try try
@ -424,10 +425,11 @@ namespace ASC.Data.Backup.Service
if (UserId != Guid.Empty && !IsScheduled) if (UserId != Guid.Empty && !IsScheduled)
{ {
notifyHelper.SendAboutBackupCompleted(TenantId, UserId, Link); notifyHelper.SendAboutBackupCompleted(UserId);
} }
IsCompleted = true; IsCompleted = true;
backupWorker.PublishProgress(this);
} }
catch (Exception error) catch (Exception error)
{ {
@ -437,6 +439,15 @@ namespace ASC.Data.Backup.Service
} }
finally finally
{ {
try
{
backupWorker.PublishProgress(this);
}
catch (Exception error)
{
Log.Error("publish", error);
}
try try
{ {
if (!(storagePath == tempFile && StorageType == BackupStorageType.Local)) if (!(storagePath == tempFile && StorageType == BackupStorageType.Local))
@ -503,13 +514,13 @@ namespace ASC.Data.Backup.Service
var tempFile = PathHelper.GetTempFileName(TempFolder); var tempFile = PathHelper.GetTempFileName(TempFolder);
try try
{ {
notifyHelper.SendAboutRestoreStarted(TenantId, Notify); tenant = tenantManager.GetTenant(TenantId);
notifyHelper.SendAboutRestoreStarted(tenant, Notify);
var storage = backupStorageFactory.GetBackupStorage(StorageType, TenantId, StorageParams); var storage = backupStorageFactory.GetBackupStorage(StorageType, TenantId, StorageParams);
storage.Download(StoragePath, tempFile); storage.Download(StoragePath, tempFile);
Percentage = 10; Percentage = 10;
tenant = tenantManager.GetTenant(TenantId);
tenant.SetStatus(TenantStatus.Restoring); tenant.SetStatus(TenantStatus.Restoring);
tenantManager.SaveTenant(tenant); tenantManager.SaveTenant(tenant);
@ -536,7 +547,7 @@ namespace ASC.Data.Backup.Service
var tenants = tenantManager.GetTenants(); var tenants = tenantManager.GetTenants();
foreach (var t in tenants) foreach (var t in tenants)
{ {
notifyHelper.SendAboutRestoreCompleted(t.TenantId, Notify); notifyHelper.SendAboutRestoreCompleted(t, Notify);
} }
} }
} }
@ -557,14 +568,17 @@ namespace ASC.Data.Backup.Service
// sleep until tenants cache expires // sleep until tenants cache expires
Thread.Sleep(TimeSpan.FromMinutes(2)); Thread.Sleep(TimeSpan.FromMinutes(2));
notifyHelper.SendAboutRestoreCompleted(restoredTenant.TenantId, Notify); notifyHelper.SendAboutRestoreCompleted(restoredTenant, Notify);
} }
Percentage = 75; Percentage = 75;
backupWorker.PublishProgress(this);
File.Delete(tempFile); File.Delete(tempFile);
Percentage = 100; Percentage = 100;
backupWorker.PublishProgress(this);
} }
catch (Exception error) catch (Exception error)
{ {
@ -579,6 +593,15 @@ namespace ASC.Data.Backup.Service
} }
finally finally
{ {
try
{
backupWorker.PublishProgress(this);
}
catch (Exception error)
{
Log.Error("publish", error);
}
if (File.Exists(tempFile)) if (File.Exists(tempFile))
{ {
File.Delete(tempFile); File.Delete(tempFile);
@ -649,11 +672,12 @@ namespace ASC.Data.Backup.Service
var backupWorker = scope.ServiceProvider.GetService<BackupWorker>(); var backupWorker = scope.ServiceProvider.GetService<BackupWorker>();
var tempFile = PathHelper.GetTempFileName(TempFolder); var tempFile = PathHelper.GetTempFileName(TempFolder);
var alias = tenantManager.GetTenant(TenantId).TenantAlias; var tenant = tenantManager.GetTenant(TenantId);
var alias = tenant.TenantAlias;
try try
{ {
notifyHelper.SendAboutTransferStart(TenantId, TargetRegion, Notify); notifyHelper.SendAboutTransferStart(tenant, TargetRegion, Notify);
var transferProgressItem = scope.ServiceProvider.GetService<TransferPortalTask>(); var transferProgressItem = scope.ServiceProvider.GetService<TransferPortalTask>();
transferProgressItem.Init(TenantId, ConfigPaths[CurrentRegion], ConfigPaths[TargetRegion], Limit, TempFolder); transferProgressItem.Init(TenantId, ConfigPaths[CurrentRegion], ConfigPaths[TargetRegion], Limit, TempFolder);
transferProgressItem.ProgressChanged += (sender, args) => transferProgressItem.ProgressChanged += (sender, args) =>
@ -668,7 +692,8 @@ namespace ASC.Data.Backup.Service
transferProgressItem.RunJob(); transferProgressItem.RunJob();
Link = GetLink(alias, false); Link = GetLink(alias, false);
notifyHelper.SendAboutTransferComplete(TenantId, TargetRegion, Link, !Notify); notifyHelper.SendAboutTransferComplete(tenant, TargetRegion, Link, !Notify);
backupWorker.PublishProgress(this);
} }
catch (Exception error) catch (Exception error)
{ {
@ -676,10 +701,19 @@ namespace ASC.Data.Backup.Service
Error = error; Error = error;
Link = GetLink(alias, true); Link = GetLink(alias, true);
notifyHelper.SendAboutTransferError(TenantId, TargetRegion, Link, !Notify); notifyHelper.SendAboutTransferError(tenant, TargetRegion, Link, !Notify);
} }
finally finally
{ {
try
{
backupWorker.PublishProgress(this);
}
catch (Exception error)
{
Log.Error("publish", error);
}
if (File.Exists(tempFile)) if (File.Exists(tempFile))
{ {
File.Delete(tempFile); File.Delete(tempFile);

View File

@ -156,6 +156,7 @@ namespace ASC.Notify
} }
dbContext.SaveChanges(); dbContext.SaveChanges();
tx.Commit();
return messages; return messages;
} }

View File

@ -79,6 +79,7 @@ class SectionFilterContent extends React.Component {
const sortBy = data.sortId; const sortBy = data.sortId;
const sortOrder = const sortOrder =
data.sortDirection === "desc" ? "descending" : "ascending"; data.sortDirection === "desc" ? "descending" : "ascending";
const viewAs = data.viewAs;
const authorType = getAuthorType(data.filterValues); const authorType = getAuthorType(data.filterValues);
const withSubfolders = getSearchParams(data.filterValues); const withSubfolders = getSearchParams(data.filterValues);
@ -95,6 +96,7 @@ class SectionFilterContent extends React.Component {
newFilter.page = 0; newFilter.page = 0;
newFilter.sortBy = sortBy; newFilter.sortBy = sortBy;
newFilter.sortOrder = sortOrder; newFilter.sortOrder = sortOrder;
newFilter.viewAs = viewAs;
newFilter.filterType = filterType; newFilter.filterType = filterType;
newFilter.search = search; newFilter.search = search;
newFilter.authorType = authorType; newFilter.authorType = authorType;
@ -207,14 +209,21 @@ class SectionFilterContent extends React.Component {
getSortData = () => { getSortData = () => {
const { t } = this.props; const { t } = this.props;
return [ const commonOptions = [
{ key: "lastModifiedDate", label: t("ByLastModifiedDate"), default: true }, { key: "lastModifiedDate", label: t("ByLastModifiedDate"), default: true },
{ key: "creationDate", label: t("ByCreationDate"), default: true }, { key: "creationDate", label: t("ByCreationDate"), default: true },
{ key: "title", label: t("ByTitle"), default: true }, { key: "title", label: t("ByTitle"), default: true },
{ key: "type", label: t("ByType"), default: true }, { key: "type", label: t("ByType"), default: true },
{ key: "size", label: t("BySize"), default: true }, { key: "size", label: t("BySize"), default: true },
{ key: "author", label: t("ByAuthor"), default: true }, { key: "author", label: t("ByAuthor"), default: true }
]; ];
const viewSettings = [
{ key: "row", label: t("ViewList"), isSetting: true, default: true },
{ key: "tile", label: t("ViewTiles"), isSetting: true, default: true }
];
//TODO: Need use mobile detect for better result
return window.innerWidth < 460 ? [...commonOptions,...viewSettings] : commonOptions;
}; };
getSelectedFilterData = () => { getSelectedFilterData = () => {
@ -222,7 +231,8 @@ class SectionFilterContent extends React.Component {
const selectedFilterData = { const selectedFilterData = {
filterValues: [], filterValues: [],
sortDirection: filter.sortOrder === "ascending" ? "asc" : "desc", sortDirection: filter.sortOrder === "ascending" ? "asc" : "desc",
sortId: filter.sortBy sortId: filter.sortBy,
viewAs: filter.viewAs
}; };
selectedFilterData.inputValue = filter.search; selectedFilterData.inputValue = filter.search;
@ -274,7 +284,7 @@ class SectionFilterContent extends React.Component {
render() { render() {
const selectedFilterData = this.getSelectedFilterData(); const selectedFilterData = this.getSelectedFilterData();
const { t, i18n } = this.props; const { t, i18n } = this.props;
const filterColumnCount = window.innerWidth < 500 ? {} : {filterColumnCount: 3} const filterColumnCount = window.innerWidth < 500 ? {} : { filterColumnCount: 3 }
return ( return (
<FilterInput <FilterInput
getFilterData={this.getData} getFilterData={this.getData}

View File

@ -82,5 +82,7 @@
"TooltipElementMoveMessage": "Move {{element}}", "TooltipElementMoveMessage": "Move {{element}}",
"TooltipElementsMoveMessage": "Move {{element}} elements", "TooltipElementsMoveMessage": "Move {{element}} elements",
"TooltipElementCopyMessage": "Copy {{element}}", "TooltipElementCopyMessage": "Copy {{element}}",
"TooltipElementsCopyMessage": "Copy {{element}} elements" "TooltipElementsCopyMessage": "Copy {{element}} elements",
"ViewList": "List",
"ViewTiles": "Tiles"
} }

View File

@ -82,5 +82,7 @@
"TooltipElementMoveMessage": "Переместить {{element}}", "TooltipElementMoveMessage": "Переместить {{element}}",
"TooltipElementsMoveMessage": "Переместить {{element}} элемента(ов)", "TooltipElementsMoveMessage": "Переместить {{element}} элемента(ов)",
"TooltipElementCopyMessage": "Скопировать {{element}}", "TooltipElementCopyMessage": "Скопировать {{element}}",
"TooltipElementsCopyMessage": "Скопировать {{element}} элемента(ов)" "TooltipElementsCopyMessage": "Скопировать {{element}} элемента(ов)",
"ViewList": "Список",
"ViewTiles": "Плитки"
} }

View File

@ -5,6 +5,7 @@ export const FILTER_TYPE = "filterType";
export const SEARCH = "search"; export const SEARCH = "search";
export const SORT_BY = "sortby"; export const SORT_BY = "sortby";
export const SORT_ORDER = "sortorder"; export const SORT_ORDER = "sortorder";
export const VIEW_AS = "viewas";
export const PAGE = "page"; export const PAGE = "page";
export const PAGE_COUNT = "pagecount"; export const PAGE_COUNT = "pagecount";
export const FOLDER = "folder"; export const FOLDER = "folder";

View File

@ -4,6 +4,7 @@ import {
SEARCH, SEARCH,
SORT_BY, SORT_BY,
SORT_ORDER, SORT_ORDER,
VIEW_AS,
PAGE, PAGE,
PAGE_COUNT, PAGE_COUNT,
AUTHOR_TYPE, AUTHOR_TYPE,
@ -28,6 +29,7 @@ export function getFilterByLocation(location) {
defaultFilter.withSubfolders; defaultFilter.withSubfolders;
const search = urlFilter[SEARCH] || defaultFilter.search; const search = urlFilter[SEARCH] || defaultFilter.search;
const sortBy = urlFilter[SORT_BY] || defaultFilter.sortBy; const sortBy = urlFilter[SORT_BY] || defaultFilter.sortBy;
const viewAs = urlFilter[VIEW_AS] || defaultFilter.viewAs;
const sortOrder = urlFilter[SORT_ORDER] || defaultFilter.sortOrder; const sortOrder = urlFilter[SORT_ORDER] || defaultFilter.sortOrder;
const page = (urlFilter[PAGE] && (+urlFilter[PAGE]-1)) || defaultFilter.page; const page = (urlFilter[PAGE] && (+urlFilter[PAGE]-1)) || defaultFilter.page;
const pageCount = const pageCount =
@ -41,6 +43,7 @@ export function getFilterByLocation(location) {
defaultFilter.total, defaultFilter.total,
sortBy, sortBy,
sortOrder, sortOrder,
viewAs,
filterType, filterType,
withSubfolders, withSubfolders,
search, search,

View File

@ -1,6 +1,6 @@
{ {
"name": "asc-web-common", "name": "asc-web-common",
"version": "1.0.167", "version": "1.0.169",
"description": "Ascensio System SIA common components and solutions library", "description": "Ascensio System SIA common components and solutions library",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"files": [ "files": [

View File

@ -5,6 +5,7 @@ const DEFAULT_PAGE_COUNT = 25;
const DEFAULT_TOTAL = 0; const DEFAULT_TOTAL = 0;
const DEFAULT_SORT_BY = "lastModifiedDate"; const DEFAULT_SORT_BY = "lastModifiedDate";
const DEFAULT_SORT_ORDER = "ascending"; const DEFAULT_SORT_ORDER = "ascending";
const DEFAULT_VIEW = "row";
const DEFAULT_FILTER_TYPE = null; const DEFAULT_FILTER_TYPE = null;
const DEFAULT_SEARCH_TYPE = true; //withSubfolders const DEFAULT_SEARCH_TYPE = true; //withSubfolders
const DEFAULT_SEARCH = null; const DEFAULT_SEARCH = null;
@ -29,6 +30,7 @@ class FilesFilter {
total = DEFAULT_TOTAL, total = DEFAULT_TOTAL,
sortBy = DEFAULT_SORT_BY, sortBy = DEFAULT_SORT_BY,
sortOrder = DEFAULT_SORT_ORDER, sortOrder = DEFAULT_SORT_ORDER,
viewAs = DEFAULT_VIEW,
filterType = DEFAULT_FILTER_TYPE, filterType = DEFAULT_FILTER_TYPE,
withSubfolders = DEFAULT_SEARCH_TYPE, withSubfolders = DEFAULT_SEARCH_TYPE,
search = DEFAULT_SEARCH, search = DEFAULT_SEARCH,
@ -41,6 +43,7 @@ class FilesFilter {
this.pageCount = pageCount; this.pageCount = pageCount;
this.sortBy = sortBy; this.sortBy = sortBy;
this.sortOrder = sortOrder; this.sortOrder = sortOrder;
this.viewAs = viewAs;
this.filterType = filterType; this.filterType = filterType;
this.withSubfolders = withSubfolders; this.withSubfolders = withSubfolders;
this.search = search; this.search = search;
@ -106,6 +109,7 @@ class FilesFilter {
this.total, this.total,
this.sortBy, this.sortBy,
this.sortOrder, this.sortOrder,
this.viewAs,
this.filterType, this.filterType,
this.withSubfolders, this.withSubfolders,
this.search, this.search,
@ -124,6 +128,7 @@ class FilesFilter {
this.search === filter.search && this.search === filter.search &&
this.sortBy === filter.sortBy && this.sortBy === filter.sortBy &&
this.sortOrder === filter.sortOrder && this.sortOrder === filter.sortOrder &&
this.viewAs === filter.viewAs &&
this.page === filter.page && this.page === filter.page &&
this.selectedItem.key === filter.selectedItem.key && this.selectedItem.key === filter.selectedItem.key &&
this.folder === filter.folder && this.folder === filter.folder &&

View File

@ -5,6 +5,7 @@ import isEqual from 'lodash/isEqual';
import throttle from 'lodash/throttle'; import throttle from 'lodash/throttle';
import FilterBlock from './sub-components/FilterBlock'; import FilterBlock from './sub-components/FilterBlock';
import SortComboBox from './sub-components/SortComboBox'; import SortComboBox from './sub-components/SortComboBox';
import ViewSelector from './sub-components/ViewSelector';
import map from 'lodash/map'; import map from 'lodash/map';
import clone from 'lodash/clone'; import clone from 'lodash/clone';
import StyledFilterInput from './StyledFilterInput'; import StyledFilterInput from './StyledFilterInput';
@ -92,6 +93,7 @@ class FilterInput extends React.Component {
this.state = { this.state = {
sortDirection: props.selectedFilterData.sortDirection === "desc" ? true : false, sortDirection: props.selectedFilterData.sortDirection === "desc" ? true : false,
viewAs: props.selectedFilterData.viewAs,
sortId: props.getSortData().findIndex(x => x.key === props.selectedFilterData.sortId) != -1 ? props.selectedFilterData.sortId : props.getSortData().length > 0 ? props.getSortData()[0].key : "", sortId: props.getSortData().findIndex(x => x.key === props.selectedFilterData.sortId) != -1 ? props.selectedFilterData.sortId : props.getSortData().length > 0 ? props.getSortData()[0].key : "",
searchText: props.selectedFilterData.inputValue || props.value, searchText: props.selectedFilterData.inputValue || props.value,
@ -120,6 +122,8 @@ class FilterInput extends React.Component {
this.onDeleteFilterItem = this.onDeleteFilterItem.bind(this); this.onDeleteFilterItem = this.onDeleteFilterItem.bind(this);
this.clearFilter = this.clearFilter.bind(this); this.clearFilter = this.clearFilter.bind(this);
this.onClickViewSelector = this.onClickViewSelector.bind(this);
this.throttledResize = throttle(this.resize, 300); this.throttledResize = throttle(this.resize, 300);
} }
@ -230,9 +234,15 @@ class FilterInput extends React.Component {
}) })
} }
onChangeSortDirection(key) { onChangeSortDirection(key) {
this.onFilter(this.state.filterValues, this.state.sortId, key ? "desc" : "asc"); this.onFilter(this.state.filterValues, this.state.sortId, key ? "desc" : "asc", this.state.viewAs);
this.setState({ sortDirection: !!key }); this.setState({ sortDirection: !!key });
} }
onClickViewSelector(item) {
const itemId = (item.target && item.target.dataset.for) || item;
const viewAs = itemId.indexOf("row") === -1 ? "tile" : "row"
this.onFilter(this.state.filterValues, this.state.sortId, this.state.sortDirection ? "desc" : "asc", viewAs);
this.setState({ viewAs: viewAs });
}
getDefaultSelectedIndex() { getDefaultSelectedIndex() {
const sortData = this.props.getSortData(); const sortData = this.props.getSortData();
if (sortData.length > 0) { if (sortData.length > 0) {
@ -243,19 +253,19 @@ class FilterInput extends React.Component {
} }
onClickSortItem(key) { onClickSortItem(key) {
this.setState({ sortId: key }); this.setState({ sortId: key });
this.onFilter(this.state.filterValues, key, this.state.sortDirection ? "desc" : "asc"); this.onFilter(this.state.filterValues, key, this.state.sortDirection ? "desc" : "asc", this.state.viewAs);
} }
onSortDirectionClick() { onSortDirectionClick() {
this.onFilter(this.state.filterValues, this.state.sortId, !this.state.sortDirection ? "desc" : "asc"); this.onFilter(this.state.filterValues, this.state.sortId, !this.state.sortDirection ? "desc" : "asc", this.state.viewAs);
this.setState({ sortDirection: !this.state.sortDirection }); this.setState({ sortDirection: !this.state.sortDirection });
} }
onSearchChanged(value) { onSearchChanged(value) {
this.setState({ searchText: value }); this.setState({ searchText: value });
this.onFilter(this.state.filterValues, this.state.sortId, this.state.sortDirection ? "desc" : "asc", value); this.onFilter(this.state.filterValues, this.state.sortId, this.state.sortDirection ? "desc" : "asc", this.state.viewAs, value);
} }
onSearch(result) { onSearch(result) {
this.onFilter(result.filterValues, this.state.sortId, this.state.sortDirection ? "desc" : "asc"); this.onFilter(result.filterValues, this.state.sortId, this.state.sortDirection ? "desc" : "asc", this.state.viewAs);
} }
getFilterData() { getFilterData() {
const _this = this; const _this = this;
@ -280,7 +290,7 @@ class FilterInput extends React.Component {
openFilterItems: [], openFilterItems: [],
hideFilterItems: [] hideFilterItems: []
}); });
this.onFilter([], this.state.sortId, this.state.sortDirection ? "desc" : "asc", ''); this.onFilter([], this.state.sortId, this.state.sortDirection ? "desc" : "asc", this.state.viewAs, '');
} }
updateFilter(inputFilterItems) { updateFilter(inputFilterItems) {
const currentFilterItems = inputFilterItems || cloneObjectsArray(this.state.filterValues); const currentFilterItems = inputFilterItems || cloneObjectsArray(this.state.filterValues);
@ -344,9 +354,9 @@ class FilterInput extends React.Component {
item.key = item.key.replace(item.group + "_", ''); item.key = item.key.replace(item.group + "_", '');
return item; return item;
}) })
this.onFilter(filterValues.filter(item => item.key != '-1'), this.state.sortId, this.state.sortDirection ? "desc" : "asc"); this.onFilter(filterValues.filter(item => item.key != '-1'), this.state.sortId, this.state.sortDirection ? "desc" : "asc", this.state.viewAs);
} }
onFilter(filterValues, sortId, sortDirection, searchText) { onFilter(filterValues, sortId, sortDirection, viewAs, searchText) {
let cloneFilterValues = cloneObjectsArray(filterValues); let cloneFilterValues = cloneObjectsArray(filterValues);
cloneFilterValues = cloneFilterValues.map(function (item) { cloneFilterValues = cloneFilterValues.map(function (item) {
item.key = item.key.replace(item.group + "_", ''); item.key = item.key.replace(item.group + "_", '');
@ -356,7 +366,8 @@ class FilterInput extends React.Component {
inputValue: searchText != undefined ? searchText : this.state.searchText, inputValue: searchText != undefined ? searchText : this.state.searchText,
filterValues: cloneFilterValues.filter(item => item.key != '-1'), filterValues: cloneFilterValues.filter(item => item.key != '-1'),
sortId: sortId, sortId: sortId,
sortDirection: sortDirection sortDirection: sortDirection,
viewAs: viewAs
}); });
} }
onChangeFilter(result) { onChangeFilter(result) {
@ -364,7 +375,7 @@ class FilterInput extends React.Component {
searchText: result.inputValue, searchText: result.inputValue,
filterValues: result.filterValues, filterValues: result.filterValues,
}); });
this.onFilter(result.filterValues, this.state.sortId, this.state.sortDirection ? "desc" : "asc", result.inputValue); this.onFilter(result.filterValues, this.state.sortId, this.state.sortDirection ? "desc" : "asc", this.state.viewAs, result.inputValue);
} }
onFilterRender() { onFilterRender() {
if (this.isResizeUpdate) { if (this.isResizeUpdate) {
@ -419,7 +430,7 @@ class FilterInput extends React.Component {
}) })
this.onFilter(clone.filter(item => item.key != '-1'), this.state.sortId, this.state.sortDirection ? "desc" : "asc"); this.onFilter(clone.filter(item => item.key != '-1'), this.state.sortId, this.state.sortDirection ? "desc" : "asc", this.state.viewAs);
} }
return; return;
@ -467,7 +478,7 @@ class FilterInput extends React.Component {
item.key = item.key.replace(item.group + "_", ''); item.key = item.key.replace(item.group + "_", '');
return item; return item;
}) })
this.onFilter(clone.filter(item => item.key != '-1'), this.state.sortId, this.state.sortDirection ? "desc" : "asc"); this.onFilter(clone.filter(item => item.key != '-1'), this.state.sortId, this.state.sortDirection ? "desc" : "asc", this.state.viewAs);
this.setState({ this.setState({
filterValues: currentFilterItems, filterValues: currentFilterItems,
openFilterItems: currentFilterItems, openFilterItems: currentFilterItems,
@ -503,7 +514,7 @@ class FilterInput extends React.Component {
item.key = item.key.replace(item.group + "_", ''); item.key = item.key.replace(item.group + "_", '');
return item; return item;
}) })
this.onFilter(clone.filter(item => item.key != '-1'), this.state.sortId, this.state.sortDirection ? "desc" : "asc"); this.onFilter(clone.filter(item => item.key != '-1'), this.state.sortId, this.state.sortDirection ? "desc" : "asc", this.state.viewAs);
} }
} }
@ -517,7 +528,7 @@ class FilterInput extends React.Component {
/* eslint-enable react/prop-types */ /* eslint-enable react/prop-types */
const { searchText, filterValues, openFilterItems, const { searchText, filterValues, openFilterItems,
hideFilterItems, sortId, sortDirection } = this.state; hideFilterItems, sortId, sortDirection, viewAs } = this.state;
// console.log("filter input render, openFilterItems", openFilterItems, 'hideFilterItems', hideFilterItems); // console.log("filter input render, openFilterItems", openFilterItems, 'hideFilterItems', hideFilterItems);
let iconSize = 33; let iconSize = 33;
@ -574,6 +585,7 @@ class FilterInput extends React.Component {
options={getSortData()} options={getSortData()}
isDisabled={isDisabled} isDisabled={isDisabled}
onChangeSortId={this.onClickSortItem} onChangeSortId={this.onClickSortItem}
onChangeView={this.onClickViewSelector}
onChangeSortDirection={this.onChangeSortDirection} onChangeSortDirection={this.onChangeSortDirection}
selectedOption={getSortData().length > 0 ? getSortData().find(x => x.key === sortId) : {}} selectedOption={getSortData().length > 0 ? getSortData().find(x => x.key === sortId) : {}}
onButtonClick={this.onSortDirectionClick} onButtonClick={this.onSortDirectionClick}
@ -581,6 +593,11 @@ class FilterInput extends React.Component {
directionAscLabel={directionAscLabel} directionAscLabel={directionAscLabel}
directionDescLabel={directionDescLabel} directionDescLabel={directionDescLabel}
/> />
<ViewSelector
isDisabled={isDisabled}
onClickViewSelector={this.onClickViewSelector}
viewAs={viewAs}
/>
</StyledFilterInput> </StyledFilterInput>
); );

View File

@ -43,7 +43,8 @@ class FilterStory extends React.Component {
inputValue: "text", inputValue: "text",
filterValues: [ filterValues: [
{key: "1", group: "filter-status"} {key: "1", group: "filter-status"}
] ],
viewAs: "row"
} }
}; };
this.buttonClick = this.buttonClick.bind(this); this.buttonClick = this.buttonClick.bind(this);

View File

@ -17,7 +17,10 @@ const StyledFilterInput = styled.div`
.styled-search-input { .styled-search-input {
display: block; display: block;
float: left; float: left;
width: calc(100% - 212px);
@media (max-width: 460px) {
width: calc(100% - 140px); width: calc(100% - 140px);
}
@media ${mobile} { @media ${mobile} {
width: calc(100% - 58px); width: calc(100% - 58px);
} }
@ -121,8 +124,50 @@ const StyledFilterInput = styled.div`
color: #333; color: #333;
} }
} }
`;
export const StyledViewSelector = styled.div`
display: flex;
float: left;
width: 64px;
margin-left: 8px;
@media (max-width: 460px) {
display:none;
}
.view-selector-button{
border: 1px solid ${props => props.isDisabled ? '#ECEEF1' : '#D0D5DA'};
border-radius: 3px;
padding: 7px;
${props => props.isDisabled && 'background-color: #F8F9F9;' }
svg{
pointer-events: none;
}
&.active{
background-color:#A3A9AE;
border-color: #A3A9AE;
}
&:hover{
${props => !props.isDisabled && 'background-color: #A3A9AE;' }
${props => !props.isDisabled && 'border-color: #A3A9AE;' }
}
&:first-child{
border-right: none;
border-top-right-radius:0;
border-bottom-right-radius:0;
}
&:last-child{
border-left: none;
border-top-left-radius:0;
border-bottom-left-radius:0;
}
}
`; `;
export const StyledFilterItem = styled.div` export const StyledFilterItem = styled.div`
@ -228,4 +273,11 @@ export const StyledIconButton = styled.div`
transform: ${state => !state.sortDirection ? 'scale(1, -1)' : 'scale(1)'}; transform: ${state => !state.sortDirection ? 'scale(1, -1)' : 'scale(1)'};
`; `;
export const StyledIconWrapper = styled.div`
display: inline-flex;
width: 32px;
height: 100%;
`;
export default StyledFilterInput; export default StyledFilterInput;

View File

@ -29,6 +29,12 @@ class SortComboBox extends React.Component {
const { onChangeSortId } = this.props; const { onChangeSortId } = this.props;
typeof onChangeSortId === 'function' && onChangeSortId(e.target.value); typeof onChangeSortId === 'function' && onChangeSortId(e.target.value);
} }
onChangeView = (e) => {
const { onChangeView } = this.props;
typeof onChangeView === 'function' && onChangeView(e.target.value);
}
onChangeSortDirection = (e) => { onChangeSortDirection = (e) => {
const sortDirection = +e.target.value; const sortDirection = +e.target.value;
const { onChangeSortDirection } = this.props; const { onChangeSortDirection } = this.props;
@ -56,10 +62,17 @@ class SortComboBox extends React.Component {
const { options, directionAscLabel, directionDescLabel, isDisabled, const { options, directionAscLabel, directionDescLabel, isDisabled,
selectedOption } = this.props; selectedOption } = this.props;
const { sortDirection } = this.state; const { sortDirection } = this.state;
let sortArray = options.map(function (item) {
let settingsArray = options.filter(item => {
item.value = item.key item.value = item.key
return item; return item.isSetting === true;
}); });
let sortArray = options.filter(item => {
item.value = item.key
return item.isSetting !== true;
});
let sortDirectionArray = [ let sortDirectionArray = [
{ value: '0', label: directionAscLabel }, { value: '0', label: directionAscLabel },
{ value: '1', label: directionDescLabel } { value: '1', label: directionDescLabel }
@ -92,6 +105,23 @@ class SortComboBox extends React.Component {
spacing='0px' spacing='0px'
/> />
</DropDownItem> </DropDownItem>
{settingsArray.length !== 0 &&
<>
<DropDownItem isSeparator />
<DropDownItem noHover >
<RadioButtonGroup
fontWeight={600}
isDisabled={isDisabled}
name={'view'}
onClick={this.onChangeView}
options={settingsArray}
orientation='vertical'
selected={selectedOption.key}
spacing='0px'
/>
</DropDownItem>
</>
}
</> </>
); );
return ( return (
@ -130,6 +160,7 @@ SortComboBox.propTypes = {
onButtonClick: PropTypes.func, onButtonClick: PropTypes.func,
onChangeSortDirection: PropTypes.func, onChangeSortDirection: PropTypes.func,
onChangeSortId: PropTypes.func, onChangeSortId: PropTypes.func,
onChangeView: PropTypes.func,
sortDirection: PropTypes.number, sortDirection: PropTypes.number,
} }

View File

@ -0,0 +1,60 @@
import React from 'react';
import { IconButton } from 'asc-web-components';
import PropTypes from 'prop-types';
import { StyledViewSelector } from '../StyledFilterInput';
class ViewSelector extends React.Component {
constructor(props) {
super(props)
this.state = {
viewAs: props.viewAs
}
}
render(){
const { isDisabled, viewAs} = this.props;
return(
<StyledViewSelector isDisabled={isDisabled}>
<IconButton
className={`view-selector-button ${viewAs === "row" ? "active" : ""}`}
color={viewAs === "row" ? "#ffffff" : "#A3A9AE"}
hoverColor={"#ffffff"}
clickColor={"#ffffff"}
iconName={'FilterViewSelectorRowIcon'}
isDisabled={isDisabled}
isFill={true}
onClick={(item) => this.props.onClickViewSelector(item)}
size={16}
id="rowSelectorButton"
/>
<IconButton
className={`view-selector-button ${viewAs === "tile" ? "active" : ""}`}
color={viewAs === "tile" ? "#ffffff" : "#A3A9AE"}
hoverColor={"#ffffff"}
clickColor={"#ffffff"}
iconName={'FilterViewSelectorTileIcon'}
isDisabled={isDisabled}
isFill={true}
onClick={(item) => this.props.onClickViewSelector(item)}
size={16}
id="tileSelectorButton"
/>
</StyledViewSelector>
)
}
}
ViewSelector.propTypes = {
isDisabled: PropTypes.bool,
viewAs: PropTypes.string,
onClickViewSelector: PropTypes.func
}
ViewSelector.defaultProps = {
isDisabled: false
}
export default ViewSelector;

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 15C0 15.5523 0.447715 16 1 16H15C15.5523 16 16 15.5523 16 15C16 14.4477 15.5523 14 15 14H1C0.447715 14 0 14.4477 0 15ZM0 8C0 8.55228 0.447715 9 1 9H15C15.5523 9 16 8.55228 16 8C16 7.44771 15.5523 7 15 7H1C0.447715 7 0 7.44771 0 8ZM1 2C0.447715 2 0 1.55229 0 1C0 0.447715 0.447715 0 1 0H15C15.5523 0 16 0.447715 16 1C16 1.55229 15.5523 2 15 2H1Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 516 B

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1 0C0.447715 0 0 0.447715 0 1V6C0 6.55228 0.447715 7 1 7H6C6.55228 7 7 6.55228 7 6V1C7 0.447715 6.55228 0 6 0H1ZM1 9C0.447715 9 0 9.44771 0 10V15C0 15.5523 0.447715 16 1 16H6C6.55228 16 7 15.5523 7 15V10C7 9.44772 6.55228 9 6 9H1ZM9 1C9 0.447715 9.44772 0 10 0H15C15.5523 0 16 0.447715 16 1V6C16 6.55228 15.5523 7 15 7H10C9.44771 7 9 6.55228 9 6V1ZM10 9C9.44772 9 9 9.44771 9 10V15C9 15.5523 9.44771 16 10 16H15C15.5523 16 16 15.5523 16 15V10C16 9.44772 15.5523 9 15 9H10Z" fill="#A3A9AE"/>
</svg>

After

Width:  |  Height:  |  Size: 644 B

View File

@ -133,6 +133,8 @@ import OrigCrossSidebarIcon from './cross.sidebar.react.svg';
import OrigCheckboxIcon from './checkbox.react.svg'; import OrigCheckboxIcon from './checkbox.react.svg';
import OrigCheckboxCheckedIcon from './checkbox.checked.react.svg'; import OrigCheckboxCheckedIcon from './checkbox.checked.react.svg';
import OrigCheckboxIndeterminateIcon from './checkbox.indeterminate.react.svg'; import OrigCheckboxIndeterminateIcon from './checkbox.indeterminate.react.svg';
import OrigFilterViewSelectorRowIcon from './filter.view.selector.row.react.svg';
import OrigFilterViewSelectorTileIcon from './filter.view.selector.tile.react.svg';
import OrigEyeIcon from './eye.react.svg'; import OrigEyeIcon from './eye.react.svg';
import OrigEyeOffIcon from './eye.off.react.svg'; import OrigEyeOffIcon from './eye.off.react.svg';
@ -801,3 +803,11 @@ export const KeyIcon = createStyledIcon(
OrigKeyIcon, OrigKeyIcon,
'KeyIcon' 'KeyIcon'
); );
export const FilterViewSelectorRowIcon = createStyledIcon(
OrigFilterViewSelectorRowIcon,
'FilterViewSelectorRowIcon'
);
export const FilterViewSelectorTileIcon = createStyledIcon(
OrigFilterViewSelectorTileIcon,
'FilterViewSelectorTileIcon'
);

View File

@ -20,6 +20,14 @@
<Compile Remove="WebZones\IRenderCustomNavigation.cs" /> <Compile Remove="WebZones\IRenderCustomNavigation.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Remove="PublicResources\webstudio_patterns.xml" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="PublicResources\webstudio_patterns.xml" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\common\ASC.Common\ASC.Common.csproj" /> <ProjectReference Include="..\..\common\ASC.Common\ASC.Common.csproj" />
<ProjectReference Include="..\..\common\ASC.Core.Common\ASC.Core.Common.csproj" /> <ProjectReference Include="..\..\common\ASC.Core.Common\ASC.Core.Common.csproj" />

View File

@ -28,7 +28,7 @@ using ASC.Notify.Model;
namespace ASC.Web.Studio.Core.Notify namespace ASC.Web.Studio.Core.Notify
{ {
static class Actions public static class Actions
{ {
public static readonly INotifyAction AdminNotify = new NotifyAction("admin_notify", "admin notifications"); public static readonly INotifyAction AdminNotify = new NotifyAction("admin_notify", "admin notifications");
public static readonly INotifyAction PeriodicNotify = new NotifyAction("periodic_notify", "periodic notifications"); public static readonly INotifyAction PeriodicNotify = new NotifyAction("periodic_notify", "periodic notifications");

View File

@ -58,7 +58,7 @@ namespace ASC.Web.Studio.Core.Notify
{ {
private readonly StudioNotifyServiceHelper client; private readonly StudioNotifyServiceHelper client;
private static string EMailSenderName { get { return ASC.Core.Configuration.Constants.NotifyEMailSenderSysName; } } public static string EMailSenderName { get { return ASC.Core.Configuration.Constants.NotifyEMailSenderSysName; } }
private UserManager UserManager { get; } private UserManager UserManager { get; }
private StudioNotifyHelper StudioNotifyHelper { get; } private StudioNotifyHelper StudioNotifyHelper { get; }
@ -653,49 +653,6 @@ namespace ASC.Web.Studio.Core.Notify
tagValues.ToArray()); tagValues.ToArray());
} }
#region Backup & Restore
public void SendMsgBackupCompleted(Guid userId, string link)
{
client.SendNoticeToAsync(
Actions.BackupCreated,
new[] { StudioNotifyHelper.ToRecipient(userId) },
new[] { EMailSenderName },
new TagValue(Tags.OwnerName, UserManager.GetUsers(userId).DisplayUserName(DisplayUserSettingsHelper)));
}
public void SendMsgRestoreStarted(Tenant tenant, bool notifyAllUsers)
{
var owner = UserManager.GetUsers(tenant.OwnerId);
var users =
notifyAllUsers
? StudioNotifyHelper.RecipientFromEmail(UserManager.GetUsers(EmployeeStatus.Active).Where(r => r.ActivationStatus == EmployeeActivationStatus.Activated).Select(u => u.Email).ToList(), false)
: owner.ActivationStatus == EmployeeActivationStatus.Activated ? StudioNotifyHelper.RecipientFromEmail(owner.Email, false) : new IDirectRecipient[0];
client.SendNoticeToAsync(
Actions.RestoreStarted,
users,
new[] { EMailSenderName });
}
public void SendMsgRestoreCompleted(Tenant tenant, bool notifyAllUsers)
{
var owner = UserManager.GetUsers(tenant.OwnerId);
var users =
notifyAllUsers
? UserManager.GetUsers(EmployeeStatus.Active).Select(u => StudioNotifyHelper.ToRecipient(u.ID)).ToArray()
: new[] { StudioNotifyHelper.ToRecipient(owner.ID) };
client.SendNoticeToAsync(
Actions.RestoreCompleted,
users,
new[] { EMailSenderName },
new TagValue(Tags.OwnerName, owner.DisplayUserName(DisplayUserSettingsHelper)));
}
#endregion
#region Portal Deactivation & Deletion #region Portal Deactivation & Deletion
public void SendMsgPortalDeactivation(Tenant t, string deactivateUrl, string activateUrl) public void SendMsgPortalDeactivation(Tenant t, string deactivateUrl, string activateUrl)
@ -861,39 +818,6 @@ namespace ASC.Web.Studio.Core.Notify
#region Migration Portal #region Migration Portal
public void MigrationPortalStart(Tenant tenant, string region, bool notify)
{
MigrationNotify(tenant, Actions.MigrationPortalStart, region, string.Empty, notify);
}
public void MigrationPortalSuccess(Tenant tenant, string region, string url, bool notify)
{
MigrationNotify(tenant, Actions.MigrationPortalSuccess, region, url, notify);
}
public void MigrationPortalError(Tenant tenant, string region, string url, bool notify)
{
MigrationNotify(tenant, !string.IsNullOrEmpty(region) ? Actions.MigrationPortalError : Actions.MigrationPortalServerFailure, region, url, notify);
}
private void MigrationNotify(Tenant tenant, INotifyAction action, string region, string url, bool notify)
{
var users = UserManager.GetUsers()
.Where(u => notify ? u.ActivationStatus.HasFlag(EmployeeActivationStatus.Activated) : u.IsOwner(tenant))
.Select(u => StudioNotifyHelper.ToRecipient(u.ID))
.ToArray();
if (users.Any())
{
client.SendNoticeToAsync(
action,
users,
new[] { EMailSenderName },
new TagValue(Tags.RegionName, TransferResourceHelper.GetRegionDescription(region)),
new TagValue(Tags.PortalUrl, url));
}
}
public void PortalRenameNotify(Tenant tenant, string oldVirtualRootPath) public void PortalRenameNotify(Tenant tenant, string oldVirtualRootPath)
{ {
var users = UserManager.GetUsers() var users = UserManager.GetUsers()

View File

@ -25,7 +25,7 @@
namespace ASC.Web.Studio.Core.Notify namespace ASC.Web.Studio.Core.Notify
{ {
static class Tags public static class Tags
{ {
public const string UserName = "UserName"; public const string UserName = "UserName";
public const string UserLastName = "UserLastName"; public const string UserLastName = "UserLastName";

View File

@ -105,15 +105,6 @@ namespace ASC.Web.Core.PublicResources {
} }
} }
/// <summary>
/// Looks up a localized string similar to CRM task was created at.
/// </summary>
internal static string ActionCreateCrmTask {
get {
return ResourceManager.GetString("ActionCreateCrmTask", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to CRM opportunity was created at. /// Looks up a localized string similar to CRM opportunity was created at.
/// </summary> /// </summary>
@ -411,15 +402,6 @@ namespace ASC.Web.Core.PublicResources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Move Right Now.
/// </summary>
internal static string ButtonMoveRightNow {
get {
return ResourceManager.GetString("ButtonMoveRightNow", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Remove profile. /// Looks up a localized string similar to Remove profile.
/// </summary> /// </summary>
@ -492,87 +474,6 @@ namespace ASC.Web.Core.PublicResources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Add your email account to ${LetterLogoText} Mail and manage all correspondence in one place.
/// </summary>
internal static string ItemAddEmailAccount {
get {
return ResourceManager.GetString("ItemAddEmailAccount", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Connect cloud storages you use to ONLYOFFICE to create a single workspace for all your docs..
/// </summary>
internal static string ItemAddFilesCreatWorkspace {
get {
return ResourceManager.GetString("ItemAddFilesCreatWorkspace", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Add your email account and manage all correspondence in one place.
/// </summary>
internal static string ItemAddTeamlabMail {
get {
return ResourceManager.GetString("ItemAddTeamlabMail", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Explore different ways of co-authoring documents - real-time co-editing, chat, comments, review..
/// </summary>
internal static string ItemCoAuthoringDocuments {
get {
return ResourceManager.GetString("ItemCoAuthoringDocuments", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Create a single workspace for all your docs.
/// </summary>
internal static string ItemCreateWorkspaceDocs {
get {
return ResourceManager.GetString("ItemCreateWorkspaceDocs", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Import projects.
/// </summary>
internal static string ItemImportProjects {
get {
return ResourceManager.GetString("ItemImportProjects", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Import projects from Basecamp.
/// </summary>
internal static string ItemImportProjectsBasecamp {
get {
return ResourceManager.GetString("ItemImportProjectsBasecamp", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Integrate documents.
/// </summary>
internal static string ItemIntegrateDocs {
get {
return ResourceManager.GetString("ItemIntegrateDocs", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Integrate your IM client with ONLYOFFICE talk and stay tuned.
/// </summary>
internal static string ItemIntegrateIM {
get {
return ResourceManager.GetString("ItemIntegrateIM", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to &lt;b&gt;Tip #1.&lt;/b&gt; Use the &lt;b&gt;Real-time Co-editing&lt;/b&gt; feature to speed up the interaction process when specifying details or proofreading a document. Choose between two co-editing modes - &lt;b&gt;Fast&lt;/b&gt; when you see changes as your co-author is typing or &lt;b&gt;Strict&lt;/b&gt; when the changes are shown only after saving the document.. /// Looks up a localized string similar to &lt;b&gt;Tip #1.&lt;/b&gt; Use the &lt;b&gt;Real-time Co-editing&lt;/b&gt; feature to speed up the interaction process when specifying details or proofreading a document. Choose between two co-editing modes - &lt;b&gt;Fast&lt;/b&gt; when you see changes as your co-author is typing or &lt;b&gt;Strict&lt;/b&gt; when the changes are shown only after saving the document..
/// </summary> /// </summary>
@ -636,51 +537,6 @@ namespace ASC.Web.Core.PublicResources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Share documents with your teammates..
/// </summary>
internal static string ItemShareDocuments {
get {
return ResourceManager.GetString("ItemShareDocuments", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Try full-featured online document editing in your browser.
/// </summary>
internal static string ItemTryOnlineDocEditor {
get {
return ResourceManager.GetString("ItemTryOnlineDocEditor", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Upload CRM contacts.
/// </summary>
internal static string ItemUploadCrm {
get {
return ResourceManager.GetString("ItemUploadCrm", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Upload CRM contacts to ONLYOFFICE to access them everywhere.
/// </summary>
internal static string ItemUploadCrmContacts {
get {
return ResourceManager.GetString("ItemUploadCrmContacts", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Upload CRM contacts from a csv. file.
/// </summary>
internal static string ItemUploadCrmContactsCsv {
get {
return ResourceManager.GetString("ItemUploadCrmContactsCsv", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Learn More &gt;&gt;. /// Looks up a localized string similar to Learn More &gt;&gt;.
/// </summary> /// </summary>
@ -693,7 +549,7 @@ namespace ASC.Web.Core.PublicResources {
/// <summary> /// <summary>
/// Looks up a localized string similar to h1.Activate your email for &quot;${__VirtualRootPath}&quot;:&quot;$InviteLink&quot; portal. /// Looks up a localized string similar to h1.Activate your email for &quot;${__VirtualRootPath}&quot;:&quot;$InviteLink&quot; portal.
/// ///
///Hello $UserDisplayName, ///Hello!
/// ///
///To start participating in the &quot;${__VirtualRootPath}&quot;:&quot;$InviteLink&quot; portal life you need to activate your email address. This is done for security reasons. ///To start participating in the &quot;${__VirtualRootPath}&quot;:&quot;$InviteLink&quot; portal life you need to activate your email address. This is done for security reasons.
/// ///
@ -707,26 +563,6 @@ namespace ASC.Web.Core.PublicResources {
} }
} }
/// <summary>
/// Looks up a localized string similar to h1.Message from the &quot;${__VirtualRootPath}&quot;:&quot;${__VirtualRootPath}&quot; VoIP disabled.
/// </summary>
internal static string pattern_admin_voip_blocked {
get {
return ResourceManager.GetString("pattern_admin_voip_blocked", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to h1.Message from the &quot;${__VirtualRootPath}&quot;:&quot;${__VirtualRootPath}&quot; portal
///
///VoIP Balance: $Body.
/// </summary>
internal static string pattern_admin_voip_warning {
get {
return ResourceManager.GetString("pattern_admin_voip_warning", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to h1.&quot;${__VirtualRootPath}&quot;:&quot;${__VirtualRootPath}&quot; portal backup created /// Looks up a localized string similar to h1.&quot;${__VirtualRootPath}&quot;:&quot;${__VirtualRootPath}&quot; portal backup created
/// ///
@ -734,12 +570,12 @@ namespace ASC.Web.Core.PublicResources {
/// ///
///A backup file containing data from your &quot;${__VirtualRootPath}&quot;:&quot;${__VirtualRootPath}&quot; portal has been created. ///A backup file containing data from your &quot;${__VirtualRootPath}&quot;:&quot;${__VirtualRootPath}&quot; portal has been created.
/// ///
///To learn more on the backup procedure please refer to our &quot;Data backup&quot;:&quot;${__HelpLink}/tipstricks/data-backup-restore.aspx&quot; user guide. ///To learn more on the backup procedure please refer to our &quot;Data backup&quot;:&quot;http://helpcenter.onlyoffice.com/tipstricks/data-backup-restore.aspx&quot; user guide.
///
/// ///
///If you have any questions or need assistance please feel free to contact us at &quot;support.onlyoffice.com&quot;:&quot;http://support.onlyoffice.com&quot; ///If you have any questions or need assistance please feel free to contact us at &quot;support.onlyoffice.com&quot;:&quot;http://support.onlyoffice.com&quot;
/// ///
///Best regards, ///Best [rest of string was truncated]&quot;;.
///ONLYOFFICE™ [rest of string was truncated]&quot;;.
/// </summary> /// </summary>
internal static string pattern_backup_created { internal static string pattern_backup_created {
get { get {
@ -760,7 +596,7 @@ namespace ASC.Web.Core.PublicResources {
/// ///
///You might need your old email address to access the portal when you follow the link. If you do not remember the old email address, please contact your portal administrator. ///You might need your old email address to access the portal when you follow the link. If you do not remember the old email address, please contact your portal administrator.
/// ///
///If you do not want [rest of string was truncated]&quot;;. ///If you do not want to change yo [rest of string was truncated]&quot;;.
/// </summary> /// </summary>
internal static string pattern_change_email { internal static string pattern_change_email {
get { get {
@ -779,7 +615,7 @@ namespace ASC.Web.Core.PublicResources {
/// ///
///*Note*: this link is valid for 7 days only. Please complete the password change process within that period. ///*Note*: this link is valid for 7 days only. Please complete the password change process within that period.
/// ///
///If you do not want to change your password or received this email by mistake, please ignore it or contact your &quot;$ [rest of string was truncated]&quot;;. ///If you do not want to change your password or received this email by mistake, please ignore it or contact your &quot;${__Virtual [rest of string was truncated]&quot;;.
/// </summary> /// </summary>
internal static string pattern_change_password { internal static string pattern_change_password {
get { get {
@ -831,9 +667,7 @@ namespace ASC.Web.Core.PublicResources {
/// ///
///$GreenButton ///$GreenButton
/// ///
///*Note*: this link is valid for 7 days only. Please complete the portal owner change process within that period. ///*Note*: this link is valid for 7 days only. Please complete the portal owner change process within that period..
///
///If you have any questions or need assistance please feel free to contact us at &quot;support@onlyoffice.com&quot;:&quot;mailto:s [rest of string was truncated]&quot;;.
/// </summary> /// </summary>
internal static string pattern_confirm_owner_change { internal static string pattern_confirm_owner_change {
get { get {
@ -852,9 +686,7 @@ namespace ASC.Web.Core.PublicResources {
/// ///
///$GreenButton ///$GreenButton
/// ///
///*Note*: this link is valid for 7 days only. Please complete the portal address change process within that period. ///*Note*: this link is valid for 7 days only. Please complete the portal address change process within that period..
///
///If you have any questions or need assistance please feel free to contact u [rest of string was truncated]&quot;;.
/// </summary> /// </summary>
internal static string pattern_dns_change { internal static string pattern_dns_change {
get { get {
@ -1117,7 +949,7 @@ namespace ASC.Web.Core.PublicResources {
/// <summary> /// <summary>
/// Looks up a localized string similar to Hello, $UserName! /// Looks up a localized string similar to Hello, $UserName!
/// ///
///We hope you enjoy using ONLYOFFICE. How everything is going with your trial? We would really appreciate you feedback! ///We hope you enjoy using ONLYOFFICE. How everything is going with your trial? We would really appreciate your feedback!
/// ///
///Please, dont hesitate to contact us at &quot;support.onlyoffice.com&quot;:&quot;https://support.onlyoffice.com&quot; whenever you have questions or ideas.. ///Please, dont hesitate to contact us at &quot;support.onlyoffice.com&quot;:&quot;https://support.onlyoffice.com&quot; whenever you have questions or ideas..
/// </summary> /// </summary>
@ -1438,7 +1270,9 @@ namespace ASC.Web.Core.PublicResources {
/// ///
///Best regards, ///Best regards,
///ONLYOFFICE™ Support Team ///ONLYOFFICE™ Support Team
///&quot;www.onlyoffice.com&quot;:&quot;http://onlyoffice.com/&quot;. ///&quot;www.onlyoffice.com&quot;:&quot;http://onlyoffice.com/&quot;
///
///^You receive this email because you are a registered user of the &quot; [rest of string was truncated]&quot;;.
/// </summary> /// </summary>
internal static string pattern_migration_error { internal static string pattern_migration_error {
get { get {
@ -1456,7 +1290,9 @@ namespace ASC.Web.Core.PublicResources {
/// ///
///Best regards, ///Best regards,
///ONLYOFFICE™ Support Team ///ONLYOFFICE™ Support Team
///&quot;www.onlyoffice.com&quot;:&quot;http://onlyoffice.com/&quot;. ///&quot;www.onlyoffice.com&quot;:&quot;http://onlyoffice.com/&quot;
///
///^You receive this email because you are a registered user of the &quot;${__Virt [rest of string was truncated]&quot;;.
/// </summary> /// </summary>
internal static string pattern_migration_server_failure { internal static string pattern_migration_server_failure {
get { get {
@ -1511,7 +1347,9 @@ namespace ASC.Web.Core.PublicResources {
/// ///
///Please, complete your email activation within a week as the link is valid for 7 days only. ///Please, complete your email activation within a week as the link is valid for 7 days only.
/// ///
///Note: we do not send out confidential information (like your password) in emails for safety reasons. You may change your email or password in your &quot;Profile page&quot;:&quot;$MyStaffLink&quot; [rest of string was truncated]&quot;;. ///Note: we do not send out confidential information (like your password) in emails for safety reasons. You may change your email or password in your &quot;Profile page&quot;:&quot;$MyStaffLink&quot;.
///
///To se [rest of string was truncated]&quot;;.
/// </summary> /// </summary>
internal static string pattern_opensource_admin_activation { internal static string pattern_opensource_admin_activation {
get { get {
@ -1581,7 +1419,7 @@ namespace ASC.Web.Core.PublicResources {
/// ///
///ONLYOFFICE is compatible with Microsoft Office™ document formats and guarantees no loss of formatting or quality of created objects. We promise to relieve you from formatting fidelity headache and give full professional editing toolset in your hands. ///ONLYOFFICE is compatible with Microsoft Office™ document formats and guarantees no loss of formatting or quality of created objects. We promise to relieve you from formatting fidelity headache and give full professional editing toolset in your hands.
/// ///
///h3.For a quic [rest of string was truncated]&quot;;. ///h3.For a quick start, [rest of string was truncated]&quot;;.
/// </summary> /// </summary>
internal static string pattern_personal_after_registration1 { internal static string pattern_personal_after_registration1 {
get { get {
@ -1657,7 +1495,7 @@ namespace ASC.Web.Core.PublicResources {
/// ///
///It has been a week since you created your cloud office, so we believe it&apos;s time to unveil some beneficial features you might have missed. ///It has been a week since you created your cloud office, so we believe it&apos;s time to unveil some beneficial features you might have missed.
/// ///
///Connect *Dropbox*, *Google Drive*, *Box*, *OneDrive*, *Nextcloud*, *ownCloud* or *Yandex.Disk* to ONLYOFFICE and create a single document management space for all your documents. You&apos;ll be able to edit external files in ONLYOFFICE and save them to the storage you keep documents [rest of string was truncated]&quot;;. ///Connect *Dropbox*, *Google Drive*, *Box*, *OneDrive*, *Nextcloud*, *ownCloud* or *Yandex.Disk* to ONLYOFFICE and create a single document management space for all your documents. You&apos;ll be able to edit external files in ONLYOFFICE and save them to the storage you keep documents in. [rest of string was truncated]&quot;;.
/// </summary> /// </summary>
internal static string pattern_personal_after_registration7 { internal static string pattern_personal_after_registration7 {
get { get {
@ -1708,7 +1546,7 @@ namespace ASC.Web.Core.PublicResources {
/// <summary> /// <summary>
/// Looks up a localized string similar to Hello, /// Looks up a localized string similar to Hello,
/// ///
///you&apos;ve just registered an account at the ONLYOFFICE solution for personal use. Click &quot;here&quot;:&quot;$InviteLink&quot; to confirm the registration and create a password. ///You&apos;ve just registered an account at the ONLYOFFICE solution for personal use. Click &quot;here&quot;:&quot;$InviteLink&quot; to confirm the registration and create a password.
/// ///
///If you can&apos;t open the link, please copy the following &quot;$InviteLink&quot;:&quot;$InviteLink&quot; and paste it into your browser address bar. ///If you can&apos;t open the link, please copy the following &quot;$InviteLink&quot;:&quot;$InviteLink&quot; and paste it into your browser address bar.
/// ///
@ -1728,7 +1566,7 @@ namespace ASC.Web.Core.PublicResources {
/// ///
///*Note*: After the deletion, your account and all data associated with it will be erased permanently in accordance with our &quot;Privacy statement&quot;:&quot;https://help.onlyoffice.com/products/files/doceditor.aspx?fileid=5048502&amp;doc=SXhWMEVzSEYxNlVVaXJJeUVtS0kyYk14YWdXTEFUQmRWL250NllHNUFGbz0_IjUwNDg1MDIi0&quot;. ///*Note*: After the deletion, your account and all data associated with it will be erased permanently in accordance with our &quot;Privacy statement&quot;:&quot;https://help.onlyoffice.com/products/files/doceditor.aspx?fileid=5048502&amp;doc=SXhWMEVzSEYxNlVVaXJJeUVtS0kyYk14YWdXTEFUQmRWL250NllHNUFGbz0_IjUwNDg1MDIi0&quot;.
/// ///
///Ignore this email if you do not wa [rest of string was truncated]&quot;;. ///Ignore this email if you do not want to [rest of string was truncated]&quot;;.
/// </summary> /// </summary>
internal static string pattern_personal_profile_delete { internal static string pattern_personal_profile_delete {
get { get {
@ -1748,7 +1586,7 @@ namespace ASC.Web.Core.PublicResources {
///*Note*: this link is valid for 7 days only. Please complete the portal deactivation process within that period. ///*Note*: this link is valid for 7 days only. Please complete the portal deactivation process within that period.
/// ///
///You can reactivate your portal any time by clicking the following link: ///You can reactivate your portal any time by clicking the following link:
///p=. &quot;Reactivate Portal&quot;:&quot;$ActivateUrl&quot; (this [rest of string was truncated]&quot;;. ///p=. &quot;Reactivate Portal&quot;:&quot;$ActivateUrl&quot; (this link has no [rest of string was truncated]&quot;;.
/// </summary> /// </summary>
internal static string pattern_portal_deactivate { internal static string pattern_portal_deactivate {
get { get {
@ -1766,7 +1604,7 @@ namespace ASC.Web.Core.PublicResources {
///*Important! All the data stored on your portal, as well as your registration details will be lost and cannot be recovered.* ///*Important! All the data stored on your portal, as well as your registration details will be lost and cannot be recovered.*
/// ///
///#if($AutoRenew == &quot;True&quot;) ///#if($AutoRenew == &quot;True&quot;)
///Before you delete the portal, please make sure that automatic billing is turned off. You may check the status of automatic billing in your &quot;Avangate account&quot;:&quot;h [rest of string was truncated]&quot;;. ///Before you delete the portal, please make sure that automatic billing is turned off. You may check the status of automatic billing in your &quot;Avangate account&quot;:&quot;https://se [rest of string was truncated]&quot;;.
/// </summary> /// </summary>
internal static string pattern_portal_delete { internal static string pattern_portal_delete {
get { get {
@ -1800,7 +1638,7 @@ namespace ASC.Web.Core.PublicResources {
/// ///
///Your $PortalUrl portal address was changed to the new &quot;${__VirtualRootPath}&quot;:&quot;${__VirtualRootPath}&quot; address. ///Your $PortalUrl portal address was changed to the new &quot;${__VirtualRootPath}&quot;:&quot;${__VirtualRootPath}&quot; address.
/// ///
///*Note*: All the shared documents links are inaccessible now, as well as DNS settings and single sign-on options stop working until you change them. The third party iCal links added to your calendar will also stop updating until you reload them.. ///*Note*: All the shared documents links are inaccessible now, as well as DNS settings and single sign-on options stop working until you change them. The third-party iCal links added to your calendar will also stop updating until you reload them..
/// </summary> /// </summary>
internal static string pattern_portal_rename { internal static string pattern_portal_rename {
get { get {
@ -1817,9 +1655,7 @@ namespace ASC.Web.Core.PublicResources {
/// ///
///$GreenButton ///$GreenButton
/// ///
///*Note*: this link is valid for 7 days only. Please complete the profile deletion process within that period. ///*Note*: this link is valid for 7 days only. Please complete the profile deletion process within that period..
///
///If you have any questions or need assistance please feel free to contact u [rest of string was truncated]&quot;;.
/// </summary> /// </summary>
internal static string pattern_profile_delete { internal static string pattern_profile_delete {
get { get {
@ -1827,6 +1663,19 @@ namespace ASC.Web.Core.PublicResources {
} }
} }
/// <summary>
/// Looks up a localized string similar to User &quot;$FromUserName&quot;:&quot;$FromUserLink&quot; has deleted his/her profile, and this profile is now blocked. All users files are still assigned to him/her and occupy disk space.
///
///You can reassign the users documents shared with others to another active user or remove them to free up the portal disk space.
///
///Please go to the user profile using &quot;this link&quot;:&quot;$FromUserLink&quot; to reassign documents to another user or remove the data..
/// </summary>
internal static string pattern_profile_has_deleted_itself {
get {
return ResourceManager.GetString("pattern_profile_has_deleted_itself", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to h1.Your profile at &quot;$__VirtualRootPath&quot;:&quot;$__VirtualRootPath&quot; has been changed /// Looks up a localized string similar to h1.Your profile at &quot;$__VirtualRootPath&quot;:&quot;$__VirtualRootPath&quot; has been changed
/// ///
@ -1837,8 +1686,8 @@ namespace ASC.Web.Core.PublicResources {
///To view your profile follow the link below: ///To view your profile follow the link below:
///&quot;$UserName&quot;:&quot;$MyStaffLink&quot; ///&quot;$UserName&quot;:&quot;$MyStaffLink&quot;
/// ///
///^You receive this email because you are a registered user of the &quot;${__VirtualRootPath}&quot;:&quot;${__VirtualRootPath}&quot; portal.^ ///
///. ///^You receive this email because you are a registered user of the &quot;${__VirtualRootPath}&quot;:&quot;${__VirtualRootPath}&quot; portal.^.
/// </summary> /// </summary>
internal static string pattern_profile_updated { internal static string pattern_profile_updated {
get { get {
@ -1849,7 +1698,7 @@ namespace ASC.Web.Core.PublicResources {
/// <summary> /// <summary>
/// Looks up a localized string similar to Dear $UserName, /// Looks up a localized string similar to Dear $UserName,
/// ///
///The process of reassign data from user &quot;$FromUserName&quot;:&quot;$FromUserLink&quot; to user &quot;$ToUserName&quot;:&quot;$ToUserLink&quot; has been successfully completed. ///The process of data reassignment from user &quot;$FromUserName&quot;:&quot;$FromUserLink&quot; to user &quot;$ToUserName&quot;:&quot;$ToUserLink&quot; has been successfully completed.
/// ///
///^You receive this email because you are a registered user of the &quot;${__VirtualRootPath}&quot;:&quot;${__VirtualRootPath}&quot; portal.^. ///^You receive this email because you are a registered user of the &quot;${__VirtualRootPath}&quot;:&quot;${__VirtualRootPath}&quot; portal.^.
/// </summary> /// </summary>
@ -1862,7 +1711,7 @@ namespace ASC.Web.Core.PublicResources {
/// <summary> /// <summary>
/// Looks up a localized string similar to Dear $UserName, /// Looks up a localized string similar to Dear $UserName,
/// ///
///The process of reassign data from user &quot;$FromUserName&quot;:&quot;$FromUserLink&quot; to user &quot;$ToUserName&quot;:&quot;$ToUserLink&quot; has been failed. ///The process of data reassignment from user &quot;$FromUserName&quot;:&quot;$FromUserLink&quot; to user &quot;$ToUserName&quot;:&quot;$ToUserLink&quot; failed.
/// ///
///Exception message: $Message ///Exception message: $Message
/// ///
@ -1877,7 +1726,7 @@ namespace ASC.Web.Core.PublicResources {
/// <summary> /// <summary>
/// Looks up a localized string similar to Dear $UserName, /// Looks up a localized string similar to Dear $UserName,
/// ///
///The process of removing data from user &quot;$FromUserName&quot;:&quot;$FromUserLink&quot; has been successfully completed. ///The process of data removal from user &quot;$FromUserName&quot;:&quot;$FromUserLink&quot; has been successfully completed.
/// ///
///The deletion of personal data allowed to free: ///The deletion of personal data allowed to free:
/// ///
@ -1897,7 +1746,7 @@ namespace ASC.Web.Core.PublicResources {
/// <summary> /// <summary>
/// Looks up a localized string similar to Dear $UserName, /// Looks up a localized string similar to Dear $UserName,
/// ///
///The process of removing data from user &quot;$FromUserName&quot;:&quot;$FromUserLink&quot; has been failed. ///The process of data removal for user &quot;$FromUserName&quot;:&quot;$FromUserLink&quot; failed.
/// ///
///Exception message: $Message ///Exception message: $Message
/// ///
@ -1986,7 +1835,9 @@ namespace ASC.Web.Core.PublicResources {
/// ///
///Best regards, ///Best regards,
///ONLYOFFICE™ Support Team ///ONLYOFFICE™ Support Team
///&quot;www.onlyoffice.com&quot;:&quot;http://onlyoffice.com/&quot;. ///&quot;www.onlyoffice.com&quot;:&quot;http://onlyoffice.com/&quot;
///
///^You receive this email because you are a registered user of the &quot;${__VirtualRoot [rest of string was truncated]&quot;;.
/// </summary> /// </summary>
internal static string pattern_restore_started { internal static string pattern_restore_started {
get { get {
@ -2104,7 +1955,7 @@ namespace ASC.Web.Core.PublicResources {
/// <summary> /// <summary>
/// Looks up a localized string similar to Hello, $UserName! /// Looks up a localized string similar to Hello, $UserName!
/// ///
///You havent added any teammates, so maybe ONLYOFFICE Personal suits you more? It free and you will be able to: ///You havent added any teammates, so maybe ONLYOFFICE Personal suits you more? It is free and you will be able to:
/// ///
///# Work with text documents, spreadsheets, and presentations. ///# Work with text documents, spreadsheets, and presentations.
///# Share docs with your friends and collaborate on them in real-time. ///# Share docs with your friends and collaborate on them in real-time.
@ -2115,7 +1966,7 @@ namespace ASC.Web.Core.PublicResources {
/// ///
///$GreenButton ///$GreenButton
/// ///
///You can also install this [rest of string was truncated]&quot;;. ///You can also install this &quot;Chrome e [rest of string was truncated]&quot;;.
/// </summary> /// </summary>
internal static string pattern_saas_admin_trial_warning_after30_v10 { internal static string pattern_saas_admin_trial_warning_after30_v10 {
get { get {
@ -2450,7 +2301,7 @@ namespace ASC.Web.Core.PublicResources {
/// <summary> /// <summary>
/// Looks up a localized string similar to Hello, $UserName! /// Looks up a localized string similar to Hello, $UserName!
/// ///
///We hope you enjoy using ONLYOFFICE. How everything is going with your trial? We would really appreciate you feedback! ///We hope you enjoy using ONLYOFFICE. How everything is going with your trial? We would really appreciate your feedback!
/// ///
///Please, dont hesitate to contact us at &quot;support@onlyoffice.com&quot;:&quot;mailto:support@onlyoffice.com&quot; whenever you have questions or ideas. ///Please, dont hesitate to contact us at &quot;support@onlyoffice.com&quot;:&quot;mailto:support@onlyoffice.com&quot; whenever you have questions or ideas.
/// ///
@ -2467,7 +2318,7 @@ namespace ASC.Web.Core.PublicResources {
/// <summary> /// <summary>
/// Looks up a localized string similar to Hello! /// Looks up a localized string similar to Hello!
/// ///
///$__AuthorName has invited you as a guest user to &quot;${__VirtualRootPath}&quot;:&quot;${__VirtualRootPath}&quot;. Accept the invitation by clicking the link: ///You are invited to join &quot;${__VirtualRootPath}&quot;:&quot;${__VirtualRootPath}&quot; as a guest user. Accept the invitation by clicking the link:
/// ///
///$GreenButton ///$GreenButton
/// ///
@ -2484,14 +2335,14 @@ namespace ASC.Web.Core.PublicResources {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Hello $UserName! /// Looks up a localized string similar to Hello!
/// ///
///Your guest profile has been successfully added to &quot;${__VirtualRootPath}&quot;:&quot;${__VirtualRootPath}&quot;. Now you can: ///Your guest profile has been successfully added to &quot;${__VirtualRootPath}&quot;:&quot;${__VirtualRootPath}&quot;. Now you can:
/// ///
///# Edit your &quot;profile&quot;:&quot;$MyStaffLink&quot;. ///# Edit your &quot;profile&quot;:&quot;$MyStaffLink&quot;.
///# View and comment the content available in the &quot;Community&quot;:&quot;${__VirtualRootPath}/products/community/&quot; and &quot;Projects&quot;:&quot;${__VirtualRootPath}/products/projects/&quot;. ///# View and comment the content available in the &quot;Community&quot;:&quot;${__VirtualRootPath}/products/community/&quot; and &quot;Projects&quot;:&quot;${__VirtualRootPath}/products/projects/&quot;.
///# Add and download files available for you in the &quot;Documents&quot;:&quot;${__VirtualRootPath}/products/files/&quot;. ///# Add and download files available for you in the &quot;Documents&quot;:&quot;${__VirtualRootPath}/products/files/&quot;.
///# Organize your schedule with the built-in &quot;Calendar&quot;:&quot;${__VirtualRootPath [rest of string was truncated]&quot;;. ///# Organize your schedule with the built-in &quot;Calendar&quot;:&quot;${__VirtualRootPath}/addons/calendar [rest of string was truncated]&quot;;.
/// </summary> /// </summary>
internal static string pattern_saas_guest_welcome_v10 { internal static string pattern_saas_guest_welcome_v10 {
get { get {
@ -2502,7 +2353,7 @@ namespace ASC.Web.Core.PublicResources {
/// <summary> /// <summary>
/// Looks up a localized string similar to Hello! /// Looks up a localized string similar to Hello!
/// ///
///$__AuthorName has invited you to join ONLYOFFICE at &quot;${__VirtualRootPath}&quot;:&quot;${__VirtualRootPath}&quot;. Accept the invitation by clicking the link: ///You are invited to join ONLYOFFICE at &quot;${__VirtualRootPath}&quot;:&quot;${__VirtualRootPath}&quot;. Accept the invitation by clicking the link:
/// ///
///$GreenButton ///$GreenButton
/// ///
@ -2519,13 +2370,13 @@ namespace ASC.Web.Core.PublicResources {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Hello, $UserName! /// Looks up a localized string similar to Hello!
/// ///
///Welcome to ONLYOFFICE! Your user profile has been successfully added to the portal at &quot;${__VirtualRootPath}&quot;:&quot;${__VirtualRootPath}&quot;. Now you can: ///Welcome to ONLYOFFICE! Your user profile has been successfully added to the portal at &quot;${__VirtualRootPath}&quot;:&quot;${__VirtualRootPath}&quot;. Now you can:
/// ///
///# Create and edit &quot;Documents&quot;:&quot;${__VirtualRootPath}/products/files/&quot;, share them with teammates, and collaborate in real time. ///# Create and edit &quot;Documents&quot;:&quot;${__VirtualRootPath}/products/files/&quot;, share them with teammates, and collaborate in real time.
///# Add your email accounts and manage all correspondence in one place with &quot;Mail&quot;:&quot;${__VirtualRootPath}/addons/mail/&quot;. ///# Add your email accounts and manage all correspondence in one place with &quot;Mail&quot;:&quot;${__VirtualRootPath}/addons/mail/&quot;.
///# Manage your workflow with &quot;Projects&quot;:&quot;${__VirtualRootPath}/products/projects/&quot; and your custo [rest of string was truncated]&quot;;. ///# Manage your workflow with &quot;Projects&quot;:&quot;${__VirtualRootPath}/products/projects/&quot; and your customer relationships [rest of string was truncated]&quot;;.
/// </summary> /// </summary>
internal static string pattern_saas_user_welcome_v10 { internal static string pattern_saas_user_welcome_v10 {
get { get {
@ -2536,7 +2387,10 @@ namespace ASC.Web.Core.PublicResources {
/// <summary> /// <summary>
/// Looks up a localized string similar to h1.&quot;${__VirtualRootPath}&quot;:&quot;${__VirtualRootPath}&quot; portal profile change notification /// Looks up a localized string similar to h1.&quot;${__VirtualRootPath}&quot;:&quot;${__VirtualRootPath}&quot; portal profile change notification
/// ///
///&quot;$__AuthorName&quot;:&quot;$__AuthorUrl&quot; has changed his/her profile details at the &quot;${__VirtualRootPath}&quot;:&quot;${__VirtualRootPath}&quot; portal.. ///&quot;$__AuthorName&quot;:&quot;$__AuthorUrl&quot; has changed his/her profile details at the &quot;${__VirtualRootPath}&quot;:&quot;${__VirtualRootPath}&quot; portal.
///
///
///^You receive this email because you are an administrator of the &quot;${__VirtualRootPath}&quot;:&quot;${__VirtualRootPath}&quot; portal. If you do not want to receive the notifications about profile updates, please manage your &quot;subscription settings&quot;:&quot;$RecipientSubscriptionConfigURL&quot;.^.
/// </summary> /// </summary>
internal static string pattern_self_profile_updated { internal static string pattern_self_profile_updated {
get { get {
@ -2559,7 +2413,10 @@ namespace ASC.Web.Core.PublicResources {
/// ///
///#end ///#end
/// ///
///#end. ///#end
///
///
///^You receive this email because you are a regi [rest of string was truncated]&quot;;.
/// </summary> /// </summary>
internal static string pattern_send_whats_new { internal static string pattern_send_whats_new {
get { get {
@ -2585,7 +2442,10 @@ namespace ASC.Web.Core.PublicResources {
/// <summary> /// <summary>
/// Looks up a localized string similar to h1.New user added to &quot;${__VirtualRootPath}&quot;:&quot;${__VirtualRootPath}&quot; portal /// Looks up a localized string similar to h1.New user added to &quot;${__VirtualRootPath}&quot;:&quot;${__VirtualRootPath}&quot; portal
/// ///
///&quot;$__AuthorName&quot;:&quot;$__AuthorUrl&quot; has joined your portal at &quot;${__VirtualRootPath}&quot;:&quot;${__VirtualRootPath}&quot;.. ///&quot;$__AuthorName&quot;:&quot;$__AuthorUrl&quot; has joined your portal at &quot;${__VirtualRootPath}&quot;:&quot;${__VirtualRootPath}&quot;.
///
///
///^You receive this email because you are an administrator user of the &quot;${__VirtualRootPath}&quot;:&quot;${__VirtualRootPath}&quot; portal. If you do not want to receive the notifications about new users, please manage your &quot;subscription settings&quot;:&quot;$RecipientSubscriptionConfigURL&quot;.^.
/// </summary> /// </summary>
internal static string pattern_user_has_join { internal static string pattern_user_has_join {
get { get {
@ -2619,24 +2479,6 @@ namespace ASC.Web.Core.PublicResources {
} }
} }
/// <summary>
/// Looks up a localized string similar to VoIP disabled.
/// </summary>
internal static string subject_admin_voip_blocked {
get {
return ResourceManager.GetString("subject_admin_voip_blocked", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Notification to administrators about VoIP balance.
/// </summary>
internal static string subject_admin_voip_warning {
get {
return ResourceManager.GetString("subject_admin_voip_warning", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to ${LetterLogoText}. ${__VirtualRootPath} portal backup created. /// Looks up a localized string similar to ${LetterLogoText}. ${__VirtualRootPath} portal backup created.
/// </summary> /// </summary>
@ -3141,6 +2983,15 @@ namespace ASC.Web.Core.PublicResources {
} }
} }
/// <summary>
/// Looks up a localized string similar to User has deleted his/her profile.
/// </summary>
internal static string subject_profile_has_deleted_itself {
get {
return ResourceManager.GetString("subject_profile_has_deleted_itself", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Your profile at ${__VirtualRootPath} has been changed. /// Looks up a localized string similar to Your profile at ${__VirtualRootPath} has been changed.
/// </summary> /// </summary>
@ -3151,7 +3002,7 @@ namespace ASC.Web.Core.PublicResources {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to ${LetterLogoText}. Reassigns user data is completed. /// Looks up a localized string similar to ${LetterLogoText}. User data reassignment is completed.
/// </summary> /// </summary>
internal static string subject_reassigns_completed { internal static string subject_reassigns_completed {
get { get {
@ -3160,7 +3011,7 @@ namespace ASC.Web.Core.PublicResources {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to ${LetterLogoText}. Reassigns user data is failed. /// Looks up a localized string similar to ${LetterLogoText}. User data reassignment failed.
/// </summary> /// </summary>
internal static string subject_reassigns_failed { internal static string subject_reassigns_failed {
get { get {
@ -3169,7 +3020,7 @@ namespace ASC.Web.Core.PublicResources {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to ${LetterLogoText}. Remove user data is completed. /// Looks up a localized string similar to ${LetterLogoText}. User data removal completed.
/// </summary> /// </summary>
internal static string subject_remove_user_data_completed { internal static string subject_remove_user_data_completed {
get { get {
@ -3178,7 +3029,7 @@ namespace ASC.Web.Core.PublicResources {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to ${LetterLogoText}. Remove user data is failed. /// Looks up a localized string similar to ${LetterLogoText}. User data removal failed.
/// </summary> /// </summary>
internal static string subject_remove_user_data_failed { internal static string subject_remove_user_data_failed {
get { get {