Merge branch 'feature/files' of https://github.com/ONLYOFFICE/CommunityServer-AspNetCore into feature/files
This commit is contained in:
commit
0025f3b858
@ -29,6 +29,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using ASC.Common.Logging;
|
||||
using ASC.Common.Notify.Patterns;
|
||||
using ASC.Notify.Channels;
|
||||
@ -36,6 +37,7 @@ using ASC.Notify.Cron;
|
||||
using ASC.Notify.Messages;
|
||||
using ASC.Notify.Patterns;
|
||||
using ASC.Notify.Recipients;
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
@ -360,9 +362,10 @@ namespace ASC.Notify.Engine
|
||||
PrepareRequestFillPatterns(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)
|
||||
|
@ -26,10 +26,10 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<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.Core.Common\ASC.Core.Common.csproj" />
|
||||
<ProjectReference Include="..\..\ASC.Data.Storage\ASC.Data.Storage.csproj" />
|
||||
<ProjectReference Include="..\ASC.Notify\ASC.Notify.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Protobuf Include="protos\BackupProgress.proto" />
|
||||
|
@ -24,32 +24,37 @@
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using ASC.Common;
|
||||
using ASC.Common.Utils;
|
||||
|
||||
using ASC.Web.Studio.Core.Notify;
|
||||
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace ASC.Data.Backup.Service
|
||||
{
|
||||
internal class BackupServiceLauncher : IHostedService
|
||||
{
|
||||
{
|
||||
public IServiceProvider ServiceProvider { get; }
|
||||
private BackupCleanerService CleanerService { get; set; }
|
||||
private BackupSchedulerService SchedulerService { get; set; }
|
||||
private BackupWorker BackupWorker { get; set; }
|
||||
private IConfiguration Configuration { get; set; }
|
||||
public BackupServiceNotifier BackupServiceNotifier { get; }
|
||||
|
||||
public BackupServiceLauncher(
|
||||
public BackupServiceLauncher(
|
||||
IServiceProvider serviceProvider,
|
||||
BackupCleanerService cleanerService,
|
||||
BackupSchedulerService schedulerService,
|
||||
BackupWorker backupWorker,
|
||||
IConfiguration configuration,
|
||||
BackupServiceNotifier backupServiceNotifier)
|
||||
{
|
||||
{
|
||||
ServiceProvider = serviceProvider;
|
||||
CleanerService = cleanerService;
|
||||
SchedulerService = schedulerService;
|
||||
BackupWorker = backupWorker;
|
||||
@ -58,7 +63,9 @@ namespace ASC.Data.Backup.Service
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
{
|
||||
NotifyConfiguration.Configure(ServiceProvider);
|
||||
|
||||
var settings = Configuration.GetSetting<BackupSettings>("backup");
|
||||
|
||||
BackupWorker.Start(settings);
|
||||
|
@ -25,79 +25,143 @@
|
||||
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
using ASC.Common;
|
||||
using ASC.Common.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using ASC.Notify;
|
||||
|
||||
using ASC.Core;
|
||||
using ASC.Core.Tenants;
|
||||
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
|
||||
{
|
||||
public class NotifyHelper
|
||||
{
|
||||
private const string NotifyService = "ASC.Web.Studio.Core.Notify.StudioNotifyService, ASC.Web.Studio";
|
||||
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 IServiceProvider ServiceProvider { get; }
|
||||
|
||||
public NotifyHelper(IServiceProvider serviceProvider)
|
||||
{
|
||||
ServiceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public NotifyHelper(IOptionsMonitor<ILog> options, NotifyService notifyService)
|
||||
{
|
||||
this.notifyService = notifyService;
|
||||
log = options.CurrentValue;
|
||||
}
|
||||
public void SendAboutTransferStart(int tenantId, string targetRegion, bool notifyUsers)
|
||||
{
|
||||
SendNotification(MethodTransferStart, tenantId, targetRegion, notifyUsers);
|
||||
public void SendAboutTransferStart(Tenant tenant, string targetRegion, bool notifyUsers)
|
||||
{
|
||||
MigrationNotify(tenant, Actions.MigrationPortalStart, targetRegion, string.Empty, notifyUsers);
|
||||
}
|
||||
|
||||
public void SendAboutTransferComplete(int tenantId, string targetRegion, string targetAddress, bool notifyOnlyOwner)
|
||||
{
|
||||
SendNotification(MethodTransferCompleted, tenantId, targetRegion, targetAddress, !notifyOnlyOwner);
|
||||
public void SendAboutTransferComplete(Tenant tenant, string targetRegion, string targetAddress, bool notifyOnlyOwner)
|
||||
{
|
||||
MigrationNotify(tenant, Actions.MigrationPortalSuccess, targetRegion, targetAddress, !notifyOnlyOwner);
|
||||
}
|
||||
|
||||
public void SendAboutTransferError(int tenantId, string targetRegion, string resultAddress, bool notifyOnlyOwner)
|
||||
{
|
||||
SendNotification(MethodTransferError, tenantId, targetRegion, resultAddress, !notifyOnlyOwner);
|
||||
public void SendAboutTransferError(Tenant tenant, string targetRegion, string resultAddress, bool notifyOnlyOwner)
|
||||
{
|
||||
MigrationNotify(tenant, !string.IsNullOrEmpty(targetRegion) ? Actions.MigrationPortalError : Actions.MigrationPortalServerFailure, targetRegion, resultAddress, !notifyOnlyOwner);
|
||||
}
|
||||
|
||||
public void SendAboutBackupCompleted(int tenantId, Guid userId, string link)
|
||||
{
|
||||
SendNotification(MethodBackupCompleted, tenantId, userId, link);
|
||||
public void SendAboutBackupCompleted(Guid userId)
|
||||
{
|
||||
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 SendAboutRestoreStarted(int tenantId, bool notifyAllUsers)
|
||||
{
|
||||
SendNotification(MethodRestoreStarted, tenantId, notifyAllUsers);
|
||||
public void SendAboutRestoreStarted(Tenant tenant, bool 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 });
|
||||
}
|
||||
|
||||
public void SendAboutRestoreCompleted(int tenantId, bool notifyAllUsers)
|
||||
{
|
||||
SendNotification(MethodRestoreCompleted, tenantId, notifyAllUsers);
|
||||
}
|
||||
|
||||
private void SendNotification(string method, int tenantId, params object[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
notifyService.InvokeSendMethod(NotifyService, method, tenantId, args);
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
log.Warn("Error while sending notification", error);
|
||||
}
|
||||
public void SendAboutRestoreCompleted(Tenant tenant, bool 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
|
||||
? 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)));
|
||||
}
|
||||
|
||||
private void MigrationNotify(Tenant tenant, INotifyAction action, string region, string url, bool notify)
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
public static class NotifyHelperExtension
|
||||
{
|
||||
public static DIHelper AddNotifyHelperService(this DIHelper services)
|
||||
{
|
||||
services.TryAddScoped<NotifyHelper>();
|
||||
return services
|
||||
.AddNotifyService();
|
||||
services.TryAddSingleton<NotifyHelper>();
|
||||
|
||||
return services
|
||||
.AddNotifyConfiguration()
|
||||
.AddStudioNotifySourceService()
|
||||
.AddUserManagerService()
|
||||
.AddStudioNotifyHelperService()
|
||||
.AddDisplayUserSettingsService();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -376,7 +376,8 @@ namespace ASC.Data.Backup.Service
|
||||
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 storagePath = tempFile;
|
||||
try
|
||||
@ -424,10 +425,11 @@ namespace ASC.Data.Backup.Service
|
||||
|
||||
if (UserId != Guid.Empty && !IsScheduled)
|
||||
{
|
||||
notifyHelper.SendAboutBackupCompleted(TenantId, UserId, Link);
|
||||
notifyHelper.SendAboutBackupCompleted(UserId);
|
||||
}
|
||||
|
||||
IsCompleted = true;
|
||||
backupWorker.PublishProgress(this);
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
@ -437,6 +439,15 @@ namespace ASC.Data.Backup.Service
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
backupWorker.PublishProgress(this);
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
Log.Error("publish", error);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (!(storagePath == tempFile && StorageType == BackupStorageType.Local))
|
||||
@ -503,13 +514,13 @@ namespace ASC.Data.Backup.Service
|
||||
var tempFile = PathHelper.GetTempFileName(TempFolder);
|
||||
try
|
||||
{
|
||||
notifyHelper.SendAboutRestoreStarted(TenantId, Notify);
|
||||
tenant = tenantManager.GetTenant(TenantId);
|
||||
notifyHelper.SendAboutRestoreStarted(tenant, Notify);
|
||||
var storage = backupStorageFactory.GetBackupStorage(StorageType, TenantId, StorageParams);
|
||||
storage.Download(StoragePath, tempFile);
|
||||
|
||||
Percentage = 10;
|
||||
|
||||
tenant = tenantManager.GetTenant(TenantId);
|
||||
tenant.SetStatus(TenantStatus.Restoring);
|
||||
tenantManager.SaveTenant(tenant);
|
||||
|
||||
@ -536,7 +547,7 @@ namespace ASC.Data.Backup.Service
|
||||
var tenants = tenantManager.GetTenants();
|
||||
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
|
||||
Thread.Sleep(TimeSpan.FromMinutes(2));
|
||||
|
||||
notifyHelper.SendAboutRestoreCompleted(restoredTenant.TenantId, Notify);
|
||||
notifyHelper.SendAboutRestoreCompleted(restoredTenant, Notify);
|
||||
}
|
||||
|
||||
Percentage = 75;
|
||||
|
||||
backupWorker.PublishProgress(this);
|
||||
|
||||
File.Delete(tempFile);
|
||||
|
||||
Percentage = 100;
|
||||
backupWorker.PublishProgress(this);
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
@ -579,6 +593,15 @@ namespace ASC.Data.Backup.Service
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
backupWorker.PublishProgress(this);
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
Log.Error("publish", error);
|
||||
}
|
||||
|
||||
if (File.Exists(tempFile))
|
||||
{
|
||||
File.Delete(tempFile);
|
||||
@ -649,11 +672,12 @@ namespace ASC.Data.Backup.Service
|
||||
var backupWorker = scope.ServiceProvider.GetService<BackupWorker>();
|
||||
|
||||
var tempFile = PathHelper.GetTempFileName(TempFolder);
|
||||
var alias = tenantManager.GetTenant(TenantId).TenantAlias;
|
||||
var tenant = tenantManager.GetTenant(TenantId);
|
||||
var alias = tenant.TenantAlias;
|
||||
|
||||
try
|
||||
{
|
||||
notifyHelper.SendAboutTransferStart(TenantId, TargetRegion, Notify);
|
||||
notifyHelper.SendAboutTransferStart(tenant, TargetRegion, Notify);
|
||||
var transferProgressItem = scope.ServiceProvider.GetService<TransferPortalTask>();
|
||||
transferProgressItem.Init(TenantId, ConfigPaths[CurrentRegion], ConfigPaths[TargetRegion], Limit, TempFolder);
|
||||
transferProgressItem.ProgressChanged += (sender, args) =>
|
||||
@ -668,7 +692,8 @@ namespace ASC.Data.Backup.Service
|
||||
transferProgressItem.RunJob();
|
||||
|
||||
Link = GetLink(alias, false);
|
||||
notifyHelper.SendAboutTransferComplete(TenantId, TargetRegion, Link, !Notify);
|
||||
notifyHelper.SendAboutTransferComplete(tenant, TargetRegion, Link, !Notify);
|
||||
backupWorker.PublishProgress(this);
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
@ -676,10 +701,19 @@ namespace ASC.Data.Backup.Service
|
||||
Error = error;
|
||||
|
||||
Link = GetLink(alias, true);
|
||||
notifyHelper.SendAboutTransferError(TenantId, TargetRegion, Link, !Notify);
|
||||
notifyHelper.SendAboutTransferError(tenant, TargetRegion, Link, !Notify);
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
backupWorker.PublishProgress(this);
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
Log.Error("publish", error);
|
||||
}
|
||||
|
||||
if (File.Exists(tempFile))
|
||||
{
|
||||
File.Delete(tempFile);
|
||||
|
@ -156,6 +156,7 @@ namespace ASC.Notify
|
||||
}
|
||||
|
||||
dbContext.SaveChanges();
|
||||
tx.Commit();
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
@ -79,6 +79,7 @@ class SectionFilterContent extends React.Component {
|
||||
const sortBy = data.sortId;
|
||||
const sortOrder =
|
||||
data.sortDirection === "desc" ? "descending" : "ascending";
|
||||
const viewAs = data.viewAs;
|
||||
const authorType = getAuthorType(data.filterValues);
|
||||
const withSubfolders = getSearchParams(data.filterValues);
|
||||
|
||||
@ -95,6 +96,7 @@ class SectionFilterContent extends React.Component {
|
||||
newFilter.page = 0;
|
||||
newFilter.sortBy = sortBy;
|
||||
newFilter.sortOrder = sortOrder;
|
||||
newFilter.viewAs = viewAs;
|
||||
newFilter.filterType = filterType;
|
||||
newFilter.search = search;
|
||||
newFilter.authorType = authorType;
|
||||
@ -207,14 +209,21 @@ class SectionFilterContent extends React.Component {
|
||||
getSortData = () => {
|
||||
const { t } = this.props;
|
||||
|
||||
return [
|
||||
const commonOptions = [
|
||||
{ key: "lastModifiedDate", label: t("ByLastModifiedDate"), default: true },
|
||||
{ key: "creationDate", label: t("ByCreationDate"), default: true },
|
||||
{ key: "title", label: t("ByTitle"), default: true },
|
||||
{ key: "type", label: t("ByType"), 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 = () => {
|
||||
@ -222,7 +231,8 @@ class SectionFilterContent extends React.Component {
|
||||
const selectedFilterData = {
|
||||
filterValues: [],
|
||||
sortDirection: filter.sortOrder === "ascending" ? "asc" : "desc",
|
||||
sortId: filter.sortBy
|
||||
sortId: filter.sortBy,
|
||||
viewAs: filter.viewAs
|
||||
};
|
||||
|
||||
selectedFilterData.inputValue = filter.search;
|
||||
@ -269,12 +279,12 @@ class SectionFilterContent extends React.Component {
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
return (!isEqual(this.props.filter, nextProps.filter) || this.props.selectedFolderId !== nextProps.selectedFolderId || this.state.isReady !== nextState.isReady);
|
||||
}
|
||||
|
||||
|
||||
|
||||
render() {
|
||||
const selectedFilterData = this.getSelectedFilterData();
|
||||
const { t, i18n } = this.props;
|
||||
const filterColumnCount = window.innerWidth < 500 ? {} : {filterColumnCount: 3}
|
||||
const filterColumnCount = window.innerWidth < 500 ? {} : { filterColumnCount: 3 }
|
||||
return (
|
||||
<FilterInput
|
||||
getFilterData={this.getData}
|
||||
|
@ -82,5 +82,7 @@
|
||||
"TooltipElementMoveMessage": "Move {{element}}",
|
||||
"TooltipElementsMoveMessage": "Move {{element}} elements",
|
||||
"TooltipElementCopyMessage": "Copy {{element}}",
|
||||
"TooltipElementsCopyMessage": "Copy {{element}} elements"
|
||||
"TooltipElementsCopyMessage": "Copy {{element}} elements",
|
||||
"ViewList": "List",
|
||||
"ViewTiles": "Tiles"
|
||||
}
|
@ -82,5 +82,7 @@
|
||||
"TooltipElementMoveMessage": "Переместить {{element}}",
|
||||
"TooltipElementsMoveMessage": "Переместить {{element}} элемента(ов)",
|
||||
"TooltipElementCopyMessage": "Скопировать {{element}}",
|
||||
"TooltipElementsCopyMessage": "Скопировать {{element}} элемента(ов)"
|
||||
"TooltipElementsCopyMessage": "Скопировать {{element}} элемента(ов)",
|
||||
"ViewList": "Список",
|
||||
"ViewTiles": "Плитки"
|
||||
}
|
@ -5,6 +5,7 @@ export const FILTER_TYPE = "filterType";
|
||||
export const SEARCH = "search";
|
||||
export const SORT_BY = "sortby";
|
||||
export const SORT_ORDER = "sortorder";
|
||||
export const VIEW_AS = "viewas";
|
||||
export const PAGE = "page";
|
||||
export const PAGE_COUNT = "pagecount";
|
||||
export const FOLDER = "folder";
|
||||
|
@ -4,6 +4,7 @@ import {
|
||||
SEARCH,
|
||||
SORT_BY,
|
||||
SORT_ORDER,
|
||||
VIEW_AS,
|
||||
PAGE,
|
||||
PAGE_COUNT,
|
||||
AUTHOR_TYPE,
|
||||
@ -28,6 +29,7 @@ export function getFilterByLocation(location) {
|
||||
defaultFilter.withSubfolders;
|
||||
const search = urlFilter[SEARCH] || defaultFilter.search;
|
||||
const sortBy = urlFilter[SORT_BY] || defaultFilter.sortBy;
|
||||
const viewAs = urlFilter[VIEW_AS] || defaultFilter.viewAs;
|
||||
const sortOrder = urlFilter[SORT_ORDER] || defaultFilter.sortOrder;
|
||||
const page = (urlFilter[PAGE] && (+urlFilter[PAGE]-1)) || defaultFilter.page;
|
||||
const pageCount =
|
||||
@ -41,6 +43,7 @@ export function getFilterByLocation(location) {
|
||||
defaultFilter.total,
|
||||
sortBy,
|
||||
sortOrder,
|
||||
viewAs,
|
||||
filterType,
|
||||
withSubfolders,
|
||||
search,
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "asc-web-common",
|
||||
"version": "1.0.167",
|
||||
"version": "1.0.169",
|
||||
"description": "Ascensio System SIA common components and solutions library",
|
||||
"license": "AGPL-3.0",
|
||||
"files": [
|
||||
|
@ -5,6 +5,7 @@ const DEFAULT_PAGE_COUNT = 25;
|
||||
const DEFAULT_TOTAL = 0;
|
||||
const DEFAULT_SORT_BY = "lastModifiedDate";
|
||||
const DEFAULT_SORT_ORDER = "ascending";
|
||||
const DEFAULT_VIEW = "row";
|
||||
const DEFAULT_FILTER_TYPE = null;
|
||||
const DEFAULT_SEARCH_TYPE = true; //withSubfolders
|
||||
const DEFAULT_SEARCH = null;
|
||||
@ -29,6 +30,7 @@ class FilesFilter {
|
||||
total = DEFAULT_TOTAL,
|
||||
sortBy = DEFAULT_SORT_BY,
|
||||
sortOrder = DEFAULT_SORT_ORDER,
|
||||
viewAs = DEFAULT_VIEW,
|
||||
filterType = DEFAULT_FILTER_TYPE,
|
||||
withSubfolders = DEFAULT_SEARCH_TYPE,
|
||||
search = DEFAULT_SEARCH,
|
||||
@ -41,6 +43,7 @@ class FilesFilter {
|
||||
this.pageCount = pageCount;
|
||||
this.sortBy = sortBy;
|
||||
this.sortOrder = sortOrder;
|
||||
this.viewAs = viewAs;
|
||||
this.filterType = filterType;
|
||||
this.withSubfolders = withSubfolders;
|
||||
this.search = search;
|
||||
@ -106,6 +109,7 @@ class FilesFilter {
|
||||
this.total,
|
||||
this.sortBy,
|
||||
this.sortOrder,
|
||||
this.viewAs,
|
||||
this.filterType,
|
||||
this.withSubfolders,
|
||||
this.search,
|
||||
@ -124,6 +128,7 @@ class FilesFilter {
|
||||
this.search === filter.search &&
|
||||
this.sortBy === filter.sortBy &&
|
||||
this.sortOrder === filter.sortOrder &&
|
||||
this.viewAs === filter.viewAs &&
|
||||
this.page === filter.page &&
|
||||
this.selectedItem.key === filter.selectedItem.key &&
|
||||
this.folder === filter.folder &&
|
||||
|
@ -5,6 +5,7 @@ import isEqual from 'lodash/isEqual';
|
||||
import throttle from 'lodash/throttle';
|
||||
import FilterBlock from './sub-components/FilterBlock';
|
||||
import SortComboBox from './sub-components/SortComboBox';
|
||||
import ViewSelector from './sub-components/ViewSelector';
|
||||
import map from 'lodash/map';
|
||||
import clone from 'lodash/clone';
|
||||
import StyledFilterInput from './StyledFilterInput';
|
||||
@ -92,6 +93,7 @@ class FilterInput extends React.Component {
|
||||
|
||||
this.state = {
|
||||
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 : "",
|
||||
searchText: props.selectedFilterData.inputValue || props.value,
|
||||
|
||||
@ -120,6 +122,8 @@ class FilterInput extends React.Component {
|
||||
this.onDeleteFilterItem = this.onDeleteFilterItem.bind(this);
|
||||
this.clearFilter = this.clearFilter.bind(this);
|
||||
|
||||
this.onClickViewSelector = this.onClickViewSelector.bind(this);
|
||||
|
||||
this.throttledResize = throttle(this.resize, 300);
|
||||
|
||||
}
|
||||
@ -230,9 +234,15 @@ class FilterInput extends React.Component {
|
||||
})
|
||||
}
|
||||
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 });
|
||||
}
|
||||
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() {
|
||||
const sortData = this.props.getSortData();
|
||||
if (sortData.length > 0) {
|
||||
@ -243,19 +253,19 @@ class FilterInput extends React.Component {
|
||||
}
|
||||
onClickSortItem(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() {
|
||||
|
||||
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 });
|
||||
}
|
||||
onSearchChanged(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) {
|
||||
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() {
|
||||
const _this = this;
|
||||
@ -280,7 +290,7 @@ class FilterInput extends React.Component {
|
||||
openFilterItems: [],
|
||||
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) {
|
||||
const currentFilterItems = inputFilterItems || cloneObjectsArray(this.state.filterValues);
|
||||
@ -344,9 +354,9 @@ class FilterInput extends React.Component {
|
||||
item.key = item.key.replace(item.group + "_", '');
|
||||
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);
|
||||
cloneFilterValues = cloneFilterValues.map(function (item) {
|
||||
item.key = item.key.replace(item.group + "_", '');
|
||||
@ -356,7 +366,8 @@ class FilterInput extends React.Component {
|
||||
inputValue: searchText != undefined ? searchText : this.state.searchText,
|
||||
filterValues: cloneFilterValues.filter(item => item.key != '-1'),
|
||||
sortId: sortId,
|
||||
sortDirection: sortDirection
|
||||
sortDirection: sortDirection,
|
||||
viewAs: viewAs
|
||||
});
|
||||
}
|
||||
onChangeFilter(result) {
|
||||
@ -364,7 +375,7 @@ class FilterInput extends React.Component {
|
||||
searchText: result.inputValue,
|
||||
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() {
|
||||
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;
|
||||
@ -467,7 +478,7 @@ class FilterInput extends React.Component {
|
||||
item.key = item.key.replace(item.group + "_", '');
|
||||
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({
|
||||
filterValues: currentFilterItems,
|
||||
openFilterItems: currentFilterItems,
|
||||
@ -503,7 +514,7 @@ class FilterInput extends React.Component {
|
||||
item.key = item.key.replace(item.group + "_", '');
|
||||
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 */
|
||||
|
||||
const { searchText, filterValues, openFilterItems,
|
||||
hideFilterItems, sortId, sortDirection } = this.state;
|
||||
hideFilterItems, sortId, sortDirection, viewAs } = this.state;
|
||||
|
||||
// console.log("filter input render, openFilterItems", openFilterItems, 'hideFilterItems', hideFilterItems);
|
||||
let iconSize = 33;
|
||||
@ -574,6 +585,7 @@ class FilterInput extends React.Component {
|
||||
options={getSortData()}
|
||||
isDisabled={isDisabled}
|
||||
onChangeSortId={this.onClickSortItem}
|
||||
onChangeView={this.onClickViewSelector}
|
||||
onChangeSortDirection={this.onChangeSortDirection}
|
||||
selectedOption={getSortData().length > 0 ? getSortData().find(x => x.key === sortId) : {}}
|
||||
onButtonClick={this.onSortDirectionClick}
|
||||
@ -581,6 +593,11 @@ class FilterInput extends React.Component {
|
||||
directionAscLabel={directionAscLabel}
|
||||
directionDescLabel={directionDescLabel}
|
||||
/>
|
||||
<ViewSelector
|
||||
isDisabled={isDisabled}
|
||||
onClickViewSelector={this.onClickViewSelector}
|
||||
viewAs={viewAs}
|
||||
/>
|
||||
</StyledFilterInput>
|
||||
|
||||
);
|
||||
|
@ -43,7 +43,8 @@ class FilterStory extends React.Component {
|
||||
inputValue: "text",
|
||||
filterValues: [
|
||||
{key: "1", group: "filter-status"}
|
||||
]
|
||||
],
|
||||
viewAs: "row"
|
||||
}
|
||||
};
|
||||
this.buttonClick = this.buttonClick.bind(this);
|
||||
|
@ -17,7 +17,10 @@ const StyledFilterInput = styled.div`
|
||||
.styled-search-input {
|
||||
display: block;
|
||||
float: left;
|
||||
width: calc(100% - 140px);
|
||||
width: calc(100% - 212px);
|
||||
@media (max-width: 460px) {
|
||||
width: calc(100% - 140px);
|
||||
}
|
||||
@media ${mobile} {
|
||||
width: calc(100% - 58px);
|
||||
}
|
||||
@ -121,8 +124,50 @@ const StyledFilterInput = styled.div`
|
||||
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`
|
||||
@ -228,4 +273,11 @@ export const StyledIconButton = styled.div`
|
||||
transform: ${state => !state.sortDirection ? 'scale(1, -1)' : 'scale(1)'};
|
||||
`;
|
||||
|
||||
|
||||
export const StyledIconWrapper = styled.div`
|
||||
display: inline-flex;
|
||||
width: 32px;
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
export default StyledFilterInput;
|
@ -5,137 +5,168 @@ import PropTypes from 'prop-types';
|
||||
import { StyledIconButton } from '../StyledFilterInput';
|
||||
|
||||
class SortComboBox extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const { sortDirection } = props;
|
||||
const { sortDirection } = props;
|
||||
|
||||
this.state = {
|
||||
sortDirection
|
||||
}
|
||||
|
||||
this.combobox = React.createRef();
|
||||
}
|
||||
onButtonClick = () => {
|
||||
const { onChangeSortDirection } = this.props;
|
||||
const { sortDirection } = this.state;
|
||||
typeof onChangeSortDirection === 'function' && onChangeSortDirection(+(sortDirection === 0 ? 1 : 0));
|
||||
this.setState({
|
||||
sortDirection: sortDirection === 0 ? 1 : 0
|
||||
});
|
||||
this.state = {
|
||||
sortDirection
|
||||
}
|
||||
|
||||
onChangeSortId = (e) => {
|
||||
const { onChangeSortId } = this.props;
|
||||
typeof onChangeSortId === 'function' && onChangeSortId(e.target.value);
|
||||
}
|
||||
onChangeSortDirection = (e) => {
|
||||
const sortDirection = +e.target.value;
|
||||
const { onChangeSortDirection } = this.props;
|
||||
this.setState({ sortDirection });
|
||||
typeof onChangeSortDirection === 'function' && onChangeSortDirection(sortDirection);
|
||||
}
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
//TODO
|
||||
/*const comboboxText = this.combobox.current.ref.current.children[0].children[1];
|
||||
if(comboboxText.scrollWidth > Math.round(comboboxText.getBoundingClientRect().width)){
|
||||
comboboxText.style.opacity = "0";
|
||||
}else{
|
||||
comboboxText.style.opacity = "1";
|
||||
}*/
|
||||
const { sortDirection } = nextProps;
|
||||
if (this.props.sortDirection !== sortDirection) {
|
||||
this.setState({
|
||||
sortDirection
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return (!isEqual(this.props, nextProps) || !isEqual(this.state, nextState));
|
||||
}
|
||||
render() {
|
||||
const { options, directionAscLabel, directionDescLabel, isDisabled,
|
||||
selectedOption } = this.props;
|
||||
const { sortDirection } = this.state;
|
||||
let sortArray = options.map(function (item) {
|
||||
item.value = item.key
|
||||
return item;
|
||||
});
|
||||
let sortDirectionArray = [
|
||||
{ value: '0', label: directionAscLabel },
|
||||
{ value: '1', label: directionDescLabel }
|
||||
];
|
||||
this.combobox = React.createRef();
|
||||
}
|
||||
onButtonClick = () => {
|
||||
const { onChangeSortDirection } = this.props;
|
||||
const { sortDirection } = this.state;
|
||||
typeof onChangeSortDirection === 'function' && onChangeSortDirection(+(sortDirection === 0 ? 1 : 0));
|
||||
this.setState({
|
||||
sortDirection: sortDirection === 0 ? 1 : 0
|
||||
});
|
||||
}
|
||||
|
||||
const advancedOptions = (
|
||||
<>
|
||||
<DropDownItem noHover >
|
||||
<RadioButtonGroup
|
||||
fontWeight={600}
|
||||
isDisabled={isDisabled}
|
||||
name={'direction'}
|
||||
onClick={this.onChangeSortDirection}
|
||||
options={sortDirectionArray}
|
||||
orientation='vertical'
|
||||
selected={sortDirection.toString()}
|
||||
spacing='0px'
|
||||
/>
|
||||
</DropDownItem>
|
||||
<DropDownItem isSeparator />
|
||||
<DropDownItem noHover >
|
||||
<RadioButtonGroup
|
||||
fontWeight={600}
|
||||
isDisabled={isDisabled}
|
||||
name={'sort'}
|
||||
onClick={this.onChangeSortId}
|
||||
options={sortArray}
|
||||
orientation='vertical'
|
||||
selected={selectedOption.key}
|
||||
spacing='0px'
|
||||
/>
|
||||
</DropDownItem>
|
||||
</>
|
||||
);
|
||||
return (
|
||||
<ComboBox
|
||||
advancedOptions={advancedOptions}
|
||||
className='styled-sort-combobox'
|
||||
directionX="right"
|
||||
onChangeSortId = (e) => {
|
||||
const { onChangeSortId } = this.props;
|
||||
typeof onChangeSortId === 'function' && onChangeSortId(e.target.value);
|
||||
}
|
||||
|
||||
onChangeView = (e) => {
|
||||
const { onChangeView } = this.props;
|
||||
typeof onChangeView === 'function' && onChangeView(e.target.value);
|
||||
}
|
||||
|
||||
onChangeSortDirection = (e) => {
|
||||
const sortDirection = +e.target.value;
|
||||
const { onChangeSortDirection } = this.props;
|
||||
this.setState({ sortDirection });
|
||||
typeof onChangeSortDirection === 'function' && onChangeSortDirection(sortDirection);
|
||||
}
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
//TODO
|
||||
/*const comboboxText = this.combobox.current.ref.current.children[0].children[1];
|
||||
if(comboboxText.scrollWidth > Math.round(comboboxText.getBoundingClientRect().width)){
|
||||
comboboxText.style.opacity = "0";
|
||||
}else{
|
||||
comboboxText.style.opacity = "1";
|
||||
}*/
|
||||
const { sortDirection } = nextProps;
|
||||
if (this.props.sortDirection !== sortDirection) {
|
||||
this.setState({
|
||||
sortDirection
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return (!isEqual(this.props, nextProps) || !isEqual(this.state, nextState));
|
||||
}
|
||||
render() {
|
||||
const { options, directionAscLabel, directionDescLabel, isDisabled,
|
||||
selectedOption } = this.props;
|
||||
const { sortDirection } = this.state;
|
||||
|
||||
let settingsArray = options.filter(item => {
|
||||
item.value = item.key
|
||||
return item.isSetting === true;
|
||||
});
|
||||
|
||||
let sortArray = options.filter(item => {
|
||||
item.value = item.key
|
||||
return item.isSetting !== true;
|
||||
});
|
||||
|
||||
let sortDirectionArray = [
|
||||
{ value: '0', label: directionAscLabel },
|
||||
{ value: '1', label: directionDescLabel }
|
||||
];
|
||||
|
||||
const advancedOptions = (
|
||||
<>
|
||||
<DropDownItem noHover >
|
||||
<RadioButtonGroup
|
||||
fontWeight={600}
|
||||
isDisabled={isDisabled}
|
||||
name={'direction'}
|
||||
onClick={this.onChangeSortDirection}
|
||||
options={sortDirectionArray}
|
||||
orientation='vertical'
|
||||
selected={sortDirection.toString()}
|
||||
spacing='0px'
|
||||
/>
|
||||
</DropDownItem>
|
||||
<DropDownItem isSeparator />
|
||||
<DropDownItem noHover >
|
||||
<RadioButtonGroup
|
||||
fontWeight={600}
|
||||
isDisabled={isDisabled}
|
||||
name={'sort'}
|
||||
onClick={this.onChangeSortId}
|
||||
options={sortArray}
|
||||
orientation='vertical'
|
||||
selected={selectedOption.key}
|
||||
spacing='0px'
|
||||
/>
|
||||
</DropDownItem>
|
||||
{settingsArray.length !== 0 &&
|
||||
<>
|
||||
<DropDownItem isSeparator />
|
||||
<DropDownItem noHover >
|
||||
<RadioButtonGroup
|
||||
fontWeight={600}
|
||||
isDisabled={isDisabled}
|
||||
options={[]}
|
||||
ref={this.combobox}
|
||||
scaled={true}
|
||||
selectedOption={selectedOption}
|
||||
size="content"
|
||||
>
|
||||
<StyledIconButton sortDirection={!!sortDirection}>
|
||||
<IconButton
|
||||
clickColor={"#333"}
|
||||
color={"#A3A9AE"}
|
||||
hoverColor={"#333"}
|
||||
iconName={'ZASortingIcon'}
|
||||
isDisabled={isDisabled}
|
||||
isFill={true}
|
||||
onClick={this.onButtonClick}
|
||||
size={10}
|
||||
/>
|
||||
</StyledIconButton>
|
||||
</ComboBox>
|
||||
);
|
||||
}
|
||||
name={'view'}
|
||||
onClick={this.onChangeView}
|
||||
options={settingsArray}
|
||||
orientation='vertical'
|
||||
selected={selectedOption.key}
|
||||
spacing='0px'
|
||||
/>
|
||||
</DropDownItem>
|
||||
</>
|
||||
}
|
||||
</>
|
||||
);
|
||||
return (
|
||||
<ComboBox
|
||||
advancedOptions={advancedOptions}
|
||||
className='styled-sort-combobox'
|
||||
directionX="right"
|
||||
isDisabled={isDisabled}
|
||||
options={[]}
|
||||
ref={this.combobox}
|
||||
scaled={true}
|
||||
selectedOption={selectedOption}
|
||||
size="content"
|
||||
>
|
||||
<StyledIconButton sortDirection={!!sortDirection}>
|
||||
<IconButton
|
||||
clickColor={"#333"}
|
||||
color={"#A3A9AE"}
|
||||
hoverColor={"#333"}
|
||||
iconName={'ZASortingIcon'}
|
||||
isDisabled={isDisabled}
|
||||
isFill={true}
|
||||
onClick={this.onButtonClick}
|
||||
size={10}
|
||||
/>
|
||||
</StyledIconButton>
|
||||
</ComboBox>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SortComboBox.propTypes = {
|
||||
directionAscLabel: PropTypes.string,
|
||||
directionDescLabel: PropTypes.string,
|
||||
isDisabled: PropTypes.bool,
|
||||
onButtonClick: PropTypes.func,
|
||||
onChangeSortDirection: PropTypes.func,
|
||||
onChangeSortId: PropTypes.func,
|
||||
sortDirection: PropTypes.number,
|
||||
directionAscLabel: PropTypes.string,
|
||||
directionDescLabel: PropTypes.string,
|
||||
isDisabled: PropTypes.bool,
|
||||
onButtonClick: PropTypes.func,
|
||||
onChangeSortDirection: PropTypes.func,
|
||||
onChangeSortId: PropTypes.func,
|
||||
onChangeView: PropTypes.func,
|
||||
sortDirection: PropTypes.number,
|
||||
}
|
||||
|
||||
SortComboBox.defaultProps = {
|
||||
isDisabled: false,
|
||||
sortDirection: 0
|
||||
isDisabled: false,
|
||||
sortDirection: 0
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
@ -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 |
@ -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 |
@ -133,6 +133,8 @@ import OrigCrossSidebarIcon from './cross.sidebar.react.svg';
|
||||
import OrigCheckboxIcon from './checkbox.react.svg';
|
||||
import OrigCheckboxCheckedIcon from './checkbox.checked.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 OrigEyeOffIcon from './eye.off.react.svg';
|
||||
@ -800,4 +802,12 @@ export const ShareLinkedInIcon = createStyledIcon(
|
||||
export const KeyIcon = createStyledIcon(
|
||||
OrigKeyIcon,
|
||||
'KeyIcon'
|
||||
);
|
||||
export const FilterViewSelectorRowIcon = createStyledIcon(
|
||||
OrigFilterViewSelectorRowIcon,
|
||||
'FilterViewSelectorRowIcon'
|
||||
);
|
||||
export const FilterViewSelectorTileIcon = createStyledIcon(
|
||||
OrigFilterViewSelectorTileIcon,
|
||||
'FilterViewSelectorTileIcon'
|
||||
);
|
@ -20,6 +20,14 @@
|
||||
<Compile Remove="WebZones\IRenderCustomNavigation.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="PublicResources\webstudio_patterns.xml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="PublicResources\webstudio_patterns.xml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\common\ASC.Common\ASC.Common.csproj" />
|
||||
<ProjectReference Include="..\..\common\ASC.Core.Common\ASC.Core.Common.csproj" />
|
||||
|
@ -28,7 +28,7 @@ using ASC.Notify.Model;
|
||||
|
||||
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 PeriodicNotify = new NotifyAction("periodic_notify", "periodic notifications");
|
||||
@ -49,8 +49,8 @@ namespace ASC.Web.Studio.Core.Notify
|
||||
public static readonly INotifyAction RestoreCompleted = new NotifyAction("restore_completed", "restore_completed");
|
||||
public static readonly INotifyAction PortalDeactivate = new NotifyAction("portal_deactivate", "portal deactivate");
|
||||
public static readonly INotifyAction PortalDelete = new NotifyAction("portal_delete", "portal delete");
|
||||
public static readonly INotifyAction PortalDeleteSuccessV10 = new NotifyAction("portal_delete_success_v10");
|
||||
|
||||
public static readonly INotifyAction PortalDeleteSuccessV10 = new NotifyAction("portal_delete_success_v10");
|
||||
|
||||
public static readonly INotifyAction ProfileDelete = new NotifyAction("profile_delete", "profile_delete");
|
||||
public static readonly INotifyAction ProfileHasDeletedItself = new NotifyAction("profile_has_deleted_itself", "profile_has_deleted_itself");
|
||||
public static readonly INotifyAction ReassignsCompleted = new NotifyAction("reassigns_completed", "reassigns_completed");
|
||||
|
@ -58,7 +58,7 @@ namespace ASC.Web.Studio.Core.Notify
|
||||
{
|
||||
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 StudioNotifyHelper StudioNotifyHelper { get; }
|
||||
@ -653,49 +653,6 @@ namespace ASC.Web.Studio.Core.Notify
|
||||
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
|
||||
|
||||
public void SendMsgPortalDeactivation(Tenant t, string deactivateUrl, string activateUrl)
|
||||
@ -861,39 +818,6 @@ namespace ASC.Web.Studio.Core.Notify
|
||||
|
||||
#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)
|
||||
{
|
||||
var users = UserManager.GetUsers()
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
namespace ASC.Web.Studio.Core.Notify
|
||||
{
|
||||
static class Tags
|
||||
public static class Tags
|
||||
{
|
||||
public const string UserName = "UserName";
|
||||
public const string UserLastName = "UserLastName";
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user