Merge branch 'develop' into feature/shared-redesign-third-party

This commit is contained in:
Vlada Gazizova 2022-10-13 13:37:05 +03:00
commit efde1f00a9
49 changed files with 1115 additions and 184 deletions

View File

@ -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

View File

@ -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",

View File

@ -29,6 +29,7 @@
<ItemGroup>
<ProjectReference Include="..\..\web\ASC.Web.Core\ASC.Web.Core.csproj" />
<ProjectReference Include="..\ASC.EventBus.ActiveMQ\ASC.EventBus.ActiveMQ.csproj" />
<ProjectReference Include="..\ASC.EventBus.Extensions.Logger\ASC.EventBus.Extensions.Logger.csproj" />
<ProjectReference Include="..\ASC.EventBus.RabbitMQ\ASC.EventBus.RabbitMQ.csproj" />
<ProjectReference Include="..\ASC.Webhooks.Core\ASC.Webhooks.Core.csproj" />

View File

@ -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);

View File

@ -75,6 +75,7 @@ public static class ServiceCollectionExtension
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();
var rabbitMQConfiguration = configuration.GetSection("RabbitMQ").Get<RabbitMQSettings>();
var activeMQConfiguration = configuration.GetSection("ActiveMQ").Get<ActiveMQSettings>();
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<IActiveMQPersistentConnection>(sp =>
{
var cfg = sp.GetRequiredService<IConfiguration>();
var logger = sp.GetRequiredService<ILogger<DefaultActiveMQPersistentConnection>>();
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<IEventBus, EventBusActiveMQ>(sp =>
{
var cfg = sp.GetRequiredService<IConfiguration>();
var activeMQPersistentConnection = sp.GetRequiredService<IActiveMQPersistentConnection>();
var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>();
var logger = sp.GetRequiredService<ILogger<EventBusActiveMQ>>();
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
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
{

View File

@ -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;

View File

@ -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<AllowSuspendedAttribute>().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;

View File

@ -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
{
}

View File

@ -232,17 +232,4 @@ public class RabbitMQCache<T> : IDisposable, ICacheNotify<T> 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; }
}
}

View File

@ -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; }
}

View File

@ -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; }
}

View File

@ -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<string>(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";

View File

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Apache.NMS.AMQP" Version="2.0.0" />
<PackageReference Include="Polly" Version="7.2.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ASC.Common\ASC.Common.csproj" />
<ProjectReference Include="..\ASC.EventBus\ASC.EventBus.csproj" />
</ItemGroup>
</Project>

View File

@ -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<DefaultActiveMQPersistentConnection> _logger;
private readonly int _retryCount;
private IConnection _connection;
private bool _disposed;
readonly object sync_root = new object();
public DefaultActiveMQPersistentConnection(IConnectionFactory connectionFactory, ILogger<DefaultActiveMQPersistentConnection> 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<SocketException>()
.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);
}
}

View File

@ -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<EventBusActiveMQ> _logger;
private readonly IEventBusSubscriptionsManager _subsManager;
private readonly ILifetimeScope _autofac;
private static ConcurrentQueue<Guid> _rejectedEvents;
private readonly IActiveMQPersistentConnection _persistentConnection;
private readonly IIntegrationEventSerializer _serializer;
private ISession _consumerSession;
private readonly List<IMessageConsumer> _consumers;
private readonly int _retryCount;
private string _queueName;
public EventBusActiveMQ(IActiveMQPersistentConnection persistentConnection,
ILogger<EventBusActiveMQ> 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<Guid>();
_consumerSession = CreateConsumerSession();
_subsManager.OnEventRemoved += SubsManager_OnEventRemoved;
_consumers = new List<IMessageConsumer>();
}
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<SocketException>()
.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<T, TH>()
where T : IntegrationEvent
where TH : IIntegrationEventHandler<T>
{
var eventName = _subsManager.GetEventKey<T>();
_logger.InformationSubscribing(eventName, typeof(TH).GetGenericTypeName());
_subsManager.AddSubscription<T, TH>();
StartBasicConsume(eventName);
}
public void SubscribeDynamic<TH>(string eventName) where TH : IDynamicIntegrationEventHandler
{
_logger.InformationSubscribingDynamic(eventName, typeof(TH).GetGenericTypeName());
_subsManager.AddDynamicSubscription<TH>(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<T, TH>()
where T : IntegrationEvent
where TH : IIntegrationEventHandler<T>
{
var eventName = _subsManager.GetEventKey<T>();
_logger.InformationUnsubscribing(eventName);
_subsManager.RemoveSubscription<T, TH>();
}
public void UnsubscribeDynamic<TH>(string eventName) where TH : IDynamicIntegrationEventHandler
{
_subsManager.RemoveDynamicSubscription<TH>(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();
}
}

View File

@ -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;

View File

@ -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();
}

View File

@ -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<DefaultActiveMQPersistentConnection> logger, Exception exception);
[LoggerMessage(Level = LogLevel.Information, Message = "ActiveMQ Client is trying to connect")]
public static partial void InformationActiveMQTryingConnect(this ILogger<DefaultActiveMQPersistentConnection> logger);
[LoggerMessage(Level = LogLevel.Warning, Message = "ActiveMQ Client could not connect after {timeOut}s")]
public static partial void WarningActiveMQCouldNotConnect(this ILogger<DefaultActiveMQPersistentConnection> 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<DefaultActiveMQPersistentConnection> 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<DefaultActiveMQPersistentConnection> logger);
[LoggerMessage(Level = LogLevel.Warning, Message = "A ActiveMQ connection is shutdown. Trying to re-connect...")]
public static partial void WarningActiveMQConnectionShutdown(this ILogger<DefaultActiveMQPersistentConnection> logger);
[LoggerMessage(Level = LogLevel.Warning, Message = "A ActiveMQ connection throw exception. Trying to re-connect...")]
public static partial void WarningActiveMQConnectionThrowException(this ILogger<DefaultActiveMQPersistentConnection> logger);
[LoggerMessage(Level = LogLevel.Warning, Message = "A ActiveMQ connection is on shutdown. Trying to re-connect...")]
public static partial void WarningActiveMQConnectionIsOnShutDown(this ILogger<DefaultActiveMQPersistentConnection> logger);
}

View File

@ -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<EventBusActiveMQ> 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<EventBusActiveMQ> logger, Guid eventId, string eventName);
[LoggerMessage(Level = LogLevel.Trace, Message = "Declaring ActiveMQ exchange to publish event: {eventId}")]
public static partial void TraceDeclaringActiveMQSession(this ILogger<EventBusActiveMQ> logger, Guid eventId);
[LoggerMessage(Level = LogLevel.Trace, Message = "Publishing event to ActiveMQ: {eventId}")]
public static partial void TracePublishingEvent(this ILogger<EventBusActiveMQ> logger, Guid eventId);
[LoggerMessage(Level = LogLevel.Information, Message = "Subscribing to dynamic event {eventName} with {eventHandler}")]
public static partial void InformationSubscribingDynamic(this ILogger<EventBusActiveMQ> logger, string eventName, string eventHandler);
[LoggerMessage(Level = LogLevel.Information, Message = "Subscribing to event {eventName} with {eventHandler}")]
public static partial void InformationSubscribing(this ILogger<EventBusActiveMQ> logger, string eventName, string eventHandler);
[LoggerMessage(Level = LogLevel.Information, Message = "Unsubscribing from event {eventName}")]
public static partial void InformationUnsubscribing(this ILogger<EventBusActiveMQ> logger, string eventName);
[LoggerMessage(Level = LogLevel.Trace, Message = "Starting ActiveMQ basic consume")]
public static partial void TraceStartingBasicConsume(this ILogger<EventBusActiveMQ> logger);
[LoggerMessage(Level = LogLevel.Trace, Message = "Consumer tag {consumerTag} already exist. Cancelled BasicConsume again")]
public static partial void TraceConsumerTagExist(this ILogger<EventBusActiveMQ> logger, string consumerTag);
[LoggerMessage(Level = LogLevel.Error, Message = "StartBasicConsume can't call on _consumerSession == null")]
public static partial void ErrorStartBasicConsumeCantCall(this ILogger<EventBusActiveMQ> logger);
[LoggerMessage(Level = LogLevel.Warning, Message = "----- ERROR Processing message \"{message}\"")]
public static partial void WarningProcessingMessage(this ILogger<EventBusActiveMQ> logger, string message, Exception exception);
[LoggerMessage(Level = LogLevel.Trace, Message = "Creating ActiveMQ consumer session")]
public static partial void TraceCreatingConsumerSession(this ILogger<EventBusActiveMQ> logger);
[LoggerMessage(Level = LogLevel.Warning, Message = "Recreating ActiveMQ consumer session")]
public static partial void WarningRecreatingConsumerSession(this ILogger<EventBusActiveMQ> logger, Exception exception);
[LoggerMessage(Level = LogLevel.Trace, Message = "Processing ActiveMQ event: {eventName}")]
public static partial void TraceProcessingEvent(this ILogger<EventBusActiveMQ> logger, string eventName);
[LoggerMessage(Level = LogLevel.Warning, Message = "No subscription for ActiveMQ event: {eventName}")]
public static partial void WarningNoSubscription(this ILogger<EventBusActiveMQ> logger, string eventName);
}

View File

@ -65,7 +65,6 @@ public class EventBusRabbitMQ : IEventBus, IDisposable
_subsManager.OnEventRemoved += SubsManager_OnEventRemoved;
_serializer = serializer;
_rejectedEvents = new ConcurrentQueue<Guid>();
}
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);
}
}

5
config/activemq.json Normal file
View File

@ -0,0 +1,5 @@
{
"ActiveMQ": {
"Uri": "amqp://127.0.0.1:5672"
}
}

View File

@ -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,

View File

@ -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,

View File

@ -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;

View File

@ -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;
}
`;

View File

@ -11,7 +11,7 @@ const CommentEditor = ({
item,
setSelection,
isRecycleBinFolder,
fetchFileVersions,
updateCommentVersion,
}) => {
@ -60,15 +60,17 @@ const CommentEditor = ({
{comment}
</Text>
)}
<div className="edit_toggle" onClick={onOpenEditor}>
<ReactSVG
className="edit_toggle-icon"
src="images/pencil.react.svg"
/>
<div className="property-content edit_toggle-text">
{comment ? t("Common:EditButton") : t("Common:AddButton")}
{!isRecycleBinFolder && (
<div className="edit_toggle" onClick={onOpenEditor}>
<ReactSVG
className="edit_toggle-icon"
src="images/pencil.react.svg"
/>
<div className="property-content edit_toggle-text">
{comment ? t("Common:EditButton") : t("Common:AddButton")}
</div>
</div>
</div>
)}
</div>
) : (
<div className="property-comment_editor-editor">
@ -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,
};

View File

@ -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;

View File

@ -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")}

View File

@ -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%;

View File

@ -64,6 +64,7 @@ const ScheduleComponent = ({
scaledOptions={true}
dropDownMaxHeight={500}
className="schedule-backup_combobox days_option"
showDisabledItems
/>
{weeklySchedule && (
<ComboBox
@ -79,6 +80,7 @@ const ScheduleComponent = ({
scaledOptions={true}
dropDownMaxHeight={400}
className="schedule-backup_combobox weekly_option"
showDisabledItems
/>
)}
{monthlySchedule && (
@ -95,6 +97,7 @@ const ScheduleComponent = ({
scaledOptions={true}
dropDownMaxHeight={400}
className="schedule-backup_combobox month_options"
showDisabledItems
/>
)}
<ComboBox
@ -110,6 +113,7 @@ const ScheduleComponent = ({
scaledOptions={true}
dropDownMaxHeight={isMobileOnly ? 100 : 200}
className="schedule-backup_combobox time_options"
showDisabledItems
/>
</div>
<div className="maxCopiesOption">
@ -128,6 +132,7 @@ const ScheduleComponent = ({
scaledOptions={true}
dropDownMaxHeight={isMobileOnly ? 100 : 200}
className="schedule-backup_combobox max_copies"
showDisabledItems
/>
</div>
</StyledScheduleComponent>

View File

@ -70,6 +70,7 @@ class ThirdPartyStorageModule extends React.PureComponent {
scaledOptions
dropDownMaxHeight={300}
className="backup_combo"
showDisabledItems
/>
{selectedStorageId === GoogleId && (

View File

@ -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 (
<StyledBackup>
@ -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 && (
<ConnectDialog
passedItem={selectedAccount}
updateInfo={updateAccountsInfo}
isConnectionViaBackupModule
/>
)}
{deleteThirdPartyDialogVisible && (
<DeleteThirdPartyDialog
updateInfo={updateAccountsInfo}
key="thirdparty-delete-dialog"
item={selectedAccount}
isConnectionViaBackupModule
/>
)}
@ -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"])(

View File

@ -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 && <GoogleCloudStorage {...commonProps} />}

View File

@ -82,6 +82,7 @@ class ThirdPartyStoragesModule extends React.PureComponent {
scaledOptions
dropDownMaxHeight={400}
className="backup_combo"
showDisabledItems
/>
{selectedStorageId === GoogleId && (

View File

@ -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;

View File

@ -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%;

View File

@ -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 };

View File

@ -2893,7 +2893,6 @@ const Base = {
backup: {
rectangleBackgroundColor: "#f8f9f9",
separatorBorder: "1px solid #eceef1",
contextBorder: "1px solid #D0D5DA",
},
payment: {

View File

@ -2900,7 +2900,6 @@ const Dark = {
backup: {
rectangleBackgroundColor: "#292929",
separatorBorder: "1px solid #474747",
contextBorder: "1px solid #D0D5DA",
},
payment: {

View File

@ -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;

View File

@ -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<int>();
@ -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<int>();

View File

@ -114,7 +114,7 @@ public class FoldersControllerHelper<T> : FilesHelperBase<T>
yield return await _globalFolderHelper.FolderShareAsync;
}
if (!IsVisitor && !withoutAdditionalFolder)
if (!withoutAdditionalFolder)
{
if (_filesSettingsHelper.FavoritesSection)
{
@ -126,8 +126,10 @@ public class FoldersControllerHelper<T> : FilesHelperBase<T>
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<T> : FilesHelperBase<T>
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;
}
}
}

View File

@ -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;

View File

@ -301,7 +301,7 @@ public class AuthenticationController : ControllerBase
_securityContext.Logout();
}
[AllowNotPayment]
[AllowNotPayment, AllowSuspended]
[HttpPost("confirm")]
public ValidationResult CheckConfirm(EmailValidationKeyModel inDto)
{

View File

@ -444,7 +444,7 @@ public class PortalController : ControllerBase
}
[HttpDelete("delete")]
[Authorize(AuthenticationSchemes = "confirm", Roles = "ProfileRemove")]
[Authorize(AuthenticationSchemes = "confirm", Roles = "PortalRemove")]
public async Task<object> DeletePortal()
{
_tenantManager.RemoveTenant(Tenant.Id);

View File

@ -132,8 +132,7 @@ public class SettingsController : BaseSettingsController
}
[HttpGet("")]
[AllowNotPayment]
[AllowAnonymous]
[AllowNotPayment, AllowSuspended, AllowAnonymous]
public SettingsDto GetSettings(bool? withpassword)
{
var studioAdminMessageSettings = _settingsManager.Load<StudioAdminMessageSettings>();

View File

@ -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<CompanyWhiteLabelSettings>());
}
@ -280,8 +280,6 @@ public class WhitelabelController : BaseSettingsController
[HttpGet("rebranding/additional")]
public AdditionalWhiteLabelSettings GetAdditionalWhiteLabelSettings()
{
_permissionContext.DemandPermissions(SecutiryConstants.EditPortalSettings);
return _settingsManager.Load<AdditionalWhiteLabelSettings>();
}

View File

@ -60,22 +60,25 @@ public class AdditionalWhiteLabelSettings : ISettings<AdditionalWhiteLabelSettin
public string LicenseAgreementsUrl { get; set; }
public bool IsDefault()
public bool IsDefault
{
var defaultSettings = GetDefault();
return StartDocsEnabled == defaultSettings.StartDocsEnabled &&
HelpCenterEnabled == defaultSettings.HelpCenterEnabled &&
FeedbackAndSupportEnabled == defaultSettings.FeedbackAndSupportEnabled &&
FeedbackAndSupportUrl == defaultSettings.FeedbackAndSupportUrl &&
UserForumEnabled == defaultSettings.UserForumEnabled &&
UserForumUrl == defaultSettings.UserForumUrl &&
VideoGuidesEnabled == defaultSettings.VideoGuidesEnabled &&
VideoGuidesUrl == defaultSettings.VideoGuidesUrl &&
SalesEmail == defaultSettings.SalesEmail &&
BuyUrl == defaultSettings.BuyUrl &&
LicenseAgreementsEnabled == defaultSettings.LicenseAgreementsEnabled &&
LicenseAgreementsUrl == defaultSettings.LicenseAgreementsUrl;
get
{
var defaultSettings = GetDefault();
return StartDocsEnabled == defaultSettings.StartDocsEnabled &&
HelpCenterEnabled == defaultSettings.HelpCenterEnabled &&
FeedbackAndSupportEnabled == defaultSettings.FeedbackAndSupportEnabled &&
FeedbackAndSupportUrl == defaultSettings.FeedbackAndSupportUrl &&
UserForumEnabled == defaultSettings.UserForumEnabled &&
UserForumUrl == defaultSettings.UserForumUrl &&
VideoGuidesEnabled == defaultSettings.VideoGuidesEnabled &&
VideoGuidesUrl == defaultSettings.VideoGuidesUrl &&
SalesEmail == defaultSettings.SalesEmail &&
BuyUrl == defaultSettings.BuyUrl &&
LicenseAgreementsEnabled == defaultSettings.LicenseAgreementsEnabled &&
LicenseAgreementsUrl == defaultSettings.LicenseAgreementsUrl;
}
}
[JsonIgnore]

View File

@ -34,7 +34,7 @@ public class CompanyWhiteLabelSettingsWrapper
[Serializable]
public class CompanyWhiteLabelSettings : ISettings<CompanyWhiteLabelSettings>
{
private readonly CoreSettings _coreSettings;
private CoreSettings _coreSettings;
public string CompanyName { get; set; }
@ -59,16 +59,19 @@ public class CompanyWhiteLabelSettings : ISettings<CompanyWhiteLabelSettings>
}
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<CompanyWhiteLabelSettings>
{
var settings = _coreSettings.GetSetting("CompanyWhiteLabelSettings");
return string.IsNullOrEmpty(settings) ? new CompanyWhiteLabelSettings(_coreSettings) : JsonConvert.DeserializeObject<CompanyWhiteLabelSettings>(settings);
var result = string.IsNullOrEmpty(settings) ? new CompanyWhiteLabelSettings(_coreSettings) : JsonConvert.DeserializeObject<CompanyWhiteLabelSettings>(settings);
result._coreSettings = _coreSettings;
return result;
}
#endregion