diff --git a/ASC.Web.sln b/ASC.Web.sln index 5e956f6b43..7eeb12063d 100644 --- a/ASC.Web.sln +++ b/ASC.Web.sln @@ -101,6 +101,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.Migration", "common\ASC EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.ActiveDirectory", "common\ASC.ActiveDirectory\ASC.ActiveDirectory.csproj", "{9F81862F-303D-467F-8DC9-044BE2CCF329}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.EventBus.ActiveMQ", "common\ASC.EventBus.ActiveMQ\ASC.EventBus.ActiveMQ.csproj", "{86916EF2-4A1B-441C-B673-EB0F68EC9C3A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -287,6 +289,10 @@ Global {9F81862F-303D-467F-8DC9-044BE2CCF329}.Debug|Any CPU.Build.0 = Debug|Any CPU {9F81862F-303D-467F-8DC9-044BE2CCF329}.Release|Any CPU.ActiveCfg = Release|Any CPU {9F81862F-303D-467F-8DC9-044BE2CCF329}.Release|Any CPU.Build.0 = Release|Any CPU + {86916EF2-4A1B-441C-B673-EB0F68EC9C3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {86916EF2-4A1B-441C-B673-EB0F68EC9C3A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {86916EF2-4A1B-441C-B673-EB0F68EC9C3A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {86916EF2-4A1B-441C-B673-EB0F68EC9C3A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ASC.Web.slnf b/ASC.Web.slnf index 97a3273494..8696da7e39 100644 --- a/ASC.Web.slnf +++ b/ASC.Web.slnf @@ -10,6 +10,7 @@ "common\\ASC.Data.Encryption\\ASC.Data.Encryption.csproj", "common\\ASC.Data.Reassigns\\ASC.Data.Reassigns.csproj", "common\\ASC.Data.Storage\\ASC.Data.Storage.csproj", + "common\\ASC.EventBus.ActiveMQ\\ASC.EventBus.ActiveMQ.csproj", "common\\ASC.EventBus.Extensions.Logger\\ASC.EventBus.Extensions.Logger.csproj", "common\\ASC.EventBus.RabbitMQ\\ASC.EventBus.RabbitMQ.csproj", "common\\ASC.EventBus\\ASC.EventBus.csproj", diff --git a/common/ASC.Api.Core/ASC.Api.Core.csproj b/common/ASC.Api.Core/ASC.Api.Core.csproj index f9bd664cde..a20d31ef2e 100644 --- a/common/ASC.Api.Core/ASC.Api.Core.csproj +++ b/common/ASC.Api.Core/ASC.Api.Core.csproj @@ -29,6 +29,7 @@ + diff --git a/common/ASC.Api.Core/Extensions/ConfigurationManagerExtension.cs b/common/ASC.Api.Core/Extensions/ConfigurationManagerExtension.cs index 79df0e4421..981f98994c 100644 --- a/common/ASC.Api.Core/Extensions/ConfigurationManagerExtension.cs +++ b/common/ASC.Api.Core/Extensions/ConfigurationManagerExtension.cs @@ -54,6 +54,8 @@ public static class ConfigurationManagerExtension .AddJsonFile($"kafka.{env.EnvironmentName}.json", optional: true, reloadOnChange: true) .AddJsonFile("rabbitmq.json", optional: true, reloadOnChange: true) .AddJsonFile($"rabbitmq.{env.EnvironmentName}.json", optional: true, reloadOnChange: true) + .AddJsonFile("activemq.json", optional: true, reloadOnChange: true) + .AddJsonFile($"activemq.{env.EnvironmentName}.json", optional: true, reloadOnChange: true) .AddJsonFile("redis.json", optional: true, reloadOnChange: true) .AddJsonFile($"redis.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); diff --git a/common/ASC.Api.Core/Extensions/ServiceCollectionExtension.cs b/common/ASC.Api.Core/Extensions/ServiceCollectionExtension.cs index 3b6aa600ec..2793136231 100644 --- a/common/ASC.Api.Core/Extensions/ServiceCollectionExtension.cs +++ b/common/ASC.Api.Core/Extensions/ServiceCollectionExtension.cs @@ -75,6 +75,7 @@ public static class ServiceCollectionExtension services.AddSingleton(); var rabbitMQConfiguration = configuration.GetSection("RabbitMQ").Get(); + var activeMQConfiguration = configuration.GetSection("ActiveMQ").Get(); if (rabbitMQConfiguration != null) { @@ -154,6 +155,54 @@ public static class ServiceCollectionExtension return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, serializer, subscriptionClientName, retryCount); }); + } + else if (activeMQConfiguration != null) + { + services.AddSingleton(sp => + { + var cfg = sp.GetRequiredService(); + + var logger = sp.GetRequiredService>(); + + var factory = new Apache.NMS.NMSConnectionFactory(activeMQConfiguration.Uri); + + var retryCount = 5; + + if (!string.IsNullOrEmpty(cfg["core:eventBus:connectRetryCount"])) + { + retryCount = int.Parse(cfg["core:eventBus:connectRetryCount"]); + } + + return new DefaultActiveMQPersistentConnection(factory, logger, retryCount); + }); + + services.AddSingleton(sp => + { + var cfg = sp.GetRequiredService(); + + var activeMQPersistentConnection = sp.GetRequiredService(); + var iLifetimeScope = sp.GetRequiredService(); + var logger = sp.GetRequiredService>(); + var eventBusSubcriptionsManager = sp.GetRequiredService(); + + var serializer = new EventBus.Serializers.ProtobufSerializer(); + + var subscriptionClientName = "asc_event_bus_default_queue"; + + if (!string.IsNullOrEmpty(cfg["core:eventBus:subscriptionClientName"])) + { + subscriptionClientName = cfg["core:eventBus:subscriptionClientName"]; + } + + var retryCount = 5; + + if (!string.IsNullOrEmpty(cfg["core:eventBus:connectRetryCount"])) + { + retryCount = int.Parse(cfg["core:eventBus:connectRetryCount"]); + } + + return new EventBusActiveMQ(activeMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, serializer, subscriptionClientName, retryCount); + }); } else { diff --git a/common/ASC.Api.Core/GlobalUsings.cs b/common/ASC.Api.Core/GlobalUsings.cs index e230c1b254..1c11008fa8 100644 --- a/common/ASC.Api.Core/GlobalUsings.cs +++ b/common/ASC.Api.Core/GlobalUsings.cs @@ -144,3 +144,7 @@ global using StackExchange.Redis.Extensions.Core.Configuration; global using StackExchange.Redis.Extensions.Newtonsoft; global using LogLevel = Microsoft.Extensions.Logging.LogLevel; + +global using ASC.Common.Caching.Settings; +global using ASC.EventBus.ActiveMQ; + diff --git a/common/ASC.Api.Core/Middleware/TenantStatusFilter.cs b/common/ASC.Api.Core/Middleware/TenantStatusFilter.cs index e959b180f3..973abcace0 100644 --- a/common/ASC.Api.Core/Middleware/TenantStatusFilter.cs +++ b/common/ASC.Api.Core/Middleware/TenantStatusFilter.cs @@ -49,15 +49,20 @@ public class TenantStatusFilter : IResourceFilter { context.Result = new StatusCodeResult((int)HttpStatusCode.NotFound); _logger.WarningTenantNotFound(); - return; } if (tenant.Status == TenantStatus.RemovePending || tenant.Status == TenantStatus.Suspended) { + if (tenant.Status == TenantStatus.Suspended && + context.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor && + controllerActionDescriptor.EndpointMetadata.OfType().Any()) + { + return; + } + context.Result = new StatusCodeResult((int)HttpStatusCode.NotFound); _logger.WarningTenantIsNotRemoved(tenant.Id); - return; } @@ -67,6 +72,7 @@ public class TenantStatusFilter : IResourceFilter { return; } + context.Result = new StatusCodeResult((int)HttpStatusCode.Forbidden); _logger.WarningTenantStatus(tenant.Id, tenant.Status); return; diff --git a/common/ASC.Api.Core/Routing/FormatRoute.cs b/common/ASC.Api.Core/Routing/FormatRoute.cs index 477e68e99b..3b90c0a8e5 100644 --- a/common/ASC.Api.Core/Routing/FormatRoute.cs +++ b/common/ASC.Api.Core/Routing/FormatRoute.cs @@ -30,4 +30,10 @@ namespace ASC.Web.Api.Routing; public class AllowNotPaymentAttribute : Attribute { +} + +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)] +public class AllowSuspendedAttribute : Attribute +{ + } \ No newline at end of file diff --git a/common/ASC.Common/Caching/RabbitMQCache.cs b/common/ASC.Common/Caching/RabbitMQCache.cs index 48b8873287..56531ac1bd 100644 --- a/common/ASC.Common/Caching/RabbitMQCache.cs +++ b/common/ASC.Common/Caching/RabbitMQCache.cs @@ -232,17 +232,4 @@ public class RabbitMQCache : IDisposable, ICacheNotify where T : IMessage< _disposed = true; } -} - -public class RabbitMQSettings -{ - public string HostName { get; set; } - public string UserName { get; set; } - public string Password { get; set; } - public int Port { get; set; } - public string VirtualHost { get; set; } - public string Uri { get; set; } - public bool EnableSsl { get; set; } - public string SslServerName { get; set; } - public string SslCertPath { get; set; } -} +} \ No newline at end of file diff --git a/common/ASC.Common/Caching/Settings/ActiveMQSettings.cs b/common/ASC.Common/Caching/Settings/ActiveMQSettings.cs new file mode 100644 index 0000000000..dab21df243 --- /dev/null +++ b/common/ASC.Common/Caching/Settings/ActiveMQSettings.cs @@ -0,0 +1,31 @@ +// (c) Copyright Ascensio System SIA 2010-2022 +// +// This program is a free software product. +// You can redistribute it and/or modify it under the terms +// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software +// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended +// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of +// any third-party rights. +// +// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see +// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html +// +// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021. +// +// The interactive user interfaces in modified source and object code versions of the Program must +// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3. +// +// Pursuant to Section 7(b) of the License you must retain the original Product logo when +// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under +// trademark law for use of our trademarks. +// +// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing +// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 +// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode + +namespace ASC.Common.Caching.Settings; +public class ActiveMQSettings +{ + public string Uri { get; set; } +} diff --git a/common/ASC.Common/Caching/Settings/RabbitMQSettings.cs b/common/ASC.Common/Caching/Settings/RabbitMQSettings.cs new file mode 100644 index 0000000000..9dc1b85b5d --- /dev/null +++ b/common/ASC.Common/Caching/Settings/RabbitMQSettings.cs @@ -0,0 +1,39 @@ +// (c) Copyright Ascensio System SIA 2010-2022 +// +// This program is a free software product. +// You can redistribute it and/or modify it under the terms +// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software +// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended +// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of +// any third-party rights. +// +// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see +// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html +// +// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021. +// +// The interactive user interfaces in modified source and object code versions of the Program must +// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3. +// +// Pursuant to Section 7(b) of the License you must retain the original Product logo when +// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under +// trademark law for use of our trademarks. +// +// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing +// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 +// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode + +namespace ASC.Common.Caching; +public class RabbitMQSettings +{ + public string HostName { get; set; } + public string UserName { get; set; } + public string Password { get; set; } + public int Port { get; set; } + public string VirtualHost { get; set; } + public string Uri { get; set; } + public bool EnableSsl { get; set; } + public string SslServerName { get; set; } + public string SslCertPath { get; set; } +} diff --git a/common/ASC.Core.Common/Billing/TariffService.cs b/common/ASC.Core.Common/Billing/TariffService.cs index a61653cad2..7923d65505 100644 --- a/common/ASC.Core.Common/Billing/TariffService.cs +++ b/common/ASC.Core.Common/Billing/TariffService.cs @@ -45,6 +45,8 @@ public class TariffServiceStorage Notify = notify; Notify.Subscribe((i) => { + Cache.Insert(TariffService.GetTariffNeedToUpdateCacheKey(i.TenantId), "update", _cacheExpiration); + Cache.Remove(TariffService.GetTariffCacheKey(i.TenantId)); Cache.Remove(TariffService.GetBillingUrlCacheKey(i.TenantId)); Cache.Remove(TariffService.GetBillingPaymentCacheKey(i.TenantId)); // clear all payments @@ -169,7 +171,11 @@ public class TariffService : ITariffService { tariff = GetBillingInfo(tenantId) ?? CreateDefault(); tariff = CalculateTariff(tenantId, tariff); - tariffId = tariff.Id; + + if (string.IsNullOrEmpty(_cache.Get(GetTariffNeedToUpdateCacheKey(tenantId)))) + { + tariffId = tariff.Id; + } if (_billingClient.Configured && withRequestToPaymentSystem) { @@ -210,9 +216,9 @@ public class TariffService : ITariffService if (SaveBillingInfo(tenantId, asynctariff)) { asynctariff = CalculateTariff(tenantId, asynctariff); + tariff = asynctariff; + tariffId = asynctariff.Id; } - - tariffId = asynctariff.Id; } catch (BillingNotFoundException) { @@ -237,9 +243,9 @@ public class TariffService : ITariffService if (SaveBillingInfo(tenantId, asynctariff)) { asynctariff = CalculateTariff(tenantId, asynctariff); + tariff = asynctariff; + tariffId = asynctariff.Id; } - - tariffId = asynctariff.Id; } catch (Exception error) { @@ -367,6 +373,11 @@ public class TariffService : ITariffService return $"{tenantId}:tariff"; } + internal static string GetTariffNeedToUpdateCacheKey(int tenantId) + { + return $"{tenantId}:update"; + } + internal static string GetBillingUrlCacheKey(int tenantId) { return $"{tenantId}:billing:urls"; diff --git a/common/ASC.EventBus.ActiveMQ/ASC.EventBus.ActiveMQ.csproj b/common/ASC.EventBus.ActiveMQ/ASC.EventBus.ActiveMQ.csproj new file mode 100644 index 0000000000..54b5c44169 --- /dev/null +++ b/common/ASC.EventBus.ActiveMQ/ASC.EventBus.ActiveMQ.csproj @@ -0,0 +1,20 @@ + + + + net6.0 + enable + disable + enable + + + + + + + + + + + + + diff --git a/common/ASC.EventBus.ActiveMQ/DefaultActiveMQPersistentConnection.cs b/common/ASC.EventBus.ActiveMQ/DefaultActiveMQPersistentConnection.cs new file mode 100644 index 0000000000..e46584f8bf --- /dev/null +++ b/common/ASC.EventBus.ActiveMQ/DefaultActiveMQPersistentConnection.cs @@ -0,0 +1,175 @@ +// (c) Copyright Ascensio System SIA 2010-2022 +// +// This program is a free software product. +// You can redistribute it and/or modify it under the terms +// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software +// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended +// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of +// any third-party rights. +// +// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see +// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html +// +// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021. +// +// The interactive user interfaces in modified source and object code versions of the Program must +// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3. +// +// Pursuant to Section 7(b) of the License you must retain the original Product logo when +// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under +// trademark law for use of our trademarks. +// +// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing +// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 +// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode + +using ASC.EventBus.ActiveMQ.Log; + +namespace ASC.EventBus.ActiveMQ; + +public class DefaultActiveMQPersistentConnection + : IActiveMQPersistentConnection +{ + private readonly IConnectionFactory _connectionFactory; + private readonly ILogger _logger; + private readonly int _retryCount; + private IConnection _connection; + private bool _disposed; + readonly object sync_root = new object(); + + public DefaultActiveMQPersistentConnection(IConnectionFactory connectionFactory, ILogger logger, int retryCount = 5) + { + _connectionFactory = connectionFactory ?? throw new ArgumentNullException(nameof(connectionFactory)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _retryCount = retryCount; + } + + public bool IsConnected + { + get + { + return _connection != null && _connection.IsStarted && !_disposed; + } + } + + public ISession CreateSession() + { + return CreateSession(AcknowledgementMode.AutoAcknowledge); + } + + public void Dispose() + { + if (_disposed) + { + return; + } + + _disposed = true; + + try + { + _connection.ExceptionListener -= OnExceptionListener; + _connection.ConnectionInterruptedListener -= OnConnectionInterruptedListener; + _connection.ConnectionResumedListener -= OnConnectionResumedListener; + + _connection.Dispose(); + } + catch (IOException ex) + { + _logger.CriticalDefaultActiveMQPersistentConnection(ex); + } + } + + private void OnExceptionListener(Exception exception) + { + if (_disposed) + { + return; + } + + _logger.WarningActiveMQConnectionThrowException(); + + TryConnect(); + } + + private void OnConnectionResumedListener() + { + if (_disposed) + { + return; + } + + _logger.WarningActiveMQConnectionThrowException(); + + TryConnect(); + } + + private void OnConnectionInterruptedListener() + { + if (_disposed) + { + return; + } + + _logger.WarningActiveMQConnectionThrowException(); + + TryConnect(); + } + + public bool TryConnect() + { + _logger.InformationActiveMQTryingConnect(); + + lock (sync_root) + { + var policy = Policy.Handle() + .WaitAndRetry(_retryCount, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (ex, time) => + { + _logger.WarningActiveMQCouldNotConnect(time.TotalSeconds, ex); + } + ); + + policy.Execute(() => + { + _connection = _connectionFactory + .CreateConnection(); + _connection.Start(); + }); + + if (IsConnected) + { + _connection.ExceptionListener += OnExceptionListener; + _connection.ConnectionInterruptedListener += OnConnectionInterruptedListener; + _connection.ConnectionResumedListener += OnConnectionResumedListener; + + if (_connection is Apache.NMS.AMQP.NmsConnection) + { + var hostname = ((Apache.NMS.AMQP.NmsConnection)_connection).ConnectionInfo.ConfiguredUri.Host; + + _logger.InformationActiveMQAcquiredPersistentConnection(hostname); + + } + + + return true; + } + else + { + _logger.CriticalActiveMQCouldNotBeCreated(); + + return false; + } + } + } + + public ISession CreateSession(AcknowledgementMode acknowledgementMode) + { + if (!IsConnected) + { + throw new InvalidOperationException("No ActiveMQ connections are available to perform this action"); + } + + return _connection.CreateSession(acknowledgementMode); + } +} diff --git a/common/ASC.EventBus.ActiveMQ/EventBusActiveMQ.cs b/common/ASC.EventBus.ActiveMQ/EventBusActiveMQ.cs new file mode 100644 index 0000000000..4d56f30ab6 --- /dev/null +++ b/common/ASC.EventBus.ActiveMQ/EventBusActiveMQ.cs @@ -0,0 +1,363 @@ +// (c) Copyright Ascensio System SIA 2010-2022 +// +// This program is a free software product. +// You can redistribute it and/or modify it under the terms +// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software +// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended +// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of +// any third-party rights. +// +// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see +// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html +// +// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021. +// +// The interactive user interfaces in modified source and object code versions of the Program must +// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3. +// +// Pursuant to Section 7(b) of the License you must retain the original Product logo when +// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under +// trademark law for use of our trademarks. +// +// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing +// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 +// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode + +namespace ASC.EventBus.ActiveMQ; + +public class EventBusActiveMQ : IEventBus, IDisposable +{ + const string EXCHANGE_NAME = "asc_event_bus"; + const string AUTOFAC_SCOPE_NAME = "asc_event_bus"; + + private readonly ILogger _logger; + private readonly IEventBusSubscriptionsManager _subsManager; + private readonly ILifetimeScope _autofac; + + private static ConcurrentQueue _rejectedEvents; + private readonly IActiveMQPersistentConnection _persistentConnection; + private readonly IIntegrationEventSerializer _serializer; + private ISession _consumerSession; + + private readonly List _consumers; + + private readonly int _retryCount; + private string _queueName; + + public EventBusActiveMQ(IActiveMQPersistentConnection persistentConnection, + ILogger logger, + ILifetimeScope autofac, + IEventBusSubscriptionsManager subsManager, + IIntegrationEventSerializer serializer, + string queueName = null, + int retryCount = 5) + { + _persistentConnection = persistentConnection ?? throw new ArgumentNullException(nameof(persistentConnection)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _subsManager = subsManager ?? new InMemoryEventBusSubscriptionsManager(); + _serializer = serializer; + _queueName = queueName; + _autofac = autofac; + _retryCount = retryCount; + _rejectedEvents = new ConcurrentQueue(); + _consumerSession = CreateConsumerSession(); + _subsManager.OnEventRemoved += SubsManager_OnEventRemoved; + _consumers = new List(); + } + + private void SubsManager_OnEventRemoved(object sender, string eventName) + { + if (!_persistentConnection.IsConnected) + { + _persistentConnection.TryConnect(); + } + + using (var session = _persistentConnection.CreateSession()) + { + var messageSelector = $"eventName='{eventName}'"; + + var findedConsumer = _consumers.Find(x => x.MessageSelector == messageSelector); + + if (findedConsumer != null) + { + findedConsumer.Close(); + + _consumers.Remove(findedConsumer); + } + + if (_subsManager.IsEmpty) + { + _queueName = string.Empty; + _consumerSession.Close(); + } + } + } + + public void Publish(IntegrationEvent @event) + { + if (!_persistentConnection.IsConnected) + { + _persistentConnection.TryConnect(); + } + + var policy = Policy.Handle() + .WaitAndRetry(_retryCount, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (ex, time) => + { + _logger.WarningCouldNotPublishEvent(@event.Id, time.TotalSeconds, ex); + }); + + using (var session = _persistentConnection.CreateSession(AcknowledgementMode.ClientAcknowledge)) + { + var destination = session.GetQueue(_queueName); + + using (var producer = session.CreateProducer(destination)) + { + producer.DeliveryMode = MsgDeliveryMode.Persistent; + + var body = _serializer.Serialize(@event); + + var request = session.CreateStreamMessage(); + var eventName = @event.GetType().Name; + + request.Properties["eventName"] = eventName; + + request.WriteBytes(body); + + producer.Send(request); + } + } + } + + public void Subscribe() + where T : IntegrationEvent + where TH : IIntegrationEventHandler + { + var eventName = _subsManager.GetEventKey(); + + _logger.InformationSubscribing(eventName, typeof(TH).GetGenericTypeName()); + + _subsManager.AddSubscription(); + + StartBasicConsume(eventName); + } + + public void SubscribeDynamic(string eventName) where TH : IDynamicIntegrationEventHandler + { + _logger.InformationSubscribingDynamic(eventName, typeof(TH).GetGenericTypeName()); + + _subsManager.AddDynamicSubscription(eventName); + + StartBasicConsume(eventName); + } + + private ISession CreateConsumerSession() + { + if (!_persistentConnection.IsConnected) + { + _persistentConnection.TryConnect(); + } + + _logger.TraceCreatingConsumerSession(); + + _consumerSession = _persistentConnection.CreateSession(AcknowledgementMode.ClientAcknowledge); + + return _consumerSession; + } + + private void StartBasicConsume(string eventName) + { + _logger.TraceStartingBasicConsume(); + + if (!_persistentConnection.IsConnected) + { + _persistentConnection.TryConnect(); + } + + var destination = _consumerSession.GetQueue(_queueName); + + var messageSelector = $"eventName='{eventName}'"; + + var consumer = _consumerSession.CreateConsumer(destination, messageSelector); + + _consumers.Add(consumer); + + if (_consumerSession != null) + { + consumer.Listener += Consumer_Listener; + } + else + { + _logger.ErrorStartBasicConsumeCantCall(); + } + } + + private void Consumer_Listener(IMessage objMessage) + { + var streamMessage = objMessage as IStreamMessage; + + var eventName = streamMessage.Properties["eventName"].ToString(); + + var buffer = new byte[4 * 1024]; + + byte[] serializedMessage; + + using (var ms = new MemoryStream()) + { + int read; + + while ((read = streamMessage.ReadBytes(buffer)) > 0) + { + ms.Write(buffer, 0, read); + + if (read < buffer.Length) + { + break; + } + } + + serializedMessage = ms.ToArray(); + } + + var @event = GetEvent(eventName, serializedMessage); + var message = @event.ToString(); + + try + { + if (message.ToLowerInvariant().Contains("throw-fake-exception")) + { + throw new InvalidOperationException($"Fake exception requested: \"{message}\""); + } + + ProcessEvent(eventName, @event) + .GetAwaiter() + .GetResult(); + + streamMessage.Acknowledge(); + } + catch (IntegrationEventRejectExeption ex) + { + _logger.WarningProcessingMessage(message, ex); + + if (_rejectedEvents.TryPeek(out var result) && result.Equals(ex.EventId)) + { + _rejectedEvents.TryDequeue(out var _); + streamMessage.Acknowledge(); + } + else + { + _rejectedEvents.Enqueue(ex.EventId); + } + + } + catch (Exception ex) + { + _logger.WarningProcessingMessage(message, ex); + + streamMessage.Acknowledge(); + } + } + + private IntegrationEvent GetEvent(string eventName, byte[] serializedMessage) + { + var eventType = _subsManager.GetEventTypeByName(eventName); + + var integrationEvent = (IntegrationEvent)_serializer.Deserialize(serializedMessage, eventType); + + return integrationEvent; + } + + + public void Unsubscribe() + where T : IntegrationEvent + where TH : IIntegrationEventHandler + { + var eventName = _subsManager.GetEventKey(); + + _logger.InformationUnsubscribing(eventName); + + _subsManager.RemoveSubscription(); + } + + public void UnsubscribeDynamic(string eventName) where TH : IDynamicIntegrationEventHandler + { + _subsManager.RemoveDynamicSubscription(eventName); + } + + private void PreProcessEvent(IntegrationEvent @event) + { + if (_rejectedEvents.Count == 0) + { + return; + } + + if (_rejectedEvents.TryPeek(out var result) && result.Equals(@event.Id)) + { + @event.Redelivered = true; + } + } + + private async Task ProcessEvent(string eventName, IntegrationEvent @event) + { + _logger.TraceProcessingEvent(eventName); + + PreProcessEvent(@event); + + if (_subsManager.HasSubscriptionsForEvent(eventName)) + { + using (var scope = _autofac.BeginLifetimeScope(AUTOFAC_SCOPE_NAME)) + { + var subscriptions = _subsManager.GetHandlersForEvent(eventName); + + foreach (var subscription in subscriptions) + { + if (subscription.IsDynamic) + { + var handler = scope.ResolveOptional(subscription.HandlerType) as IDynamicIntegrationEventHandler; + if (handler == null) + { + continue; + } + + using dynamic eventData = @event; + await Task.Yield(); + await handler.Handle(eventData); + } + else + { + var handler = scope.ResolveOptional(subscription.HandlerType); + if (handler == null) + { + continue; + } + + var eventType = _subsManager.GetEventTypeByName(eventName); + var concreteType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType); + + await Task.Yield(); + await (Task)concreteType.GetMethod("Handle").Invoke(handler, new object[] { @event }); + } + } + } + } + else + { + _logger.WarningNoSubscription(eventName); + } + } + + public void Dispose() + { + foreach (var consumer in _consumers) + { + consumer.Dispose(); + } + + if (_consumerSession != null) + { + _consumerSession.Dispose(); + } + + _subsManager.Clear(); + } +} \ No newline at end of file diff --git a/common/ASC.EventBus.ActiveMQ/GlobalUsings.cs b/common/ASC.EventBus.ActiveMQ/GlobalUsings.cs new file mode 100644 index 0000000000..2236131caf --- /dev/null +++ b/common/ASC.EventBus.ActiveMQ/GlobalUsings.cs @@ -0,0 +1,42 @@ +// (c) Copyright Ascensio System SIA 2010-2022 +// +// This program is a free software product. +// You can redistribute it and/or modify it under the terms +// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software +// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended +// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of +// any third-party rights. +// +// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see +// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html +// +// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021. +// +// The interactive user interfaces in modified source and object code versions of the Program must +// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3. +// +// Pursuant to Section 7(b) of the License you must retain the original Product logo when +// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under +// trademark law for use of our trademarks. +// +// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing +// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 +// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode + +global using System.Collections.Concurrent; +global using System.Net.Sockets; + +global using ASC.EventBus.Abstractions; +global using ASC.EventBus.Events; +global using ASC.EventBus.ActiveMQ.Log; +global using Apache.NMS; +global using Autofac; + +global using Microsoft.Extensions.Logging; + +global using Polly; + +global using ASC.EventBus.Exceptions; +global using ASC.EventBus.Extensions; +global using ASC.EventBus.Serializers; diff --git a/common/ASC.EventBus.ActiveMQ/IActiveMQPersistentConnection.cs b/common/ASC.EventBus.ActiveMQ/IActiveMQPersistentConnection.cs new file mode 100644 index 0000000000..103978f6ff --- /dev/null +++ b/common/ASC.EventBus.ActiveMQ/IActiveMQPersistentConnection.cs @@ -0,0 +1,35 @@ +// (c) Copyright Ascensio System SIA 2010-2022 +// +// This program is a free software product. +// You can redistribute it and/or modify it under the terms +// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software +// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended +// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of +// any third-party rights. +// +// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see +// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html +// +// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021. +// +// The interactive user interfaces in modified source and object code versions of the Program must +// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3. +// +// Pursuant to Section 7(b) of the License you must retain the original Product logo when +// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under +// trademark law for use of our trademarks. +// +// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing +// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 +// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode + +namespace ASC.EventBus.ActiveMQ; + +public interface IActiveMQPersistentConnection +{ + bool IsConnected { get; } + bool TryConnect(); + ISession CreateSession(AcknowledgementMode acknowledgementMode); + ISession CreateSession(); +} \ No newline at end of file diff --git a/common/ASC.EventBus.ActiveMQ/Log/DefaultActiveMQPersistentConnectionLogger.cs b/common/ASC.EventBus.ActiveMQ/Log/DefaultActiveMQPersistentConnectionLogger.cs new file mode 100644 index 0000000000..23d9c83936 --- /dev/null +++ b/common/ASC.EventBus.ActiveMQ/Log/DefaultActiveMQPersistentConnectionLogger.cs @@ -0,0 +1,53 @@ +// (c) Copyright Ascensio System SIA 2010-2022 +// +// This program is a free software product. +// You can redistribute it and/or modify it under the terms +// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software +// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended +// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of +// any third-party rights. +// +// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see +// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html +// +// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021. +// +// The interactive user interfaces in modified source and object code versions of the Program must +// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3. +// +// Pursuant to Section 7(b) of the License you must retain the original Product logo when +// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under +// trademark law for use of our trademarks. +// +// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing +// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 +// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode + +namespace ASC.EventBus.ActiveMQ.Log; +internal static partial class DefaultActiveMQPersistentConnectionLogger +{ + [LoggerMessage(Level = LogLevel.Critical, Message = "DefaultActiveMQPersistentConnection")] + public static partial void CriticalDefaultActiveMQPersistentConnection(this ILogger logger, Exception exception); + + [LoggerMessage(Level = LogLevel.Information, Message = "ActiveMQ Client is trying to connect")] + public static partial void InformationActiveMQTryingConnect(this ILogger logger); + + [LoggerMessage(Level = LogLevel.Warning, Message = "ActiveMQ Client could not connect after {timeOut}s")] + public static partial void WarningActiveMQCouldNotConnect(this ILogger logger, double timeOut, Exception exception); + + [LoggerMessage(Level = LogLevel.Information, Message = "ActiveMQ Client acquired a persistent connection to '{hostName}' and is subscribed to failure events")] + public static partial void InformationActiveMQAcquiredPersistentConnection(this ILogger logger, string hostName); + + [LoggerMessage(Level = LogLevel.Critical, Message = "FATAL ERROR: ActiveMQ connections could not be created and opened")] + public static partial void CriticalActiveMQCouldNotBeCreated(this ILogger logger); + + [LoggerMessage(Level = LogLevel.Warning, Message = "A ActiveMQ connection is shutdown. Trying to re-connect...")] + public static partial void WarningActiveMQConnectionShutdown(this ILogger logger); + + [LoggerMessage(Level = LogLevel.Warning, Message = "A ActiveMQ connection throw exception. Trying to re-connect...")] + public static partial void WarningActiveMQConnectionThrowException(this ILogger logger); + + [LoggerMessage(Level = LogLevel.Warning, Message = "A ActiveMQ connection is on shutdown. Trying to re-connect...")] + public static partial void WarningActiveMQConnectionIsOnShutDown(this ILogger logger); +} diff --git a/common/ASC.EventBus.ActiveMQ/Log/EventBusActiveMQLogger.cs b/common/ASC.EventBus.ActiveMQ/Log/EventBusActiveMQLogger.cs new file mode 100644 index 0000000000..1e093c9dd6 --- /dev/null +++ b/common/ASC.EventBus.ActiveMQ/Log/EventBusActiveMQLogger.cs @@ -0,0 +1,74 @@ +// (c) Copyright Ascensio System SIA 2010-2022 +// +// This program is a free software product. +// You can redistribute it and/or modify it under the terms +// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software +// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended +// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of +// any third-party rights. +// +// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see +// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html +// +// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021. +// +// The interactive user interfaces in modified source and object code versions of the Program must +// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3. +// +// Pursuant to Section 7(b) of the License you must retain the original Product logo when +// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under +// trademark law for use of our trademarks. +// +// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing +// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 +// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode + +namespace ASC.EventBus.ActiveMQ.Log; +internal static partial class EventBusActiveMQLogger +{ + [LoggerMessage(Level = LogLevel.Warning, Message = "Could not publish event: {eventId} after {timeout}s")] + public static partial void WarningCouldNotPublishEvent(this ILogger logger, Guid eventId, double timeout, Exception exception); + + [LoggerMessage(Level = LogLevel.Trace, Message = "Creating ActiveMQ session to publish event: {eventId} ({eventName})")] + public static partial void TraceCreatingActiveMQSession(this ILogger logger, Guid eventId, string eventName); + + [LoggerMessage(Level = LogLevel.Trace, Message = "Declaring ActiveMQ exchange to publish event: {eventId}")] + public static partial void TraceDeclaringActiveMQSession(this ILogger logger, Guid eventId); + + [LoggerMessage(Level = LogLevel.Trace, Message = "Publishing event to ActiveMQ: {eventId}")] + public static partial void TracePublishingEvent(this ILogger logger, Guid eventId); + + [LoggerMessage(Level = LogLevel.Information, Message = "Subscribing to dynamic event {eventName} with {eventHandler}")] + public static partial void InformationSubscribingDynamic(this ILogger logger, string eventName, string eventHandler); + + [LoggerMessage(Level = LogLevel.Information, Message = "Subscribing to event {eventName} with {eventHandler}")] + public static partial void InformationSubscribing(this ILogger logger, string eventName, string eventHandler); + + [LoggerMessage(Level = LogLevel.Information, Message = "Unsubscribing from event {eventName}")] + public static partial void InformationUnsubscribing(this ILogger logger, string eventName); + + [LoggerMessage(Level = LogLevel.Trace, Message = "Starting ActiveMQ basic consume")] + public static partial void TraceStartingBasicConsume(this ILogger logger); + + [LoggerMessage(Level = LogLevel.Trace, Message = "Consumer tag {consumerTag} already exist. Cancelled BasicConsume again")] + public static partial void TraceConsumerTagExist(this ILogger logger, string consumerTag); + + [LoggerMessage(Level = LogLevel.Error, Message = "StartBasicConsume can't call on _consumerSession == null")] + public static partial void ErrorStartBasicConsumeCantCall(this ILogger logger); + + [LoggerMessage(Level = LogLevel.Warning, Message = "----- ERROR Processing message \"{message}\"")] + public static partial void WarningProcessingMessage(this ILogger logger, string message, Exception exception); + + [LoggerMessage(Level = LogLevel.Trace, Message = "Creating ActiveMQ consumer session")] + public static partial void TraceCreatingConsumerSession(this ILogger logger); + + [LoggerMessage(Level = LogLevel.Warning, Message = "Recreating ActiveMQ consumer session")] + public static partial void WarningRecreatingConsumerSession(this ILogger logger, Exception exception); + + [LoggerMessage(Level = LogLevel.Trace, Message = "Processing ActiveMQ event: {eventName}")] + public static partial void TraceProcessingEvent(this ILogger logger, string eventName); + + [LoggerMessage(Level = LogLevel.Warning, Message = "No subscription for ActiveMQ event: {eventName}")] + public static partial void WarningNoSubscription(this ILogger logger, string eventName); +} diff --git a/common/ASC.EventBus.RabbitMQ/EventBusRabbitMQ.cs b/common/ASC.EventBus.RabbitMQ/EventBusRabbitMQ.cs index cf974ec5c8..e6da29c8f6 100644 --- a/common/ASC.EventBus.RabbitMQ/EventBusRabbitMQ.cs +++ b/common/ASC.EventBus.RabbitMQ/EventBusRabbitMQ.cs @@ -65,7 +65,6 @@ public class EventBusRabbitMQ : IEventBus, IDisposable _subsManager.OnEventRemoved += SubsManager_OnEventRemoved; _serializer = serializer; _rejectedEvents = new ConcurrentQueue(); - } private void SubsManager_OnEventRemoved(object sender, string eventName) @@ -250,22 +249,16 @@ public class EventBusRabbitMQ : IEventBus, IDisposable } catch (IntegrationEventRejectExeption ex) { - _logger.WarningProcessingMessage(message, ex); - - if (eventArgs.Redelivered) - { - if (_rejectedEvents.TryPeek(out var result) && result.Equals(ex.EventId)) - { - _rejectedEvents.TryDequeue(out var _); - _consumerChannel.BasicReject(eventArgs.DeliveryTag, requeue: false); - } - else - { - _rejectedEvents.Enqueue(ex.EventId); - } - } - else - { + _logger.WarningProcessingMessage(message, ex); + + if (_rejectedEvents.TryPeek(out var result) && result.Equals(ex.EventId)) + { + _rejectedEvents.TryDequeue(out var _); + _consumerChannel.BasicReject(eventArgs.DeliveryTag, requeue: false); + } + else + { + _rejectedEvents.Enqueue(ex.EventId); _consumerChannel.BasicNack(eventArgs.DeliveryTag, multiple: false, requeue: true); } } diff --git a/config/activemq.json b/config/activemq.json new file mode 100644 index 0000000000..9d1e05bcae --- /dev/null +++ b/config/activemq.json @@ -0,0 +1,5 @@ +{ + "ActiveMQ": { + "Uri": "amqp://127.0.0.1:5672" + } +} \ No newline at end of file diff --git a/packages/client/src/components/dialogs/ConnectDialog/index.js b/packages/client/src/components/dialogs/ConnectDialog/index.js index d3224213b5..c149bc93b7 100644 --- a/packages/client/src/components/dialogs/ConnectDialog/index.js +++ b/packages/client/src/components/dialogs/ConnectDialog/index.js @@ -385,10 +385,7 @@ const ConnectDialog = withTranslation([ ])(PureConnectDialogContainer); export default inject( - ( - { auth, settingsStore, selectedFolderStore, dialogsStore }, - { passedItem, isConnectionViaBackupModule } - ) => { + ({ auth, settingsStore, selectedFolderStore, dialogsStore, backup }) => { const { providers, saveThirdParty, @@ -398,6 +395,7 @@ export default inject( const { personal, folderFormValidation } = auth.settingsStore; const { id, folders } = selectedFolderStore; + const { selectedThirdPartyAccount: backupConnectionItem } = backup; const { connectDialogVisible: visible, setConnectDialogVisible, @@ -410,7 +408,8 @@ export default inject( setSaveAfterReconnectOAuth, } = dialogsStore; - const item = isConnectionViaBackupModule ? passedItem : connectItem; + const item = backupConnectionItem ?? connectItem; + const isConnectionViaBackupModule = backupConnectionItem ? true : false; return { selectedFolderId: id, @@ -421,7 +420,7 @@ export default inject( roomCreation, setSaveThirdpartyResponse, folderFormValidation, - + isConnectionViaBackupModule, saveThirdParty, openConnectWindow, fetchThirdPartyProviders, diff --git a/packages/client/src/components/dialogs/DeleteThirdPartyDialog/index.js b/packages/client/src/components/dialogs/DeleteThirdPartyDialog/index.js index 09faba3447..de38cfee41 100644 --- a/packages/client/src/components/dialogs/DeleteThirdPartyDialog/index.js +++ b/packages/client/src/components/dialogs/DeleteThirdPartyDialog/index.js @@ -97,24 +97,27 @@ const DeleteThirdPartyDialog = (props) => { }; export default inject( - ( - { filesStore, settingsStore, dialogsStore, selectedFolderStore }, - { item, isConnectionViaBackupModule } - ) => { + ({ + filesStore, + settingsStore, + dialogsStore, + selectedFolderStore, + backup, + }) => { const { providers, setThirdPartyProviders, deleteThirdParty, } = settingsStore.thirdPartyStore; const { fetchFiles } = filesStore; - + const { selectedThirdPartyAccount: backupConnectionItem } = backup; const { deleteThirdPartyDialogVisible: visible, setDeleteThirdPartyDialogVisible, removeItem: storeItem, } = dialogsStore; - const removeItem = isConnectionViaBackupModule ? item : storeItem; + const removeItem = backupConnectionItem ?? storeItem; return { currentFolderId: selectedFolderStore.id, diff --git a/packages/client/src/components/panels/SelectionPanel/FilesListWrapper.js b/packages/client/src/components/panels/SelectionPanel/FilesListWrapper.js index 1da2726e26..9fd79b9509 100644 --- a/packages/client/src/components/panels/SelectionPanel/FilesListWrapper.js +++ b/packages/client/src/components/panels/SelectionPanel/FilesListWrapper.js @@ -79,10 +79,15 @@ class FilesListWrapper extends React.Component { if (axios.isCancel(thrown)) { console.log("Request canceled", thrown.message); } else { - console.error(thrown); + const errorBody = thrown.response; + + if (errorBody.data && errorBody.data.error) { + toastr.error(errorBody.data.error.message); + } } return; }); + if (!response) return; const data = response.data.response; diff --git a/packages/client/src/pages/AccountsHome/Section/Header/index.js b/packages/client/src/pages/AccountsHome/Section/Header/index.js index f0368853b0..c12c6e2fcb 100644 --- a/packages/client/src/pages/AccountsHome/Section/Header/index.js +++ b/packages/client/src/pages/AccountsHome/Section/Header/index.js @@ -23,6 +23,8 @@ import withPeopleLoader from "SRC_DIR/HOCs/withPeopleLoader"; const StyledContainer = styled.div` width: 100%; + min-height: 33px; + height: 60px; .group-button-menu-container { margin: 0 0 0 -20px; @@ -142,6 +144,7 @@ const StyledInfoPanelToggleWrapper = styled.div` align-items: center; justify-content: center; border-radius: 50%; + margin-bottom: 1px; } `; diff --git a/packages/client/src/pages/Home/InfoPanel/Body/sub-components/CommentEditor.js b/packages/client/src/pages/Home/InfoPanel/Body/sub-components/CommentEditor.js index 8857a944be..a99711cd3e 100644 --- a/packages/client/src/pages/Home/InfoPanel/Body/sub-components/CommentEditor.js +++ b/packages/client/src/pages/Home/InfoPanel/Body/sub-components/CommentEditor.js @@ -11,7 +11,7 @@ const CommentEditor = ({ item, setSelection, - + isRecycleBinFolder, fetchFileVersions, updateCommentVersion, }) => { @@ -60,15 +60,17 @@ const CommentEditor = ({ {comment} )} -
- -
- {comment ? t("Common:EditButton") : t("Common:AddButton")} + {!isRecycleBinFolder && ( +
+ +
+ {comment ? t("Common:EditButton") : t("Common:AddButton")} +
-
+ )}
) : (
@@ -101,14 +103,14 @@ const CommentEditor = ({ ); }; -export default inject(({ auth, versionHistoryStore }) => { +export default inject(({ auth, versionHistoryStore, treeFoldersStore }) => { const { setSelection } = auth.infoPanelStore; const { fetchFileVersions, updateCommentVersion } = versionHistoryStore; - + const { isRecycleBinFolder } = treeFoldersStore; return { setSelection, - + isRecycleBinFolder, fetchFileVersions, updateCommentVersion, }; diff --git a/packages/client/src/pages/Home/Section/Header/index.js b/packages/client/src/pages/Home/Section/Header/index.js index 5d286de435..0b28d5afb8 100644 --- a/packages/client/src/pages/Home/Section/Header/index.js +++ b/packages/client/src/pages/Home/Section/Header/index.js @@ -60,6 +60,7 @@ const StyledContainer = styled.div` .header-container { min-height: 33px; + height: 60px; } `; @@ -303,7 +304,7 @@ class SectionHeaderContent extends React.Component { }; getContextOptionsFolder = () => { - const { t, toggleInfoPanel, personal } = this.props; + const { t, isRecycleBinFolder } = this.props; return [ { @@ -493,7 +494,6 @@ class SectionHeaderContent extends React.Component { isHeaderChecked, isHeaderIndeterminate, showText, - toggleInfoPanel, isRoomsFolder, isEmptyPage, } = this.props; diff --git a/packages/client/src/pages/PortalSettings/categories/common/settingsBranding/whitelabel.js b/packages/client/src/pages/PortalSettings/categories/common/settingsBranding/whitelabel.js index 0942c81ad2..fefaf7560e 100644 --- a/packages/client/src/pages/PortalSettings/categories/common/settingsBranding/whitelabel.js +++ b/packages/client/src/pages/PortalSettings/categories/common/settingsBranding/whitelabel.js @@ -449,7 +449,6 @@ const WhiteLabel = (props) => { fontWeight="600" isHovered type="action" - color={!isPortalPaid ? "#A3A9AE" : ""} className="settings_unavailable" > {t("ChangeLogoButton")} @@ -505,7 +504,6 @@ const WhiteLabel = (props) => { fontWeight="600" isHovered type="action" - color={!isPortalPaid ? "#A3A9AE" : ""} className="settings_unavailable" > {t("ChangeLogoButton")} @@ -561,7 +559,6 @@ const WhiteLabel = (props) => { fontWeight="600" isHovered type="action" - color={!isPortalPaid ? "#A3A9AE" : ""} className="settings_unavailable" > {t("ChangeLogoButton")} @@ -617,7 +614,6 @@ const WhiteLabel = (props) => { fontWeight="600" isHovered type="action" - color={!isPortalPaid ? "#A3A9AE" : ""} className="settings_unavailable" > {t("ChangeLogoButton")} @@ -674,7 +670,6 @@ const WhiteLabel = (props) => { fontWeight="600" isHovered type="action" - color={!isPortalPaid ? "#A3A9AE" : ""} className="settings_unavailable" > {t("ChangeLogoButton")} @@ -779,7 +774,6 @@ const WhiteLabel = (props) => { fontWeight="600" isHovered type="action" - color={!isPortalPaid ? "#A3A9AE" : ""} className="settings_unavailable" > {t("ChangeLogoButton")} @@ -836,7 +830,6 @@ const WhiteLabel = (props) => { fontWeight="600" isHovered type="action" - color={!isPortalPaid ? "#A3A9AE" : ""} className="settings_unavailable" > {t("ChangeLogoButton")} diff --git a/packages/client/src/pages/PortalSettings/categories/data-management/backup/StyledBackup.js b/packages/client/src/pages/PortalSettings/categories/data-management/backup/StyledBackup.js index 000589edce..82c0eb6223 100644 --- a/packages/client/src/pages/PortalSettings/categories/data-management/backup/StyledBackup.js +++ b/packages/client/src/pages/PortalSettings/categories/data-management/backup/StyledBackup.js @@ -64,6 +64,7 @@ const commonStyles = css` max-width: ${TEXT_LENGTH}; font-size: 12px; line-height: 15px; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } `; @@ -230,7 +231,7 @@ const StyledModules = styled.div` margin-bottom: 24px; .backup-description { ${(props) => props.isDisabled && `color: #A3A9AE`}; - margin-left: 24px; + margin-left: 29px; max-width: 700px; } `; @@ -325,10 +326,18 @@ const StyledBackup = styled.div` .backup_connection { display: flex; margin-bottom: 12px; + display: grid; + grid-template-columns: minmax(100px, 310px) 32px; + grid-gap: 8px; } .backup_third-party-combo { - max-width: 310px; - margin-right: 8px; + .combo-button { + justify-content: flex-start; + .combo-button-label { + width: 100%; + max-width: 100%; + } + } } .backup_modules-separation { @@ -363,6 +372,9 @@ const StyledBackup = styled.div` } } } + .backup_third-party-context { + margin-top: 4px; + } `; const StyledBackupList = styled.div` height: 100%; diff --git a/packages/client/src/pages/PortalSettings/categories/data-management/backup/auto-backup/sub-components/ScheduleComponent.js b/packages/client/src/pages/PortalSettings/categories/data-management/backup/auto-backup/sub-components/ScheduleComponent.js index 554f0eb002..604b39c21f 100644 --- a/packages/client/src/pages/PortalSettings/categories/data-management/backup/auto-backup/sub-components/ScheduleComponent.js +++ b/packages/client/src/pages/PortalSettings/categories/data-management/backup/auto-backup/sub-components/ScheduleComponent.js @@ -64,6 +64,7 @@ const ScheduleComponent = ({ scaledOptions={true} dropDownMaxHeight={500} className="schedule-backup_combobox days_option" + showDisabledItems /> {weeklySchedule && ( )} {monthlySchedule && ( @@ -95,6 +97,7 @@ const ScheduleComponent = ({ scaledOptions={true} dropDownMaxHeight={400} className="schedule-backup_combobox month_options" + showDisabledItems /> )}
@@ -128,6 +132,7 @@ const ScheduleComponent = ({ scaledOptions={true} dropDownMaxHeight={isMobileOnly ? 100 : 200} className="schedule-backup_combobox max_copies" + showDisabledItems />
diff --git a/packages/client/src/pages/PortalSettings/categories/data-management/backup/auto-backup/sub-components/ThirdPartyStorageModule.js b/packages/client/src/pages/PortalSettings/categories/data-management/backup/auto-backup/sub-components/ThirdPartyStorageModule.js index 815641d28a..2967592d12 100644 --- a/packages/client/src/pages/PortalSettings/categories/data-management/backup/auto-backup/sub-components/ThirdPartyStorageModule.js +++ b/packages/client/src/pages/PortalSettings/categories/data-management/backup/auto-backup/sub-components/ThirdPartyStorageModule.js @@ -70,6 +70,7 @@ class ThirdPartyStorageModule extends React.PureComponent { scaledOptions dropDownMaxHeight={300} className="backup_combo" + showDisabledItems /> {selectedStorageId === GoogleId && ( diff --git a/packages/client/src/pages/PortalSettings/categories/data-management/backup/common-container/DirectThirdPartyConnection.js b/packages/client/src/pages/PortalSettings/categories/data-management/backup/common-container/DirectThirdPartyConnection.js index ade0c237f1..ca28e10369 100644 --- a/packages/client/src/pages/PortalSettings/categories/data-management/backup/common-container/DirectThirdPartyConnection.js +++ b/packages/client/src/pages/PortalSettings/categories/data-management/backup/common-container/DirectThirdPartyConnection.js @@ -16,6 +16,7 @@ import ConnectDialog from "../../../../../../components/dialogs/ConnectDialog"; import { ContextMenuButton } from "@docspace/components"; import DeleteThirdPartyDialog from "../../../../../../components/dialogs/DeleteThirdPartyDialog"; import { withTranslation } from "react-i18next"; +import { getOAuthToken } from "@docspace/common/utils"; let accounts = [], connectedAccount, @@ -23,7 +24,6 @@ let accounts = [], const DirectThirdPartyConnection = (props) => { const { openConnectWindow, - getOAuthToken, t, onSelectFolder, onClose, @@ -42,14 +42,21 @@ const DirectThirdPartyConnection = (props) => { deleteThirdPartyDialogVisible, tReady, clearLocalStorage, + setSelectedThirdPartyAccount, + selectedThirdPartyAccount, } = props; useEffect(() => { tReady && updateAccountsInfo(true); }, [tReady]); + useEffect(() => { + return () => { + setSelectedThirdPartyAccount(null); + }; + }, []); + const initialState = { - selectedAccount: {}, folderList: [], isLoading: false, isInitialLoading: true, @@ -149,14 +156,17 @@ const DirectThirdPartyConnection = (props) => { setAccount("Yandex", t("Translations:TypeTitleYandex")); setAccount("WebDav", t("Translations:TypeTitleWebDav")); + setSelectedThirdPartyAccount( + Object.keys(selectedAccount).length !== 0 + ? selectedAccount + : { ...accounts[0] } + ); + setState({ isLoading: false, isUpdatingInfo: false, isInitialLoading: false, - selectedAccount: - Object.keys(selectedAccount).length !== 0 - ? selectedAccount - : { ...accounts[0] }, + folderList: account ? account : [], }); } catch (e) { @@ -172,7 +182,7 @@ const DirectThirdPartyConnection = (props) => { const onConnect = () => { clearLocalStorage(); - const { provider_link, provider_key } = state.selectedAccount; + const { provider_link, provider_key } = selectedThirdPartyAccount; const directConnection = provider_link; @@ -184,10 +194,10 @@ const DirectThirdPartyConnection = (props) => { ); openConnectWindow(provider_key, authModal) - .then(getOAuthToken) + .then((modal) => getOAuthToken(modal)) .then((token) => { - saveSettings(token); authModal.close(); + saveSettings(token); }) .catch((e) => { if (!e) return; @@ -209,7 +219,7 @@ const DirectThirdPartyConnection = (props) => { loginValue = "", passwordValue = "" ) => { - const { label, provider_key, provider_id } = state.selectedAccount; + const { label, provider_key, provider_id } = selectedThirdPartyAccount; setState({ isLoading: true, isUpdatingInfo: true }); connectDialogVisible && setConnectDialogVisible(false); onSelectFolder && onSelectFolder(""); @@ -235,14 +245,13 @@ const DirectThirdPartyConnection = (props) => { const onSelectAccount = (options) => { const key = options.key; - - setState({ selectedAccount: { ...accounts[+key] } }); + setSelectedThirdPartyAccount({ ...accounts[+key] }); }; const onReconnect = () => { clearLocalStorage(); - const { provider_link } = state.selectedAccount; + const { provider_link } = selectedThirdPartyAccount; const directConnection = provider_link; @@ -252,12 +261,12 @@ const DirectThirdPartyConnection = (props) => { "Authorization", "height=600, width=1020" ); - openConnectWindow(selectedAccount.provider_key, authModal).then((modal) => - getOAuthToken(modal).then((token) => { + openConnectWindow(selectedThirdPartyAccount.provider_key, authModal) + .then((modal) => getOAuthToken(modal)) + .then((token) => { authModal.close(); saveSettings(token); - }) - ); + }); } else { setConnectDialogVisible(true); } @@ -271,10 +280,10 @@ const DirectThirdPartyConnection = (props) => { return [ { key: "connection-settings", - label: selectedAccount.connected + label: selectedThirdPartyAccount.connected ? t("Common:Reconnect") : t("Common:Connect"), - onClick: selectedAccount.connected ? onReconnect : onConnect, + onClick: selectedThirdPartyAccount.connected ? onReconnect : onConnect, disabled: false, icon: "/static/images/refresh.react.svg", }, @@ -282,19 +291,13 @@ const DirectThirdPartyConnection = (props) => { key: "Disconnect-settings", label: t("Common:Disconnect"), onClick: onDisconnect, - disabled: selectedAccount.connected ? false : true, + disabled: selectedThirdPartyAccount.connected ? false : true, icon: "/static/images/access.none.react.svg", }, ]; }; - const { - selectedAccount, - isLoading, - folderList, - isInitialLoading, - isUpdatingInfo, - } = state; + const { isLoading, folderList, isInitialLoading, isUpdatingInfo } = state; return ( @@ -304,13 +307,14 @@ const DirectThirdPartyConnection = (props) => { options={accounts} selectedOption={{ key: 0, - label: selectedAccount?.label, + label: selectedThirdPartyAccount?.label, }} onSelect={onSelectAccount} noBorder={false} scaledOptions dropDownMaxHeight={300} tabIndex={1} + showDisabledItems isDisabled={ !tReady || isDisabled || @@ -374,19 +378,10 @@ const DirectThirdPartyConnection = (props) => { /> )} - {connectDialogVisible && ( - - )} - {deleteThirdPartyDialogVisible && ( )} @@ -395,9 +390,14 @@ const DirectThirdPartyConnection = (props) => { }; export default inject(({ auth, backup, dialogsStore, settingsStore }) => { - const { commonThirdPartyList, clearLocalStorage } = backup; + const { + commonThirdPartyList, + clearLocalStorage, + setSelectedThirdPartyAccount, + selectedThirdPartyAccount, + } = backup; const { openConnectWindow } = settingsStore.thirdPartyStore; - const { getOAuthToken } = auth.settingsStore; + const { connectDialogVisible, setConnectDialogVisible, @@ -409,11 +409,12 @@ export default inject(({ auth, backup, dialogsStore, settingsStore }) => { clearLocalStorage, commonThirdPartyList, openConnectWindow, - getOAuthToken, setConnectDialogVisible, connectDialogVisible, setDeleteThirdPartyDialogVisible, deleteThirdPartyDialogVisible, + setSelectedThirdPartyAccount, + selectedThirdPartyAccount, }; })( withTranslation(["Settings", "Common", "Translations"])( diff --git a/packages/client/src/pages/PortalSettings/categories/data-management/backup/manual-backup/sub-components/ThirdPartyStorageModule.js b/packages/client/src/pages/PortalSettings/categories/data-management/backup/manual-backup/sub-components/ThirdPartyStorageModule.js index dc95d4fdd3..0be5c2a89f 100644 --- a/packages/client/src/pages/PortalSettings/categories/data-management/backup/manual-backup/sub-components/ThirdPartyStorageModule.js +++ b/packages/client/src/pages/PortalSettings/categories/data-management/backup/manual-backup/sub-components/ThirdPartyStorageModule.js @@ -21,7 +21,7 @@ class ThirdPartyStorageModule extends React.PureComponent { storageTitle = getFromLocalStorage("LocalCopyThirdPartyStorageType"); storageId = getFromLocalStorage("LocalCopyStorage"); - console.log("storageId", storageId); + this.state = { comboBoxOptions: [], storagesInfo: {}, @@ -122,6 +122,7 @@ class ThirdPartyStorageModule extends React.PureComponent { scaledOptions dropDownMaxHeight={400} className="backup_combo" + showDisabledItems /> {selectedId === GoogleId && } diff --git a/packages/client/src/pages/PortalSettings/categories/data-management/backup/restore-backup/sub-components/ThirdPartyStoragesModule.js b/packages/client/src/pages/PortalSettings/categories/data-management/backup/restore-backup/sub-components/ThirdPartyStoragesModule.js index 7d88f16c4a..0b378857d7 100644 --- a/packages/client/src/pages/PortalSettings/categories/data-management/backup/restore-backup/sub-components/ThirdPartyStoragesModule.js +++ b/packages/client/src/pages/PortalSettings/categories/data-management/backup/restore-backup/sub-components/ThirdPartyStoragesModule.js @@ -82,6 +82,7 @@ class ThirdPartyStoragesModule extends React.PureComponent { scaledOptions dropDownMaxHeight={400} className="backup_combo" + showDisabledItems /> {selectedStorageId === GoogleId && ( diff --git a/packages/client/src/store/BackupStore.js b/packages/client/src/store/BackupStore.js index 62fd973616..dc018b6c76 100644 --- a/packages/client/src/store/BackupStore.js +++ b/packages/client/src/store/BackupStore.js @@ -63,6 +63,7 @@ class BackupStore { defaultEnableSchedule = false; storageRegions = []; + selectedThirdPartyAccount = null; constructor() { makeAutoObservable(this); @@ -143,6 +144,11 @@ class BackupStore { return false; } + setSelectedThirdPartyAccount = (elem) => { + this.selectedThirdPartyAccount = elem; + + }; + toDefault = () => { this.selectedMonthlySchedule = this.defaultMonthlySchedule; this.selectedWeeklySchedule = this.defaultWeeklySchedule; diff --git a/packages/common/custom.scss b/packages/common/custom.scss index 68b5f428f2..d648207645 100644 --- a/packages/common/custom.scss +++ b/packages/common/custom.scss @@ -6,6 +6,7 @@ $font-family-base: "Open Sans", sans-serif; html, body { height: 100%; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } #root { min-height: 100%; diff --git a/packages/components/context-menu-button/styled-context-menu-button.js b/packages/components/context-menu-button/styled-context-menu-button.js index 3d46670505..7813410909 100644 --- a/packages/components/context-menu-button/styled-context-menu-button.js +++ b/packages/components/context-menu-button/styled-context-menu-button.js @@ -10,14 +10,19 @@ const StyledOuter = styled.div` ${(props) => props.isNeedBorder && css` - border: ${(props) => props.theme.client.settings.backup.contextBorder}; + border: ${(props) => props.theme.comboBox.button.border}; width: 32px; + height: 32px; box-sizing: border-box; border-radius: 3px; svg { padding: 7px 13px 7px 2px; } + :hover { + border-color: ${(props) => + props.theme.comboBox.button.hoverBorderColor}; + } `} `; StyledOuter.defaultProps = { theme: Base }; diff --git a/packages/components/themes/base.js b/packages/components/themes/base.js index fca161b44e..03dad71102 100644 --- a/packages/components/themes/base.js +++ b/packages/components/themes/base.js @@ -2893,7 +2893,6 @@ const Base = { backup: { rectangleBackgroundColor: "#f8f9f9", separatorBorder: "1px solid #eceef1", - contextBorder: "1px solid #D0D5DA", }, payment: { diff --git a/packages/components/themes/dark.js b/packages/components/themes/dark.js index 4e704446e3..e864ebb3fa 100644 --- a/packages/components/themes/dark.js +++ b/packages/components/themes/dark.js @@ -2900,7 +2900,6 @@ const Dark = { backup: { rectangleBackgroundColor: "#292929", separatorBorder: "1px solid #474747", - contextBorder: "1px solid #D0D5DA", }, payment: { diff --git a/products/ASC.Files/Core/Core/Security/FileSecurity.cs b/products/ASC.Files/Core/Core/Security/FileSecurity.cs index c0dc3ecd5f..7cfb69c819 100644 --- a/products/ASC.Files/Core/Core/Security/FileSecurity.cs +++ b/products/ASC.Files/Core/Core/Security/FileSecurity.cs @@ -509,16 +509,6 @@ public class FileSecurity : IFileSecurity return false; } - if (isVisitor && e.RootFolderType == FolderType.Recent) - { - return false; - } - - if (isVisitor && e.RootFolderType == FolderType.Favorites) - { - return false; - } - if (isVisitor && e.RootFolderType == FolderType.Templates) { return false; diff --git a/products/ASC.Files/Core/Helpers/Global.cs b/products/ASC.Files/Core/Helpers/Global.cs index 31796f41eb..ca343451fa 100644 --- a/products/ASC.Files/Core/Helpers/Global.cs +++ b/products/ASC.Files/Core/Helpers/Global.cs @@ -379,6 +379,11 @@ public class GlobalFolder return default; } + if (_userManager.IsVisitor(_authContext.CurrentAccount.ID)) + { + return default; + } + var key = $"archive/{_tenantManager.GetCurrentTenant().Id}"; if (!DocSpaceFolderCache.TryGetValue(key, out var result)) @@ -517,11 +522,6 @@ public class GlobalFolder return 0; } - if (_userManager.IsVisitor(_authContext.CurrentAccount.ID)) - { - return 0; - } - if (!RecentFolderCache.TryGetValue(_tenantManager.GetCurrentTenant().Id, out var recentFolderId)) { var folderDao = daoFactory.GetFolderDao(); @@ -546,11 +546,6 @@ public class GlobalFolder return 0; } - if (_userManager.IsVisitor(_authContext.CurrentAccount.ID)) - { - return 0; - } - if (!FavoritesFolderCache.TryGetValue(_tenantManager.GetCurrentTenant().Id, out var favoriteFolderId)) { var folderDao = daoFactory.GetFolderDao(); diff --git a/products/ASC.Files/Server/Helpers/FoldersControllerHelper.cs b/products/ASC.Files/Server/Helpers/FoldersControllerHelper.cs index 355133bfaf..039ecac9c2 100644 --- a/products/ASC.Files/Server/Helpers/FoldersControllerHelper.cs +++ b/products/ASC.Files/Server/Helpers/FoldersControllerHelper.cs @@ -114,7 +114,7 @@ public class FoldersControllerHelper : FilesHelperBase yield return await _globalFolderHelper.FolderShareAsync; } - if (!IsVisitor && !withoutAdditionalFolder) + if (!withoutAdditionalFolder) { if (_filesSettingsHelper.FavoritesSection) { @@ -126,8 +126,10 @@ public class FoldersControllerHelper : FilesHelperBase yield return await _globalFolderHelper.FolderRecentAsync; } - if (!_coreBaseSettings.Personal && _coreBaseSettings.DisableDocSpace - && PrivacyRoomSettings.IsAvailable()) + if (!IsVisitor && + !_coreBaseSettings.Personal && + _coreBaseSettings.DisableDocSpace && + PrivacyRoomSettings.IsAvailable()) { yield return await _globalFolderHelper.FolderPrivacyAsync; } @@ -147,15 +149,19 @@ public class FoldersControllerHelper : FilesHelperBase yield return await _globalFolderHelper.FolderTemplatesAsync; } - if (!withoutTrash) + if (!withoutTrash && !IsVisitor) { yield return (int)_globalFolderHelper.FolderTrash; } if (!_coreBaseSettings.DisableDocSpace) { - yield return await _globalFolderHelper.FolderVirtualRoomsAsync; - yield return await _globalFolderHelper.FolderArchiveAsync; + yield return await _globalFolderHelper.FolderVirtualRoomsAsync; + + if (!IsVisitor) + { + yield return await _globalFolderHelper.FolderArchiveAsync; + } } } diff --git a/products/ASC.People/Server/Api/UserController.cs b/products/ASC.People/Server/Api/UserController.cs index 827e9a4bd9..26260c98c8 100644 --- a/products/ASC.People/Server/Api/UserController.cs +++ b/products/ASC.People/Server/Api/UserController.cs @@ -32,8 +32,6 @@ public class UserController : PeopleControllerBase private readonly ICache _cache; private readonly TenantManager _tenantManager; - private readonly GlobalSpace _globalSpace; - private readonly Constants _constants; private readonly CookiesManager _cookiesManager; private readonly CoreBaseSettings _coreBaseSettings; private readonly CustomNamingPeople _customNamingPeople; @@ -69,8 +67,6 @@ public class UserController : PeopleControllerBase public UserController( ICache cache, TenantManager tenantManager, - GlobalSpace globalSpace, - Constants constants, CookiesManager cookiesManager, CoreBaseSettings coreBaseSettings, CustomNamingPeople customNamingPeople, @@ -112,8 +108,6 @@ public class UserController : PeopleControllerBase { _cache = cache; _tenantManager = tenantManager; - _globalSpace = globalSpace; - _constants = constants; _cookiesManager = cookiesManager; _coreBaseSettings = coreBaseSettings; _customNamingPeople = customNamingPeople; diff --git a/web/ASC.Web.Api/Api/AuthenticationController.cs b/web/ASC.Web.Api/Api/AuthenticationController.cs index 8d3ade7bdf..601da1b234 100644 --- a/web/ASC.Web.Api/Api/AuthenticationController.cs +++ b/web/ASC.Web.Api/Api/AuthenticationController.cs @@ -301,7 +301,7 @@ public class AuthenticationController : ControllerBase _securityContext.Logout(); } - [AllowNotPayment] + [AllowNotPayment, AllowSuspended] [HttpPost("confirm")] public ValidationResult CheckConfirm(EmailValidationKeyModel inDto) { diff --git a/web/ASC.Web.Api/Api/PortalController.cs b/web/ASC.Web.Api/Api/PortalController.cs index cef1438169..bfee4cabe7 100644 --- a/web/ASC.Web.Api/Api/PortalController.cs +++ b/web/ASC.Web.Api/Api/PortalController.cs @@ -444,7 +444,7 @@ public class PortalController : ControllerBase } [HttpDelete("delete")] - [Authorize(AuthenticationSchemes = "confirm", Roles = "ProfileRemove")] + [Authorize(AuthenticationSchemes = "confirm", Roles = "PortalRemove")] public async Task DeletePortal() { _tenantManager.RemoveTenant(Tenant.Id); diff --git a/web/ASC.Web.Api/Api/Settings/SettingsController.cs b/web/ASC.Web.Api/Api/Settings/SettingsController.cs index c6da3c062f..2db5d3987f 100644 --- a/web/ASC.Web.Api/Api/Settings/SettingsController.cs +++ b/web/ASC.Web.Api/Api/Settings/SettingsController.cs @@ -132,8 +132,7 @@ public class SettingsController : BaseSettingsController } [HttpGet("")] - [AllowNotPayment] - [AllowAnonymous] + [AllowNotPayment, AllowSuspended, AllowAnonymous] public SettingsDto GetSettings(bool? withpassword) { var studioAdminMessageSettings = _settingsManager.Load(); diff --git a/web/ASC.Web.Api/Api/Settings/WhitelabelController.cs b/web/ASC.Web.Api/Api/Settings/WhitelabelController.cs index 71937a06c6..0bbe8cd7bd 100644 --- a/web/ASC.Web.Api/Api/Settings/WhitelabelController.cs +++ b/web/ASC.Web.Api/Api/Settings/WhitelabelController.cs @@ -209,7 +209,7 @@ public class WhitelabelController : BaseSettingsController result.Add(instance); - if (!instance.IsDefault() && !instance.IsLicensor) + if (!instance.IsDefault && !instance.IsLicensor) { result.Add(_settingsManager.GetDefault()); } @@ -280,8 +280,6 @@ public class WhitelabelController : BaseSettingsController [HttpGet("rebranding/additional")] public AdditionalWhiteLabelSettings GetAdditionalWhiteLabelSettings() { - _permissionContext.DemandPermissions(SecutiryConstants.EditPortalSettings); - return _settingsManager.Load(); } diff --git a/web/ASC.Web.Core/WhiteLabel/AdditionalWhiteLabelSettings.cs b/web/ASC.Web.Core/WhiteLabel/AdditionalWhiteLabelSettings.cs index 38d5c23cf7..a5335bbf5e 100644 --- a/web/ASC.Web.Core/WhiteLabel/AdditionalWhiteLabelSettings.cs +++ b/web/ASC.Web.Core/WhiteLabel/AdditionalWhiteLabelSettings.cs @@ -60,22 +60,25 @@ public class AdditionalWhiteLabelSettings : ISettings { - private readonly CoreSettings _coreSettings; + private CoreSettings _coreSettings; public string CompanyName { get; set; } @@ -59,16 +59,19 @@ public class CompanyWhiteLabelSettings : ISettings } - public bool IsDefault() + public bool IsDefault { - var defaultSettings = GetDefault(); - - return CompanyName == defaultSettings.CompanyName && - Site == defaultSettings.Site && - Email == defaultSettings.Email && - Address == defaultSettings.Address && - Phone == defaultSettings.Phone && - IsLicensor == defaultSettings.IsLicensor; + get + { + var defaultSettings = GetDefault(); + + return CompanyName == defaultSettings.CompanyName && + Site == defaultSettings.Site && + Email == defaultSettings.Email && + Address == defaultSettings.Address && + Phone == defaultSettings.Phone && + IsLicensor == defaultSettings.IsLicensor; + } } #region ISettings Members @@ -84,7 +87,11 @@ public class CompanyWhiteLabelSettings : ISettings { var settings = _coreSettings.GetSetting("CompanyWhiteLabelSettings"); - return string.IsNullOrEmpty(settings) ? new CompanyWhiteLabelSettings(_coreSettings) : JsonConvert.DeserializeObject(settings); + var result = string.IsNullOrEmpty(settings) ? new CompanyWhiteLabelSettings(_coreSettings) : JsonConvert.DeserializeObject(settings); + + result._coreSettings = _coreSettings; + + return result; } #endregion