diff --git a/ASC.Web.sln b/ASC.Web.sln index 4f0d10abf9..389a3111cf 100644 --- a/ASC.Web.sln +++ b/ASC.Web.sln @@ -40,10 +40,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.Notify", "common\servic EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.Studio.Notify", "common\services\ASC.Studio.Notify\ASC.Studio.Notify.csproj", "{E3567AB9-0926-444D-A0D0-A369D5890EAA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ASC.Textile", "common\ASC.Textile\ASC.Textile.csproj", "{C8F410B4-B83B-47B9-9ECD-07590A8750A7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.Textile", "common\ASC.Textile\ASC.Textile.csproj", "{C8F410B4-B83B-47B9-9ECD-07590A8750A7}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.Notify.Textile", "common\ASC.Notify.Textile\ASC.Notify.Textile.csproj", "{DB50E2EF-B4D8-493A-8568-29CAC0DF9062}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ASC.ElasticSearch", "common\services\ASC.ElasticSearch\ASC.ElasticSearch.csproj", "{AE1A0E06-6CD4-4E1D-8209-22BBBD6D5652}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASC.Files", "products\ASC.Files\Server\ASC.Files.csproj", "{77BA2F61-6155-4283-BB39-F8E42F46A0B0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -126,6 +130,14 @@ Global {DB50E2EF-B4D8-493A-8568-29CAC0DF9062}.Debug|Any CPU.Build.0 = Debug|Any CPU {DB50E2EF-B4D8-493A-8568-29CAC0DF9062}.Release|Any CPU.ActiveCfg = Release|Any CPU {DB50E2EF-B4D8-493A-8568-29CAC0DF9062}.Release|Any CPU.Build.0 = Release|Any CPU + {AE1A0E06-6CD4-4E1D-8209-22BBBD6D5652}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AE1A0E06-6CD4-4E1D-8209-22BBBD6D5652}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AE1A0E06-6CD4-4E1D-8209-22BBBD6D5652}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AE1A0E06-6CD4-4E1D-8209-22BBBD6D5652}.Release|Any CPU.Build.0 = Release|Any CPU + {77BA2F61-6155-4283-BB39-F8E42F46A0B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77BA2F61-6155-4283-BB39-F8E42F46A0B0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77BA2F61-6155-4283-BB39-F8E42F46A0B0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77BA2F61-6155-4283-BB39-F8E42F46A0B0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/all-clients.code-workspace b/all-clients.code-workspace index 45bc87642c..bffaca251d 100644 --- a/all-clients.code-workspace +++ b/all-clients.code-workspace @@ -15,6 +15,10 @@ { "name": "ASC.People.Client" "path": "./products/ASC.People/Client" + }, + { + "name": "ASC.Files.Client" + "path": "./products/ASC.Files/Client" } ], "settings": {} diff --git a/build/build.bat b/build/build.bat index 5f87cc8422..d5def627ce 100644 --- a/build/build.bat +++ b/build/build.bat @@ -18,6 +18,9 @@ call build\scripts\client.sh echo "ASC.Web.People.Client" call build\scripts\people.sh +echo "ASC.Web.Files.Client" +call build\scripts\files.sh + echo "ASC.Web.sln" call dotnet build ASC.Web.sln /fl1 /flp1:LogFile=build/ASC.Web.log;Verbosity=Normal diff --git a/build/rebuild.bat b/build/rebuild.bat index 7f87a22c45..23ec784dc7 100644 --- a/build/rebuild.bat +++ b/build/rebuild.bat @@ -29,6 +29,11 @@ call yarn link "asc-web-components" --cwd products/ASC.People/Client call yarn link "asc-web-common" --cwd products/ASC.People/Client call yarn install --cwd products/ASC.People/Client > build\ASC.Web.People.Client.log +echo "ASC.Web.Files.Client" +call yarn link "asc-web-components" --cwd products/ASC.Files/Client +call yarn link "asc-web-common" --cwd products/ASC.Files/Client +call yarn install --cwd products/ASC.Files/Client > build\ASC.Web.Files.Client.log + echo "ASC.Web.sln" call dotnet build ASC.Web.sln /fl1 /flp1:LogFile=build/ASC.Web.log;Verbosity=Normal diff --git a/build/run/FileClient.bat b/build/run/FileClient.bat new file mode 100644 index 0000000000..19e57b7b20 --- /dev/null +++ b/build/run/FileClient.bat @@ -0,0 +1,2 @@ +echo "RUN ASC.Web.Files" +call set BROWSER=none&&npm start --prefix ../../products/ASC.Files/Client \ No newline at end of file diff --git a/build/run/FileServer.bat b/build/run/FileServer.bat new file mode 100644 index 0000000000..ffca875cc2 --- /dev/null +++ b/build/run/FileServer.bat @@ -0,0 +1,2 @@ +echo "RUN ASC.Files" +call dotnet run --project ..\..\products\ASC.Files\Server\ASC.Files.csproj --no-build --$STORAGE_ROOT=..\..\..\Data --log__dir=..\..\..\Logs --log__name=files \ No newline at end of file diff --git a/build/scripts/files.sh b/build/scripts/files.sh new file mode 100644 index 0000000000..44770da72b --- /dev/null +++ b/build/scripts/files.sh @@ -0,0 +1,3 @@ +yarn link "asc-web-components" --cwd products/ASC.Files/Client +yarn link "asc-web-common" --cwd products/ASC.Files/Client +yarn install --cwd products/ASC.Files/Client --frozen-lockfile \ No newline at end of file diff --git a/common/ASC.Api.Core/Auth/ConfirmAuthHandler.cs b/common/ASC.Api.Core/Auth/ConfirmAuthHandler.cs index efcb88e617..87d2a3c887 100644 --- a/common/ASC.Api.Core/Auth/ConfirmAuthHandler.cs +++ b/common/ASC.Api.Core/Auth/ConfirmAuthHandler.cs @@ -6,11 +6,12 @@ using System.Security.Claims; using System.Text.Encodings.Web; using System.Threading.Tasks; +using ASC.Common; using ASC.Core; using ASC.Security.Cryptography; using ASC.Web.Studio.Core; + using Microsoft.AspNetCore.Authentication; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -109,7 +110,7 @@ namespace ASC.Api.Core.Auth public static class ConfirmAuthHandlerExtension { - public static IServiceCollection AddConfirmAuthHandler(this IServiceCollection services) + public static DIHelper AddConfirmAuthHandler(this DIHelper services) { return services .AddSecurityContextService() diff --git a/common/ASC.Api.Core/Auth/CookieAuthHandler.cs b/common/ASC.Api.Core/Auth/CookieAuthHandler.cs index 5afe221d63..fbcbfb7539 100644 --- a/common/ASC.Api.Core/Auth/CookieAuthHandler.cs +++ b/common/ASC.Api.Core/Auth/CookieAuthHandler.cs @@ -2,9 +2,11 @@ using System.Security.Authentication; using System.Text.Encodings.Web; using System.Threading.Tasks; + +using ASC.Common; using ASC.Core; + using Microsoft.AspNetCore.Authentication; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -39,7 +41,7 @@ namespace ASC.Api.Core.Auth public static class CookieAuthHandlerExtension { - public static IServiceCollection AddCookieAuthHandler(this IServiceCollection services) + public static DIHelper AddCookieAuthHandler(this DIHelper services) { return services.AddSecurityContextService(); } diff --git a/common/ASC.Api.Core/Core/ApiContext.cs b/common/ASC.Api.Core/Core/ApiContext.cs index a53573f546..97af24245e 100644 --- a/common/ASC.Api.Core/Core/ApiContext.cs +++ b/common/ASC.Api.Core/Core/ApiContext.cs @@ -27,11 +27,12 @@ using System; using System.Linq; using System.Security.Claims; + +using ASC.Common; using ASC.Core; using ASC.Core.Tenants; + using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; namespace ASC.Api.Core { @@ -270,13 +271,12 @@ namespace ASC.Api.Core public static class ApiContextConfigExtension { - public static IServiceCollection AddApiContextService(this IServiceCollection services) + public static DIHelper AddApiContextService(this DIHelper services) { services.TryAddScoped(); return services .AddTenantManagerService() - .AddHttpContextAccessor() .AddSecurityContextService(); } } diff --git a/common/ASC.Api.Core/Core/ApiDateTime.cs b/common/ASC.Api.Core/Core/ApiDateTime.cs index 0d788b3779..f4caa6d5cb 100644 --- a/common/ASC.Api.Core/Core/ApiDateTime.cs +++ b/common/ASC.Api.Core/Core/ApiDateTime.cs @@ -28,8 +28,11 @@ using System; using System.ComponentModel; using System.Globalization; using System.Runtime.Serialization; + +using ASC.Common; using ASC.Common.Utils; using ASC.Core; + using Newtonsoft.Json; namespace ASC.Api.Core @@ -360,4 +363,29 @@ namespace ASC.Api.Core return typeof(ApiDateTime).IsAssignableFrom(objectType); } } + + public class ApiDateTimeHelper + { + public TenantManager TenantManager { get; } + public TimeZoneConverter TimeZoneConverter { get; } + + public ApiDateTimeHelper(TenantManager tenantManager, TimeZoneConverter timeZoneConverter) + { + TenantManager = tenantManager; + TimeZoneConverter = timeZoneConverter; + } + + public ApiDateTime Get(DateTime? from) => ApiDateTime.FromDate(TenantManager, TimeZoneConverter, from); + } + + public static class ApiDateTimeHelperExtension + { + public static DIHelper AddApiDateTimeHelper(this DIHelper services) + { + services.TryAddScoped(); + + return services + .AddTenantManagerService(); + } + } } \ No newline at end of file diff --git a/common/ASC.Api.Core/Middleware/CultureMiddleware.cs b/common/ASC.Api.Core/Middleware/CultureMiddleware.cs index 9a5a7e9f5b..33e20a5022 100644 --- a/common/ASC.Api.Core/Middleware/CultureMiddleware.cs +++ b/common/ASC.Api.Core/Middleware/CultureMiddleware.cs @@ -1,10 +1,12 @@ using System.Globalization; using System.Threading; using System.Threading.Tasks; + +using ASC.Common; using ASC.Core; + using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; namespace ASC.Api.Core.Middleware { @@ -50,7 +52,7 @@ namespace ASC.Api.Core.Middleware return builder.UseMiddleware(); } - public static IServiceCollection AddCultureMiddleware(this IServiceCollection services) + public static DIHelper AddCultureMiddleware(this DIHelper services) { return services .AddUserManagerService() diff --git a/common/ASC.Api.Core/Middleware/IpSecurityFilter.cs b/common/ASC.Api.Core/Middleware/IpSecurityFilter.cs index bc5e978781..1a36265103 100644 --- a/common/ASC.Api.Core/Middleware/IpSecurityFilter.cs +++ b/common/ASC.Api.Core/Middleware/IpSecurityFilter.cs @@ -1,11 +1,13 @@ using System.Net; + +using ASC.Common; using ASC.Common.Logging; using ASC.Core; using ASC.Core.Common.Settings; using ASC.IPSecurity; + using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; namespace ASC.Api.Core.Middleware @@ -45,7 +47,7 @@ namespace ASC.Api.Core.Middleware public static class IpSecurityFilterExtension { - public static IServiceCollection AddIpSecurityFilter(this IServiceCollection services) + public static DIHelper AddIpSecurityFilter(this DIHelper services) { return services .AddSettingsManagerService() diff --git a/common/ASC.Api.Core/Middleware/PaymentFilter.cs b/common/ASC.Api.Core/Middleware/PaymentFilter.cs index fa5f38f8dc..6195db9727 100644 --- a/common/ASC.Api.Core/Middleware/PaymentFilter.cs +++ b/common/ASC.Api.Core/Middleware/PaymentFilter.cs @@ -1,13 +1,15 @@ using System.Linq; using System.Net; using System.Web; + +using ASC.Common; using ASC.Common.Logging; using ASC.Web.Api.Routing; using ASC.Web.Studio.Utility; + using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; namespace ASC.Api.Core.Middleware @@ -50,7 +52,7 @@ namespace ASC.Api.Core.Middleware public static class PaymentFilterExtension { - public static IServiceCollection AddPaymentFilter(this IServiceCollection services) + public static DIHelper AddPaymentFilter(this DIHelper services) { return services .AddTenantExtraService(); diff --git a/common/ASC.Api.Core/Middleware/ProductSecurityFilter.cs b/common/ASC.Api.Core/Middleware/ProductSecurityFilter.cs index 8de5f6dfc2..73641fa92a 100644 --- a/common/ASC.Api.Core/Middleware/ProductSecurityFilter.cs +++ b/common/ASC.Api.Core/Middleware/ProductSecurityFilter.cs @@ -2,15 +2,17 @@ using System.Collections.Generic; using System.Net; using System.Reflection; + +using ASC.Common; using ASC.Common.Logging; using ASC.Common.Notify.Engine; using ASC.Core; using ASC.Web.Core; + using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Routing; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; namespace ASC.Api.Core.Middleware @@ -123,7 +125,7 @@ namespace ASC.Api.Core.Middleware public static class ProductSecurityFilterExtension { - public static IServiceCollection AddProductSecurityFilter(this IServiceCollection services) + public static DIHelper AddProductSecurityFilter(this DIHelper services) { return services .AddUserManagerService() diff --git a/common/ASC.Api.Core/Middleware/TenantStatusFilter.cs b/common/ASC.Api.Core/Middleware/TenantStatusFilter.cs index 466a4e5058..c02a3e2b3f 100644 --- a/common/ASC.Api.Core/Middleware/TenantStatusFilter.cs +++ b/common/ASC.Api.Core/Middleware/TenantStatusFilter.cs @@ -1,10 +1,12 @@ using System.Net; + +using ASC.Common; using ASC.Common.Logging; using ASC.Core; using ASC.Core.Tenants; + using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; namespace ASC.Api.Core.Middleware @@ -46,7 +48,7 @@ namespace ASC.Api.Core.Middleware public static class TenantStatusFilterExtension { - public static IServiceCollection AddTenantStatusFilter(this IServiceCollection services) + public static DIHelper AddTenantStatusFilter(this DIHelper services) { return services .AddTenantManagerService(); diff --git a/products/ASC.People/Server/Models/Contact.cs b/common/ASC.Api.Core/Model/Contact.cs similarity index 100% rename from products/ASC.People/Server/Models/Contact.cs rename to common/ASC.Api.Core/Model/Contact.cs diff --git a/common/ASC.Api.Core/Model/EmployeeWraper.cs b/common/ASC.Api.Core/Model/EmployeeWraper.cs index 83db7529e5..bdd82021eb 100644 --- a/common/ASC.Api.Core/Model/EmployeeWraper.cs +++ b/common/ASC.Api.Core/Model/EmployeeWraper.cs @@ -26,12 +26,13 @@ using System; using System.Runtime.Serialization; + using ASC.Api.Core; +using ASC.Common; +using ASC.Core; using ASC.Core.Users; using ASC.Web.Core.Users; using ASC.Web.Studio.Utility; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; namespace ASC.Web.Api.Models { @@ -67,18 +68,24 @@ namespace ASC.Web.Api.Models public class EmployeeWraperHelper { - public UserInfo UserInfo { get; set; } - public ApiContext HttpContext { get; } - public DisplayUserSettingsHelper DisplayUserSettingsHelper { get; } - public UserPhotoManager UserPhotoManager { get; } - public CommonLinkUtility CommonLinkUtility { get; } + private ApiContext HttpContext { get; } + private DisplayUserSettingsHelper DisplayUserSettingsHelper { get; } + protected UserPhotoManager UserPhotoManager { get; } + private CommonLinkUtility CommonLinkUtility { get; } + protected UserManager UserManager { get; } - public EmployeeWraperHelper(ApiContext httpContext, DisplayUserSettingsHelper displayUserSettingsHelper, UserPhotoManager userPhotoManager, CommonLinkUtility commonLinkUtility) + public EmployeeWraperHelper( + ApiContext httpContext, + DisplayUserSettingsHelper displayUserSettingsHelper, + UserPhotoManager userPhotoManager, + CommonLinkUtility commonLinkUtility, + UserManager userManager) { HttpContext = httpContext; DisplayUserSettingsHelper = displayUserSettingsHelper; UserPhotoManager = userPhotoManager; CommonLinkUtility = commonLinkUtility; + UserManager = userManager; } public EmployeeWraper Get(UserInfo userInfo) @@ -86,6 +93,18 @@ namespace ASC.Web.Api.Models return Init(new EmployeeWraper(), userInfo); } + public EmployeeWraper Get(Guid userId) + { + try + { + return Get(UserManager.GetUsers(userId)); + } + catch (Exception) + { + return Get(Constants.LostUser); + } + } + protected EmployeeWraper Init(EmployeeWraper result, UserInfo userInfo) { result.Id = userInfo.ID; @@ -114,7 +133,7 @@ namespace ASC.Web.Api.Models public static class EmployeeWraperExtension { - public static IServiceCollection AddEmployeeWraper(this IServiceCollection services) + public static DIHelper AddEmployeeWraper(this DIHelper services) { services.TryAddScoped(); diff --git a/products/ASC.People/Server/Models/EmployeeWraperFull.cs b/common/ASC.Api.Core/Model/EmployeeWraperFull.cs similarity index 88% rename from products/ASC.People/Server/Models/EmployeeWraperFull.cs rename to common/ASC.Api.Core/Model/EmployeeWraperFull.cs index 8e5c48b37e..6aefb91d3a 100644 --- a/products/ASC.People/Server/Models/EmployeeWraperFull.cs +++ b/common/ASC.Api.Core/Model/EmployeeWraperFull.cs @@ -28,15 +28,14 @@ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; + using ASC.Api.Core; -using ASC.Common.Utils; +using ASC.Common; using ASC.Core; using ASC.Core.Users; using ASC.Web.Core; using ASC.Web.Core.Users; using ASC.Web.Studio.Utility; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; namespace ASC.Web.Api.Models { @@ -160,28 +159,23 @@ namespace ASC.Web.Api.Models public class EmployeeWraperFullHelper : EmployeeWraperHelper { - public ApiContext Context { get; } - public UserManager UserManager { get; } - public WebItemSecurity WebItemSecurity { get; } - public TenantManager TenantManager { get; } - public TimeZoneConverter TimeZoneConverter { get; } + private ApiContext Context { get; } + private WebItemSecurity WebItemSecurity { get; } + private ApiDateTimeHelper ApiDateTimeHelper { get; } public EmployeeWraperFullHelper( ApiContext context, UserManager userManager, UserPhotoManager userPhotoManager, WebItemSecurity webItemSecurity, - TenantManager tenantManager, CommonLinkUtility commonLinkUtility, DisplayUserSettingsHelper displayUserSettingsHelper, - TimeZoneConverter timeZoneConverter) - : base(context, displayUserSettingsHelper, userPhotoManager, commonLinkUtility) + ApiDateTimeHelper apiDateTimeHelper) + : base(context, displayUserSettingsHelper, userPhotoManager, commonLinkUtility, userManager) { Context = context; - UserManager = userManager; WebItemSecurity = webItemSecurity; - TenantManager = tenantManager; - TimeZoneConverter = timeZoneConverter; + ApiDateTimeHelper = apiDateTimeHelper; } public EmployeeWraperFull GetFull(UserInfo userInfo) @@ -191,11 +185,11 @@ namespace ASC.Web.Api.Models UserName = userInfo.UserName, FirstName = userInfo.FirstName, LastName = userInfo.LastName, - Birthday = ApiDateTime.FromDate(TenantManager, TimeZoneConverter, userInfo.BirthDate), + Birthday = ApiDateTimeHelper.Get(userInfo.BirthDate), Status = userInfo.Status, ActivationStatus = userInfo.ActivationStatus & ~EmployeeActivationStatus.AutoGenerated, - Terminated = ApiDateTime.FromDate(TenantManager, TimeZoneConverter, userInfo.TerminatedDate), - WorkFrom = ApiDateTime.FromDate(TenantManager, TimeZoneConverter, userInfo.WorkFromDate), + Terminated = ApiDateTimeHelper.Get(userInfo.TerminatedDate), + WorkFrom = ApiDateTimeHelper.Get(userInfo.WorkFromDate), Email = userInfo.Email, IsVisitor = userInfo.IsVisitor(UserManager), IsAdmin = userInfo.IsAdmin(UserManager), @@ -303,7 +297,7 @@ namespace ASC.Web.Api.Models public static class EmployeeWraperFullExtension { - public static IServiceCollection AddEmployeeWraperFull(this IServiceCollection services) + public static DIHelper AddEmployeeWraperFull(this DIHelper services) { services.TryAddScoped(); @@ -311,7 +305,8 @@ namespace ASC.Web.Api.Models .AddTenantManagerService() .AddWebItemSecurity() .AddUserManagerService() - .AddEmployeeWraper(); + .AddEmployeeWraper() + .AddApiDateTimeHelper(); } } } \ No newline at end of file diff --git a/common/ASC.Common/Caching/KafkaCache.cs b/common/ASC.Common/Caching/KafkaCache.cs index 10bc313dfb..6ed0062528 100644 --- a/common/ASC.Common/Caching/KafkaCache.cs +++ b/common/ASC.Common/Caching/KafkaCache.cs @@ -3,12 +3,17 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; + using ASC.Common.Logging; using ASC.Common.Utils; + using Confluent.Kafka; using Google.Protobuf; + using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; namespace ASC.Common.Caching @@ -235,4 +240,14 @@ namespace ASC.Common.Caching return $"{typeof(T).Name}{cacheNotifyAction}"; } } + + public static class KafkaExtention + { + public static DIHelper AddKafkaService(this DIHelper services) + { + services.TryAddSingleton(typeof(ICacheNotify<>), typeof(KafkaCache<>)); + + return services; + } + } } \ No newline at end of file diff --git a/common/ASC.Common/DIHelper.cs b/common/ASC.Common/DIHelper.cs new file mode 100644 index 0000000000..0e2aaea44e --- /dev/null +++ b/common/ASC.Common/DIHelper.cs @@ -0,0 +1,169 @@ +using System; +using System.Collections.Generic; + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace ASC.Common +{ + public class DIHelper + { + public List Singleton { get; set; } + public List Scoped { get; set; } + public List Transient { get; set; } + public List Configured { get; set; } + public IServiceCollection ServiceCollection { get; } + + public DIHelper(IServiceCollection serviceCollection) + { + Singleton = new List(); + Scoped = new List(); + Transient = new List(); + Configured = new List(); + ServiceCollection = serviceCollection; + } + + public DIHelper TryAddScoped() where TService : class + { + var serviceName = $"{typeof(TService)}"; + if (!Scoped.Contains(serviceName)) + { + Scoped.Add(serviceName); + ServiceCollection.TryAddScoped(); + } + return this; + } + + public DIHelper TryAddScoped() where TService : class where TImplementation : class, TService + { + var serviceName = $"{typeof(TService)}{typeof(TImplementation)}"; + if (!Scoped.Contains(serviceName)) + { + Scoped.Add(serviceName); + ServiceCollection.TryAddScoped(); + } + + return this; + } + + public DIHelper TryAddScoped(TService tservice, TImplementation tImplementation) where TService : Type where TImplementation : Type + { + var serviceName = $"{tservice}{tImplementation}"; + if (!Scoped.Contains(serviceName)) + { + Scoped.Add(serviceName); + ServiceCollection.TryAddScoped(tservice, tImplementation); + } + + return this; + } + + public DIHelper TryAddSingleton() where TService : class + { + var serviceName = $"{typeof(TService)}"; + if (!Singleton.Contains(serviceName)) + { + Singleton.Add(serviceName); + ServiceCollection.TryAddSingleton(); + } + + return this; + } + + public DIHelper TryAddSingleton(Func implementationFactory) where TService : class + { + var serviceName = $"{typeof(TService)}"; + if (!Singleton.Contains(serviceName)) + { + Singleton.Add(serviceName); + ServiceCollection.TryAddSingleton(implementationFactory); + } + + return this; + } + + public DIHelper TryAddSingleton(TService t) where TService : class + { + var serviceName = $"{typeof(TService)}"; + if (!Singleton.Contains(serviceName)) + { + Singleton.Add(serviceName); + ServiceCollection.TryAddSingleton(t); + } + + return this; + } + + public DIHelper TryAddSingleton() where TService : class where TImplementation : class, TService + { + var serviceName = $"{typeof(TService)}{typeof(TImplementation)}"; + if (!Singleton.Contains(serviceName)) + { + Singleton.Add(serviceName); + ServiceCollection.TryAddSingleton(); + } + + return this; + } + + public DIHelper AddSingleton() where TService : class where TImplementation : class, TService + { + var serviceName = $"{typeof(TService)}{typeof(TImplementation)}"; + if (!Singleton.Contains(serviceName)) + { + Singleton.Add(serviceName); + ServiceCollection.AddSingleton(); + } + + return this; + } + + public DIHelper TryAddSingleton(TService tservice, TImplementation tImplementation) where TService : Type where TImplementation : Type + { + var serviceName = $"{tservice}{tImplementation}"; + if (!Singleton.Contains(serviceName)) + { + Singleton.Add(serviceName); + ServiceCollection.TryAddSingleton(tservice, tImplementation); + } + return this; + } + + + public DIHelper TryAddTransient() where TService : class + { + var serviceName = $"{typeof(TService)}"; + if (!Transient.Contains(serviceName)) + { + Transient.Add(serviceName); + ServiceCollection.TryAddTransient(); + } + + return this; + } + + public DIHelper Configure(Action configureOptions) where TOptions : class + { + var serviceName = $"{typeof(TOptions)}"; + if (!Configured.Contains(serviceName)) + { + Configured.Add(serviceName); + ServiceCollection.Configure(configureOptions); + } + + return this; + } + + public DIHelper Configure(string name, Action configureOptions) where TOptions : class + { + var serviceName = $"{typeof(TOptions)}{name}"; + if (!Configured.Contains(serviceName)) + { + Configured.Add(serviceName); + ServiceCollection.Configure(name, configureOptions); + } + + return this; + } + } +} diff --git a/common/ASC.Common/DependencyInjection/AutofacExtension.cs b/common/ASC.Common/DependencyInjection/AutofacExtension.cs index c35a365a77..f2cc70e240 100644 --- a/common/ASC.Common/DependencyInjection/AutofacExtension.cs +++ b/common/ASC.Common/DependencyInjection/AutofacExtension.cs @@ -107,7 +107,9 @@ namespace ASC.Common.DependencyInjection AssemblyLoadContext.Default.Resolving += (c, n) => { var path = GetFullPath(n.Name); - return c.LoadFromAssemblyPath(Path.Combine(Path.GetDirectoryName(path), $"{n.Name}.dll")); + return path != null ? + c.LoadFromAssemblyPath(Path.Combine(Path.GetDirectoryName(path), $"{n.Name}.dll")) : + null; }; } } diff --git a/common/ASC.Common/Logging/EFLoggerFactory.cs b/common/ASC.Common/Logging/EFLoggerFactory.cs index dfb44b0e96..7cee860a0e 100644 --- a/common/ASC.Common/Logging/EFLoggerFactory.cs +++ b/common/ASC.Common/Logging/EFLoggerFactory.cs @@ -119,7 +119,7 @@ namespace ASC.Common.Logging public static class LoggerExtension { - public static IServiceCollection AddLoggerService(this IServiceCollection services) + public static DIHelper AddLoggerService(this DIHelper services) { services.TryAddScoped(); services.TryAddScoped(); diff --git a/common/ASC.Common/Logging/Log.cs b/common/ASC.Common/Logging/Log.cs index c0790ac798..eb5278c264 100644 --- a/common/ASC.Common/Logging/Log.cs +++ b/common/ASC.Common/Logging/Log.cs @@ -26,15 +26,18 @@ using System; using System.Collections.Generic; -using System.IO; -using System.Reflection; -using ASC.Common.Utils; +using System.IO; +using System.Reflection; + +using ASC.Common.Utils; + using log4net.Config; -using log4net.Core; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Options; +using log4net.Core; + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + using NLog; namespace ASC.Common.Logging @@ -51,7 +54,7 @@ namespace ASC.Common.Logging void Trace(object message); void TraceFormat(string message, object arg0); - void DebugWithProps(string message, params KeyValuePair[] props); + void DebugWithProps(string message, params KeyValuePair[] props); void DebugWithProps(string message, KeyValuePair prop1, KeyValuePair prop2, KeyValuePair prop3); void Debug(object message); void Debug(object message, Exception exception); @@ -94,7 +97,7 @@ namespace ASC.Common.Logging void FatalFormat(string format, object arg0, object arg1, object arg2); void FatalFormat(IFormatProvider provider, string format, params object[] args); - string LogDirectory { get; } + string LogDirectory { get; } string Name { get; set; } } @@ -330,74 +333,74 @@ namespace ASC.Common.Logging public void FatalFormat(IFormatProvider provider, string format, params object[] args) { if (IsFatalEnabled) loger.FatalFormat(provider, format, args); - } - - public void DebugWithProps(string message, KeyValuePair prop1, KeyValuePair prop2, KeyValuePair prop3) - { - if (!IsDebugEnabled) return; - - AddProp(prop1); - AddProp(prop2); - AddProp(prop3); - - loger.Debug(message); - - static void AddProp(KeyValuePair p) - { - log4net.ThreadContext.Properties[p.Key] = p.Value; - } - } - + } + + public void DebugWithProps(string message, KeyValuePair prop1, KeyValuePair prop2, KeyValuePair prop3) + { + if (!IsDebugEnabled) return; + + AddProp(prop1); + AddProp(prop2); + AddProp(prop3); + + loger.Debug(message); + + static void AddProp(KeyValuePair p) + { + log4net.ThreadContext.Properties[p.Key] = p.Value; + } + } + public string LogDirectory { get { return log4net.GlobalContext.Properties["LogDirectory"].ToString(); } - } - - public string Name - { - get; - - set; - } + } + + public string Name + { + get; + + set; + } } - - public class NLogSettings - { - public string Name { get; set; } - public string Dir { get; set; } - } - - public class ConfigureLogNLog : IConfigureOptions - { - public ConfigureLogNLog(IConfiguration configuration) - { - Configuration = configuration; - } - - public IConfiguration Configuration { get; } - - public void Configure(LogNLog options) - { - LogManager.Configuration = new NLog.Config.XmlLoggingConfiguration(Path.Combine(Configuration["pathToConf"], "nlog.config"), true); - - var settings = Configuration.GetSetting("log"); - if (!string.IsNullOrEmpty(settings.Name)) - { - LogManager.Configuration.Variables["name"] = settings.Name; - } - - if (!string.IsNullOrEmpty(settings.Dir)) - { - LogManager.Configuration.Variables["dir"] = settings.Dir.TrimEnd('/').TrimEnd('\\') + Path.DirectorySeparatorChar; + + public class NLogSettings + { + public string Name { get; set; } + public string Dir { get; set; } + } + + public class ConfigureLogNLog : IConfigureOptions + { + public ConfigureLogNLog(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + public void Configure(LogNLog options) + { + LogManager.Configuration = new NLog.Config.XmlLoggingConfiguration(Path.Combine(Configuration["pathToConf"], "nlog.config"), true); + + var settings = Configuration.GetSetting("log"); + if (!string.IsNullOrEmpty(settings.Name)) + { + LogManager.Configuration.Variables["name"] = settings.Name; } - NLog.Targets.Target.Register("SelfCleaning"); - } - } - + if (!string.IsNullOrEmpty(settings.Dir)) + { + LogManager.Configuration.Variables["dir"] = settings.Dir.TrimEnd('/').TrimEnd('\\') + Path.DirectorySeparatorChar; + } + + NLog.Targets.Target.Register("SelfCleaning"); + } + } + public class LogNLog : ILog { private NLog.ILogger loger; @@ -429,7 +432,7 @@ namespace ASC.Common.Logging public bool IsFatalEnabled { get; private set; } - public bool IsTraceEnabled { get; private set; } + public bool IsTraceEnabled { get; private set; } public void Trace(object message) { @@ -493,23 +496,23 @@ namespace ASC.Common.Logging { if (!IsDebugEnabled) return; - var theEvent = new LogEventInfo - { - Message = message, - LoggerName = Name, - Level = LogLevel.Debug + var theEvent = new LogEventInfo + { + Message = message, + LoggerName = Name, + Level = LogLevel.Debug }; - - AddProp(prop1); - AddProp(prop2); - AddProp(prop3); - - Loger.Log(theEvent); - - void AddProp(KeyValuePair p) - { - theEvent.Properties[p.Key] = p.Value; + AddProp(prop1); + AddProp(prop2); + AddProp(prop3); + + + Loger.Log(theEvent); + + void AddProp(KeyValuePair p) + { + theEvent.Properties[p.Key] = p.Value; } } @@ -670,7 +673,7 @@ namespace ASC.Common.Logging name = value; Loger = NLog.LogManager.GetLogger(name); } - } + } } public class NullLog : ILog @@ -832,60 +835,60 @@ namespace ASC.Common.Logging public void FatalFormat(IFormatProvider provider, string format, params object[] args) { - } - - public void DebugWithProps(string message, KeyValuePair prop1, KeyValuePair prop2, KeyValuePair prop3) - { - } - + } + + public void DebugWithProps(string message, KeyValuePair prop1, KeyValuePair prop2, KeyValuePair prop3) + { + } + public string LogDirectory { get { return ""; } } public string Name { get; set; } } - - - public class LogManager : OptionsMonitor where T : class, ILog, new() - { - public LogManager(IOptionsFactory factory, IEnumerable> sources, IOptionsMonitorCache cache) : base(factory, sources, cache) - { - } - - public override T Get(string name) - { - var log = base.Get(name); - - if (string.IsNullOrEmpty(log?.Name)) - { - log = CurrentValue; - } - - return log; - } - } - + + + public class LogManager : OptionsMonitor where T : class, ILog, new() + { + public LogManager(IOptionsFactory factory, IEnumerable> sources, IOptionsMonitorCache cache) : base(factory, sources, cache) + { + } + + public override T Get(string name) + { + var log = base.Get(name); + + if (string.IsNullOrEmpty(log?.Name)) + { + log = CurrentValue; + } + + return log; + } + } + public static class StudioNotifyHelperExtension { - public static IServiceCollection AddLogManager(this IServiceCollection services, params string[] additionalLoggers) where T : class, ILog, new() - { - const string baseName = "ASC"; - var baseSqlName = $"{baseName}.SQL"; - services.Configure(r => r.Name = baseName); - services.Configure(baseName, r => r.Name = baseName); - services.Configure(baseSqlName, r => r.Name = baseSqlName); - - foreach (var l in additionalLoggers) - { - services.Configure(l, r => r.Name = l); - } - - services.TryAddSingleton(typeof(IOptionsMonitor), typeof(LogManager)); - return services; - } - - public static IServiceCollection AddNLogManager(this IServiceCollection services, params string[] additionalLoggers) - { - services.TryAddSingleton, ConfigureLogNLog>(); - return services.AddLogManager(additionalLoggers); - } - } + public static DIHelper AddLogManager(this DIHelper services, params string[] additionalLoggers) where T : class, ILog, new() + { + const string baseName = "ASC"; + var baseSqlName = $"{baseName}.SQL"; + services.Configure(r => r.Name = baseName); + services.Configure(baseName, r => r.Name = baseName); + services.Configure(baseSqlName, r => r.Name = baseSqlName); + + foreach (var l in additionalLoggers) + { + services.Configure(l, r => r.Name = l); + } + + services.TryAddSingleton(typeof(IOptionsMonitor), typeof(LogManager)); + return services; + } + + public static DIHelper AddNLogManager(this DIHelper services, params string[] additionalLoggers) + { + services.TryAddSingleton, ConfigureLogNLog>(); + return services.AddLogManager(additionalLoggers); + } + } } diff --git a/common/ASC.Common/Security/Cryptography/InstanceCrypto.cs b/common/ASC.Common/Security/Cryptography/InstanceCrypto.cs index 2c9ee02f46..216ce15b74 100644 --- a/common/ASC.Common/Security/Cryptography/InstanceCrypto.cs +++ b/common/ASC.Common/Security/Cryptography/InstanceCrypto.cs @@ -29,8 +29,7 @@ using System.IO; using System.Security.Cryptography; using System.Text; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; +using ASC.Common; namespace ASC.Security.Cryptography { @@ -89,7 +88,7 @@ namespace ASC.Security.Cryptography } public static class InstanceCryptoExtension { - public static IServiceCollection AddInstanceCryptoService(this IServiceCollection services) + public static DIHelper AddInstanceCryptoService(this DIHelper services) { services.TryAddSingleton(); diff --git a/common/ASC.Common/Security/Cryptography/MachinePseudoKeys.cs b/common/ASC.Common/Security/Cryptography/MachinePseudoKeys.cs index dd62b2c01a..9096b9d89d 100644 --- a/common/ASC.Common/Security/Cryptography/MachinePseudoKeys.cs +++ b/common/ASC.Common/Security/Cryptography/MachinePseudoKeys.cs @@ -28,10 +28,11 @@ using System; using System.IO; using System.Linq; using System.Text; + +using ASC.Common; using ASC.Common.Security; + using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; namespace ASC.Security.Cryptography { @@ -41,14 +42,14 @@ namespace ASC.Security.Cryptography public MachinePseudoKeys(IConfiguration configuration) { - var key = configuration["core:machinekey"]; - if (string.IsNullOrEmpty(key)) - { - key = configuration["asc:common.machinekey"]; - } - if (!string.IsNullOrEmpty(key)) - { - confkey = Encoding.UTF8.GetBytes(key); + var key = configuration["core:machinekey"]; + if (string.IsNullOrEmpty(key)) + { + key = configuration["asc:common.machinekey"]; + } + if (!string.IsNullOrEmpty(key)) + { + confkey = Encoding.UTF8.GetBytes(key); } } @@ -77,7 +78,7 @@ namespace ASC.Security.Cryptography } public static class MachinePseudoKeysExtension { - public static IServiceCollection AddMachinePseudoKeysService(this IServiceCollection services) + public static DIHelper AddMachinePseudoKeysService(this DIHelper services) { services.TryAddSingleton(); diff --git a/common/ASC.Common/Threading/DistributedTaskQueue.cs b/common/ASC.Common/Threading/DistributedTaskQueue.cs index aecd43d2e7..068fd2d2a6 100644 --- a/common/ASC.Common/Threading/DistributedTaskQueue.cs +++ b/common/ASC.Common/Threading/DistributedTaskQueue.cs @@ -30,6 +30,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; + using ASC.Common.Caching; namespace ASC.Common.Threading @@ -174,8 +175,23 @@ namespace ASC.Common.Threading task.Publication = GetPublication(); } return task; + } + + public void SetTask(DistributedTask task) + { + DistributedTaskCacheNotify.SetTask(task); } + public void RemoveTask(string id) + { + DistributedTaskCacheNotify.RemoveTask(id, key); + } + + public void CancelTask(string id) + { + DistributedTaskCacheNotify.CancelTask(id); + } + private void OnCompleted(Task task, string id) { var distributedTask = GetTask(id); diff --git a/common/ASC.Common/Utils/Signature.cs b/common/ASC.Common/Utils/Signature.cs index ab9a59efac..aa1161afb5 100644 --- a/common/ASC.Common/Utils/Signature.cs +++ b/common/ASC.Common/Utils/Signature.cs @@ -101,7 +101,7 @@ namespace ASC.Common.Utils public static class SignatureExtension { - public static IServiceCollection AddSignatureService(this IServiceCollection services) + public static DIHelper AddSignatureService(this DIHelper services) { services.TryAddSingleton(); return services.AddMachinePseudoKeysService(); diff --git a/common/ASC.Core.Common/BaseCommonLinkUtility.cs b/common/ASC.Core.Common/BaseCommonLinkUtility.cs index dc4b02d574..54111e4805 100644 --- a/common/ASC.Core.Common/BaseCommonLinkUtility.cs +++ b/common/ASC.Core.Common/BaseCommonLinkUtility.cs @@ -28,12 +28,14 @@ using System; using System.Linq; using System.Text.RegularExpressions; using System.Web; + +using ASC.Common; using ASC.Common.Logging; using ASC.Common.Web; + using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; + using HttpContext = Microsoft.AspNetCore.Http.HttpContext; namespace ASC.Core.Common @@ -235,9 +237,9 @@ namespace ASC.Core.Common public static class BaseCommonLinkUtilityExtension { - public static IServiceCollection AddBaseCommonLinkUtilityService(this IServiceCollection services) + public static DIHelper AddBaseCommonLinkUtilityService(this DIHelper services) { - services.TryAddScoped(); ; + services.TryAddScoped(); return services .AddCoreBaseSettingsService() diff --git a/common/ASC.Core.Common/Billing/License/LicenseReader.cs b/common/ASC.Core.Common/Billing/License/LicenseReader.cs index c17f7d279d..529499fc2c 100644 --- a/common/ASC.Core.Common/Billing/License/LicenseReader.cs +++ b/common/ASC.Core.Common/Billing/License/LicenseReader.cs @@ -29,14 +29,14 @@ using System.IO; using System.Linq; using System.Security.Cryptography; using System.Text; - + +using ASC.Common; using ASC.Common.Logging; using ASC.Core.Tenants; -using ASC.Core.Users; +using ASC.Core.Users; + using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; namespace ASC.Core.Billing @@ -339,7 +339,7 @@ namespace ASC.Core.Billing public static class LicenseReaderExtension { - public static IServiceCollection AddLicenseReaderService(this IServiceCollection services) + public static DIHelper AddLicenseReaderService(this DIHelper services) { services.TryAddScoped(); diff --git a/common/ASC.Core.Common/Billing/TariffService.cs b/common/ASC.Core.Common/Billing/TariffService.cs index 98322ae989..6de9ddd4cd 100644 --- a/common/ASC.Core.Common/Billing/TariffService.cs +++ b/common/ASC.Core.Common/Billing/TariffService.cs @@ -30,16 +30,15 @@ using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; - + +using ASC.Common; using ASC.Common.Caching; using ASC.Common.Logging; using ASC.Core.Caching; using ASC.Core.Common.EF; -using ASC.Core.Tenants; - +using ASC.Core.Tenants; + using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; namespace ASC.Core.Billing @@ -75,8 +74,8 @@ namespace ASC.Core.Billing log.Error(err); }*/ } - } - + } + class ConfigureTariffService : IConfigureNamedOptions { public ConfigureTariffService( @@ -142,8 +141,8 @@ namespace ASC.Core.Billing private static readonly TimeSpan DEFAULT_CACHE_EXPIRATION = TimeSpan.FromMinutes(5); private static readonly TimeSpan STANDALONE_CACHE_EXPIRATION = TimeSpan.FromMinutes(15); - private readonly static bool billingConfigured = false; - + private readonly static bool billingConfigured = false; + internal ICache Cache { get; set; } internal ICacheNotify Notify { get; set; } internal ILog Log { get; set; } @@ -156,43 +155,43 @@ namespace ASC.Core.Billing public CoreBaseSettings CoreBaseSettings { get; set; } public CoreSettings CoreSettings { get; set; } public IConfiguration Configuration { get; set; } - public CoreDbContext CoreDbContext { get; set; } + public CoreDbContext CoreDbContext { get; set; } public TariffServiceStorage TariffServiceStorage { get; set; } public IOptionsMonitor Options { get; set; } - + public TariffService() { CacheExpiration = DEFAULT_CACHE_EXPIRATION; } public TariffService( - IQuotaService quotaService, - ITenantService tenantService, - CoreBaseSettings coreBaseSettings, - CoreSettings coreSettings, - IConfiguration configuration, + IQuotaService quotaService, + ITenantService tenantService, + CoreBaseSettings coreBaseSettings, + CoreSettings coreSettings, + IConfiguration configuration, DbContextManager coreDbContextManager, - TariffServiceStorage tariffServiceStorage, - IOptionsMonitor options) + TariffServiceStorage tariffServiceStorage, + IOptionsMonitor options) : this() - - { + + { Log = options.CurrentValue; QuotaService = quotaService; TenantService = tenantService; - CoreSettings = coreSettings; - Configuration = configuration; - TariffServiceStorage = tariffServiceStorage; - Options = options; - CoreBaseSettings = coreBaseSettings; + CoreSettings = coreSettings; + Configuration = configuration; + TariffServiceStorage = tariffServiceStorage; + Options = options; + CoreBaseSettings = coreBaseSettings; Test = configuration["core:payment:test"] == "true"; int.TryParse(configuration["core:payment:delay"], out var paymentDelay); - + PaymentDelay = paymentDelay; - + Cache = TariffServiceStorage.Cache; Notify = TariffServiceStorage.Notify; - CoreDbContext = coreDbContextManager.Value; + CoreDbContext = coreDbContextManager.Value; } public Tariff GetTariff(int tenantId, bool withRequestToPaymentSystem = true) @@ -356,9 +355,9 @@ namespace ASC.Core.Billing .Select(q => q.AvangateId) .ToArray(); - using var client = GetBillingClient(); - urls = tenant.HasValue ? - client.GetPaymentUrls(GetPortalId(tenant.Value), products, GetAffiliateId(tenant.Value), GetCampaign(tenant.Value), "__Currency__", "__Language__", "__CustomerID__") : + using var client = GetBillingClient(); + urls = tenant.HasValue ? + client.GetPaymentUrls(GetPortalId(tenant.Value), products, GetAffiliateId(tenant.Value), GetCampaign(tenant.Value), "__Currency__", "__Language__", "__CustomerID__") : client.GetPaymentUrls(null, products, !string.IsNullOrEmpty(affiliateId) ? affiliateId : null, null, "__Currency__", "__Language__", "__CustomerID__"); } catch (Exception error) @@ -440,34 +439,34 @@ namespace ASC.Core.Billing public string GetButton(int tariffId, string partnerId) - { - return CoreDbContext.Buttons - .Where(r => r.TariffId == tariffId && r.PartnerId == partnerId) - .Select(r => r.ButtonUrl) + { + return CoreDbContext.Buttons + .Where(r => r.TariffId == tariffId && r.PartnerId == partnerId) + .Select(r => r.ButtonUrl) .SingleOrDefault(); } public void SaveButton(int tariffId, string partnerId, string buttonUrl) - { - var efButton = new DbButton() - { - TariffId = tariffId, - PartnerId = partnerId, - ButtonUrl = buttonUrl - }; - - CoreDbContext.AddOrUpdate(r => r.Buttons, efButton); + { + var efButton = new DbButton() + { + TariffId = tariffId, + PartnerId = partnerId, + ButtonUrl = buttonUrl + }; + + CoreDbContext.AddOrUpdate(r => r.Buttons, efButton); CoreDbContext.SaveChanges(); } private Tuple GetBillingInfo(int tenant) - { - var r = CoreDbContext.Tariffs - .Where(r => r.Tenant == tenant) - .OrderByDescending(r => r.Id) - .SingleOrDefault(); - + { + var r = CoreDbContext.Tariffs + .Where(r => r.Tenant == tenant) + .OrderByDescending(r => r.Id) + .SingleOrDefault(); + return r != null ? Tuple.Create(r.Tariff, r.Stamp.Year < 9999 ? r.Stamp : DateTime.MaxValue) : null; } @@ -476,23 +475,23 @@ namespace ASC.Core.Billing var inserted = false; if (!Equals(bi, GetBillingInfo(tenant))) { - using var tx = CoreDbContext.Database.BeginTransaction(); - - // last record is not the same - var count = CoreDbContext.Tariffs - .Count(r => r.Tenant == tenant && r.Tariff == bi.Item1 && r.Stamp == bi.Item2); + using var tx = CoreDbContext.Database.BeginTransaction(); + + // last record is not the same + var count = CoreDbContext.Tariffs + .Count(r => r.Tenant == tenant && r.Tariff == bi.Item1 && r.Stamp == bi.Item2); if (bi.Item2 == DateTime.MaxValue || renewal || count == 0) - { - var efTariff = new DbTariff - { - Tenant = tenant, - Tariff = bi.Item1, - Stamp = bi.Item2 - }; - - CoreDbContext.Tariffs.Add(efTariff); - CoreDbContext.SaveChanges(); + { + var efTariff = new DbTariff + { + Tenant = tenant, + Tariff = bi.Item1, + Stamp = bi.Item2 + }; + + CoreDbContext.Tariffs.Add(efTariff); + CoreDbContext.SaveChanges(); Cache.Remove(GetTariffCacheKey(tenant)); inserted = true; @@ -514,15 +513,15 @@ namespace ASC.Core.Billing public void DeleteDefaultBillingInfo() { - const int tenant = Tenant.DEFAULT_TENANT; - - var tariffs = CoreDbContext.Tariffs.Where(r => r.Tenant == tenant).ToList(); - - foreach (var t in tariffs) - { - t.Tenant = -2; - } - + const int tenant = Tenant.DEFAULT_TENANT; + + var tariffs = CoreDbContext.Tariffs.Where(r => r.Tenant == tenant).ToList(); + + foreach (var t in tariffs) + { + t.Tenant = -2; + } + CoreDbContext.SaveChanges(); ClearCache(tenant); @@ -637,12 +636,12 @@ namespace ASC.Core.Billing private string GetAffiliateId(int tenant) { return CoreSettings.GetAffiliateId(tenant); - } - - private string GetCampaign(int tenant) - { - return CoreSettings.GetCampaign(tenant); - } + } + + private string GetCampaign(int tenant) + { + return CoreSettings.GetCampaign(tenant); + } private TimeSpan GetCacheExpiration() { @@ -687,13 +686,13 @@ namespace ASC.Core.Billing public static class TariffConfigExtension { - public static IServiceCollection AddTariffService(this IServiceCollection services) - { - services.AddCoreDbContextService(); + public static DIHelper AddTariffService(this DIHelper services) + { + services.AddCoreDbContextService(); - services.TryAddSingleton(); + services.TryAddSingleton(); - services.TryAddScoped(); + services.TryAddScoped(); services.TryAddScoped, ConfigureTariffService>(); return services; diff --git a/common/ASC.Core.Common/Caching/CachedAzService.cs b/common/ASC.Core.Common/Caching/CachedAzService.cs index 2b716cf2fc..129c7665be 100644 --- a/common/ASC.Core.Common/Caching/CachedAzService.cs +++ b/common/ASC.Core.Common/Caching/CachedAzService.cs @@ -25,29 +25,29 @@ using System; -using System.Collections.Generic; +using System.Collections.Generic; + +using ASC.Common; using ASC.Common.Caching; -using ASC.Core.Common.EF; -using ASC.Core.Data; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; - +using ASC.Core.Common.EF; +using ASC.Core.Data; + namespace ASC.Core.Caching -{ - class AzServiceCache - { - internal ICache Cache { get; } - internal ICacheNotify CacheNotify { get; } - - public AzServiceCache(ICacheNotify cacheNotify) - { - CacheNotify = cacheNotify; - Cache = AscCache.Memory; - - cacheNotify.Subscribe((r) => UpdateCache(r, true), CacheNotifyAction.Remove); - cacheNotify.Subscribe((r) => UpdateCache(r, false), CacheNotifyAction.InsertOrUpdate); - } - +{ + class AzServiceCache + { + internal ICache Cache { get; } + internal ICacheNotify CacheNotify { get; } + + public AzServiceCache(ICacheNotify cacheNotify) + { + CacheNotify = cacheNotify; + Cache = AscCache.Memory; + + cacheNotify.Subscribe((r) => UpdateCache(r, true), CacheNotifyAction.Remove); + cacheNotify.Subscribe((r) => UpdateCache(r, false), CacheNotifyAction.InsertOrUpdate); + } + private void UpdateCache(AzRecord r, bool remove) { var aces = Cache.Get(GetKey(r.Tenant)); @@ -65,29 +65,29 @@ namespace ASC.Core.Caching } } } - } - + } + public static string GetKey(int tenant) { return "acl" + tenant.ToString(); - } - } + } + } class CachedAzService : IAzService { private readonly IAzService service; private readonly ICacheNotify cacheNotify; - + private ICache Cache { get; } private TimeSpan CacheExpiration { get; set; } public CachedAzService(DbAzService service, AzServiceCache azServiceCache) - { + { this.service = service ?? throw new ArgumentNullException("service"); - Cache = azServiceCache.Cache; + Cache = azServiceCache.Cache; cacheNotify = azServiceCache.CacheNotify; CacheExpiration = TimeSpan.FromMinutes(10); } @@ -117,20 +117,20 @@ namespace ASC.Core.Caching service.RemoveAce(tenant, r); cacheNotify.Publish(r, CacheNotifyAction.Remove); } - } - - public static class AzConfigExtension - { - public static IServiceCollection AddAzService(this IServiceCollection services) - { - services.AddCoreDbContextService(); - - services.TryAddScoped(); - services.TryAddScoped(); - services.TryAddSingleton(); - services.TryAddSingleton(typeof(ICacheNotify<>), typeof(KafkaCache<>)); - - return services; - } - } + } + + public static class AzConfigExtension + { + public static DIHelper AddAzService(this DIHelper services) + { + services.AddCoreDbContextService(); + + services.TryAddScoped(); + services.TryAddScoped(); + services.TryAddSingleton(); + services.TryAddSingleton(typeof(ICacheNotify<>), typeof(KafkaCache<>)); + + return services; + } + } } \ No newline at end of file diff --git a/common/ASC.Core.Common/Caching/CachedQuotaService.cs b/common/ASC.Core.Common/Caching/CachedQuotaService.cs index 0bac319620..4a27b0b98e 100644 --- a/common/ASC.Core.Common/Caching/CachedQuotaService.cs +++ b/common/ASC.Core.Common/Caching/CachedQuotaService.cs @@ -26,46 +26,46 @@ using System; using System.Collections.Generic; -using System.Linq; - +using System.Linq; + +using ASC.Common; using ASC.Common.Caching; -using ASC.Core.Common.EF; -using ASC.Core.Data; -using ASC.Core.Tenants; +using ASC.Core.Common.EF; +using ASC.Core.Data; +using ASC.Core.Tenants; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; - + namespace ASC.Core.Caching -{ - class QuotaServiceCache - { +{ + class QuotaServiceCache + { internal const string KEY_QUOTA = "quota"; - internal const string KEY_QUOTA_ROWS = "quotarows"; - - internal TrustInterval Interval { get; set; } - internal ICache Cache { get; } - internal ICacheNotify CacheNotify { get; } - - internal bool QuotaCacheEnabled { get; } - - public QuotaServiceCache(IConfiguration Configuration, ICacheNotify cacheNotify) - { - if (Configuration["core:enable-quota-cache"] == null) - { - QuotaCacheEnabled = true; - } - else - { - QuotaCacheEnabled = !bool.TryParse(Configuration["core:enable-quota-cache"], out var enabled) || enabled; - } - - CacheNotify = cacheNotify; - Cache = AscCache.Memory; - Interval = new TrustInterval(); - + internal const string KEY_QUOTA_ROWS = "quotarows"; + + internal TrustInterval Interval { get; set; } + internal ICache Cache { get; } + internal ICacheNotify CacheNotify { get; } + + internal bool QuotaCacheEnabled { get; } + + public QuotaServiceCache(IConfiguration Configuration, ICacheNotify cacheNotify) + { + if (Configuration["core:enable-quota-cache"] == null) + { + QuotaCacheEnabled = true; + } + else + { + QuotaCacheEnabled = !bool.TryParse(Configuration["core:enable-quota-cache"], out var enabled) || enabled; + } + + CacheNotify = cacheNotify; + Cache = AscCache.Memory; + Interval = new TrustInterval(); + cacheNotify.Subscribe((i) => { if (i.Key == KEY_QUOTA_ROWS) @@ -76,10 +76,10 @@ namespace ASC.Core.Caching { Cache.Remove(KEY_QUOTA); } - }, CacheNotifyAction.Any); - } - } - + }, CacheNotifyAction.Any); + } + } + class ConfigureCachedQuotaService : IConfigureNamedOptions { public IOptionsSnapshot Service { get; } @@ -122,8 +122,8 @@ namespace ASC.Core.Caching { Interval = new TrustInterval(); CacheExpiration = TimeSpan.FromMinutes(10); - } - + } + public CachedQuotaService(DbQuotaService service, QuotaServiceCache quotaServiceCache) : this() { Service = service ?? throw new ArgumentNullException("service"); @@ -136,10 +136,10 @@ namespace ASC.Core.Caching { var quotas = Cache.Get>(QuotaServiceCache.KEY_QUOTA); if (quotas == null) - { + { quotas = Service.GetTenantQuotas(); - if (QuotaServiceCache.QuotaCacheEnabled) - { + if (QuotaServiceCache.QuotaCacheEnabled) + { Cache.Insert(QuotaServiceCache.KEY_QUOTA, quotas, DateTime.UtcNow.Add(CacheExpiration)); } } @@ -211,9 +211,9 @@ namespace ASC.Core.Caching } } } - - if (QuotaServiceCache.QuotaCacheEnabled) - { + + if (QuotaServiceCache.QuotaCacheEnabled) + { Cache.Insert(QuotaServiceCache.KEY_QUOTA_ROWS, rows, DateTime.UtcNow.Add(CacheExpiration)); } } @@ -237,24 +237,24 @@ namespace ASC.Core.Caching return list.ToList(); } } - } - - public static class QuotaConfigExtension - { - public static IServiceCollection AddQuotaService(this IServiceCollection services) - { - services.AddCoreDbContextService(); - - services.TryAddSingleton(typeof(ICacheNotify<>), typeof(KafkaCache<>)); - services.TryAddSingleton(); - - services.TryAddScoped(); - services.TryAddScoped(); + } + + public static class QuotaConfigExtension + { + public static DIHelper AddQuotaService(this DIHelper services) + { + services.AddCoreDbContextService(); + + services.TryAddSingleton(typeof(ICacheNotify<>), typeof(KafkaCache<>)); + services.TryAddSingleton(); + services.TryAddScoped(); + services.TryAddScoped(); + services.TryAddScoped, ConfigureDbQuotaService>(); services.TryAddScoped, ConfigureCachedQuotaService>(); - return services; - } + return services; + } } } diff --git a/common/ASC.Core.Common/Caching/CachedSubscriptionService.cs b/common/ASC.Core.Common/Caching/CachedSubscriptionService.cs index 92044581a0..e9bcfbf083 100644 --- a/common/ASC.Core.Common/Caching/CachedSubscriptionService.cs +++ b/common/ASC.Core.Common/Caching/CachedSubscriptionService.cs @@ -27,11 +27,11 @@ using System; using System.Collections.Generic; using System.Linq; + +using ASC.Common; using ASC.Common.Caching; using ASC.Core.Common.EF; using ASC.Core.Data; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; namespace ASC.Core.Caching { @@ -288,7 +288,7 @@ namespace ASC.Core.Caching public static class SubscriptionConfigExtension { - public static IServiceCollection AddSubscriptionService(this IServiceCollection services) + public static DIHelper AddSubscriptionService(this DIHelper services) { services.TryAddSingleton(typeof(ICacheNotify<>), typeof(KafkaCache<>)); diff --git a/common/ASC.Core.Common/Caching/CachedTenantService.cs b/common/ASC.Core.Common/Caching/CachedTenantService.cs index 57494e94f0..0eac8a9ca7 100644 --- a/common/ASC.Core.Common/Caching/CachedTenantService.cs +++ b/common/ASC.Core.Common/Caching/CachedTenantService.cs @@ -27,46 +27,46 @@ using System; using System.Collections.Generic; +using ASC.Common; using ASC.Common.Caching; -using ASC.Common.Utils; -using ASC.Core.Common.EF.Context; -using ASC.Core.Data; -using ASC.Core.Tenants; - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; +using ASC.Common.Utils; +using ASC.Core.Common.EF.Context; +using ASC.Core.Data; +using ASC.Core.Tenants; + +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; - + namespace ASC.Core.Caching -{ - class TenantServiceCache - { - private const string KEY = "tenants"; - private TimeSpan CacheExpiration { get; set; } - internal ICache Cache { get; } - internal ICacheNotify CacheNotifyItem { get; } - internal ICacheNotify CacheNotifySettings { get; } - - public TenantServiceCache(CoreBaseSettings coreBaseSettings, ICacheNotify cacheNotifyItem, ICacheNotify cacheNotifySettings) - { - CacheNotifyItem = cacheNotifyItem; - CacheNotifySettings = cacheNotifySettings; - Cache = AscCache.Memory; - CacheExpiration = TimeSpan.FromMinutes(2); - +{ + class TenantServiceCache + { + private const string KEY = "tenants"; + private TimeSpan CacheExpiration { get; set; } + internal ICache Cache { get; } + internal ICacheNotify CacheNotifyItem { get; } + internal ICacheNotify CacheNotifySettings { get; } + + public TenantServiceCache(CoreBaseSettings coreBaseSettings, ICacheNotify cacheNotifyItem, ICacheNotify cacheNotifySettings) + { + CacheNotifyItem = cacheNotifyItem; + CacheNotifySettings = cacheNotifySettings; + Cache = AscCache.Memory; + CacheExpiration = TimeSpan.FromMinutes(2); + cacheNotifyItem.Subscribe((t) => { var tenants = GetTenantStore(); tenants.Remove(t.TenantId); tenants.Clear(coreBaseSettings); - }, CacheNotifyAction.InsertOrUpdate); + }, CacheNotifyAction.InsertOrUpdate); cacheNotifySettings.Subscribe((s) => { Cache.Remove(s.Key); - }, CacheNotifyAction.Remove); - } - + }, CacheNotifyAction.Remove); + } + internal TenantStore GetTenantStore() { var store = Cache.Get(KEY); @@ -150,7 +150,7 @@ namespace ASC.Core.Caching byDomain.Clear(); } } - } + } } class ConfigureCachedTenantService : IConfigureNamedOptions @@ -185,23 +185,23 @@ namespace ASC.Core.Caching { internal ITenantService Service { get; set; } - private readonly ICache cache; + private readonly ICache cache; internal ICacheNotify CacheNotifySettings { get; set; } internal ICacheNotify CacheNotifyItem { get; set; } - private TimeSpan SettingsExpiration { get; set; } + private TimeSpan SettingsExpiration { get; set; } internal TenantServiceCache TenantServiceCache { get; set; } - + public CachedTenantService() - { + { cache = AscCache.Memory; SettingsExpiration = TimeSpan.FromMinutes(2); } - + public CachedTenantService(DbTenantService service, TenantServiceCache tenantServiceCache) : this() { Service = service ?? throw new ArgumentNullException("service"); - TenantServiceCache = tenantServiceCache; + TenantServiceCache = tenantServiceCache; CacheNotifyItem = tenantServiceCache.CacheNotifyItem; CacheNotifySettings = tenantServiceCache.CacheNotifySettings; } @@ -302,26 +302,26 @@ namespace ASC.Core.Caching var cacheKey = string.Format("settings/{0}/{1}", tenant, key); CacheNotifySettings.Publish(new TenantSetting { Key = cacheKey }, CacheNotifyAction.Remove); } - } - - public static class TenantConfigExtension - { - public static IServiceCollection AddTenantService(this IServiceCollection services) - { - services.TryAddSingleton(typeof(ICacheNotify<>), typeof(KafkaCache<>)); - services.TryAddSingleton(); - services.TryAddSingleton(); - services.TryAddSingleton(); - - services.TryAddScoped(); - services.TryAddScoped(); + } + + public static class TenantConfigExtension + { + public static DIHelper AddTenantService(this DIHelper services) + { + services.TryAddSingleton(typeof(ICacheNotify<>), typeof(KafkaCache<>)); + services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddScoped(); + services.TryAddScoped(); + services.TryAddScoped, ConfigureDbTenantService>(); services.TryAddScoped, ConfigureCachedTenantService>(); - return services - .AddCoreBaseSettingsService() - .AddTenantDbContextService(); - } + return services + .AddCoreBaseSettingsService() + .AddTenantDbContextService(); + } } } diff --git a/common/ASC.Core.Common/Caching/CachedUserService.cs b/common/ASC.Core.Common/Caching/CachedUserService.cs index c9014c6d31..f55d236c38 100644 --- a/common/ASC.Core.Common/Caching/CachedUserService.cs +++ b/common/ASC.Core.Common/Caching/CachedUserService.cs @@ -27,19 +27,19 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading; +using System.Threading; +using ASC.Common; using ASC.Common.Caching; -using ASC.Common.Logging; -using ASC.Core.Common.EF; +using ASC.Common.Logging; +using ASC.Core.Common.EF; using ASC.Core.Data; using ASC.Core.Tenants; -using ASC.Core.Users; +using ASC.Core.Users; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; - + namespace ASC.Core.Caching { public class UserServiceCache @@ -135,14 +135,14 @@ namespace ASC.Core.Caching { return tenant.ToString() + USERS + userId; } - } - + } + class ConfigureCachedUserService : IConfigureNamedOptions - { + { public IOptionsSnapshot Service { get; } public UserServiceCache UserServiceCache { get; } public CoreBaseSettings CoreBaseSettings { get; } - + public ConfigureCachedUserService( IOptionsSnapshot service, UserServiceCache userServiceCache, @@ -198,22 +198,22 @@ namespace ASC.Core.Caching PhotoExpiration = TimeSpan.FromMinutes(10); } - public CachedUserService( - EFUserService service, - CoreBaseSettings coreBaseSettings, - UserServiceCache userServiceCache + public CachedUserService( + EFUserService service, + CoreBaseSettings coreBaseSettings, + UserServiceCache userServiceCache ) : this() - { + { Service = service ?? throw new ArgumentNullException("service"); - CoreBaseSettings = coreBaseSettings; - UserServiceCache = userServiceCache; + CoreBaseSettings = coreBaseSettings; + UserServiceCache = userServiceCache; Cache = userServiceCache.Cache; - CacheUserInfoItem = userServiceCache.CacheUserInfoItem; - CacheUserPhotoItem = userServiceCache.CacheUserPhotoItem; - CacheGroupCacheItem = userServiceCache.CacheGroupCacheItem; - CacheUserGroupRefItem = userServiceCache.CacheUserGroupRefItem; + CacheUserInfoItem = userServiceCache.CacheUserInfoItem; + CacheUserPhotoItem = userServiceCache.CacheUserPhotoItem; + CacheGroupCacheItem = userServiceCache.CacheGroupCacheItem; + CacheUserGroupRefItem = userServiceCache.CacheUserGroupRefItem; TrustInterval = userServiceCache.TrustInterval; - } + } public IDictionary GetUsers(int tenant, DateTime from) { @@ -224,19 +224,19 @@ namespace ASC.Core.Caching } } - public IQueryable GetUsers( - int tenant, - bool isAdmin, - EmployeeStatus? employeeStatus, - List> includeGroups, - List excludeGroups, - EmployeeActivationStatus? activationStatus, - string text, - string sortBy, - bool sortOrderAsc, - long limit, - long offset, - out int total, + public IQueryable GetUsers( + int tenant, + bool isAdmin, + EmployeeStatus? employeeStatus, + List> includeGroups, + List excludeGroups, + EmployeeActivationStatus? activationStatus, + string text, + string sortBy, + bool sortOrderAsc, + long limit, + long offset, + out int total, out int count) { return Service.GetUsers(tenant, isAdmin, employeeStatus, includeGroups, excludeGroups, activationStatus, text, sortBy, sortOrderAsc, limit, offset, out total, out count); @@ -515,22 +515,22 @@ namespace ASC.Core.Caching } public static class UserConfigExtension { - public static IServiceCollection AddUserService(this IServiceCollection services) - { - services.TryAddSingleton(typeof(ICacheNotify<>), typeof(KafkaCache<>)); - - services - .AddCoreSettingsService() - .AddLoggerService() - .AddUserDbContextService(); - - services.TryAddScoped(); - services.TryAddScoped(); + public static DIHelper AddUserService(this DIHelper services) + { + services.TryAddSingleton(typeof(ICacheNotify<>), typeof(KafkaCache<>)); + + services + .AddCoreSettingsService() + .AddLoggerService() + .AddUserDbContextService(); + + services.TryAddScoped(); + services.TryAddScoped(); services.TryAddScoped, ConfigureEFUserService>(); services.TryAddScoped, ConfigureCachedUserService>(); - services.TryAddSingleton(); + services.TryAddSingleton(); return services; } } diff --git a/common/ASC.Core.Common/Configuration/Consumer.cs b/common/ASC.Core.Common/Configuration/Consumer.cs index 37f28aa3b9..f689ce400f 100644 --- a/common/ASC.Core.Common/Configuration/Consumer.cs +++ b/common/ASC.Core.Common/Configuration/Consumer.cs @@ -27,17 +27,16 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Linq; +using System.Linq; +using ASC.Common; using ASC.Common.Caching; -using ASC.Core.Tenants; - -using Autofac; - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; - +using ASC.Core.Tenants; + +using Autofac; + +using Microsoft.Extensions.Configuration; + namespace ASC.Core.Common.Configuration { public class Consumer : IDictionary @@ -79,57 +78,57 @@ namespace ASC.Core.Common.Configuration } private readonly bool OnlyDefault; - - public TenantManager TenantManager { get; set; } - public CoreBaseSettings CoreBaseSettings { get; set; } - public CoreSettings CoreSettings { get; set; } - public ConsumerFactory ConsumerFactory { get; } - public IConfiguration Configuration { get; } - public ICacheNotify Cache { get; } - + + public TenantManager TenantManager { get; set; } + public CoreBaseSettings CoreBaseSettings { get; set; } + public CoreSettings CoreSettings { get; set; } + public ConsumerFactory ConsumerFactory { get; } + public IConfiguration Configuration { get; } + public ICacheNotify Cache { get; } + public bool IsSet { get { return Props.Any() && !Props.All(r => string.IsNullOrEmpty(this[r.Key])); } } static Consumer() - { - + { + } public Consumer() - { + { Props = new Dictionary(); Additional = new Dictionary(); - } - - public Consumer( - TenantManager tenantManager, - CoreBaseSettings coreBaseSettings, - CoreSettings coreSettings, - ConsumerFactory consumerFactory, - IConfiguration configuration, + } + + public Consumer( + TenantManager tenantManager, + CoreBaseSettings coreBaseSettings, + CoreSettings coreSettings, + ConsumerFactory consumerFactory, + IConfiguration configuration, ICacheNotify cache) : this() - { - TenantManager = tenantManager; - CoreBaseSettings = coreBaseSettings; - CoreSettings = coreSettings; - ConsumerFactory = consumerFactory; - Configuration = configuration; - Cache = cache; + { + TenantManager = tenantManager; + CoreBaseSettings = coreBaseSettings; + CoreSettings = coreSettings; + ConsumerFactory = consumerFactory; + Configuration = configuration; + Cache = cache; OnlyDefault = configuration["core:default-consumers"] == "true"; Name = ""; Order = int.MaxValue; } - public Consumer( - TenantManager tenantManager, - CoreBaseSettings coreBaseSettings, - CoreSettings coreSettings, - ConsumerFactory consumerFactory, - IConfiguration configuration, - ICacheNotify cache, - string name, int order, Dictionary additional) + public Consumer( + TenantManager tenantManager, + CoreBaseSettings coreBaseSettings, + CoreSettings coreSettings, + ConsumerFactory consumerFactory, + IConfiguration configuration, + ICacheNotify cache, + string name, int order, Dictionary additional) : this(tenantManager, coreBaseSettings, coreSettings, consumerFactory, configuration, cache) { Name = name; @@ -138,14 +137,14 @@ namespace ASC.Core.Common.Configuration Additional = additional; } - public Consumer( - TenantManager tenantManager, - CoreBaseSettings coreBaseSettings, - CoreSettings coreSettings, - ConsumerFactory consumerFactory, - IConfiguration configuration, - ICacheNotify cache, - string name, int order, Dictionary props, Dictionary additional) + public Consumer( + TenantManager tenantManager, + CoreBaseSettings coreBaseSettings, + CoreSettings coreSettings, + ConsumerFactory consumerFactory, + IConfiguration configuration, + ICacheNotify cache, + string name, int order, Dictionary props, Dictionary additional) : this(tenantManager, coreBaseSettings, coreSettings, consumerFactory, configuration, cache) { Name = name; @@ -294,45 +293,45 @@ namespace ASC.Core.Common.Configuration public const string HandlerTypeKey = "handlerType"; public const string CdnKey = "cdn"; - - public DataStoreConsumer() : base() - { - - } - public DataStoreConsumer( - TenantManager tenantManager, - CoreBaseSettings coreBaseSettings, - CoreSettings coreSettings, - ConsumerFactory consumerFactory, - IConfiguration configuration, - ICacheNotify cache) - : base(tenantManager, coreBaseSettings, coreSettings, consumerFactory, configuration, cache) - { - + public DataStoreConsumer() : base() + { + } - public DataStoreConsumer( - TenantManager tenantManager, - CoreBaseSettings coreBaseSettings, - CoreSettings coreSettings, - ConsumerFactory consumerFactory, - IConfiguration configuration, - ICacheNotify cache, - string name, int order, Dictionary additional) + public DataStoreConsumer( + TenantManager tenantManager, + CoreBaseSettings coreBaseSettings, + CoreSettings coreSettings, + ConsumerFactory consumerFactory, + IConfiguration configuration, + ICacheNotify cache) + : base(tenantManager, coreBaseSettings, coreSettings, consumerFactory, configuration, cache) + { + + } + + public DataStoreConsumer( + TenantManager tenantManager, + CoreBaseSettings coreBaseSettings, + CoreSettings coreSettings, + ConsumerFactory consumerFactory, + IConfiguration configuration, + ICacheNotify cache, + string name, int order, Dictionary additional) : base(tenantManager, coreBaseSettings, coreSettings, consumerFactory, configuration, cache, name, order, additional) { Init(additional); } - public DataStoreConsumer( - TenantManager tenantManager, - CoreBaseSettings coreBaseSettings, - CoreSettings coreSettings, - ConsumerFactory consumerFactory, - IConfiguration configuration, - ICacheNotify cache, - string name, int order, Dictionary props, Dictionary additional) + public DataStoreConsumer( + TenantManager tenantManager, + CoreBaseSettings coreBaseSettings, + CoreSettings coreSettings, + ConsumerFactory consumerFactory, + IConfiguration configuration, + ICacheNotify cache, + string name, int order, Dictionary props, Dictionary additional) : base(tenantManager, coreBaseSettings, coreSettings, consumerFactory, configuration, cache, name, order, props, additional) { Init(additional); @@ -380,14 +379,14 @@ namespace ASC.Core.Common.Configuration public class ConsumerFactory { - public ILifetimeScope Builder { get; set; } - + public ILifetimeScope Builder { get; set; } + public ConsumerFactory(IContainer builder) { Builder = builder; - } - - + } + + public ConsumerFactory(ILifetimeScope builder) { Builder = builder; @@ -427,14 +426,14 @@ namespace ASC.Core.Common.Configuration { return Builder.Resolve>(); } - } - - public static class ConsumerFactoryExtension - { - public static IServiceCollection AddConsumerFactoryService(this IServiceCollection services) - { - services.TryAddScoped(); - return services; - } - } + } + + public static class ConsumerFactoryExtension + { + public static DIHelper AddConsumerFactoryService(this DIHelper services) + { + services.TryAddScoped(); + return services; + } + } } diff --git a/common/ASC.Core.Common/Context/Impl/AuthManager.cs b/common/ASC.Core.Common/Context/Impl/AuthManager.cs index 2d8f53f932..e85dbac28a 100644 --- a/common/ASC.Core.Common/Context/Impl/AuthManager.cs +++ b/common/ASC.Core.Common/Context/Impl/AuthManager.cs @@ -25,31 +25,29 @@ using System; -using System.Linq; - +using System.Linq; + +using ASC.Common; using ASC.Common.Security.Authentication; -using ASC.Core.Caching; +using ASC.Core.Caching; using ASC.Core.Security.Authentication; -using ASC.Core.Tenants; -using ASC.Core.Users; - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; - +using ASC.Core.Tenants; +using ASC.Core.Users; + namespace ASC.Core { public class AuthManager { - private readonly IUserService userService; - - public UserManager UserManager { get; } - public UserFormatter UserFormatter { get; } - + private readonly IUserService userService; + + public UserManager UserManager { get; } + public UserFormatter UserFormatter { get; } + public AuthManager(IUserService service, UserManager userManager, UserFormatter userFormatter) { - userService = service; - UserManager = userManager; - UserFormatter = userFormatter; + userService = service; + UserManager = userManager; + UserFormatter = userFormatter; } @@ -71,8 +69,8 @@ namespace ASC.Core public IAccount GetAccountByID(int tenantId, Guid id) { var s = ASC.Core.Configuration.Constants.SystemAccounts.FirstOrDefault(a => a.ID == id); - if (s != null) return s; - + if (s != null) return s; + var u = UserManager.GetUsers(id); return !Constants.LostUser.Equals(u) && u.Status == EmployeeStatus.Active ? (IAccount)ToAccount(tenantId, u) : ASC.Core.Configuration.Constants.Guest; } @@ -82,16 +80,16 @@ namespace ASC.Core { return new UserAccount(u, tenantId, UserFormatter); } - } - public static class AuthManagerExtension - { - public static IServiceCollection AddAuthManager(this IServiceCollection services) - { - services.TryAddScoped(); - return services - .AddUserService() - .AddUserFormatter() - .AddUserManagerService(); - } - } + } + public static class AuthManagerExtension + { + public static DIHelper AddAuthManager(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddUserService() + .AddUserFormatter() + .AddUserManagerService(); + } + } } \ No newline at end of file diff --git a/common/ASC.Core.Common/Context/Impl/AuthorizationManager.cs b/common/ASC.Core.Common/Context/Impl/AuthorizationManager.cs index b879b6e834..f4617c3831 100644 --- a/common/ASC.Core.Common/Context/Impl/AuthorizationManager.cs +++ b/common/ASC.Core.Common/Context/Impl/AuthorizationManager.cs @@ -26,25 +26,25 @@ using System; using System.Collections.Generic; -using System.Linq; +using System.Linq; + +using ASC.Common; using ASC.Common.Security; using ASC.Common.Security.Authorizing; -using ASC.Core.Caching; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; - +using ASC.Core.Caching; + namespace ASC.Core { public class AuthorizationManager { - private readonly IAzService service; - - private TenantManager TenantManager { get; } - + private readonly IAzService service; + + private TenantManager TenantManager { get; } + public AuthorizationManager(IAzService service, TenantManager tenantManager) { - this.service = service; - TenantManager = tenantManager; + this.service = service; + TenantManager = tenantManager; } @@ -127,16 +127,16 @@ namespace ASC.Core store.Get(objId).Where(a => (a.SubjectId == subjectId || subjectId == Guid.Empty) && (a.ActionId == actionId || actionId == Guid.Empty)) : aces.Where(a => (a.SubjectId == subjectId || subjectId == Guid.Empty) && (a.ActionId == actionId || actionId == Guid.Empty) && a.ObjectId == objId); } - } - - public static class AuthorizationManagerConfigExtension - { - public static IServiceCollection AddAuthorizationManagerService(this IServiceCollection services) - { - services.TryAddScoped(); - return services - .AddAzService() - .AddTenantManagerService(); - } + } + + public static class AuthorizationManagerConfigExtension + { + public static DIHelper AddAuthorizationManagerService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddAzService() + .AddTenantManagerService(); + } } } \ No newline at end of file diff --git a/common/ASC.Core.Common/Context/Impl/CoreConfiguration.cs b/common/ASC.Core.Common/Context/Impl/CoreConfiguration.cs index d33b66aef2..508dd2be11 100644 --- a/common/ASC.Core.Common/Context/Impl/CoreConfiguration.cs +++ b/common/ASC.Core.Common/Context/Impl/CoreConfiguration.cs @@ -25,18 +25,17 @@ using System; -using System.Text; +using System.Text; +using ASC.Common; using ASC.Core.Caching; -using ASC.Core.Common.Settings; +using ASC.Core.Common.Settings; using ASC.Core.Configuration; -using ASC.Core.Tenants; - +using ASC.Core.Tenants; + using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; - + using Newtonsoft.Json; namespace ASC.Core @@ -61,10 +60,10 @@ namespace ASC.Core public bool Personal { - get - { - //TODO:if (CustomMode && HttpContext.Current != null && HttpContext.Current.Request.SailfishApp()) return true; - return personal ?? (bool)(personal = Configuration["core.personal"] == "true"); + get + { + //TODO:if (CustomMode && HttpContext.Current != null && HttpContext.Current.Request.SailfishApp()) return true; + return personal ?? (bool)(personal = Configuration["core.personal"] == "true"); } } @@ -72,8 +71,8 @@ namespace ASC.Core { get { return customMode ?? (bool)(customMode = Configuration["core.custom-mode"] == "true"); } } - } - + } + class ConfigureCoreSettings : IConfigureNamedOptions { public IOptionsSnapshot TenantService { get; } @@ -143,7 +142,7 @@ namespace ASC.Core internal IConfiguration Configuration { get; set; } public CoreSettings() - { + { } @@ -232,15 +231,15 @@ namespace ASC.Core return t.AffiliateId; return null; - } - - public string GetCampaign(int tenant) - { - var t = TenantService.GetTenant(tenant); - if (t != null && !string.IsNullOrWhiteSpace(t.Campaign)) - return t.Campaign; - - return null; + } + + public string GetCampaign(int tenant) + { + var t = TenantService.GetTenant(tenant); + if (t != null && !string.IsNullOrWhiteSpace(t.Campaign)) + return t.Campaign; + + return null; } } @@ -371,26 +370,26 @@ namespace ASC.Core public static class CoreSettingsConfigExtension { - public static IServiceCollection AddCoreBaseSettingsService(this IServiceCollection services) + public static DIHelper AddCoreBaseSettingsService(this DIHelper services) { - services.TryAddSingleton(); + services.TryAddSingleton(); return services; } - public static IServiceCollection AddCoreSettingsService(this IServiceCollection services) - { - services.TryAddScoped(); - services.TryAddScoped(); + public static DIHelper AddCoreSettingsService(this DIHelper services) + { + services.TryAddScoped(); + services.TryAddScoped(); services.TryAddScoped, ConfigureCoreSettings>(); - return services - .AddCoreBaseSettingsService() + return services + .AddCoreBaseSettingsService() .AddTenantService(); } - public static IServiceCollection AddCoreConfigurationService(this IServiceCollection services) + public static DIHelper AddCoreConfigurationService(this DIHelper services) { - services.TryAddScoped(); - return services + services.TryAddScoped(); + return services .AddCoreSettingsService(); } } diff --git a/common/ASC.Core.Common/Context/Impl/PaymentManager.cs b/common/ASC.Core.Common/Context/Impl/PaymentManager.cs index 37c924365d..1bc2357407 100644 --- a/common/ASC.Core.Common/Context/Impl/PaymentManager.cs +++ b/common/ASC.Core.Common/Context/Impl/PaymentManager.cs @@ -34,14 +34,13 @@ using System.Text; using System.Threading; using System.Web; +using ASC.Common; using ASC.Core.Billing; -using ASC.Core.Caching; - -using Microsoft.AspNetCore.WebUtilities; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; - +using ASC.Core.Caching; + +using Microsoft.AspNetCore.WebUtilities; +using Microsoft.Extensions.Configuration; + using Newtonsoft.Json; @@ -51,16 +50,16 @@ namespace ASC.Core { private readonly ITariffService tariffService; private readonly string partnerUrl; - private readonly string partnerKey; - - public TenantManager TenantManager { get; } - public IConfiguration Configuration { get; } - + private readonly string partnerKey; + + public TenantManager TenantManager { get; } + public IConfiguration Configuration { get; } + public PaymentManager(TenantManager tenantManager, ITariffService tariffService, IConfiguration configuration) - { + { TenantManager = tenantManager; - this.tariffService = tariffService; - Configuration = configuration; + this.tariffService = tariffService; + Configuration = configuration; partnerUrl = (Configuration["core:payment:partners"] ?? "https://partners.onlyoffice.com/api").TrimEnd('/'); partnerKey = (Configuration["core:machinekey"] ?? "C5C1F4E85A3A43F5B3202C24D97351DF"); } @@ -120,38 +119,38 @@ namespace ASC.Core var now = DateTime.UtcNow; var actionUrl = "/partnerapi/ActivateKey?code=" + HttpUtility.UrlEncode(key) + "&portal=" + HttpUtility.UrlEncode(TenantManager.GetCurrentTenant().TenantAlias); - using var webClient = new WebClient(); - webClient.Headers.Add("Authorization", GetPartnerAuthHeader(actionUrl)); - try - { - webClient.DownloadData(partnerUrl + actionUrl); - } - catch (WebException we) - { - var error = GetException(we); - if (error != null) - { - throw error; - } - throw; - } - tariffService.ClearCache(TenantManager.GetCurrentTenant().TenantId); - - var timeout = DateTime.UtcNow - now - TimeSpan.FromSeconds(5); - if (TimeSpan.Zero < timeout) - { - // clear tenant cache - Thread.Sleep(timeout); - } + using var webClient = new WebClient(); + webClient.Headers.Add("Authorization", GetPartnerAuthHeader(actionUrl)); + try + { + webClient.DownloadData(partnerUrl + actionUrl); + } + catch (WebException we) + { + var error = GetException(we); + if (error != null) + { + throw error; + } + throw; + } + tariffService.ClearCache(TenantManager.GetCurrentTenant().TenantId); + + var timeout = DateTime.UtcNow - now - TimeSpan.FromSeconds(5); + if (TimeSpan.Zero < timeout) + { + // clear tenant cache + Thread.Sleep(timeout); + } TenantManager.GetTenant(TenantManager.GetCurrentTenant().TenantId); } private string GetPartnerAuthHeader(string url) { - using var hasher = new HMACSHA1(Encoding.UTF8.GetBytes(partnerKey)); - var now = DateTime.UtcNow.ToString("yyyyMMddHHmmss", CultureInfo.InvariantCulture); - var data = string.Join("\n", now, "/api/" + url.TrimStart('/')); //data: UTC DateTime (yyyy:MM:dd HH:mm:ss) + \n + url - var hash = WebEncoders.Base64UrlEncode(hasher.ComputeHash(Encoding.UTF8.GetBytes(data))); + using var hasher = new HMACSHA1(Encoding.UTF8.GetBytes(partnerKey)); + var now = DateTime.UtcNow.ToString("yyyyMMddHHmmss", CultureInfo.InvariantCulture); + var data = string.Join("\n", now, "/api/" + url.TrimStart('/')); //data: UTC DateTime (yyyy:MM:dd HH:mm:ss) + \n + url + var hash = WebEncoders.Base64UrlEncode(hasher.ComputeHash(Encoding.UTF8.GetBytes(data))); return string.Format("ASC :{0}:{1}", now, hash); } @@ -160,10 +159,10 @@ namespace ASC.Core var response = (HttpWebResponse)we.Response; if (response.StatusCode == HttpStatusCode.InternalServerError) { - using var stream = response.GetResponseStream(); - using var reader = new StreamReader(stream, Encoding.UTF8); - var result = reader.ReadToEnd(); - var excInfo = JsonConvert.DeserializeObject(result); + using var stream = response.GetResponseStream(); + using var reader = new StreamReader(stream, Encoding.UTF8); + var result = reader.ReadToEnd(); + var excInfo = JsonConvert.DeserializeObject(result); return (Exception)Activator.CreateInstance(Type.GetType(excInfo.exceptionType, true), excInfo.exceptionMessage); } return null; @@ -177,18 +176,18 @@ namespace ASC.Core public string exceptionType = null; public string stackTrace = null; } - } - - public static class PaymentManagerExtension - { - public static IServiceCollection AddPaymentManagerService(this IServiceCollection services) - { - services.TryAddScoped(); - - return services - .AddTenantManagerService() - .AddQuotaService() - .AddTariffService(); - } + } + + public static class PaymentManagerExtension + { + public static DIHelper AddPaymentManagerService(this DIHelper services) + { + services.TryAddScoped(); + + return services + .AddTenantManagerService() + .AddQuotaService() + .AddTariffService(); + } } } diff --git a/common/ASC.Core.Common/Context/Impl/SubscriptionManager.cs b/common/ASC.Core.Common/Context/Impl/SubscriptionManager.cs index 123aef36f9..4651b111e8 100644 --- a/common/ASC.Core.Common/Context/Impl/SubscriptionManager.cs +++ b/common/ASC.Core.Common/Context/Impl/SubscriptionManager.cs @@ -25,11 +25,11 @@ using System; -using System.Linq; -using ASC.Core.Caching; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; +using System.Linq; +using ASC.Common; +using ASC.Core.Caching; + namespace ASC.Core { public class SubscriptionManager @@ -151,8 +151,8 @@ namespace ASC.Core public static class SubscriptionConfigExtension { - public static IServiceCollection AddSubscriptionManagerService(this IServiceCollection services) - { + public static DIHelper AddSubscriptionManagerService(this DIHelper services) + { services.TryAddScoped(); return services .AddSubscriptionService() diff --git a/common/ASC.Core.Common/Context/Impl/TenantManager.cs b/common/ASC.Core.Common/Context/Impl/TenantManager.cs index 4e14b2c576..4e9bf083ba 100644 --- a/common/ASC.Core.Common/Context/Impl/TenantManager.cs +++ b/common/ASC.Core.Common/Context/Impl/TenantManager.cs @@ -31,19 +31,17 @@ using System.Net; using System.Threading; using System.Web; - +using ASC.Common; using ASC.Common.Notify.Engine; using ASC.Core.Billing; -using ASC.Core.Caching; -using ASC.Core.Tenants; +using ASC.Core.Caching; +using ASC.Core.Tenants; using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; - + namespace ASC.Core -{ +{ class ConfigureTenantManager : IConfigureNamedOptions { public IOptionsSnapshot TenantService { get; } @@ -98,14 +96,14 @@ namespace ASC.Core internal IQuotaService QuotaService { get; set; } internal ITariffService TariffService { get; set; } - private static List thisCompAddresses = new List(); - + private static List thisCompAddresses = new List(); + internal HttpContext HttpContext { get; set; } internal CoreBaseSettings CoreBaseSettings { get; set; } internal CoreSettings CoreSettings { get; set; } - - static TenantManager() - { + + static TenantManager() + { thisCompAddresses.Add("localhost"); thisCompAddresses.Add(Dns.GetHostName().ToLowerInvariant()); thisCompAddresses.AddRange(Dns.GetHostAddresses("localhost").Select(a => a.ToString())); @@ -116,27 +114,27 @@ namespace ASC.Core catch { // ignore - } + } } - + public TenantManager() { } - public TenantManager( - ITenantService tenantService, - IQuotaService quotaService, - ITariffService tariffService, - IHttpContextAccessor httpContextAccessor, - CoreBaseSettings coreBaseSettings, + public TenantManager( + ITenantService tenantService, + IQuotaService quotaService, + ITariffService tariffService, + IHttpContextAccessor httpContextAccessor, + CoreBaseSettings coreBaseSettings, CoreSettings coreSettings) { TenantService = tenantService; QuotaService = quotaService; TariffService = tariffService; - CoreBaseSettings = coreBaseSettings; - CoreSettings = coreSettings; + CoreBaseSettings = coreBaseSettings; + CoreSettings = coreSettings; HttpContext = httpContextAccessor?.HttpContext; } @@ -235,8 +233,8 @@ namespace ASC.Core throw new Exception("Could not resolve current tenant :-(."); } return tenant; - } - + } + public Tenant GetCurrentTenant() { return GetCurrentTenant(true); @@ -262,16 +260,16 @@ namespace ASC.Core } public Tenant SetCurrentTenant(int tenantId) - { + { var result = GetTenant(tenantId); - SetCurrentTenant(result); + SetCurrentTenant(result); return result; } public Tenant SetCurrentTenant(string domain) - { + { var result = GetTenant(domain); - SetCurrentTenant(result); + SetCurrentTenant(result); return result; } @@ -336,21 +334,21 @@ namespace ASC.Core { return QuotaService.FindTenantQuotaRows(query).ToList(); } - } - - public static class TenantManagerConfigExtension - { - public static IServiceCollection AddTenantManagerService(this IServiceCollection services) - { - services.TryAddScoped(); + } + + public static class TenantManagerConfigExtension + { + public static DIHelper AddTenantManagerService(this DIHelper services) + { + services.TryAddScoped(); services.TryAddScoped, ConfigureTenantManager>(); - - return services - .AddTenantService() - .AddQuotaService() - .AddTariffService() - .AddCoreBaseSettingsService() - .AddCoreSettingsService(); - } - } + + return services + .AddTenantService() + .AddQuotaService() + .AddTariffService() + .AddCoreBaseSettingsService() + .AddCoreSettingsService(); + } + } } diff --git a/common/ASC.Core.Common/Context/Impl/UserManager.cs b/common/ASC.Core.Common/Context/Impl/UserManager.cs index bc2ced442d..30c46985c9 100644 --- a/common/ASC.Core.Common/Context/Impl/UserManager.cs +++ b/common/ASC.Core.Common/Context/Impl/UserManager.cs @@ -26,17 +26,16 @@ using System; using System.Collections.Generic; -using System.Linq; - +using System.Linq; + using ASC.Collections; +using ASC.Common; using ASC.Core.Caching; using ASC.Core.Tenants; -using ASC.Core.Users; - +using ASC.Core.Users; + using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; - + namespace ASC.Core { public class UserManagerConstants @@ -68,6 +67,11 @@ namespace ASC.Core private Tenant tenant; private Tenant Tenant { get { return tenant ?? (tenant = TenantManager.GetCurrentTenant()); } } + public UserManager() + { + + } + public UserManager( IUserService service, IHttpContextAccessor httpContextAccessor, @@ -120,18 +124,18 @@ namespace ASC.Core return users.ToArray(); } - public IQueryable GetUsers( - bool isAdmin, - EmployeeStatus? employeeStatus, - List> includeGroups, - List excludeGroups, - EmployeeActivationStatus? activationStatus, - string text, - string sortBy, - bool sortOrderAsc, - long limit, - long offset, - out int total, + public IQueryable GetUsers( + bool isAdmin, + EmployeeStatus? employeeStatus, + List> includeGroups, + List excludeGroups, + EmployeeActivationStatus? activationStatus, + string text, + string sortBy, + bool sortOrderAsc, + long limit, + long offset, + out int total, out int count) { return UserService.GetUsers(Tenant.TenantId, isAdmin, employeeStatus, includeGroups, excludeGroups, activationStatus, text, sortBy, sortOrderAsc, limit, offset, out total, out count); @@ -266,12 +270,12 @@ namespace ASC.Core { throw new TenantQuotaException(string.Format("Exceeds the maximum active users ({0})", q.ActiveUsers)); } - } - - if (u.Status == EmployeeStatus.Terminated && u.ID == TenantManager.GetCurrentTenant().OwnerId) - { - throw new InvalidOperationException("Can not disable tenant owner."); - } + } + + if (u.Status == EmployeeStatus.Terminated && u.ID == TenantManager.GetCurrentTenant().OwnerId) + { + throw new InvalidOperationException("Can not disable tenant owner."); + } var newUser = UserService.SaveUser(Tenant.TenantId, u); @@ -634,10 +638,10 @@ namespace ASC.Core public static class UserManagerConfigExtension { - public static IServiceCollection AddUserManagerService(this IServiceCollection services) - { + public static DIHelper AddUserManagerService(this DIHelper services) + { services.TryAddSingleton(); - services.TryAddScoped(); + services.TryAddScoped(); return services .AddUserService() diff --git a/common/ASC.Core.Common/Context/SecurityContext.cs b/common/ASC.Core.Common/Context/SecurityContext.cs index c97b70337c..55f6108890 100644 --- a/common/ASC.Core.Common/Context/SecurityContext.cs +++ b/common/ASC.Core.Common/Context/SecurityContext.cs @@ -33,6 +33,7 @@ using System.Security.Claims; using System.Threading; using System.Web; +using ASC.Common; using ASC.Common.Logging; using ASC.Common.Security; using ASC.Common.Security.Authentication; @@ -46,8 +47,6 @@ using ASC.Core.Users; using ASC.Security.Cryptography; using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; namespace ASC.Core @@ -265,7 +264,7 @@ namespace ASC.Core public void Logout() { - AuthContext.Principal = null; + AuthContext.Logout(); } public void SetUserPassword(Guid userID, string password) @@ -320,6 +319,11 @@ namespace ASC.Core { private IHttpContextAccessor HttpContextAccessor { get; } + public AuthContext() + { + + } + public AuthContext(IHttpContextAccessor httpContextAccessor) { HttpContextAccessor = httpContextAccessor; @@ -335,6 +339,11 @@ namespace ASC.Core get { return CurrentAccount.IsAuthenticated; } } + public void Logout() + { + Principal = null; + } + internal ClaimsPrincipal Principal { get => Thread.CurrentPrincipal as ClaimsPrincipal ?? HttpContextAccessor?.HttpContext?.User; @@ -348,7 +357,7 @@ namespace ASC.Core public static class AuthContextConfigExtension { - public static IServiceCollection AddSecurityContextService(this IServiceCollection services) + public static DIHelper AddSecurityContextService(this DIHelper services) { services.TryAddScoped(); @@ -361,13 +370,13 @@ namespace ASC.Core .AddUserManagerService() .AddTenantManagerService(); } - public static IServiceCollection AddAuthContextService(this IServiceCollection services) + public static DIHelper AddAuthContextService(this DIHelper services) { services.TryAddScoped(); return services; } - public static IServiceCollection AddPermissionContextService(this IServiceCollection services) + public static DIHelper AddPermissionContextService(this DIHelper services) { services.TryAddScoped(); diff --git a/common/ASC.Core.Common/Core/DBResourceManager.cs b/common/ASC.Core.Common/Core/DBResourceManager.cs index 5a5233a1ad..9b32adeded 100644 --- a/common/ASC.Core.Common/Core/DBResourceManager.cs +++ b/common/ASC.Core.Common/Core/DBResourceManager.cs @@ -34,17 +34,16 @@ using System.Reflection; using System.Resources; using System.Runtime.Caching; using System.Text.RegularExpressions; -using System.Web; +using System.Web; +using ASC.Common; using ASC.Common.Logging; -using ASC.Core; -using ASC.Core.Common.EF; -using ASC.Core.Common.EF.Context; - +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.Core.Common.EF.Context; + using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; namespace TMResourceData @@ -57,19 +56,19 @@ namespace TMResourceData public DBResourceManager(string filename, Assembly assembly) : base(filename, assembly) { - } + } - public DBResourceManager( - IConfiguration configuration, - IOptionsMonitor option, - DbContextManager dbContext, - string filename, + public DBResourceManager( + IConfiguration configuration, + IOptionsMonitor option, + DbContextManager dbContext, + string filename, Assembly assembly) : base(filename, assembly) { Configuration = configuration; - Option = option; - DbContext = dbContext; + Option = option; + DbContext = dbContext; } @@ -136,9 +135,9 @@ namespace TMResourceData } public IConfiguration Configuration { get; } - public IOptionsMonitor Option { get; } - public DbContextManager DbContext { get; } - + public IOptionsMonitor Option { get; } + public DbContextManager DbContext { get; } + protected override ResourceSet InternalGetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents) { resourceSets.TryGetValue(culture.Name, out var set); @@ -165,15 +164,15 @@ namespace TMResourceData private readonly ILog log; public IConfiguration Configuration { get; } - public IOptionsMonitor Option { get; } - public DbContextManager DbContext { get; } - - public DBResourceSet( - IConfiguration configuration, - IOptionsMonitor option, - DbContextManager dbContext, - ResourceSet invariant, - CultureInfo culture, + public IOptionsMonitor Option { get; } + public DbContextManager DbContext { get; } + + public DBResourceSet( + IConfiguration configuration, + IOptionsMonitor option, + DbContextManager dbContext, + ResourceSet invariant, + CultureInfo culture, string filename) { if (culture == null) @@ -186,8 +185,8 @@ namespace TMResourceData } Configuration = configuration; - Option = option; - DbContext = dbContext; + Option = option; + DbContext = dbContext; log = option.CurrentValue; try @@ -271,12 +270,12 @@ namespace TMResourceData } private Dictionary LoadResourceSet(string filename, string culture) - { + { using var context = DbContext.Get("tmresource"); - var q = context.ResData - .Where(r => r.CultureTitle == culture) - .Join(context.ResFiles, r => r.FileId, a => a.Id, (d, f) => new { data = d, files = f }) - .Where(r => r.files.ResName == filename); + var q = context.ResData + .Where(r => r.CultureTitle == culture) + .Join(context.ResFiles, r => r.FileId, a => a.Id, (d, f) => new { data = d, files = f }) + .Where(r => r.files.ResName == filename); return q .ToDictionary(r => r.data.Title, r => r.data.TextValue, StringComparer.InvariantCultureIgnoreCase); @@ -375,7 +374,7 @@ namespace TMResourceData public static class WhiteLabelHelperExtension { - public static IServiceCollection AddWhiteLabelHelperService(this IServiceCollection services) + public static DIHelper AddWhiteLabelHelperService(this DIHelper services) { services.TryAddSingleton(); return services; diff --git a/common/ASC.Core.Common/Data/DbSettingsManager.cs b/common/ASC.Core.Common/Data/DbSettingsManager.cs index d2f5a04273..96d1d9c86f 100644 --- a/common/ASC.Core.Common/Data/DbSettingsManager.cs +++ b/common/ASC.Core.Common/Data/DbSettingsManager.cs @@ -31,38 +31,37 @@ using System.Linq; using System.Runtime.Serialization.Json; using System.Text; +using ASC.Common; using ASC.Common.Caching; using ASC.Common.Logging; -using ASC.Core.Common.EF; -using ASC.Core.Common.EF.Context; -using ASC.Core.Common.EF.Model; -using ASC.Core.Common.Settings; -using ASC.Core.Tenants; - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Options; +using ASC.Core.Common.EF; +using ASC.Core.Common.EF.Context; +using ASC.Core.Common.EF.Model; +using ASC.Core.Common.Settings; +using ASC.Core.Tenants; +using Microsoft.Extensions.Options; + namespace ASC.Core.Data -{ - public class DbSettingsManagerCache - { +{ + public class DbSettingsManagerCache + { public ICache Cache { get; } - public ICacheNotify Notify { get; } - + public ICacheNotify Notify { get; } + public DbSettingsManagerCache(ICacheNotify notify) - { - Cache = AscCache.Memory; + { + Cache = AscCache.Memory; Notify = notify; Notify.Subscribe((i) => Cache.Remove(i.Key), CacheNotifyAction.Remove); - } - - public void Remove(string key) - { - Notify.Publish(new SettingsCacheItem { Key = key }, CacheNotifyAction.Remove); - } - } - + } + + public void Remove(string key) + { + Notify.Publish(new SettingsCacheItem { Key = key }, CacheNotifyAction.Remove); + } + } + class ConfigureDbSettingsManager : IConfigureNamedOptions { private IServiceProvider ServiceProvider { get; } @@ -112,8 +111,8 @@ namespace ASC.Core.Data public class DbSettingsManager { private readonly TimeSpan expirationTimeout = TimeSpan.FromMinutes(5); - private readonly IDictionary jsonSerializers = new Dictionary(); - + private readonly IDictionary jsonSerializers = new Dictionary(); + internal ILog Log { get; set; } internal ICache Cache { get; set; } internal IServiceProvider ServiceProvider { get; set; } @@ -121,29 +120,29 @@ namespace ASC.Core.Data internal AuthContext AuthContext { get; set; } internal TenantManager TenantManager { get; set; } internal WebstudioDbContext WebstudioDbContext { get; set; } - + public DbSettingsManager() { } - public DbSettingsManager( - IServiceProvider serviceProvider, - DbSettingsManagerCache dbSettingsManagerCache, - IOptionsMonitor option, - AuthContext authContext, - TenantManager tenantManager, + public DbSettingsManager( + IServiceProvider serviceProvider, + DbSettingsManagerCache dbSettingsManagerCache, + IOptionsMonitor option, + AuthContext authContext, + TenantManager tenantManager, DbContextManager dbContextManager) - { - ServiceProvider = serviceProvider; - DbSettingsManagerCache = dbSettingsManagerCache; - AuthContext = authContext; - TenantManager = tenantManager; - Cache = dbSettingsManagerCache.Cache; + { + ServiceProvider = serviceProvider; + DbSettingsManagerCache = dbSettingsManagerCache; + AuthContext = authContext; + TenantManager = tenantManager; + Cache = dbSettingsManagerCache.Cache; Log = option.CurrentValue; - WebstudioDbContext = dbContextManager.Value; - } - + WebstudioDbContext = dbContextManager.Value; + } + private int TenantID { get { return TenantManager.GetCurrentTenant().TenantId; } @@ -152,7 +151,7 @@ namespace ASC.Core.Data private Guid CurrentUserID { get { return AuthContext.CurrentAccount.ID; } - } + } public bool SaveSettings(T settings, int tenantId) where T : ISettings { @@ -167,7 +166,7 @@ namespace ASC.Core.Data public void ClearCache(int tenantId) where T : class, ISettings { var settings = LoadSettings(tenantId); - var key = settings.ID.ToString() + tenantId + Guid.Empty; + var key = settings.ID.ToString() + tenantId + Guid.Empty; DbSettingsManagerCache.Remove(key); } @@ -179,43 +178,43 @@ namespace ASC.Core.Data { var key = settings.ID.ToString() + tenantId + userId; var data = Serialize(settings); - - var def = (T)settings.GetDefault(ServiceProvider); + + var def = (T)settings.GetDefault(ServiceProvider); var defaultData = Serialize(def); - - var tr = WebstudioDbContext.Database.BeginTransaction(); + + var tr = WebstudioDbContext.Database.BeginTransaction(); if (data.SequenceEqual(defaultData)) - { - // remove default settings - var s = WebstudioDbContext.WebstudioSettings - .Where(r => r.Id == settings.ID) - .Where(r => r.TenantId == tenantId) - .Where(r => r.UserId == userId) - .FirstOrDefault(); - - if (s != null) - { - WebstudioDbContext.WebstudioSettings.Remove(s); + { + // remove default settings + var s = WebstudioDbContext.WebstudioSettings + .Where(r => r.Id == settings.ID) + .Where(r => r.TenantId == tenantId) + .Where(r => r.UserId == userId) + .FirstOrDefault(); + + if (s != null) + { + WebstudioDbContext.WebstudioSettings.Remove(s); } } else - { - var s = new DbWebstudioSettings - { - Id = settings.ID, - UserId = userId, - TenantId = tenantId, - Data = data - }; - + { + var s = new DbWebstudioSettings + { + Id = settings.ID, + UserId = userId, + TenantId = tenantId, + Data = data + }; + WebstudioDbContext.AddOrUpdate(r => r.WebstudioSettings, s); - } - - WebstudioDbContext.SaveChanges(); - tr.Commit(); - + } + + WebstudioDbContext.SaveChanges(); + tr.Commit(); + DbSettingsManagerCache.Remove(key); Cache.Insert(key, settings, expirationTimeout); @@ -232,19 +231,19 @@ namespace ASC.Core.Data { var settingsInstance = Activator.CreateInstance(); var key = settingsInstance.ID.ToString() + tenantId + userId; - var def = (T)settingsInstance.GetDefault(ServiceProvider); + var def = (T)settingsInstance.GetDefault(ServiceProvider); try { var settings = Cache.Get(key); - if (settings != null) return settings; - - var result = WebstudioDbContext.WebstudioSettings - .Where(r => r.Id == settingsInstance.ID) - .Where(r => r.TenantId == tenantId) - .Where(r => r.UserId == userId) - .Select(r => r.Data) - .FirstOrDefault(); + if (settings != null) return settings; + + var result = WebstudioDbContext.WebstudioSettings + .Where(r => r.Id == settingsInstance.ID) + .Where(r => r.TenantId == tenantId) + .Where(r => r.UserId == userId) + .Select(r => r.Data) + .FirstOrDefault(); if (result != null) { @@ -253,7 +252,7 @@ namespace ASC.Core.Data else { settings = def; - } + } Cache.Insert(key, settings, expirationTimeout); return settings; @@ -264,7 +263,7 @@ namespace ASC.Core.Data } return def; } - + public T Load() where T : class, ISettings { return LoadSettings(TenantID); @@ -318,19 +317,19 @@ namespace ASC.Core.Data public void ClearCache() where T : class, ISettings { ClearCache(TenantID); - } + } private T Deserialize(string data) { - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(data)); - var settings = GetJsonSerializer(typeof(T)).ReadObject(stream); + using var stream = new MemoryStream(Encoding.UTF8.GetBytes(data)); + var settings = GetJsonSerializer(typeof(T)).ReadObject(stream); return (T)settings; } private string Serialize(ISettings settings) { - using var stream = new MemoryStream(); - GetJsonSerializer(settings.GetType()).WriteObject(stream, settings); + using var stream = new MemoryStream(); + GetJsonSerializer(settings.GetType()).WriteObject(stream, settings); return Encoding.UTF8.GetString(stream.ToArray()); } @@ -345,14 +344,14 @@ namespace ASC.Core.Data return jsonSerializers[type]; } } - } - + } + public static class DbSettingsManagerExtension { - public static IServiceCollection AddDbSettingsManagerService(this IServiceCollection services) - { - services.TryAddSingleton(); - services.TryAddScoped(); + public static DIHelper AddDbSettingsManagerService(this DIHelper services) + { + services.TryAddSingleton(); + services.TryAddScoped(); services.TryAddScoped, ConfigureDbSettingsManager>(); return services.AddWebstudioDbContextService(); diff --git a/common/ASC.Core.Common/EF/Context/AccountLinkContext.cs b/common/ASC.Core.Common/EF/Context/AccountLinkContext.cs index 3844bb0165..cfaae1a40c 100644 --- a/common/ASC.Core.Common/EF/Context/AccountLinkContext.cs +++ b/common/ASC.Core.Common/EF/Context/AccountLinkContext.cs @@ -1,7 +1,7 @@ -using ASC.Core.Common.EF.Model; +using ASC.Common; +using ASC.Core.Common.EF.Model; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; namespace ASC.Core.Common.EF.Context { @@ -17,7 +17,7 @@ namespace ASC.Core.Common.EF.Context public static class AccountLinkContextExtension { - public static IServiceCollection AddAccountLinkContextService(this IServiceCollection services) + public static DIHelper AddAccountLinkContextService(this DIHelper services) { return services.AddDbContextManagerService(); } diff --git a/common/ASC.Core.Common/EF/Context/CoreDbContext.cs b/common/ASC.Core.Common/EF/Context/CoreDbContext.cs index 63e0911c47..9419c18a9e 100644 --- a/common/ASC.Core.Common/EF/Context/CoreDbContext.cs +++ b/common/ASC.Core.Common/EF/Context/CoreDbContext.cs @@ -1,6 +1,7 @@  +using ASC.Common; + using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; namespace ASC.Core.Common.EF { @@ -30,7 +31,7 @@ namespace ASC.Core.Common.EF public static class CoreDbExtension { - public static IServiceCollection AddCoreDbContextService(this IServiceCollection services) + public static DIHelper AddCoreDbContextService(this DIHelper services) { return services.AddDbContextManagerService(); } diff --git a/common/ASC.Core.Common/EF/Context/DbContext.cs b/common/ASC.Core.Common/EF/Context/DbContext.cs index 505f819b14..1811a2510f 100644 --- a/common/ASC.Core.Common/EF/Context/DbContext.cs +++ b/common/ASC.Core.Common/EF/Context/DbContext.cs @@ -1,7 +1,7 @@ -using ASC.Core.Common.EF.Model; +using ASC.Common; +using ASC.Core.Common.EF.Model; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; namespace ASC.Core.Common.EF.Context { @@ -27,7 +27,7 @@ namespace ASC.Core.Common.EF.Context public static class DbContextExtension { - public static IServiceCollection AddDbContextService(this IServiceCollection services) + public static DIHelper AddDbContextService(this DIHelper services) { return services.AddDbContextManagerService(); } diff --git a/common/ASC.Core.Common/EF/Context/DbContextManager.cs b/common/ASC.Core.Common/EF/Context/DbContextManager.cs index 498a370fc7..73257798dc 100644 --- a/common/ASC.Core.Common/EF/Context/DbContextManager.cs +++ b/common/ASC.Core.Common/EF/Context/DbContextManager.cs @@ -1,7 +1,8 @@ using System; using System.Collections.Generic; -using Microsoft.Extensions.DependencyInjection; +using ASC.Common; + using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; @@ -70,7 +71,7 @@ namespace ASC.Core.Common.EF public static class DbContextManagerExtension { - public static IServiceCollection AddDbContextManagerService(this IServiceCollection services) where T : BaseDbContext, new() + public static DIHelper AddDbContextManagerService(this DIHelper services) where T : BaseDbContext, new() { services.TryAddScoped>(); services.TryAddScoped>(); diff --git a/common/ASC.Core.Common/EF/Context/FeedDbContext.cs b/common/ASC.Core.Common/EF/Context/FeedDbContext.cs index 13c9945463..53532f93b7 100644 --- a/common/ASC.Core.Common/EF/Context/FeedDbContext.cs +++ b/common/ASC.Core.Common/EF/Context/FeedDbContext.cs @@ -1,7 +1,7 @@ -using ASC.Core.Common.EF.Model; +using ASC.Common; +using ASC.Core.Common.EF.Model; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; namespace ASC.Core.Common.EF.Context { @@ -22,7 +22,7 @@ namespace ASC.Core.Common.EF.Context public static class FeedDbExtension { - public static IServiceCollection AddFeedDbService(this IServiceCollection services) + public static DIHelper AddFeedDbService(this DIHelper services) { return services.AddDbContextManagerService(); } diff --git a/common/ASC.Core.Common/EF/Context/FilesDbContext.cs b/common/ASC.Core.Common/EF/Context/FilesDbContext.cs index 0e43a43c91..dd0f8628c9 100644 --- a/common/ASC.Core.Common/EF/Context/FilesDbContext.cs +++ b/common/ASC.Core.Common/EF/Context/FilesDbContext.cs @@ -1,7 +1,7 @@ -using ASC.Core.Common.EF.Model; +using ASC.Common; +using ASC.Core.Common.EF.Model; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; namespace ASC.Core.Common.EF.Context { @@ -17,7 +17,7 @@ namespace ASC.Core.Common.EF.Context public static class FilesDbExtension { - public static IServiceCollection AddFilesDbContextService(this IServiceCollection services) + public static DIHelper AddFilesDbContextService(this DIHelper services) { return services.AddDbContextManagerService(); } diff --git a/common/ASC.Core.Common/EF/Context/MailDbContext.cs b/common/ASC.Core.Common/EF/Context/MailDbContext.cs index 4be87ab44e..81470de43f 100644 --- a/common/ASC.Core.Common/EF/Context/MailDbContext.cs +++ b/common/ASC.Core.Common/EF/Context/MailDbContext.cs @@ -1,7 +1,7 @@ -using ASC.Core.Common.EF.Model.Mail; +using ASC.Common; +using ASC.Core.Common.EF.Model.Mail; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; namespace ASC.Core.Common.EF.Context { @@ -22,7 +22,7 @@ namespace ASC.Core.Common.EF.Context } public static class MailDbExtension { - public static IServiceCollection AddMailDbContextService(this IServiceCollection services) + public static DIHelper AddMailDbContextService(this DIHelper services) { return services.AddDbContextManagerService(); } diff --git a/common/ASC.Core.Common/EF/Context/MessagesContext.cs b/common/ASC.Core.Common/EF/Context/MessagesContext.cs index 80fde7cd0a..95322b2486 100644 --- a/common/ASC.Core.Common/EF/Context/MessagesContext.cs +++ b/common/ASC.Core.Common/EF/Context/MessagesContext.cs @@ -1,8 +1,8 @@  +using ASC.Common; using ASC.Core.Common.EF.Model; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; namespace ASC.Core.Common.EF.Context { @@ -22,7 +22,7 @@ namespace ASC.Core.Common.EF.Context public static class MessagesContextExtension { - public static IServiceCollection AddMessagesContextService(this IServiceCollection services) + public static DIHelper AddMessagesContextService(this DIHelper services) { return services.AddDbContextManagerService(); } diff --git a/common/ASC.Core.Common/EF/Context/NotifyDbContext.cs b/common/ASC.Core.Common/EF/Context/NotifyDbContext.cs index 7b4b295954..50dea90b98 100644 --- a/common/ASC.Core.Common/EF/Context/NotifyDbContext.cs +++ b/common/ASC.Core.Common/EF/Context/NotifyDbContext.cs @@ -1,7 +1,7 @@ -using ASC.Core.Common.EF.Model; +using ASC.Common; +using ASC.Core.Common.EF.Model; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; namespace ASC.Core.Common.EF.Context { @@ -13,7 +13,7 @@ namespace ASC.Core.Common.EF.Context public static class NotifyDbExtension { - public static IServiceCollection AddNotifyDbContext(this IServiceCollection services) + public static DIHelper AddNotifyDbContext(this DIHelper services) { return services.AddDbContextManagerService(); } diff --git a/common/ASC.Core.Common/EF/Context/ResourceDbContext.cs b/common/ASC.Core.Common/EF/Context/ResourceDbContext.cs index 05efb18159..8f45faafd3 100644 --- a/common/ASC.Core.Common/EF/Context/ResourceDbContext.cs +++ b/common/ASC.Core.Common/EF/Context/ResourceDbContext.cs @@ -1,7 +1,7 @@ -using ASC.Core.Common.EF.Model.Resource; +using ASC.Common; +using ASC.Core.Common.EF.Model.Resource; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; namespace ASC.Core.Common.EF.Context { @@ -25,7 +25,7 @@ namespace ASC.Core.Common.EF.Context public static class ResourceDbExtension { - public static IServiceCollection AddResourceDbService(this IServiceCollection services) + public static DIHelper AddResourceDbService(this DIHelper services) { return services.AddDbContextManagerService(); } diff --git a/common/ASC.Core.Common/EF/Context/TenantDbContext.cs b/common/ASC.Core.Common/EF/Context/TenantDbContext.cs index 10318ee103..8de2321661 100644 --- a/common/ASC.Core.Common/EF/Context/TenantDbContext.cs +++ b/common/ASC.Core.Common/EF/Context/TenantDbContext.cs @@ -1,7 +1,7 @@ -using ASC.Core.Common.EF.Model; +using ASC.Common; +using ASC.Core.Common.EF.Model; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; namespace ASC.Core.Common.EF.Context { @@ -38,7 +38,7 @@ namespace ASC.Core.Common.EF.Context public static class TenantDbExtension { - public static IServiceCollection AddTenantDbContextService(this IServiceCollection services) + public static DIHelper AddTenantDbContextService(this DIHelper services) { return services.AddDbContextManagerService(); } diff --git a/common/ASC.Core.Common/EF/Context/UserDbContext.cs b/common/ASC.Core.Common/EF/Context/UserDbContext.cs index 8422b00f7d..d9a281b90d 100644 --- a/common/ASC.Core.Common/EF/Context/UserDbContext.cs +++ b/common/ASC.Core.Common/EF/Context/UserDbContext.cs @@ -1,5 +1,6 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; +using ASC.Common; + +using Microsoft.EntityFrameworkCore; namespace ASC.Core.Common.EF { @@ -32,7 +33,7 @@ namespace ASC.Core.Common.EF public static class UserDbExtension { - public static IServiceCollection AddUserDbContextService(this IServiceCollection services) + public static DIHelper AddUserDbContextService(this DIHelper services) { return services.AddDbContextManagerService(); } diff --git a/common/ASC.Core.Common/EF/Context/VoipDbContext.cs b/common/ASC.Core.Common/EF/Context/VoipDbContext.cs index 72210e85d0..a070b80b70 100644 --- a/common/ASC.Core.Common/EF/Context/VoipDbContext.cs +++ b/common/ASC.Core.Common/EF/Context/VoipDbContext.cs @@ -1,7 +1,7 @@ -using ASC.Core.Common.EF.Model; +using ASC.Common; +using ASC.Core.Common.EF.Model; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; namespace ASC.Core.Common.EF.Context { @@ -14,7 +14,7 @@ namespace ASC.Core.Common.EF.Context public static class VoipDbExtension { - public static IServiceCollection AddVoipDbContextService(this IServiceCollection services) + public static DIHelper AddVoipDbContextService(this DIHelper services) { return services.AddDbContextManagerService(); } diff --git a/common/ASC.Core.Common/EF/Context/WebstudioDbContext.cs b/common/ASC.Core.Common/EF/Context/WebstudioDbContext.cs index b2bcf005f7..4a0d80838c 100644 --- a/common/ASC.Core.Common/EF/Context/WebstudioDbContext.cs +++ b/common/ASC.Core.Common/EF/Context/WebstudioDbContext.cs @@ -1,7 +1,7 @@ -using ASC.Core.Common.EF.Model; +using ASC.Common; +using ASC.Core.Common.EF.Model; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; namespace ASC.Core.Common.EF.Context { @@ -9,6 +9,7 @@ namespace ASC.Core.Common.EF.Context { public DbSet WebstudioSettings { get; set; } public DbSet WebstudioUserVisit { get; set; } + public DbSet WebstudioIndex { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { @@ -20,7 +21,7 @@ namespace ASC.Core.Common.EF.Context public static class WebstudioDbExtension { - public static IServiceCollection AddWebstudioDbContextService(this IServiceCollection services) + public static DIHelper AddWebstudioDbContextService(this DIHelper services) { return services.AddDbContextManagerService(); } diff --git a/common/ASC.Core.Common/EF/Model/DbWebstudioIndex.cs b/common/ASC.Core.Common/EF/Model/DbWebstudioIndex.cs new file mode 100644 index 0000000000..57bace117e --- /dev/null +++ b/common/ASC.Core.Common/EF/Model/DbWebstudioIndex.cs @@ -0,0 +1,17 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ASC.Core.Common.EF.Model +{ + [Table("webstudio_index")] + public class DbWebstudioIndex + { + [Key] + [Column("index_name")] + public string IndexName { get; set; } + + [Column("last_modified")] + public DateTime LastModified { get; set; } + } +} diff --git a/common/ASC.Core.Common/HostedSolution.cs b/common/ASC.Core.Common/HostedSolution.cs index ab0e3cf533..79235428c0 100644 --- a/common/ASC.Core.Common/HostedSolution.cs +++ b/common/ASC.Core.Common/HostedSolution.cs @@ -29,6 +29,7 @@ using System.Collections.Generic; using System.Linq; using System.Security; +using ASC.Common; using ASC.Core.Billing; using ASC.Core.Caching; using ASC.Core.Data; @@ -37,7 +38,6 @@ using ASC.Core.Tenants; using ASC.Core.Users; using ASC.Security.Cryptography; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; namespace ASC.Core @@ -295,9 +295,9 @@ namespace ASC.Core public static class HostedSolutionExtension { - public static IServiceCollection AddHostedSolutionService(this IServiceCollection services) + public static DIHelper AddHostedSolutionService(this DIHelper services) { - services.AddScoped, ConfigureHostedSolution>(); + services.TryAddScoped, ConfigureHostedSolution>(); return services .AddUserFormatter() diff --git a/common/ASC.Core.Common/Notify/Jabber/JabberServiceClient.cs b/common/ASC.Core.Common/Notify/Jabber/JabberServiceClient.cs index 57f415ebf0..36722ce0b3 100644 --- a/common/ASC.Core.Common/Notify/Jabber/JabberServiceClient.cs +++ b/common/ASC.Core.Common/Notify/Jabber/JabberServiceClient.cs @@ -26,29 +26,29 @@ using System; using System.Collections.Generic; -using System.ServiceModel; -using ASC.Core.Common.Notify.Jabber; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; +using System.ServiceModel; +using ASC.Common; +using ASC.Core.Common.Notify.Jabber; + namespace ASC.Core.Notify.Jabber { public class JabberServiceClient { private static readonly TimeSpan Timeout = TimeSpan.FromMinutes(2); - private static DateTime lastErrorTime = default; - - public UserManager UserManager { get; } - public AuthContext AuthContext { get; } - public TenantManager TenantManager { get; } - - public JabberServiceClient(UserManager userManager, AuthContext authContext, TenantManager tenantManager) - { - UserManager = userManager; - AuthContext = authContext; - TenantManager = tenantManager; - } + private static DateTime lastErrorTime = default; + + public UserManager UserManager { get; } + public AuthContext AuthContext { get; } + public TenantManager TenantManager { get; } + + public JabberServiceClient(UserManager userManager, AuthContext authContext, TenantManager tenantManager) + { + UserManager = userManager; + AuthContext = authContext; + TenantManager = tenantManager; + } private static bool IsServiceProbablyNotAvailable() { @@ -100,15 +100,15 @@ namespace ASC.Core.Notify.Jabber byte result = 4; if (IsServiceProbablyNotAvailable()) throw new Exception(); - using var service = GetService(); - try - { - result = service.AddXmppConnection(connectionId, GetCurrentUserName(), state, GetCurrentTenantId()); - } - catch (Exception error) - { - ProcessError(error); - } + using var service = GetService(); + try + { + result = service.AddXmppConnection(connectionId, GetCurrentUserName(), state, GetCurrentTenantId()); + } + catch (Exception error) + { + ProcessError(error); + } return result; } @@ -139,7 +139,7 @@ namespace ASC.Core.Notify.Jabber try { if (IsServiceProbablyNotAvailable()) return defaultState; - using var service = GetService(); + using var service = GetService(); return service.GetState(GetCurrentTenantId(), userName); } catch (Exception error) @@ -155,7 +155,7 @@ namespace ASC.Core.Notify.Jabber try { if (IsServiceProbablyNotAvailable()) throw new Exception(); - using var service = GetService(); + using var service = GetService(); return service.SendState(GetCurrentTenantId(), GetCurrentUserName(), state); } catch (Exception error) @@ -171,7 +171,7 @@ namespace ASC.Core.Notify.Jabber try { if (IsServiceProbablyNotAvailable()) throw new Exception(); - using var service = GetService(); + using var service = GetService(); states = service.GetAllStates(GetCurrentTenantId(), GetCurrentUserName()); } catch (Exception error) @@ -187,7 +187,7 @@ namespace ASC.Core.Notify.Jabber try { if (IsServiceProbablyNotAvailable()) throw new Exception(); - using var service = GetService(); + using var service = GetService(); messages = service.GetRecentMessages(GetCurrentTenantId(), GetCurrentUserName(), to, id); } catch (Exception error) @@ -202,7 +202,7 @@ namespace ASC.Core.Notify.Jabber try { if (IsServiceProbablyNotAvailable()) throw new Exception(); - using var service = GetService(); + using var service = GetService(); service.Ping(AuthContext.CurrentAccount.ID.ToString(), GetCurrentTenantId(), GetCurrentUserName(), state); } catch (Exception error) @@ -238,17 +238,17 @@ namespace ASC.Core.Notify.Jabber { return new JabberServiceClientWcf(); } - } - - public static class JabberServiceClientExtension - { - public static IServiceCollection AddJabberServiceClient(this IServiceCollection services) - { - services.TryAddScoped(); - return services - .AddUserManagerService() - .AddAuthContextService() - .AddTenantManagerService(); - } - } + } + + public static class JabberServiceClientExtension + { + public static DIHelper AddJabberServiceClient(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddUserManagerService() + .AddAuthContextService() + .AddTenantManagerService(); + } + } } diff --git a/common/ASC.Core.Common/Notify/RecipientProviderImpl.cs b/common/ASC.Core.Common/Notify/RecipientProviderImpl.cs index 3bf65437df..5240962022 100644 --- a/common/ASC.Core.Common/Notify/RecipientProviderImpl.cs +++ b/common/ASC.Core.Common/Notify/RecipientProviderImpl.cs @@ -26,19 +26,21 @@ using System; using System.Collections.Generic; -using System.Linq; -using ASC.Core.Users; -using ASC.Notify.Recipients; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; +using System.Linq; +using ASC.Common; +using ASC.Core.Users; +using ASC.Notify.Recipients; + +using Microsoft.Extensions.DependencyInjection.Extensions; + namespace ASC.Core.Notify { public class RecipientProviderImpl : IRecipientProvider - { - public UserManager UserManager { get; } - - public RecipientProviderImpl(UserManager userManager) => + { + public UserManager UserManager { get; } + + public RecipientProviderImpl(UserManager userManager) => (UserManager) = (userManager); public virtual IRecipient GetRecipient(string id) { @@ -156,13 +158,13 @@ namespace ASC.Core.Notify } return false; } - } - + } + public static class RecipientProviderImplExtension { - public static IServiceCollection AddRecipientProviderImplService(this IServiceCollection services) - { - services.TryAddScoped(typeof(IRecipientProvider), typeof(RecipientProviderImpl)); + public static DIHelper AddRecipientProviderImplService(this DIHelper services) + { + services.TryAddScoped(typeof(IRecipientProvider), typeof(RecipientProviderImpl)); return services .AddUserManagerService(); diff --git a/common/ASC.Core.Common/Notify/Senders/AWSSender.cs b/common/ASC.Core.Common/Notify/Senders/AWSSender.cs index 5b5bcff900..3d8f8bcd84 100644 --- a/common/ASC.Core.Common/Notify/Senders/AWSSender.cs +++ b/common/ASC.Core.Common/Notify/Senders/AWSSender.cs @@ -29,16 +29,19 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; + using Amazon; using Amazon.Runtime; using Amazon.SimpleEmail; using Amazon.SimpleEmail.Model; + +using ASC.Common; using ASC.Common.Logging; using ASC.Common.Utils; using ASC.Notify.Messages; using ASC.Notify.Patterns; + using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; namespace ASC.Core.Notify.Senders @@ -230,7 +233,7 @@ namespace ASC.Core.Notify.Senders public static class AWSSenderExtension { - public static IServiceCollection AddAWSSenderService(this IServiceCollection services) + public static DIHelper AddAWSSenderService(this DIHelper services) { services.TryAddSingleton(); return services diff --git a/common/ASC.Core.Common/Notify/Senders/JabberSender.cs b/common/ASC.Core.Common/Notify/Senders/JabberSender.cs index 2eab62afdf..8f8f33a48e 100644 --- a/common/ASC.Core.Common/Notify/Senders/JabberSender.cs +++ b/common/ASC.Core.Common/Notify/Senders/JabberSender.cs @@ -26,12 +26,14 @@ using System; using System.Collections.Generic; -using System.Text.RegularExpressions; +using System.Text.RegularExpressions; + +using ASC.Common; using ASC.Common.Logging; using ASC.Core.Notify.Jabber; -using ASC.Notify.Messages; +using ASC.Notify.Messages; + using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; namespace ASC.Core.Notify.Senders @@ -77,7 +79,7 @@ namespace ASC.Core.Notify.Senders public static class JabberSenderExtension { - public static IServiceCollection AddJabberSenderService(this IServiceCollection services) + public static DIHelper AddJabberSenderService(this DIHelper services) { services.TryAddSingleton(); return services.AddJabberServiceClient(); diff --git a/common/ASC.Core.Common/Notify/Senders/SmtpSender.cs b/common/ASC.Core.Common/Notify/Senders/SmtpSender.cs index 644a5c3a3c..943a340aab 100644 --- a/common/ASC.Core.Common/Notify/Senders/SmtpSender.cs +++ b/common/ASC.Core.Common/Notify/Senders/SmtpSender.cs @@ -28,16 +28,20 @@ using System; using System.Collections.Generic; using System.IO; using System.Net; + +using ASC.Common; using ASC.Common.Logging; using ASC.Common.Utils; using ASC.Notify.Messages; using ASC.Notify.Patterns; + using MailKit; using MailKit.Security; + using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; + using MimeKit; namespace ASC.Core.Notify.Senders @@ -320,7 +324,7 @@ namespace ASC.Core.Notify.Senders public static class SmtpSenderExtension { - public static IServiceCollection AddSmtpSenderService(this IServiceCollection services) + public static DIHelper AddSmtpSenderService(this DIHelper services) { services.TryAddSingleton(); return services diff --git a/common/ASC.Core.Common/Notify/Signalr/SignalrServiceClient.cs b/common/ASC.Core.Common/Notify/Signalr/SignalrServiceClient.cs index 3655f94144..d57f36ee3c 100644 --- a/common/ASC.Core.Common/Notify/Signalr/SignalrServiceClient.cs +++ b/common/ASC.Core.Common/Notify/Signalr/SignalrServiceClient.cs @@ -30,59 +30,60 @@ using System.Linq; using System.Net; using System.Security.Cryptography; using System.ServiceModel; -using System.Text; +using System.Text; + +using ASC.Common; using ASC.Common.Logging; -using ASC.Core.Common.Notify.Jabber; +using ASC.Core.Common.Notify.Jabber; + using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Options; +using Microsoft.Extensions.Options; + using Newtonsoft.Json; namespace ASC.Core.Notify.Signalr { - public class SignalrServiceClient + public class ConfigureSignalrServiceClient : IConfigureNamedOptions { - private static readonly TimeSpan Timeout; - private readonly ILog Log; - private static DateTime lastErrorTime; - public readonly bool EnableSignalr; - private readonly string CoreMachineKey; - private readonly string Url; - private readonly bool JabberReplaceDomain; - private readonly string JabberReplaceFromDomain; - private readonly string JabberReplaceToDomain; - - private readonly string hub; - public TenantManager TenantManager { get; } public CoreSettings CoreSettings { get; } + public IConfiguration Configuration { get; } + public IOptionsMonitor Options { get; } - static SignalrServiceClient() + public ConfigureSignalrServiceClient( + TenantManager tenantManager, + CoreSettings coreSettings, + IConfiguration configuration, + IOptionsMonitor options) { - Timeout = TimeSpan.FromSeconds(1); - } - - public SignalrServiceClient(string hub, TenantManager tenantManager, CoreSettings coreSettings, IConfiguration configuration, IOptionsMonitor options) - { - Log = options.CurrentValue; - this.hub = hub.Trim('/'); TenantManager = tenantManager; CoreSettings = coreSettings; - CoreMachineKey = configuration["core:machinekey"]; - Url = configuration["web:hub:internal"]; - EnableSignalr = !string.IsNullOrEmpty(Url); + Configuration = configuration; + Options = options; + } + + public void Configure(string name, SignalrServiceClient options) + { + options.Log = Options.CurrentValue; + options.hub = name.Trim('/'); + options.TenantManager = TenantManager; + options.CoreSettings = CoreSettings; + options.CoreMachineKey = Configuration["core:machinekey"]; + options.Url = Configuration["web:hub:internal"]; + options.EnableSignalr = !string.IsNullOrEmpty(options.Url); try { - var replaceSetting = configuration["jabber:replace-domain"]; + var replaceSetting = Configuration["jabber:replace-domain"]; if (!string.IsNullOrEmpty(replaceSetting)) { - JabberReplaceDomain = true; + options.JabberReplaceDomain = true; var q = replaceSetting.Split(new[] { "->" }, StringSplitOptions.RemoveEmptyEntries) .Select(s => s.Trim().ToLowerInvariant()) .ToList(); - JabberReplaceFromDomain = q.ElementAt(0); - JabberReplaceToDomain = q.ElementAt(1); + options.JabberReplaceFromDomain = q.ElementAt(0); + options.JabberReplaceToDomain = q.ElementAt(1); } } catch (Exception) @@ -90,6 +91,40 @@ namespace ASC.Core.Notify.Signalr } } + public void Configure(SignalrServiceClient options) + { + Configure("default", options); + } + } + + public class SignalrServiceClient + { + private static readonly TimeSpan Timeout; + internal ILog Log; + private static DateTime lastErrorTime; + public bool EnableSignalr; + internal string CoreMachineKey; + internal string Url; + internal bool JabberReplaceDomain; + internal string JabberReplaceFromDomain; + internal string JabberReplaceToDomain; + + internal string hub; + + public TenantManager TenantManager { get; internal set; } + public CoreSettings CoreSettings { get; internal set; } + + static SignalrServiceClient() + { + Timeout = TimeSpan.FromSeconds(1); + } + + public SignalrServiceClient() + { + + } + + public void SendMessage(string callerUserName, string calleeUserName, string messageText, int tenantId, string domain) { @@ -355,4 +390,17 @@ namespace ASC.Core.Notify.Signalr return string.Format("ASC {0}:{1}:{2}", pkey, now, hash); } } + + public static class SignalrServiceClientExtension + { + public static DIHelper AddSignalrServiceClient(this DIHelper services) + { + services.TryAddScoped(); + services.TryAddScoped, ConfigureSignalrServiceClient>(); + + return services + .AddTenantManagerService() + .AddCoreSettingsService(); + } + } } \ No newline at end of file diff --git a/common/ASC.Core.Common/Security/Authentication/CookieStorage.cs b/common/ASC.Core.Common/Security/Authentication/CookieStorage.cs index e7020ce8e1..60f60c52c6 100644 --- a/common/ASC.Core.Common/Security/Authentication/CookieStorage.cs +++ b/common/ASC.Core.Common/Security/Authentication/CookieStorage.cs @@ -28,13 +28,12 @@ using System; using System.Globalization; using System.Web; +using ASC.Common; using ASC.Common.Logging; using ASC.Core.Tenants; using ASC.Security.Cryptography; using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; namespace ASC.Core.Security.Authentication @@ -141,7 +140,7 @@ namespace ASC.Core.Security.Authentication public static class CookieStorageExtension { - public static IServiceCollection AddCookieStorageService(this IServiceCollection services) + public static DIHelper AddCookieStorageService(this DIHelper services) { services.TryAddScoped(); diff --git a/common/ASC.Core.Common/Security/Authorizing/AzManager.cs b/common/ASC.Core.Common/Security/Authorizing/AzManager.cs index 11411ca874..ba4bce27b8 100644 --- a/common/ASC.Core.Common/Security/Authorizing/AzManager.cs +++ b/common/ASC.Core.Common/Security/Authorizing/AzManager.cs @@ -25,11 +25,11 @@ using System; -using System.Collections.Generic; -using ASC.Core.Security.Authorizing; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; - +using System.Collections.Generic; +using ASC.Core.Security.Authorizing; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; + namespace ASC.Common.Security.Authorizing { public class AzManager @@ -44,7 +44,7 @@ namespace ASC.Common.Security.Authorizing public AzManager(IRoleProvider roleProvider, IPermissionProvider permissionProvider) : this() - { + { this.roleProvider = roleProvider ?? throw new ArgumentNullException("roleProvider"); this.permissionProvider = permissionProvider ?? throw new ArgumentNullException("permissionProvider"); } @@ -103,9 +103,9 @@ namespace ASC.Common.Security.Authorizing internal IEnumerable GetSubjects(ISubject subject, ISecurityObjectId objectId, ISecurityObjectProvider securityObjProvider) { - var subjects = new List - { - subject + var subjects = new List + { + subject }; subjects.AddRange( roleProvider.GetRoles(subject) @@ -146,17 +146,17 @@ namespace ASC.Common.Security.Authorizing } #endregion - } - - public static class AzManagerConfigExtension - { - public static IServiceCollection AddAzManagerService(this IServiceCollection services) - { - services.TryAddScoped(); - - return services - .AddPermissionProviderService() - .AddRoleProviderService(); - } - } + } + + public static class AzManagerConfigExtension + { + public static DIHelper AddAzManagerService(this DIHelper services) + { + services.TryAddScoped(); + + return services + .AddPermissionProviderService() + .AddRoleProviderService(); + } + } } \ No newline at end of file diff --git a/common/ASC.Core.Common/Security/Authorizing/PermissionProvider.cs b/common/ASC.Core.Common/Security/Authorizing/PermissionProvider.cs index b231cbc895..5d01b7365b 100644 --- a/common/ASC.Core.Common/Security/Authorizing/PermissionProvider.cs +++ b/common/ASC.Core.Common/Security/Authorizing/PermissionProvider.cs @@ -26,23 +26,25 @@ using System; using System.Collections.Generic; -using System.Linq; -using ASC.Common.Security; -using ASC.Common.Security.Authorizing; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; +using System.Linq; +using ASC.Common; +using ASC.Common.Security; +using ASC.Common.Security.Authorizing; + +using Microsoft.Extensions.DependencyInjection.Extensions; + namespace ASC.Core.Security.Authorizing { class PermissionProvider : IPermissionProvider - { - private AuthorizationManager AuthorizationManager { get; } - - public PermissionProvider(AuthorizationManager authorizationManager) - { - AuthorizationManager = authorizationManager; - } - + { + private AuthorizationManager AuthorizationManager { get; } + + public PermissionProvider(AuthorizationManager authorizationManager) + { + AuthorizationManager = authorizationManager; + } + public IEnumerable GetAcl(ISubject subject, IAction action, ISecurityObjectId objectId, ISecurityObjectProvider secObjProvider) { if (subject == null) throw new ArgumentNullException("subject"); @@ -52,14 +54,14 @@ namespace ASC.Core.Security.Authorizing .GetAcesWithInherits(subject.ID, action.ID, objectId, secObjProvider) .Select(r => new Ace(r.ActionId, r.Reaction)); } - } - - public static class PermissionProviderConfigExtension - { - public static IServiceCollection AddPermissionProviderService(this IServiceCollection services) - { - services.TryAddScoped(typeof(IPermissionProvider), typeof(PermissionProvider)); - return services.AddAuthorizationManagerService(); - } - } + } + + public static class PermissionProviderConfigExtension + { + public static DIHelper AddPermissionProviderService(this DIHelper services) + { + services.TryAddScoped(typeof(IPermissionProvider), typeof(PermissionProvider)); + return services.AddAuthorizationManagerService(); + } + } } \ No newline at end of file diff --git a/common/ASC.Core.Common/Security/Authorizing/PermissionResolver.cs b/common/ASC.Core.Common/Security/Authorizing/PermissionResolver.cs index 29afce57da..80614af77b 100644 --- a/common/ASC.Core.Common/Security/Authorizing/PermissionResolver.cs +++ b/common/ASC.Core.Common/Security/Authorizing/PermissionResolver.cs @@ -26,12 +26,15 @@ using System; using System.Collections.Generic; -using System.Linq; +using System.Linq; + +using ASC.Common; using ASC.Common.Security; using ASC.Common.Security.Authentication; -using ASC.Common.Security.Authorizing; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; +using ASC.Common.Security.Authorizing; + +using Microsoft.Extensions.DependencyInjection.Extensions; + using Constants = ASC.Core.Configuration.Constants; namespace ASC.Core.Security.Authorizing @@ -121,14 +124,14 @@ namespace ASC.Core.Security.Authorizing DenyAction = denyAction; } } - } - - public static class PermissionResolverConfigExtention - { - public static IServiceCollection AddPermissionResolverService(this IServiceCollection services) - { - services.TryAddScoped(typeof(IPermissionResolver), typeof(PermissionResolver)); - return services.AddAzManagerService(); - } + } + + public static class PermissionResolverConfigExtention + { + public static DIHelper AddPermissionResolverService(this DIHelper services) + { + services.TryAddScoped(typeof(IPermissionResolver), typeof(PermissionResolver)); + return services.AddAzManagerService(); + } } } \ No newline at end of file diff --git a/common/ASC.Core.Common/Security/Authorizing/RoleProvider.cs b/common/ASC.Core.Common/Security/Authorizing/RoleProvider.cs index 5bf5d23268..d5337a13f8 100644 --- a/common/ASC.Core.Common/Security/Authorizing/RoleProvider.cs +++ b/common/ASC.Core.Common/Security/Authorizing/RoleProvider.cs @@ -26,14 +26,17 @@ using System; using System.Collections.Generic; -using System.Linq; +using System.Linq; + +using ASC.Common; using ASC.Common.Security; using ASC.Common.Security.Authentication; using ASC.Common.Security.Authorizing; -using ASC.Core.Users; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; +using ASC.Core.Users; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; + namespace ASC.Core.Security.Authorizing { class RoleProvider : IRoleProvider @@ -86,9 +89,9 @@ namespace ASC.Core.Security.Authorizing public static class RoleProviderConfigExtention { - public static IServiceCollection AddRoleProviderService(this IServiceCollection services) + public static DIHelper AddRoleProviderService(this DIHelper services) { - services.TryAddScoped(typeof(IRoleProvider), typeof(RoleProvider)); + services.TryAddScoped(typeof(IRoleProvider), typeof(RoleProvider)); return services; } } diff --git a/common/ASC.Core.Common/Security/EmailValidationKeyProvider.cs b/common/ASC.Core.Common/Security/EmailValidationKeyProvider.cs index 81dcc97021..f44177d98c 100644 --- a/common/ASC.Core.Common/Security/EmailValidationKeyProvider.cs +++ b/common/ASC.Core.Common/Security/EmailValidationKeyProvider.cs @@ -25,20 +25,19 @@ using System; -using System.Text; +using System.Text; +using ASC.Common; using ASC.Common.Logging; using ASC.Core; using ASC.Core.Users; -using ASC.Web.Studio.Utility; - +using ASC.Web.Studio.Utility; + using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Options; - +using Microsoft.Extensions.Options; + using static ASC.Security.Cryptography.EmailValidationKeyProvider; namespace ASC.Security.Cryptography @@ -171,7 +170,7 @@ namespace ASC.Security.Cryptography case ConfirmType.LinkInvite: checkKeyResult = provider.ValidateEmailKey(Type.ToString() + (int)EmplType, Key, provider.ValidInterval); break; - case ConfirmType.PortalOwnerChange: + case ConfirmType.PortalOwnerChange: checkKeyResult = provider.ValidateEmailKey(Email + Type + UiD.HasValue, Key, provider.ValidInterval); break; case ConfirmType.EmailChange: @@ -251,7 +250,7 @@ namespace ASC.Security.Cryptography public static class EmailValidationKeyProviderExtension { - public static IServiceCollection AddEmailValidationKeyProviderService(this IServiceCollection services) + public static DIHelper AddEmailValidationKeyProviderService(this DIHelper services) { services.TryAddScoped(); diff --git a/common/ASC.Core.Common/Settings/SettingsManager.cs b/common/ASC.Core.Common/Settings/SettingsManager.cs index 32d510495e..f512752268 100644 --- a/common/ASC.Core.Common/Settings/SettingsManager.cs +++ b/common/ASC.Core.Common/Settings/SettingsManager.cs @@ -24,13 +24,14 @@ */ -using System; +using System; + +using ASC.Common; using ASC.Common.Logging; -using ASC.Core.Common.EF; -using ASC.Core.Common.EF.Context; -using ASC.Core.Data; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; +using ASC.Core.Common.EF; +using ASC.Core.Common.EF.Context; +using ASC.Core.Data; + using Microsoft.Extensions.Options; namespace ASC.Core.Common.Settings @@ -40,9 +41,9 @@ namespace ASC.Core.Common.Settings public SettingsManager( IServiceProvider serviceProvider, DbSettingsManagerCache dbSettingsManagerCache, - IOptionsMonitor option, - AuthContext authContext, - TenantManager tenantManager, + IOptionsMonitor option, + AuthContext authContext, + TenantManager tenantManager, DbContextManager dbContextManager) : base(serviceProvider, dbSettingsManagerCache, option, authContext, tenantManager, dbContextManager) { @@ -52,7 +53,7 @@ namespace ASC.Core.Common.Settings public static class SettingsManagerExtention { - public static IServiceCollection AddSettingsManagerService(this IServiceCollection services) + public static DIHelper AddSettingsManagerService(this DIHelper services) { services.TryAddScoped(); diff --git a/common/ASC.Core.Common/Tenants/TenantCookieSettings.cs b/common/ASC.Core.Common/Tenants/TenantCookieSettings.cs index f2df51df42..8a6c964134 100644 --- a/common/ASC.Core.Common/Tenants/TenantCookieSettings.cs +++ b/common/ASC.Core.Common/Tenants/TenantCookieSettings.cs @@ -27,10 +27,11 @@ using System; using System.Linq; using System.Runtime.Serialization; + +using ASC.Common; using ASC.Core.Common.Settings; + using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; namespace ASC.Core.Tenants { @@ -126,7 +127,7 @@ namespace ASC.Core.Tenants public static class TenantCookieSettingsExtention { - public static IServiceCollection AddTenantCookieSettingsService(this IServiceCollection services) + public static DIHelper AddTenantCookieSettingsService(this DIHelper services) { services.TryAddScoped(); return services.AddSettingsManagerService(); diff --git a/common/ASC.Core.Common/Tenants/TenantUtil.cs b/common/ASC.Core.Common/Tenants/TenantUtil.cs index c9b06d8a07..4d3084ea03 100644 --- a/common/ASC.Core.Common/Tenants/TenantUtil.cs +++ b/common/ASC.Core.Common/Tenants/TenantUtil.cs @@ -24,21 +24,19 @@ */ -using System; - -using ASC.Common.Utils; - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; +using System; + +using ASC.Common; +using ASC.Common.Utils; using Microsoft.Extensions.Options; - + namespace ASC.Core.Tenants -{ +{ class ConfigureTenantUtil : IConfigureNamedOptions - { + { public IOptionsSnapshot TenantManager { get; } - public TimeZoneConverter TimeZoneConverter { get; } - + public TimeZoneConverter TimeZoneConverter { get; } + public ConfigureTenantUtil( IOptionsSnapshot tenantManager, TimeZoneConverter timeZoneConverter @@ -70,10 +68,10 @@ namespace ASC.Core.Tenants { } - public TenantUtil(TenantManager tenantManager, TimeZoneConverter timeZoneConverter) - { - TenantManager = tenantManager; - TimeZoneConverter = timeZoneConverter; + public TenantUtil(TenantManager tenantManager, TimeZoneConverter timeZoneConverter) + { + TenantManager = tenantManager; + TimeZoneConverter = timeZoneConverter; } @@ -82,11 +80,11 @@ namespace ASC.Core.Tenants return DateTimeFromUtc(TimeZoneConverter.GetTimeZone(TenantManager.GetCurrentTenant().TimeZone), utc); } - public DateTime DateTimeFromUtc(string timeZone, DateTime utc) - { - return DateTimeFromUtc(TimeZoneConverter.GetTimeZone(timeZone), utc); - } - + public DateTime DateTimeFromUtc(string timeZone, DateTime utc) + { + return DateTimeFromUtc(TimeZoneConverter.GetTimeZone(timeZone), utc); + } + public static DateTime DateTimeFromUtc(TimeZoneInfo timeZone, DateTime utc) { if (utc.Kind != DateTimeKind.Utc) @@ -139,16 +137,16 @@ namespace ASC.Core.Tenants { return DateTimeNow(TimeZoneConverter.GetTimeZone(timeZone)); } - } - - public static class TenantUtilExtention - { - public static IServiceCollection AddTenantUtilService(this IServiceCollection services) - { - services.TryAddScoped(); + } + + public static class TenantUtilExtention + { + public static DIHelper AddTenantUtilService(this DIHelper services) + { + services.TryAddScoped(); services.TryAddScoped, ConfigureTenantUtil>(); - - return services.AddTenantManagerService(); - } + + return services.AddTenantManagerService(); + } } } \ No newline at end of file diff --git a/common/ASC.Core.Common/Users/Constants.cs b/common/ASC.Core.Common/Users/Constants.cs index 66c3950335..4a1699f97e 100644 --- a/common/ASC.Core.Common/Users/Constants.cs +++ b/common/ASC.Core.Common/Users/Constants.cs @@ -24,10 +24,12 @@ */ -using System; +using System; + +using ASC.Common; + using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; + using Action = ASC.Common.Security.Authorizing.Action; using AuthConst = ASC.Common.Security.Authorizing.Constants; @@ -145,8 +147,8 @@ namespace ASC.Core.Users public static class ConstantsConfigFactory { - public static IServiceCollection AddConstantsService(this IServiceCollection services) - { + public static DIHelper AddConstantsService(this DIHelper services) + { services.TryAddSingleton(); return services; } diff --git a/common/ASC.Core.Common/Users/DisplayUserSettings.cs b/common/ASC.Core.Common/Users/DisplayUserSettings.cs index cd4c50140f..37c77460b2 100644 --- a/common/ASC.Core.Common/Users/DisplayUserSettings.cs +++ b/common/ASC.Core.Common/Users/DisplayUserSettings.cs @@ -27,11 +27,11 @@ using System; using System.Runtime.Serialization; using System.Web; + +using ASC.Common; using ASC.Core; using ASC.Core.Common.Settings; using ASC.Core.Users; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; namespace ASC.Web.Core.Users { @@ -98,7 +98,7 @@ namespace ASC.Web.Core.Users public static class DisplayUserSettingsExtention { - public static IServiceCollection AddDisplayUserSettingsService(this IServiceCollection services) + public static DIHelper AddDisplayUserSettingsService(this DIHelper services) { services.TryAddScoped(); diff --git a/common/ASC.Core.Common/Users/UserFormatter.cs b/common/ASC.Core.Common/Users/UserFormatter.cs index 080d99b75c..2e9e18a2ac 100644 --- a/common/ASC.Core.Common/Users/UserFormatter.cs +++ b/common/ASC.Core.Common/Users/UserFormatter.cs @@ -27,12 +27,12 @@ using System; using System.Collections.Generic; using System.Text.RegularExpressions; -using System.Threading; +using System.Threading; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; +using ASC.Common; +using Microsoft.Extensions.Configuration; + namespace ASC.Core.Users { public class UserFormatter : IComparer @@ -40,12 +40,12 @@ namespace ASC.Core.Users private readonly DisplayUserNameFormat format; private static bool forceFormatChecked; private static string forceFormat; - - public UserFormatter(IConfiguration configuration) - { - Configuration = configuration; - UserNameRegex = new Regex(Configuration["core:username:regex"] ?? ""); - } + + public UserFormatter(IConfiguration configuration) + { + Configuration = configuration; + UserNameRegex = new Regex(Configuration["core:username:regex"] ?? ""); + } public string GetUserName(UserInfo userInfo, DisplayUserNameFormat format) { @@ -79,10 +79,10 @@ namespace ASC.Core.Users { if (x == null && y == null) return 0; if (x == null && y != null) return -1; - if (x != null && y == null) return +1; + if (x != null && y == null) return +1; if (format == DisplayUserNameFormat.Default) format = GetUserDisplayDefaultOrder(); - int result; + int result; if (format == DisplayUserNameFormat.FirstLast) { result = string.Compare(x.FirstName, y.FirstName, true); @@ -136,23 +136,23 @@ namespace ASC.Core.Users } var format = formats[DisplayUserNameFormat.Default]; return format.IndexOf("{0}") < format.IndexOf("{1}") ? DisplayUserNameFormat.FirstLast : DisplayUserNameFormat.LastFirst; - } - - public Regex UserNameRegex; - - public IConfiguration Configuration { get; } - - public bool IsValidUserName(string firstName, string lastName) - { - return UserNameRegex.IsMatch(firstName + lastName); } - } - public static class UserFormatterExtension - { - public static IServiceCollection AddUserFormatter(this IServiceCollection services) - { - services.TryAddSingleton(); - return services; - } + + public Regex UserNameRegex; + + public IConfiguration Configuration { get; } + + public bool IsValidUserName(string firstName, string lastName) + { + return UserNameRegex.IsMatch(firstName + lastName); + } + } + public static class UserFormatterExtension + { + public static DIHelper AddUserFormatter(this DIHelper services) + { + services.TryAddSingleton(); + return services; + } } } diff --git a/common/ASC.Core.Common/WhiteLabel/MailWhiteLabelSettings.cs b/common/ASC.Core.Common/WhiteLabel/MailWhiteLabelSettings.cs index e18185adee..52ee0fca45 100644 --- a/common/ASC.Core.Common/WhiteLabel/MailWhiteLabelSettings.cs +++ b/common/ASC.Core.Common/WhiteLabel/MailWhiteLabelSettings.cs @@ -25,13 +25,15 @@ using System; -using System.Runtime.Serialization; -using ASC.Core.Common; -using ASC.Core.Common.Settings; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; +using System.Runtime.Serialization; +using ASC.Common; +using ASC.Core.Common; +using ASC.Core.Common.Settings; + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + namespace ASC.Web.Core.WhiteLabel { [Serializable] @@ -62,59 +64,59 @@ namespace ASC.Web.Core.WhiteLabel public Guid ID { get { return new Guid("{C3602052-5BA2-452A-BD2A-ADD0FAF8EB88}"); } - } - - public ISettings GetDefault(IConfiguration configuration) - { - var mailWhiteLabelSettingsHelper = new MailWhiteLabelSettingsHelper(configuration); + } - return new MailWhiteLabelSettings - { - FooterEnabled = true, - FooterSocialEnabled = true, - SupportUrl = mailWhiteLabelSettingsHelper.DefaultMailSupportUrl, - SupportEmail = mailWhiteLabelSettingsHelper.DefaultMailSupportEmail, - SalesEmail = mailWhiteLabelSettingsHelper.DefaultMailSalesEmail, - DemotUrl = mailWhiteLabelSettingsHelper.DefaultMailDemotUrl, - SiteUrl = mailWhiteLabelSettingsHelper.DefaultMailSiteUrl + public ISettings GetDefault(IConfiguration configuration) + { + var mailWhiteLabelSettingsHelper = new MailWhiteLabelSettingsHelper(configuration); + + return new MailWhiteLabelSettings + { + FooterEnabled = true, + FooterSocialEnabled = true, + SupportUrl = mailWhiteLabelSettingsHelper.DefaultMailSupportUrl, + SupportEmail = mailWhiteLabelSettingsHelper.DefaultMailSupportEmail, + SalesEmail = mailWhiteLabelSettingsHelper.DefaultMailSalesEmail, + DemotUrl = mailWhiteLabelSettingsHelper.DefaultMailDemotUrl, + SiteUrl = mailWhiteLabelSettingsHelper.DefaultMailSiteUrl }; - } - - public bool IsDefault(IConfiguration configuration) - { - if (!(GetDefault(configuration) is MailWhiteLabelSettings defaultSettings)) return false; - - return FooterEnabled == defaultSettings.FooterEnabled && - FooterSocialEnabled == defaultSettings.FooterSocialEnabled && - SupportUrl == defaultSettings.SupportUrl && - SupportEmail == defaultSettings.SupportEmail && - SalesEmail == defaultSettings.SalesEmail && - DemotUrl == defaultSettings.DemotUrl && - SiteUrl == defaultSettings.SiteUrl; - } - + } + + public bool IsDefault(IConfiguration configuration) + { + if (!(GetDefault(configuration) is MailWhiteLabelSettings defaultSettings)) return false; + + return FooterEnabled == defaultSettings.FooterEnabled && + FooterSocialEnabled == defaultSettings.FooterSocialEnabled && + SupportUrl == defaultSettings.SupportUrl && + SupportEmail == defaultSettings.SupportEmail && + SalesEmail == defaultSettings.SalesEmail && + DemotUrl == defaultSettings.DemotUrl && + SiteUrl == defaultSettings.SiteUrl; + } + public static MailWhiteLabelSettings Instance(SettingsManager settingsManager) { return settingsManager.LoadForDefaultTenant(); - } - public static bool IsDefault(SettingsManager settingsManager, IConfiguration configuration) - { - return Instance(settingsManager).IsDefault(configuration); - } - - public ISettings GetDefault(IServiceProvider serviceProvider) - { - return GetDefault(serviceProvider.GetService()); - } - } - - public class MailWhiteLabelSettingsHelper - { - public MailWhiteLabelSettingsHelper(IConfiguration configuration) - { - Configuration = configuration; - } - + } + public static bool IsDefault(SettingsManager settingsManager, IConfiguration configuration) + { + return Instance(settingsManager).IsDefault(configuration); + } + + public ISettings GetDefault(IServiceProvider serviceProvider) + { + return GetDefault(serviceProvider.GetService()); + } + } + + public class MailWhiteLabelSettingsHelper + { + public MailWhiteLabelSettingsHelper(IConfiguration configuration) + { + Configuration = configuration; + } + public string DefaultMailSupportUrl { get @@ -158,15 +160,15 @@ namespace ASC.Web.Core.WhiteLabel var url = Configuration["web:teamlab-site"]; return !string.IsNullOrEmpty(url) ? url : "http://www.onlyoffice.com"; } - } - - public IConfiguration Configuration { get; } - } - + } + + public IConfiguration Configuration { get; } + } + public static class MailWhiteLabelSettingsExtention { - public static IServiceCollection AddMailWhiteLabelSettingsService(this IServiceCollection services) - { + public static DIHelper AddMailWhiteLabelSettingsService(this DIHelper services) + { services.TryAddSingleton(); return services.AddSettingsManagerService(); } diff --git a/common/ASC.Data.Reassigns/QueueWorker.cs b/common/ASC.Data.Reassigns/QueueWorker.cs index 37d00270c3..9ae74fff63 100644 --- a/common/ASC.Data.Reassigns/QueueWorker.cs +++ b/common/ASC.Data.Reassigns/QueueWorker.cs @@ -1,52 +1,52 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2018 - * - * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU - * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). - * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html - * - * You can contact Ascensio System SIA by email at sales@onlyoffice.com - * - * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display - * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. - * - * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains - * relevant author attributions when distributing the software. If the display of the logo in its graphic - * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" - * in every copy of the program you distribute. - * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. - * -*/ - - -using System; -using System.Collections.Generic; +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; using System.Linq; -using ASC.Common.Threading.Progress; +using ASC.Common; +using ASC.Common.Threading.Progress; using ASC.Core.Users; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; -namespace ASC.Data.Reassigns +namespace ASC.Data.Reassigns { public class QueueWorker { - public static Dictionary GetHttpHeaders(HttpRequest httpRequest) - { - return httpRequest?.Headers.Keys.ToDictionary(key => key, key => httpRequest.Headers[key].ToString()); + public static Dictionary GetHttpHeaders(HttpRequest httpRequest) + { + return httpRequest?.Headers.Keys.ToDictionary(key => key, key => httpRequest.Headers[key].ToString()); } - } - public class QueueWorker where T : class, IProgressItem - { + } + public class QueueWorker where T : class, IProgressItem + { protected readonly ProgressQueue Queue; public IHttpContextAccessor HttpContextAccessor { get; } @@ -62,49 +62,49 @@ namespace ASC.Data.Reassigns Queue = optionsQueue.Value; } - public string GetProgressItemId(int tenantId, Guid userId) - { - return string.Format("{0}_{1}_{2}", tenantId, userId, typeof(T).Name); - } - - public T GetProgressItemStatus(int tenantId, Guid userId) - { - var id = GetProgressItemId(tenantId, userId); - return Queue.GetStatus(id) as T; - } - - public void Terminate(int tenantId, Guid userId) - { - var item = GetProgressItemStatus(tenantId, userId); - - if (item != null) - Queue.Remove(item); + public string GetProgressItemId(int tenantId, Guid userId) + { + return string.Format("{0}_{1}_{2}", tenantId, userId, typeof(T).Name); + } + + public T GetProgressItemStatus(int tenantId, Guid userId) + { + var id = GetProgressItemId(tenantId, userId); + return Queue.GetStatus(id) as T; + } + + public void Terminate(int tenantId, Guid userId) + { + var item = GetProgressItemStatus(tenantId, userId); + + if (item != null) + Queue.Remove(item); } protected IProgressItem Start(int tenantId, Guid userId, Func constructor) { - lock (Queue.SynchRoot) - { - var task = GetProgressItemStatus(tenantId, userId); - - if (task != null && task.IsCompleted) - { - Queue.Remove(task); - task = null; - } - - if (task == null) - { - task = constructor(); - Queue.Add(task); - } - - if (!Queue.IsStarted) - Queue.Start(x => x.RunJob()); - - return task; + lock (Queue.SynchRoot) + { + var task = GetProgressItemStatus(tenantId, userId); + + if (task != null && task.IsCompleted) + { + Queue.Remove(task); + task = null; + } + + if (task == null) + { + task = constructor(); + Queue.Add(task); + } + + if (!Queue.IsStarted) + Queue.Start(x => x.RunJob()); + + return task; } - } + } } public class QueueWorkerReassign : QueueWorker @@ -120,9 +120,9 @@ namespace ASC.Data.Reassigns QueueWorkerRemove = queueWorkerRemove; } - public ReassignProgressItem Start(int tenantId, Guid fromUserId, Guid toUserId, Guid currentUserId, bool deleteProfile) + public ReassignProgressItem Start(int tenantId, Guid fromUserId, Guid toUserId, Guid currentUserId, bool deleteProfile) { - return Start(tenantId, fromUserId, () => new ReassignProgressItem(ServiceProvider, HttpContextAccessor.HttpContext, this, QueueWorkerRemove, tenantId, fromUserId, toUserId, currentUserId, deleteProfile)) as ReassignProgressItem; + return Start(tenantId, fromUserId, () => new ReassignProgressItem(ServiceProvider, HttpContextAccessor.HttpContext, this, QueueWorkerRemove, tenantId, fromUserId, toUserId, currentUserId, deleteProfile)) as ReassignProgressItem; } } public class QueueWorkerRemove : QueueWorker @@ -135,15 +135,15 @@ namespace ASC.Data.Reassigns { } - public RemoveProgressItem Start(int tenantId, UserInfo user, Guid currentUserId, bool notify) + public RemoveProgressItem Start(int tenantId, UserInfo user, Guid currentUserId, bool notify) { - return Start(tenantId, user.ID, () => new RemoveProgressItem(ServiceProvider, HttpContextAccessor.HttpContext, this, tenantId, user, currentUserId, notify)) as RemoveProgressItem; + return Start(tenantId, user.ID, () => new RemoveProgressItem(ServiceProvider, HttpContextAccessor.HttpContext, this, tenantId, user, currentUserId, notify)) as RemoveProgressItem; } } public static class QueueExtension { - public static IServiceCollection AddQueueWorkerRemoveService(this IServiceCollection services) + public static DIHelper AddQueueWorkerRemoveService(this DIHelper services) { services.TryAddSingleton>(); services.TryAddSingleton>(); @@ -152,7 +152,7 @@ namespace ASC.Data.Reassigns return services; } - public static IServiceCollection AddQueueWorkerReassignService(this IServiceCollection services) + public static DIHelper AddQueueWorkerReassignService(this DIHelper services) { services.TryAddSingleton>(); services.TryAddSingleton>(); @@ -162,5 +162,5 @@ namespace ASC.Data.Reassigns return services .AddQueueWorkerRemoveService(); } - } -} + } +} diff --git a/common/ASC.Data.Reassigns/ReassignProgressItem.cs b/common/ASC.Data.Reassigns/ReassignProgressItem.cs index f644a91009..efb09349d2 100644 --- a/common/ASC.Data.Reassigns/ReassignProgressItem.cs +++ b/common/ASC.Data.Reassigns/ReassignProgressItem.cs @@ -25,7 +25,9 @@ using System; -using System.Collections.Generic; +using System.Collections.Generic; + +using ASC.Common; using ASC.Common.Logging; //using System.Web; using ASC.Common.Threading.Progress; @@ -41,7 +43,6 @@ using ASC.Web.Studio.Core.Notify; //using CrmDaoFactory = ASC.CRM.Core.Dao.DaoFactory; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; namespace ASC.Data.Reassigns @@ -218,7 +219,7 @@ namespace ASC.Data.Reassigns public static class ReassignProgressItemExtension { - public static IServiceCollection AddReassignProgressItemService(this IServiceCollection services) + public static DIHelper AddReassignProgressItemService(this DIHelper services) { services.TryAddSingleton>(); services.TryAddSingleton>(); diff --git a/common/ASC.Data.Reassigns/RemoveProgressItem.cs b/common/ASC.Data.Reassigns/RemoveProgressItem.cs index aa90c83ec4..235b95e150 100644 --- a/common/ASC.Data.Reassigns/RemoveProgressItem.cs +++ b/common/ASC.Data.Reassigns/RemoveProgressItem.cs @@ -28,7 +28,9 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Security.Cryptography; -using System.Text; +using System.Text; + +using ASC.Common; using ASC.Common.Logging; using ASC.Common.Threading.Progress; using ASC.Core; @@ -43,7 +45,6 @@ using ASC.Web.Studio.Core.Notify; //using CrmDaoFactory = ASC.CRM.Core.Dao.DaoFactory; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; namespace ASC.Data.Reassigns @@ -254,7 +255,7 @@ namespace ASC.Data.Reassigns public static class RemoveProgressItemExtension { - public static IServiceCollection AddRemoveProgressItemService(this IServiceCollection services) + public static DIHelper AddRemoveProgressItemService(this DIHelper services) { services.TryAddSingleton>(); diff --git a/common/ASC.Data.Storage/Configuration/Appender.cs b/common/ASC.Data.Storage/Configuration/Appender.cs index d55f7bf477..754ca625a2 100644 --- a/common/ASC.Data.Storage/Configuration/Appender.cs +++ b/common/ASC.Data.Storage/Configuration/Appender.cs @@ -1,7 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; + +using ASC.Common; using ASC.Common.Utils; + using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; @@ -10,7 +13,7 @@ namespace ASC.Data.Storage.Configuration { public static class StorageConfigExtension { - public static IServiceCollection AddStorage(this IServiceCollection services) + public static DIHelper AddStorage(this DIHelper services) { services.TryAddSingleton(r => r.GetService().GetSetting("Storage")); return services; diff --git a/common/ASC.Data.Storage/Configuration/StorageSettings.cs b/common/ASC.Data.Storage/Configuration/StorageSettings.cs index 9f20d0b812..f3c150718d 100644 --- a/common/ASC.Data.Storage/Configuration/StorageSettings.cs +++ b/common/ASC.Data.Storage/Configuration/StorageSettings.cs @@ -28,6 +28,7 @@ using System; using System.Collections.Generic; using System.Runtime.Serialization; +using ASC.Common; using ASC.Common.Caching; using ASC.Common.Logging; using ASC.Core; @@ -37,7 +38,6 @@ using ASC.Security.Cryptography; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; namespace ASC.Data.Storage.Configuration @@ -206,7 +206,7 @@ namespace ASC.Data.Storage.Configuration public static class StorageSettingsExtension { - public static IServiceCollection AddBaseStorageSettingsService(this IServiceCollection services) + public static DIHelper AddBaseStorageSettingsService(this DIHelper services) { services.TryAddSingleton(typeof(ICacheNotify<>), typeof(KafkaCache<>)); services.TryAddSingleton(); @@ -217,7 +217,7 @@ namespace ASC.Data.Storage.Configuration .AddEmailValidationKeyProviderService(); } - public static IServiceCollection AddCdnStorageSettingsService(this IServiceCollection services) + public static DIHelper AddCdnStorageSettingsService(this DIHelper services) { services.TryAddScoped(); @@ -227,7 +227,7 @@ namespace ASC.Data.Storage.Configuration .AddConsumerFactoryService(); } - public static IServiceCollection AddStorageSettingsService(this IServiceCollection services) + public static DIHelper AddStorageSettingsService(this DIHelper services) { services.TryAddScoped(); diff --git a/common/ASC.Data.Storage/DataList.cs b/common/ASC.Data.Storage/DataList.cs index 4ffc3fdc6c..a71a7696e8 100644 --- a/common/ASC.Data.Storage/DataList.cs +++ b/common/ASC.Data.Storage/DataList.cs @@ -25,6 +25,7 @@ using System.Collections.Generic; + using ASC.Data.Storage.Configuration; namespace ASC.Data.Storage @@ -34,9 +35,16 @@ namespace ASC.Data.Storage public DataList(Module config) { Add(string.Empty, config.Data); - foreach (var domain in config.Domain) + if (config.Domain != null) { - Add(domain.Name, domain.Data); + foreach (var domain in config.Domain) + { + Add(domain.Name, domain.Data); + } + } + else + { + config.Domain = new List(); } } diff --git a/common/ASC.Data.Storage/PathUtils.cs b/common/ASC.Data.Storage/PathUtils.cs index 4ddfc36075..1be9e731da 100644 --- a/common/ASC.Data.Storage/PathUtils.cs +++ b/common/ASC.Data.Storage/PathUtils.cs @@ -27,10 +27,11 @@ using System; using System.Collections.Generic; using System.IO; + +using ASC.Common; + using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; namespace ASC.Data.Storage @@ -119,7 +120,7 @@ namespace ASC.Data.Storage public static class PathUtilsExtension { - public static IServiceCollection AddPathUtilsService(this IServiceCollection services) + public static DIHelper AddPathUtilsService(this DIHelper services) { services.TryAddSingleton(); diff --git a/common/ASC.Data.Storage/StaticUploader.cs b/common/ASC.Data.Storage/StaticUploader.cs index 9242a593d3..a3c4854c8a 100644 --- a/common/ASC.Data.Storage/StaticUploader.cs +++ b/common/ASC.Data.Storage/StaticUploader.cs @@ -30,17 +30,19 @@ using System.IO; using System.Linq; using System.Runtime.Serialization; using System.Threading; -using System.Threading.Tasks; +using System.Threading.Tasks; + +using ASC.Common; using ASC.Common.Caching; using ASC.Common.Logging; using ASC.Common.Threading; using ASC.Common.Threading.Progress; using ASC.Core; -using ASC.Core.Common.Settings; +using ASC.Core.Common.Settings; using ASC.Core.Tenants; -using ASC.Data.Storage.Configuration; +using ASC.Data.Storage.Configuration; + using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; namespace ASC.Data.Storage @@ -54,10 +56,10 @@ namespace ASC.Data.Storage private static readonly object Locker; public IServiceProvider ServiceProvider { get; } - public TenantManager TenantManager { get; } - public SettingsManager SettingsManager { get; } - public StorageSettingsHelper StorageSettingsHelper { get; } - + public TenantManager TenantManager { get; } + public SettingsManager SettingsManager { get; } + public StorageSettingsHelper StorageSettingsHelper { get; } + static StaticUploader() { Scheduler = new LimitedConcurrencyLevelTaskScheduler(4); @@ -66,16 +68,16 @@ namespace ASC.Data.Storage TokenSource = new CancellationTokenSource(); } - public StaticUploader( - IServiceProvider serviceProvider, - TenantManager tenantManager, - SettingsManager settingsManager, + public StaticUploader( + IServiceProvider serviceProvider, + TenantManager tenantManager, + SettingsManager settingsManager, StorageSettingsHelper storageSettingsHelper) { ServiceProvider = serviceProvider; - TenantManager = tenantManager; - SettingsManager = settingsManager; - StorageSettingsHelper = storageSettingsHelper; + TenantManager = tenantManager; + SettingsManager = settingsManager; + StorageSettingsHelper = storageSettingsHelper; } public string UploadFile(string relativePath, string mappedPath, Action onComplete = null) @@ -212,8 +214,8 @@ namespace ASC.Data.Storage var tenant = tenantManager.GetTenant(tenantId); tenantManager.SetCurrentTenant(tenant); - var SecurityContext = scope.ServiceProvider.GetService(); - var SettingsManager = scope.ServiceProvider.GetService(); + var SecurityContext = scope.ServiceProvider.GetService(); + var SettingsManager = scope.ServiceProvider.GetService(); var StorageSettingsHelper = scope.ServiceProvider.GetService(); SecurityContext.AuthenticateMe(tenant.OwnerId); @@ -290,7 +292,7 @@ namespace ASC.Data.Storage public static class StaticUploaderExtension { - public static IServiceCollection AddStaticUploaderService(this IServiceCollection services) + public static DIHelper AddStaticUploaderService(this DIHelper services) { services.TryAddScoped(); diff --git a/common/ASC.Data.Storage/StorageFactory.cs b/common/ASC.Data.Storage/StorageFactory.cs index 11581f3c08..3bb6cb7a2b 100644 --- a/common/ASC.Data.Storage/StorageFactory.cs +++ b/common/ASC.Data.Storage/StorageFactory.cs @@ -28,6 +28,7 @@ using System; using System.Collections.Generic; using System.Linq; +using ASC.Common; using ASC.Common.Caching; using ASC.Common.Logging; using ASC.Core; @@ -40,7 +41,6 @@ using ASC.Security.Cryptography; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; namespace ASC.Data.Storage @@ -232,20 +232,20 @@ namespace ASC.Data.Storage //Make tennant path tenant = TenantPath.CreatePath(tenant); - var store = DataStoreCache.Get(tenant, module); - if (store == null) + //remove cache + //var store = DataStoreCache.Get(tenant, module); + //if (store == null) + //{ + var section = StorageFactoryConfig.Section; + if (section == null) { - var section = StorageFactoryConfig.Section; - if (section == null) - { - throw new InvalidOperationException("config section not found"); - } - - var settings = SettingsManager.LoadForTenant(tenantId); - - store = GetStoreAndCache(tenant, module, StorageSettingsHelper.DataStoreConsumer(settings), controller); + throw new InvalidOperationException("config section not found"); } - return store; + + var settings = SettingsManager.LoadForTenant(tenantId); + + //} + return GetDataStore(tenant, module, StorageSettingsHelper.DataStoreConsumer(settings), controller); } public IDataStore GetStorageFromConsumer(string configpath, string tenant, string module, DataStoreConsumer consumer) @@ -311,14 +311,14 @@ namespace ASC.Data.Storage public static class StorageFactoryExtension { - public static IServiceCollection AddStorageFactoryConfigService(this IServiceCollection services) + public static DIHelper AddStorageFactoryConfigService(this DIHelper services) { services.TryAddSingleton(); return services; } - public static IServiceCollection AddStorageFactoryService(this IServiceCollection services) + public static DIHelper AddStorageFactoryService(this DIHelper services) { services.TryAddScoped(); services.TryAddSingleton(); diff --git a/common/ASC.Data.Storage/StorageHandler.cs b/common/ASC.Data.Storage/StorageHandler.cs index 0d2f9973a5..c5bc3f1001 100644 --- a/common/ASC.Data.Storage/StorageHandler.cs +++ b/common/ASC.Data.Storage/StorageHandler.cs @@ -25,23 +25,24 @@ using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Net; -using System.Threading.Tasks; -using System.Web; +using System.Threading.Tasks; +using System.Web; -using ASC.Common.Web; +using ASC.Common; +using ASC.Common.Web; using ASC.Core; -using ASC.Security.Cryptography; - -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; -using Microsoft.Extensions.DependencyInjection; - +using ASC.Security.Cryptography; + +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; + namespace ASC.Data.Storage.DiscStorage { public class StorageHandler @@ -52,23 +53,23 @@ namespace ASC.Data.Storage.DiscStorage private readonly bool _checkAuth; public StorageHandler(IServiceProvider serviceProvider, string path, string module, string domain, bool checkAuth = true) - { - ServiceProvider = serviceProvider; + { + ServiceProvider = serviceProvider; _path = path; _module = module; _domain = domain; _checkAuth = checkAuth; - } - - public IServiceProvider ServiceProvider { get; } - + } + + public IServiceProvider ServiceProvider { get; } + public async Task Invoke(HttpContext context) - { - using var scope = ServiceProvider.CreateScope(); - var tenantManager = scope.ServiceProvider.GetService(); - var securityContext = scope.ServiceProvider.GetService(); - var storageFactory = scope.ServiceProvider.GetService(); - var emailValidationKeyProvider = scope.ServiceProvider.GetService(); + { + using var scope = ServiceProvider.CreateScope(); + var tenantManager = scope.ServiceProvider.GetService(); + var securityContext = scope.ServiceProvider.GetService(); + var storageFactory = scope.ServiceProvider.GetService(); + var emailValidationKeyProvider = scope.ServiceProvider.GetService(); if (_checkAuth && !securityContext.IsAuthenticated) { @@ -108,7 +109,7 @@ namespace ASC.Data.Storage.DiscStorage if (storage.IsSupportInternalUri && bigSize < storage.GetFileSize(_domain, path)) { var uri = storage.GetInternalUri(_domain, path, TimeSpan.FromMinutes(15), headers); - + //TODO //context.Response.Cache.SetAllowResponseInBrowserHistory(false); //context.Response.Cache.SetCacheability(HttpCacheability.NoCache); @@ -126,59 +127,59 @@ namespace ASC.Data.Storage.DiscStorage using (var stream = storage.GetReadStream(_domain, path)) { await stream.StreamCopyToAsync(context.Response.Body); - } - - var headersToCopy = new List { "Content-Disposition", "Cache-Control", "Content-Encoding", "Content-Language", "Content-Type", "Expires" }; - foreach (var h in headers) - { - var toCopy = headersToCopy.Find(x => h.StartsWith(x)); - if (string.IsNullOrEmpty(toCopy)) continue; - context.Response.Headers[toCopy] = h.Substring(toCopy.Length + 1); + } + + var headersToCopy = new List { "Content-Disposition", "Cache-Control", "Content-Encoding", "Content-Language", "Content-Type", "Expires" }; + foreach (var h in headers) + { + var toCopy = headersToCopy.Find(x => h.StartsWith(x)); + if (string.IsNullOrEmpty(toCopy)) continue; + context.Response.Headers[toCopy] = h.Substring(toCopy.Length + 1); } context.Response.ContentType = MimeMapping.GetMimeMapping(path); if (encoding != null) - context.Response.Headers["Content-Encoding"] = encoding; - - string GetRouteValue(string name) - { - return (context.GetRouteValue(name) ?? "").ToString(); + context.Response.Headers["Content-Encoding"] = encoding; + + string GetRouteValue(string name) + { + return (context.GetRouteValue(name) ?? "").ToString(); } } - } - - public static class StorageHandlerExtensions - { + } + + public static class StorageHandlerExtensions + { public static IEndpointRouteBuilder RegisterStorageHandler(this IEndpointRouteBuilder builder, string module, string domain, bool publicRoute = false) - { - var pathUtils = builder.ServiceProvider.GetService(); + { + var pathUtils = builder.ServiceProvider.GetService(); var virtPath = pathUtils.ResolveVirtualPath(module, domain); - virtPath = virtPath.TrimStart('/'); - - var handler = new StorageHandler(builder.ServiceProvider, string.Empty, module, domain, !publicRoute); - var url = virtPath + "{*pathInfo}"; - - if (!builder.DataSources.Any(r => r.Endpoints.Any(e => e.DisplayName == url))) - { - builder.Map(url, handler.Invoke); - - var newUrl = url.Replace("{0}", "{t1}/{t2}/{t3}"); - - if (newUrl != url) - { - builder.Map(url, handler.Invoke); - } - } - + virtPath = virtPath.TrimStart('/'); + + var handler = new StorageHandler(builder.ServiceProvider, string.Empty, module, domain, !publicRoute); + var url = virtPath + "{*pathInfo}"; + + if (!builder.DataSources.Any(r => r.Endpoints.Any(e => e.DisplayName == url))) + { + builder.Map(url, handler.Invoke); + + var newUrl = url.Replace("{0}", "{t1}/{t2}/{t3}"); + + if (newUrl != url) + { + builder.Map(url, handler.Invoke); + } + } + return builder; - } - public static IServiceCollection AddStorageHandlerService(this IServiceCollection services) - { - return services - .AddTenantManagerService() - .AddSecurityContextService() - .AddStorageFactoryService() - .AddEmailValidationKeyProviderService(); - } + } + public static DIHelper AddStorageHandlerService(this DIHelper services) + { + return services + .AddTenantManagerService() + .AddSecurityContextService() + .AddStorageFactoryService() + .AddEmailValidationKeyProviderService(); + } } } \ No newline at end of file diff --git a/common/ASC.Data.Storage/WebPath.cs b/common/ASC.Data.Storage/WebPath.cs index c01fbc74c7..c08d5be376 100644 --- a/common/ASC.Data.Storage/WebPath.cs +++ b/common/ASC.Data.Storage/WebPath.cs @@ -31,14 +31,13 @@ using System.IO; using System.Linq; using System.Net; +using ASC.Common; using ASC.Common.Logging; using ASC.Core; using ASC.Core.Common.Settings; using ASC.Data.Storage.Configuration; using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; @@ -235,7 +234,7 @@ namespace ASC.Data.Storage public static class WebPathExtension { - public static IServiceCollection AddWebPathService(this IServiceCollection services) + public static DIHelper AddWebPathService(this DIHelper services) { services.TryAddScoped(); @@ -245,7 +244,7 @@ namespace ASC.Data.Storage .AddWebPathSettingsService() .AddCoreBaseSettingsService(); } - public static IServiceCollection AddWebPathSettingsService(this IServiceCollection services) + public static DIHelper AddWebPathSettingsService(this DIHelper services) { services.TryAddSingleton(); diff --git a/common/ASC.FederatedLogin/AccountLinker.cs b/common/ASC.FederatedLogin/AccountLinker.cs index 8718fc43fb..17496a2fbb 100644 --- a/common/ASC.FederatedLogin/AccountLinker.cs +++ b/common/ASC.FederatedLogin/AccountLinker.cs @@ -28,6 +28,7 @@ using System; using System.Collections.Generic; using System.Linq; +using ASC.Common; using ASC.Common.Caching; using ASC.Common.Utils; using ASC.Core.Common.EF; @@ -37,8 +38,6 @@ using ASC.FederatedLogin.Profile; using ASC.Security.Cryptography; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; namespace ASC.FederatedLogin @@ -218,7 +217,7 @@ namespace ASC.FederatedLogin public static class AccountLinkerStorageExtension { - public static IServiceCollection AddAccountLinkerStorageService(this IServiceCollection services) + public static DIHelper AddAccountLinkerStorageService(this DIHelper services) { services.TryAddSingleton(); services.TryAddSingleton(typeof(ICacheNotify<>), typeof(KafkaCache<>)); @@ -229,7 +228,7 @@ namespace ASC.FederatedLogin public static class AccountLinkerExtension { - public static IServiceCollection AddAccountLinker(this IServiceCollection services) + public static DIHelper AddAccountLinker(this DIHelper services) { services.TryAddScoped(); services.TryAddScoped, ConfigureAccountLinker>(); diff --git a/common/ASC.FederatedLogin/LoginProviders/BoxLoginProvider.cs b/common/ASC.FederatedLogin/LoginProviders/BoxLoginProvider.cs index 7467f75c7f..e76dd38c16 100644 --- a/common/ASC.FederatedLogin/LoginProviders/BoxLoginProvider.cs +++ b/common/ASC.FederatedLogin/LoginProviders/BoxLoginProvider.cs @@ -24,14 +24,15 @@ */ -using System.Collections.Generic; - -using ASC.Common.Caching; -using ASC.Core; -using ASC.Core.Common.Configuration; - -using Microsoft.Extensions.Configuration; +using System.Collections.Generic; +using ASC.Common; +using ASC.Common.Caching; +using ASC.Core; +using ASC.Core.Common.Configuration; + +using Microsoft.Extensions.Configuration; + namespace ASC.FederatedLogin.LoginProviders { public class BoxLoginProvider : Consumer, IOAuthProvider @@ -63,16 +64,30 @@ namespace ASC.FederatedLogin.LoginProviders public BoxLoginProvider() { } - public BoxLoginProvider( - TenantManager tenantManager, - CoreBaseSettings coreBaseSettings, - CoreSettings coreSettings, - ConsumerFactory consumerFactory, - IConfiguration configuration, - ICacheNotify cache, + public BoxLoginProvider( + TenantManager tenantManager, + CoreBaseSettings coreBaseSettings, + CoreSettings coreSettings, + ConsumerFactory consumerFactory, + IConfiguration configuration, + ICacheNotify cache, string name, int order, Dictionary props, Dictionary additional = null) : base(tenantManager, coreBaseSettings, coreSettings, consumerFactory, configuration, cache, name, order, props, additional) { } } + + public static class BoxLoginProviderExtension + { + public static DIHelper AddBoxLoginProviderService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddConsumerFactoryService() + .AddKafkaService() + .AddTenantManagerService() + .AddCoreBaseSettingsService() + .AddCoreSettingsService(); + } + } } \ No newline at end of file diff --git a/common/ASC.FederatedLogin/LoginProviders/DocuSignLoginProvider.cs b/common/ASC.FederatedLogin/LoginProviders/DocuSignLoginProvider.cs index f295ba9d39..0f618198fe 100644 --- a/common/ASC.FederatedLogin/LoginProviders/DocuSignLoginProvider.cs +++ b/common/ASC.FederatedLogin/LoginProviders/DocuSignLoginProvider.cs @@ -26,15 +26,16 @@ using System; using System.Collections.Generic; -using System.Text; +using System.Text; -using ASC.Common.Caching; -using ASC.Core; +using ASC.Common; +using ASC.Common.Caching; +using ASC.Core; using ASC.Core.Common.Configuration; -using ASC.FederatedLogin.Helpers; - -using Microsoft.Extensions.Configuration; - +using ASC.FederatedLogin.Helpers; + +using Microsoft.Extensions.Configuration; + namespace ASC.FederatedLogin.LoginProviders { public class DocuSignLoginProvider : Consumer, IOAuthProvider @@ -64,13 +65,13 @@ namespace ASC.FederatedLogin.LoginProviders public DocuSignLoginProvider() { } - public DocuSignLoginProvider( - TenantManager tenantManager, - CoreBaseSettings coreBaseSettings, - CoreSettings coreSettings, - ConsumerFactory consumerFactory, - IConfiguration configuration, - ICacheNotify cache, + public DocuSignLoginProvider( + TenantManager tenantManager, + CoreBaseSettings coreBaseSettings, + CoreSettings coreSettings, + ConsumerFactory consumerFactory, + IConfiguration configuration, + ICacheNotify cache, string name, int order, Dictionary props, Dictionary additional = null) : base(tenantManager, coreBaseSettings, coreSettings, consumerFactory, configuration, cache, name, order, props, additional) { @@ -133,4 +134,17 @@ namespace ASC.FederatedLogin.LoginProviders return refreshed; } } + public static class DocuSignLoginProviderExtension + { + public static DIHelper AddDocuSignLoginProviderService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddConsumerFactoryService() + .AddKafkaService() + .AddTenantManagerService() + .AddCoreBaseSettingsService() + .AddCoreSettingsService(); + } + } } \ No newline at end of file diff --git a/common/ASC.FederatedLogin/LoginProviders/DropboxLoginProvider.cs b/common/ASC.FederatedLogin/LoginProviders/DropboxLoginProvider.cs index 7a4fa86619..70b7bf689d 100644 --- a/common/ASC.FederatedLogin/LoginProviders/DropboxLoginProvider.cs +++ b/common/ASC.FederatedLogin/LoginProviders/DropboxLoginProvider.cs @@ -24,14 +24,15 @@ */ -using System.Collections.Generic; - -using ASC.Common.Caching; -using ASC.Core; -using ASC.Core.Common.Configuration; - -using Microsoft.Extensions.Configuration; +using System.Collections.Generic; +using ASC.Common; +using ASC.Common.Caching; +using ASC.Core; +using ASC.Core.Common.Configuration; + +using Microsoft.Extensions.Configuration; + namespace ASC.FederatedLogin.LoginProviders { public class DropboxLoginProvider : Consumer, IOAuthProvider @@ -60,16 +61,29 @@ namespace ASC.FederatedLogin.LoginProviders public DropboxLoginProvider() { } - public DropboxLoginProvider( - TenantManager tenantManager, - CoreBaseSettings coreBaseSettings, - CoreSettings coreSettings, - ConsumerFactory consumerFactory, - IConfiguration configuration, - ICacheNotify cache, + public DropboxLoginProvider( + TenantManager tenantManager, + CoreBaseSettings coreBaseSettings, + CoreSettings coreSettings, + ConsumerFactory consumerFactory, + IConfiguration configuration, + ICacheNotify cache, string name, int order, Dictionary props, Dictionary additional = null) : base(tenantManager, coreBaseSettings, coreSettings, consumerFactory, configuration, cache, name, order, props, additional) { } } + public static class DropboxLoginProviderExtension + { + public static DIHelper AddDropboxLoginProviderService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddConsumerFactoryService() + .AddKafkaService() + .AddTenantManagerService() + .AddCoreBaseSettingsService() + .AddCoreSettingsService(); + } + } } \ No newline at end of file diff --git a/common/ASC.FederatedLogin/LoginProviders/EncryptionLoginProvider.cs b/common/ASC.FederatedLogin/LoginProviders/EncryptionLoginProvider.cs index e34cf89e86..1a566d5ff0 100644 --- a/common/ASC.FederatedLogin/LoginProviders/EncryptionLoginProvider.cs +++ b/common/ASC.FederatedLogin/LoginProviders/EncryptionLoginProvider.cs @@ -26,42 +26,59 @@ using System; using System.Linq; -using System.Security; +using System.Security; -using ASC.Common.Utils; +using ASC.Common; +using ASC.Common.Utils; using ASC.Core; using ASC.Core.Users; using ASC.FederatedLogin; -using ASC.FederatedLogin.Profile; -using ASC.Security.Cryptography; - -using Microsoft.Extensions.Options; +using ASC.FederatedLogin.Profile; +using ASC.Security.Cryptography; +using Microsoft.Extensions.Options; + using SecurityContext = ASC.Core.SecurityContext; namespace ASC.Web.Studio.Core { - public static class BlockchainLoginProvider + public class EncryptionLoginProvider { - public static void UpdateData( - string account, - UserManager userManager, - TenantManager tenantManager, - SecurityContext securityContext, - Signature signature, - InstanceCrypto instanceCrypto, - IOptionsSnapshot snapshot) - { - var tenant = tenantManager.GetCurrentTenant(); - var user = userManager.GetUsers(securityContext.CurrentAccount.ID); - if (!securityContext.IsAuthenticated || user.IsVisitor(userManager)) throw new SecurityException(); + public UserManager UserManager { get; } + public TenantManager TenantManager { get; } + public SecurityContext SecurityContext { get; } + public Signature Signature { get; } + public InstanceCrypto InstanceCrypto { get; } + public IOptionsSnapshot Snapshot { get; } - var loginProfile = new LoginProfile(signature, instanceCrypto) - { - Provider = ProviderConstants.Encryption, + public EncryptionLoginProvider( + UserManager userManager, + TenantManager tenantManager, + SecurityContext securityContext, + Signature signature, + InstanceCrypto instanceCrypto, + IOptionsSnapshot snapshot) + { + UserManager = userManager; + TenantManager = tenantManager; + SecurityContext = securityContext; + Signature = signature; + InstanceCrypto = instanceCrypto; + Snapshot = snapshot; + } + + public void UpdateAddress(string account) + { + var tenant = TenantManager.GetCurrentTenant(); + var user = UserManager.GetUsers(SecurityContext.CurrentAccount.ID); + if (!SecurityContext.IsAuthenticated || user.IsVisitor(UserManager)) throw new SecurityException(); + + var loginProfile = new LoginProfile(Signature, InstanceCrypto) + { + Provider = ProviderConstants.Encryption, }; - var linker = snapshot.Get("webstudio"); + var linker = Snapshot.Get("webstudio"); if (string.IsNullOrEmpty(account)) { linker.RemoveLink(user.ID.ToString(), loginProfile); @@ -74,14 +91,14 @@ namespace ASC.Web.Studio.Core } - public static string GetAddress(SecurityContext securityContext, IOptionsSnapshot snapshot) + public string GetAddress() { - return GetAddress(securityContext.CurrentAccount.ID, snapshot); + return GetAddress(SecurityContext.CurrentAccount.ID); } - public static string GetAddress(Guid userId, IOptionsSnapshot snapshot) + public string GetAddress(Guid userId) { - var linker = snapshot.Get("webstudio"); + var linker = Snapshot.Get("webstudio"); var profile = linker.GetLinkedProfiles(userId.ToString(), ProviderConstants.Encryption).FirstOrDefault(); if (profile == null) return null; @@ -89,4 +106,19 @@ namespace ASC.Web.Studio.Core return account; } } + public static class EncryptionLoginProviderExtension + { + public static DIHelper AddEncryptionLoginProviderService(this DIHelper services) + { + services.TryAddScoped(); + + return services + .AddUserManagerService() + .AddTenantManagerService() + .AddSecurityContextService() + .AddSignatureService() + .AddInstanceCryptoService() + .AddAccountLinker(); + } + } } \ No newline at end of file diff --git a/common/ASC.FederatedLogin/LoginProviders/GoogleLoginProvider.cs b/common/ASC.FederatedLogin/LoginProviders/GoogleLoginProvider.cs index 3808e9739e..0f8b075503 100644 --- a/common/ASC.FederatedLogin/LoginProviders/GoogleLoginProvider.cs +++ b/common/ASC.FederatedLogin/LoginProviders/GoogleLoginProvider.cs @@ -28,6 +28,7 @@ using System; using System.Collections.Generic; using System.Web; +using ASC.Common; using ASC.Common.Caching; using ASC.Common.Utils; using ASC.Core; @@ -180,4 +181,20 @@ namespace ASC.FederatedLogin.LoginProviders public bool primary = false; } } + + public static class GoogleLoginProviderExtension + { + public static DIHelper AddGoogleLoginProviderService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddConsumerFactoryService() + .AddKafkaService() + .AddTenantManagerService() + .AddCoreBaseSettingsService() + .AddCoreSettingsService() + .AddSignatureService() + .AddInstanceCryptoService(); + } + } } \ No newline at end of file diff --git a/common/ASC.FederatedLogin/LoginProviders/OneDriveLoginProvider.cs b/common/ASC.FederatedLogin/LoginProviders/OneDriveLoginProvider.cs index 2b5f315274..5a765a09a4 100644 --- a/common/ASC.FederatedLogin/LoginProviders/OneDriveLoginProvider.cs +++ b/common/ASC.FederatedLogin/LoginProviders/OneDriveLoginProvider.cs @@ -26,6 +26,7 @@ using System.Collections.Generic; +using ASC.Common; using ASC.Common.Caching; using ASC.Core; using ASC.Core.Common.Configuration; @@ -75,4 +76,18 @@ namespace ASC.FederatedLogin.LoginProviders { } } + + public static class OneDriveLoginProviderExtension + { + public static DIHelper AddOneDriveLoginProviderService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddConsumerFactoryService() + .AddKafkaService() + .AddTenantManagerService() + .AddCoreBaseSettingsService() + .AddCoreSettingsService(); + } + } } \ No newline at end of file diff --git a/common/ASC.FederatedLogin/Profile/LoginProfile.cs b/common/ASC.FederatedLogin/Profile/LoginProfile.cs index 8a5a2452ff..b952e7deda 100644 --- a/common/ASC.FederatedLogin/Profile/LoginProfile.cs +++ b/common/ASC.FederatedLogin/Profile/LoginProfile.cs @@ -34,13 +34,16 @@ using System.Runtime.Serialization.Json; using System.Security.Permissions; using System.Text; using System.Web; + using ASC.Common.Utils; using ASC.FederatedLogin.Helpers; using ASC.Security.Cryptography; + using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Caching.Memory; + using Newtonsoft.Json; namespace ASC.FederatedLogin.Profile diff --git a/common/ASC.Feed/Data/FeedAggregateDataProvider.cs b/common/ASC.Feed/Data/FeedAggregateDataProvider.cs index c1e2574fe2..f6f0406498 100644 --- a/common/ASC.Feed/Data/FeedAggregateDataProvider.cs +++ b/common/ASC.Feed/Data/FeedAggregateDataProvider.cs @@ -29,6 +29,7 @@ using System.Collections.Generic; using System.Data; using System.Linq; +using ASC.Common; using ASC.Core; using ASC.Core.Common.EF; using ASC.Core.Common.EF.Context; @@ -36,7 +37,6 @@ using ASC.Core.Common.EF.Model; using ASC.Core.Tenants; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; @@ -380,7 +380,7 @@ namespace ASC.Feed.Data public static class FeedAggregateDataProviderExtension { - public static IServiceCollection AddFeedAggregateDataProvider(this IServiceCollection services) + public static DIHelper AddFeedAggregateDataProvider(this DIHelper services) { return services .AddAuthContextService() diff --git a/common/ASC.Feed/Data/FeedReadedDataProvider.cs b/common/ASC.Feed/Data/FeedReadedDataProvider.cs index cd63ad4b1d..58c9d6bd93 100644 --- a/common/ASC.Feed/Data/FeedReadedDataProvider.cs +++ b/common/ASC.Feed/Data/FeedReadedDataProvider.cs @@ -28,13 +28,12 @@ using System; using System.Collections.Generic; using System.Linq; +using ASC.Common; using ASC.Core; using ASC.Core.Common.EF; using ASC.Core.Common.EF.Context; using ASC.Core.Common.EF.Model; -using Microsoft.Extensions.DependencyInjection; - namespace ASC.Feed.Data { public class FeedReadedDataProvider @@ -130,7 +129,7 @@ namespace ASC.Feed.Data public static class FeedReadedDataProviderExtension { - public static IServiceCollection AddFeedReadedDataProvider(this IServiceCollection services) + public static DIHelper AddFeedReadedDataProvider(this DIHelper services) { return services .AddAuthContextService() diff --git a/common/ASC.IPSecurity/IPRestrictionsRepository.cs b/common/ASC.IPSecurity/IPRestrictionsRepository.cs index fcfdf48707..f7e43b93c8 100644 --- a/common/ASC.IPSecurity/IPRestrictionsRepository.cs +++ b/common/ASC.IPSecurity/IPRestrictionsRepository.cs @@ -27,13 +27,11 @@ using System.Collections.Generic; using System.Linq; +using ASC.Common; using ASC.Core.Common.EF; using ASC.Core.Common.EF.Context; using ASC.Core.Common.EF.Model; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; - namespace ASC.IPSecurity { public class IPRestrictionsRepository @@ -81,7 +79,7 @@ namespace ASC.IPSecurity } public static class IPRestrictionsRepositoryExtension { - public static IServiceCollection AddIPRestrictionsRepositoryService(this IServiceCollection services) + public static DIHelper AddIPRestrictionsRepositoryService(this DIHelper services) { services.TryAddScoped(); diff --git a/common/ASC.IPSecurity/IPRestrictionsService.cs b/common/ASC.IPSecurity/IPRestrictionsService.cs index 5b6605de96..fb5cd5240c 100644 --- a/common/ASC.IPSecurity/IPRestrictionsService.cs +++ b/common/ASC.IPSecurity/IPRestrictionsService.cs @@ -26,9 +26,9 @@ using System; using System.Collections.Generic; + +using ASC.Common; using ASC.Common.Caching; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; namespace ASC.IPSecurity { @@ -89,7 +89,7 @@ namespace ASC.IPSecurity public static class IPRestrictionsServiceExtension { - public static IServiceCollection AddIPRestrictionsService(this IServiceCollection services) + public static DIHelper AddIPRestrictionsService(this DIHelper services) { services.TryAddScoped(); services.TryAddSingleton(); diff --git a/common/ASC.IPSecurity/IPSecurity.cs b/common/ASC.IPSecurity/IPSecurity.cs index bf4fde7adc..bc94fe75e8 100644 --- a/common/ASC.IPSecurity/IPSecurity.cs +++ b/common/ASC.IPSecurity/IPSecurity.cs @@ -29,6 +29,7 @@ using System.Linq; using System.Net; using System.Web; +using ASC.Common; using ASC.Common.Logging; using ASC.Core; using ASC.Core.Common.Settings; @@ -36,8 +37,6 @@ using ASC.Core.Common.Settings; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; namespace ASC.IPSecurity @@ -145,7 +144,7 @@ namespace ASC.IPSecurity public static class IPSecurityExtension { - public static IServiceCollection AddIPSecurityService(this IServiceCollection services) + public static DIHelper AddIPSecurityService(this DIHelper services) { services.TryAddScoped(); diff --git a/common/ASC.MessagingSystem/DbSender/DbMessageSender.cs b/common/ASC.MessagingSystem/DbSender/DbMessageSender.cs index e7d2583884..ee0f610b0f 100644 --- a/common/ASC.MessagingSystem/DbSender/DbMessageSender.cs +++ b/common/ASC.MessagingSystem/DbSender/DbMessageSender.cs @@ -25,11 +25,12 @@ using System; + +using ASC.Common; using ASC.Common.Logging; using ASC.Core.Common.EF.Context; + using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; namespace ASC.MessagingSystem.DbSender @@ -69,7 +70,7 @@ namespace ASC.MessagingSystem.DbSender public static class DbMessageSenderExtension { - public static IServiceCollection AddDbMessageSenderService(this IServiceCollection services) + public static DIHelper AddDbMessageSenderService(this DIHelper services) { services.TryAddSingleton(); services.TryAddSingleton(); diff --git a/common/ASC.MessagingSystem/MessageFactory.cs b/common/ASC.MessagingSystem/MessageFactory.cs index c2ba8061c6..125865b313 100644 --- a/common/ASC.MessagingSystem/MessageFactory.cs +++ b/common/ASC.MessagingSystem/MessageFactory.cs @@ -28,11 +28,12 @@ using System; using System.Collections.Generic; using System.Linq; using System.Web; + +using ASC.Common; using ASC.Common.Logging; using ASC.Core; + using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; namespace ASC.MessagingSystem @@ -138,7 +139,7 @@ namespace ASC.MessagingSystem } public static class MessageFactoryExtension { - public static IServiceCollection AddMessageFactoryService(this IServiceCollection services) + public static DIHelper AddMessageFactoryService(this DIHelper services) { services.TryAddScoped(); return services diff --git a/common/ASC.MessagingSystem/MessagePolicy.cs b/common/ASC.MessagingSystem/MessagePolicy.cs index 472a39a141..fe776b7d55 100644 --- a/common/ASC.MessagingSystem/MessagePolicy.cs +++ b/common/ASC.MessagingSystem/MessagePolicy.cs @@ -27,9 +27,10 @@ using System; using System.Collections.Generic; using System.Linq; + +using ASC.Common; + using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; namespace ASC.MessagingSystem { @@ -65,7 +66,7 @@ namespace ASC.MessagingSystem public static class MessagePolicyExtension { - public static IServiceCollection AddMessagePolicyService(this IServiceCollection services) + public static DIHelper AddMessagePolicyService(this DIHelper services) { services.TryAddSingleton(); diff --git a/common/ASC.MessagingSystem/MessageService.cs b/common/ASC.MessagingSystem/MessageService.cs index 008eabf02c..f9935769ab 100644 --- a/common/ASC.MessagingSystem/MessageService.cs +++ b/common/ASC.MessagingSystem/MessageService.cs @@ -27,13 +27,12 @@ using System.Collections.Generic; using System.Linq; +using ASC.Common; using ASC.Common.Logging; using ASC.MessagingSystem.DbSender; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; namespace ASC.MessagingSystem @@ -290,7 +289,7 @@ namespace ASC.MessagingSystem public static class MessageServiceExtension { - public static IServiceCollection AddMessageServiceService(this IServiceCollection services) + public static DIHelper AddMessageServiceService(this DIHelper services) { services.TryAddScoped(); diff --git a/common/ASC.MessagingSystem/MessageTarget.cs b/common/ASC.MessagingSystem/MessageTarget.cs index 0f1073a569..76306526ef 100644 --- a/common/ASC.MessagingSystem/MessageTarget.cs +++ b/common/ASC.MessagingSystem/MessageTarget.cs @@ -27,9 +27,10 @@ using System; using System.Collections.Generic; using System.Linq; + +using ASC.Common; using ASC.Common.Logging; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; + using Microsoft.Extensions.Options; namespace ASC.MessagingSystem @@ -97,7 +98,7 @@ namespace ASC.MessagingSystem public static class MessageTargetExtension { - public static IServiceCollection AddMessageTargetService(this IServiceCollection services) + public static DIHelper AddMessageTargetService(this DIHelper services) { services.TryAddSingleton(); diff --git a/common/ASC.Notify.Textile/JabberStyler.cs b/common/ASC.Notify.Textile/JabberStyler.cs index 82752aecf4..b859db216b 100644 --- a/common/ASC.Notify.Textile/JabberStyler.cs +++ b/common/ASC.Notify.Textile/JabberStyler.cs @@ -27,15 +27,16 @@ using System; using System.Text.RegularExpressions; using System.Web; + +using ASC.Common; using ASC.Common.Notify.Patterns; using ASC.Core; using ASC.Notify.Messages; using ASC.Notify.Patterns; using ASC.Security.Cryptography; using ASC.Web.Core.WhiteLabel; + using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; namespace ASC.Notify.Textile { @@ -116,7 +117,7 @@ namespace ASC.Notify.Textile public static class StylerExtension { - public static IServiceCollection AddStylerService(this IServiceCollection services) + public static DIHelper AddStylerService(this DIHelper services) { return services .AddCoreBaseSettingsService() @@ -124,7 +125,7 @@ namespace ASC.Notify.Textile .AddMailWhiteLabelSettingsService() ; } - public static IServiceCollection AddJabberStylerService(this IServiceCollection services) + public static DIHelper AddJabberStylerService(this DIHelper services) { services.TryAddScoped(); return services.AddStylerService(); diff --git a/common/ASC.Notify.Textile/PushStyler.cs b/common/ASC.Notify.Textile/PushStyler.cs index c19c4637f0..1f3ccb94f2 100644 --- a/common/ASC.Notify.Textile/PushStyler.cs +++ b/common/ASC.Notify.Textile/PushStyler.cs @@ -26,15 +26,16 @@ using System; using System.Text.RegularExpressions; + +using ASC.Common; using ASC.Common.Notify.Patterns; using ASC.Core; using ASC.Notify.Messages; using ASC.Notify.Patterns; using ASC.Security.Cryptography; using ASC.Web.Core.WhiteLabel; + using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; namespace ASC.Notify.Textile { @@ -75,7 +76,7 @@ namespace ASC.Notify.Textile } public static class PushStylerExtension { - public static IServiceCollection AddPushStylerService(this IServiceCollection services) + public static DIHelper AddPushStylerService(this DIHelper services) { services.TryAddScoped(); return services.AddStylerService(); diff --git a/common/ASC.Notify.Textile/TextileStyler.cs b/common/ASC.Notify.Textile/TextileStyler.cs index f7f7cd13a3..26133fe30b 100644 --- a/common/ASC.Notify.Textile/TextileStyler.cs +++ b/common/ASC.Notify.Textile/TextileStyler.cs @@ -30,6 +30,7 @@ using System.Reflection; using System.Text; using System.Text.RegularExpressions; +using ASC.Common; using ASC.Common.Notify.Patterns; using ASC.Core; using ASC.Core.Common.WhiteLabel; @@ -41,8 +42,6 @@ using ASC.Web.Core.WhiteLabel; using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Textile; using Textile.Blocks; @@ -329,7 +328,7 @@ namespace ASC.Notify.Textile public static class TextileStylerExtension { - public static IServiceCollection AddTextileStylerService(this IServiceCollection services) + public static DIHelper AddTextileStylerService(this DIHelper services) { services.TryAddScoped(); return services.AddStylerService(); diff --git a/common/ASC.Resource.Manager/Startup.cs b/common/ASC.Resource.Manager/Startup.cs index d8fca7f911..3b4a54fbb2 100644 --- a/common/ASC.Resource.Manager/Startup.cs +++ b/common/ASC.Resource.Manager/Startup.cs @@ -1,10 +1,10 @@ -using ASC.Common.Logging; +using ASC.Common; +using ASC.Common.Logging; using ASC.Core.Common.EF; using ASC.Core.Common.EF.Context; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; namespace ASC.Resource.Manager { @@ -22,11 +22,12 @@ namespace ASC.Resource.Manager public void ConfigureServices(IServiceCollection services) { + var diHelper = new DIHelper(services); services.AddLogging(); - services.TryAddScoped(); - services.AddDbContextManagerService(); - services.AddLoggerService(); - services.TryAddSingleton(Configuration); + diHelper.TryAddScoped(); + diHelper.AddDbContextManagerService(); + diHelper.AddLoggerService(); + diHelper.TryAddSingleton(Configuration); } } } diff --git a/common/services/ASC.ElasticSearch/ASC.ElasticSearch.csproj b/common/services/ASC.ElasticSearch/ASC.ElasticSearch.csproj new file mode 100644 index 0000000000..aee918a9d8 --- /dev/null +++ b/common/services/ASC.ElasticSearch/ASC.ElasticSearch.csproj @@ -0,0 +1,46 @@ + + + 9.0.30729 + {AE1A0E06-6CD4-4E1D-8209-22BBBD6D5652} + netstandard2.1 + + + ASC.ElasticSearch + Ascensio System SIA + ASC.ElasticSearch + (c) Ascensio System SIA. All rights reserved + false + + + full + + + none + true + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common/services/ASC.ElasticSearch/Attributes/ColumnAttribute.cs b/common/services/ASC.ElasticSearch/Attributes/ColumnAttribute.cs new file mode 100644 index 0000000000..046fa4f93f --- /dev/null +++ b/common/services/ASC.ElasticSearch/Attributes/ColumnAttribute.cs @@ -0,0 +1,134 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; + +namespace ASC.ElasticSearch +{ + [AttributeUsage(AttributeTargets.Property)] + public class ColumnAttribute : Attribute + { + public string ColumnName { get; private set; } + + public ColumnTypeEnum ColumnType { get; private set; } + + public int Order { get; private set; } + + public Analyzer Analyzer { get; set; } + + public Filter Filter { get; set; } + + public CharFilter CharFilter { get; set; } + + public ColumnAttribute(string columnName, int order, ColumnTypeEnum columnType = ColumnTypeEnum.Content, Filter filter = Filter.lowercase, CharFilter charFilter = CharFilter.io) + { + ColumnName = columnName; + ColumnType = columnType; + Order = order; + Analyzer = Analyzer.whitespace; + Filter = filter; + CharFilter = charFilter; + + } + } + + [AttributeUsage(AttributeTargets.Property)] + public class ColumnIdAttribute : ColumnAttribute + { + public ColumnIdAttribute(string columnName) + : base(columnName, -3, ColumnTypeEnum.Id) + { + } + } + + [AttributeUsage(AttributeTargets.Property)] + public class ColumnTenantIdAttribute : ColumnAttribute + { + public ColumnTenantIdAttribute(string columnName) + : base(columnName, -2, ColumnTypeEnum.TenantId) + { + } + } + + [AttributeUsage(AttributeTargets.Property)] + public class ColumnLastModifiedAttribute : ColumnAttribute + { + public ColumnLastModifiedAttribute(string columnName) + : base(columnName, -1, ColumnTypeEnum.LastModified) + { + } + } + + [AttributeUsage(AttributeTargets.Property)] + public class ColumnConditionAttribute : ColumnAttribute + { + internal object Value { get; private set; } + public ColumnConditionAttribute(string columnName, int order, object value = null) + : base(columnName, order, ColumnTypeEnum.Condition) + { + Value = value; + } + } + + [AttributeUsage(AttributeTargets.Property)] + public class ColumnMeta : ColumnAttribute + { + public ColumnMeta(string columnName, int order) + : base(columnName, order, ColumnTypeEnum.Meta) + { + } + } + + public enum ColumnTypeEnum + { + Id, + TenantId, + LastModified, + Content, + Condition, + Meta + } + + public enum Analyzer + { + standard, + whitespace, + uax_url_email + } + + [Flags] + public enum CharFilter + { + io, + html + } + + [Flags] + public enum Filter + { + lowercase + } +} diff --git a/common/services/ASC.ElasticSearch/Attributes/JoinAttribute.cs b/common/services/ASC.ElasticSearch/Attributes/JoinAttribute.cs new file mode 100644 index 0000000000..b09e347286 --- /dev/null +++ b/common/services/ASC.ElasticSearch/Attributes/JoinAttribute.cs @@ -0,0 +1,58 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; + +namespace ASC.ElasticSearch +{ + [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)] + public class JoinAttribute : Attribute + { + public JoinTypeEnum JoinType { get; private set; } + public string[] ColumnsFrom { get; private set; } + public string[] ColumnsTo { get; private set; } + + public JoinAttribute(JoinTypeEnum joinType, params string[] columns) + { + JoinType = joinType; + ColumnsFrom = new string[columns.Length]; + ColumnsTo = new string[columns.Length]; + + for (var i = 0; i< columns.Length; i++) + { + var column = columns[i].Split(':'); + ColumnsFrom[i] = column[0]; + ColumnsTo[i] = column[1]; + } + } + } + + public enum JoinTypeEnum + { + Inner, + Sub + } +} diff --git a/common/services/ASC.ElasticSearch/Config/ElasticSection.cs b/common/services/ASC.ElasticSearch/Config/ElasticSection.cs new file mode 100644 index 0000000000..f3adbf88ba --- /dev/null +++ b/common/services/ASC.ElasticSearch/Config/ElasticSection.cs @@ -0,0 +1,64 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Configuration; + +namespace ASC.ElasticSearch.Config +{ + class ElasticSection : ConfigurationSection + { + [ConfigurationProperty("host", IsRequired = true, DefaultValue = "localhost")] + public string Host + { + get { return (string)this["host"]; } + } + + [ConfigurationProperty("port", IsRequired = false, DefaultValue = "9200")] + public int Port + { + get { return Convert.ToInt32(this["port"]); } + } + + [ConfigurationProperty("scheme", IsRequired = false, DefaultValue = "http")] + public string Scheme + { + get { return (string)this["scheme"]; } + } + + [ConfigurationProperty("period", IsRequired = false, DefaultValue = 1)] + public int Period + { + get { return Convert.ToInt32(this["period"]); } + } + + [ConfigurationProperty("memoryLimit", IsRequired = false, DefaultValue = 10 * 1024 * 1024L)] + public long MemoryLimit + { + get { return Convert.ToInt64(this["memoryLimit"]); } + } + } +} diff --git a/common/services/ASC.ElasticSearch/Core/Document.cs b/common/services/ASC.ElasticSearch/Core/Document.cs new file mode 100644 index 0000000000..c4afa1bc34 --- /dev/null +++ b/common/services/ASC.ElasticSearch/Core/Document.cs @@ -0,0 +1,37 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using Nest; + +namespace ASC.ElasticSearch +{ + public class Document + { + public string Data { get; set; } + + public Attachment Attachment { get; set; } + } +} diff --git a/common/services/ASC.ElasticSearch/Core/SearchSettings.cs b/common/services/ASC.ElasticSearch/Core/SearchSettings.cs new file mode 100644 index 0000000000..4d5c00c3f2 --- /dev/null +++ b/common/services/ASC.ElasticSearch/Core/SearchSettings.cs @@ -0,0 +1,196 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; + +using ASC.Common; +using ASC.Core; +using ASC.Core.Common.Settings; + +using Autofac; + +using Microsoft.Extensions.DependencyInjection; + +using Newtonsoft.Json; + +namespace ASC.ElasticSearch.Core +{ + [Serializable] + [DataContract] + public class SearchSettings : ISettings + { + [DataMember(Name = "Data")] + public string Data { get; set; } + + public Guid ID + { + get { return new Guid("{93784AB2-10B5-4C2F-9B36-F2662CCCF316}"); } + } + + public ISettings GetDefault(IServiceProvider serviceProvider) + { + return new SearchSettings(); + } + + private List items; + internal List Items + { + get + { + if (items != null) return items; + var parsed = JsonConvert.DeserializeObject>(Data ?? ""); + return items = parsed ?? new List(); + } + set + { + items = value; + } + } + + internal bool IsEnabled(string name) + { + var wrapper = Items.FirstOrDefault(r => r.ID == name); + + return wrapper != null && wrapper.Enabled; + } + } + + public class SearchSettingsHelper + { + public SettingsManager SettingsManager { get; } + public CoreBaseSettings CoreBaseSettings { get; } + public FactoryIndexer FactoryIndexer { get; } + public IServiceProvider ServiceProvider { get; } + + public SearchSettingsHelper( + SettingsManager settingsManager, + CoreBaseSettings coreBaseSettings, + FactoryIndexer factoryIndexer, + IServiceProvider serviceProvider) + { + SettingsManager = settingsManager; + CoreBaseSettings = coreBaseSettings; + FactoryIndexer = factoryIndexer; + ServiceProvider = serviceProvider; + } + + public List GetAllItems() + { + if (!SearchByContentEnabled) return new List(); + + var settings = SettingsManager.Load(); + + return AllItems.Select(r => new SearchSettingsItem + { + ID = r.IndexName, + Enabled = settings.IsEnabled(r.IndexName), + Title = r.SettingsTitle + }).ToList(); + } + + private List allItems; + internal List AllItems + { + get + { + return allItems ?? (allItems = FactoryIndexer.Builder.Resolve>() + .OfType() + .ToList()); + } + } + + public void Set(List items) + { + if (!SearchByContentEnabled) return; + + var settings = SettingsManager.Load(); + + var settingsItems = settings.Items; + var toReIndex = !settingsItems.Any() ? items.Where(r => r.Enabled).ToList() : items.Where(item => settingsItems.Any(r => r.ID == item.ID && r.Enabled != item.Enabled)).ToList(); + + settings.Items = items; + settings.Data = JsonConvert.SerializeObject(items); + SettingsManager.Save(settings); + + //TODO: + //using (var service = new ServiceClient()) + //{ + // service.ReIndex(toReIndex.Select(r => r.ID).ToList(), TenantManager.GetCurrentTenant().TenantId); + //} + } + + public bool CanSearchByContent(int tenantId) where T : Wrapper + { + if (!SearchByContentEnabled) return false; + + if (!typeof(T).IsSubclassOf(typeof(WrapperWithDoc))) + { + return false; + } + + var settings = SettingsManager.LoadForTenant(tenantId); + + return settings.IsEnabled(ServiceProvider.GetService().IndexName); + } + + private bool SearchByContentEnabled + { + get + { + return CoreBaseSettings.Standalone; + } + } + } + + [Serializable] + [DataContract] + public class SearchSettingsItem + { + [DataMember(Name = "ID")] + public string ID { get; set; } + + [DataMember(Name = "Enabled")] + public bool Enabled { get; set; } + + public string Title { get; set; } + } + + public static class SearchSettingsHelperExtention + { + public static DIHelper AddSearchSettingsHelperService(this DIHelper services) + { + services.TryAddScoped(); + + return services + .AddSettingsManagerService() + .AddCoreBaseSettingsService() + .AddFactoryIndexerService(); + } + } +} diff --git a/common/services/ASC.ElasticSearch/Core/Selector.cs b/common/services/ASC.ElasticSearch/Core/Selector.cs new file mode 100644 index 0000000000..298c23b6a6 --- /dev/null +++ b/common/services/ASC.ElasticSearch/Core/Selector.cs @@ -0,0 +1,457 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Globalization; +using System.Linq; +using System.Linq.Expressions; + +using Microsoft.Extensions.DependencyInjection; + +using Nest; + +namespace ASC.ElasticSearch +{ + public class Selector where T : Wrapper + { + private readonly QueryContainerDescriptor queryContainerDescriptor = new QueryContainerDescriptor(); + private SortDescriptor sortContainerDescriptor = new SortDescriptor(); + private QueryContainer queryContainer = new QueryContainer(); + private int limit = 1000, offset; + + public IServiceProvider ServiceProvider { get; } + + public Selector(IServiceProvider serviceProvider) + { + ServiceProvider = serviceProvider; + } + + public Selector Where(Expression> selector, TProperty value) + { + queryContainer = queryContainer && +Wrap(selector, (w, r) => r.Term(w, value)); + return this; + } + + public Selector Where(Expression> selector, Guid value) + { + queryContainer = queryContainer && +Wrap(selector, (a, w) => w.Match(r => r.Field(a).Query(value.ToString()))); + return this; + } + + public Selector Gt(Expression> selector, double? value) + { + queryContainer = queryContainer && +Wrap(selector, (w, r) => r.Range(a => a.Field(w).GreaterThan(value))); + return this; + } + + public Selector Lt(Expression> selector, double? value) + { + queryContainer = queryContainer && +Wrap(selector, (w, r) => r.Range(a => a.Field(w).LessThan(value))); + return this; + } + + public Selector Gt(Expression> selector, DateTime? value) + { + queryContainer = queryContainer && +Wrap(selector, (w, r) => r.DateRange(a => a.Field(w).GreaterThan(value))); + return this; + } + + public Selector Ge(Expression> selector, DateTime? value) + { + queryContainer = queryContainer && +Wrap(selector, (w, r) => r.DateRange(a => a.Field(w).GreaterThanOrEquals(value))); + return this; + } + + public Selector Lt(Expression> selector, DateTime? value) + { + queryContainer = queryContainer && +Wrap(selector, (w, r) => r.DateRange(a => a.Field(w).LessThan(value))); + return this; + } + + public Selector Le(Expression> selector, DateTime? value) + { + queryContainer = queryContainer && +Wrap(selector, (w, r) => r.DateRange(a => a.Field(w).LessThanOrEquals(value))); + return this; + } + + public Selector In(Expression> selector, TValue[] values) + { + queryContainer = queryContainer && +Wrap(selector, (w, r) => r.Terms(a => a.Field(w).Terms(values))); + + return this; + } + + public Selector InAll(Expression> selector, TValue[] values) + { + foreach (var v in values) + { + var v1 = v; + queryContainer = queryContainer && +Wrap(selector, (w, r) => r.Term(a => a.Field(w).Value(v1))); + } + return this; + } + + public Selector Match(Expression> selector, string value) + { + value = value.PrepareToSearch(); + + if (IsExactlyPhrase(value)) + { + queryContainer = queryContainer & Wrap(selector, (a, w) => w.MatchPhrase(r => r.Field(a).Query(value.TrimQuotes()))); + } + else if (value.HasOtherLetter() || IsExactly(value)) + { + queryContainer = queryContainer & Wrap(selector, (a, w) => w.Match(r => r.Field(a).Query(value.TrimQuotes()))); + } + else + { + if (IsPhrase(value)) + { + var phrase = value.Split(' '); + foreach (var p in phrase) + { + var p1 = p; + queryContainer = queryContainer & Wrap(selector, (a, w) => w.Wildcard(r => r.Field(a).Value(p1.WrapAsterisk()))); + } + } + else + { + queryContainer = queryContainer & Wrap(selector, (a, w) => w.Wildcard(r => r.Field(a).Value(value.WrapAsterisk()))); + } + + } + + if (IsExactly(value)) + { + queryContainer = queryContainer | Wrap(selector, (a, w) => w.MatchPhrase(r => r.Field(a).Query(value))); + } + + return this; + } + + private void Match(Func propsFunc, string value) + { + if (string.IsNullOrEmpty(value)) return; + + value = value.PrepareToSearch(); + + var props = propsFunc(); + + if (IsExactlyPhrase(value)) + { + queryContainer = queryContainer && MultiPhrase(props, value.TrimQuotes()); + } + else if (value.HasOtherLetter() || IsExactly(value)) + { + queryContainer = queryContainer && MultiMatch(props, value.TrimQuotes()); + } + else + { + if (IsPhrase(value)) + { + var phrase = value.Split(' '); + foreach (var p in phrase) + { + queryContainer = queryContainer && MultiWildCard(props, p.WrapAsterisk()); + } + } + else + { + queryContainer = queryContainer && MultiWildCard(props, value.WrapAsterisk()); + } + } + + if (IsExactly(value)) + { + queryContainer = queryContainer || MultiPhrase(props, value); + } + } + + public Selector Match(Expression> selector, string value) + { + Match(() => ((NewArrayExpression)selector.Body).Expressions.ToArray(), value); + + return this; + } + + public Selector MatchAll(string value) + { + Match(() => ServiceProvider.GetService().GetContentProperties(), value); + + return this; + } + + public Selector Sort(Expression> selector, bool asc) + { + sortContainerDescriptor = sortContainerDescriptor.Field(selector, asc ? SortOrder.Ascending : SortOrder.Descending); + + return this; + } + + public Selector Limit(int newOffset = 0, int newLimit = 1000) + { + offset = newOffset; + limit = newLimit; + + return this; + } + + + public Selector Or(Expression, Selector>> selectorLeft, Expression, Selector>> selectorRight) + { + return new Selector(ServiceProvider) + { + queryContainer = queryContainer & + (selectorLeft.Compile()(new Selector(ServiceProvider)).queryContainer | + selectorRight.Compile()(new Selector(ServiceProvider)).queryContainer) + }; + } + + public static Selector Or(Selector selectorLeft, Selector selectorRight) + { + return new Selector(selectorLeft.ServiceProvider) + { + queryContainer = selectorLeft.queryContainer | selectorRight.queryContainer + }; + } + + public Selector Not(Expression, Selector>> selector) + { + return new Selector(ServiceProvider) + { + queryContainer = queryContainer & !selector.Compile()(new Selector(ServiceProvider)).queryContainer + }; + } + + public static Selector Not(Selector selector) + { + return new Selector(selector.ServiceProvider) + { + queryContainer = !selector.queryContainer + }; + } + + public static Selector operator &(Selector selectorLeft, Selector selectorRight) + { + return new Selector(selectorLeft.ServiceProvider) + { + queryContainer = selectorLeft.queryContainer & selectorRight.queryContainer + }; + } + + public static Selector operator |(Selector selectorLeft, Selector selectorRight) + { + return Or(selectorLeft, selectorRight); + } + + public static Selector operator !(Selector selector) + { + return Not(selector); + } + + internal Func, ISearchRequest> GetDescriptor(BaseIndexer indexer, bool onlyId = false) + { + return s => + { + var result = s + .Query(q => queryContainer) + .Index(indexer.IndexName) + .Sort(r => sortContainerDescriptor) + .From(offset) + .Size(limit); + + if (onlyId) + { + result = result.Source(r => r.Includes(a => a.Field("id"))); + } + + return result; + }; + } + + internal Func, IDeleteByQueryRequest> GetDescriptorForDelete(BaseIndexer indexer, bool immediately = true) + { + return s => + { + var result = s + .Query(q => queryContainer) + .Index(indexer.IndexName); + if (immediately) + { + result.Refresh(); + } + return result; + }; + } + + internal Func, IUpdateByQueryRequest> GetDescriptorForUpdate(BaseIndexer indexer, Func script, bool immediately = true) + { + return s => + { + var result = s + .Query(q => queryContainer) + .Index(indexer.IndexName) + .Script(script); + + if (immediately) + { + result.Refresh(); + } + + return result; + }; + } + + private QueryContainer Wrap(Field fieldSelector, Func, QueryContainer> selector) + { + var path = IsNested(fieldSelector); + + if (string.IsNullOrEmpty(path) && + !string.IsNullOrEmpty(fieldSelector.Name) && + fieldSelector.Name.StartsWith(JoinTypeEnum.Sub + ":") && + fieldSelector.Name.IndexOf(".", StringComparison.InvariantCulture) > 0) + { + var splitted = fieldSelector.Name.Split(':')[1]; + path = splitted.Split('.')[0]; + fieldSelector = new Field(splitted); + } + + if (!string.IsNullOrEmpty(path)) + { + return queryContainerDescriptor.Nested(a => a.Query(b => selector(fieldSelector, b)).Path(path)); + } + + return selector(fieldSelector, queryContainerDescriptor); + } + + private string IsNested(Field selector) + { + if (!(selector.Expression is LambdaExpression lambdaExpression)) return null; + + if (lambdaExpression.Body is MethodCallExpression methodCallExpression && methodCallExpression.Arguments.Count > 1) + { + if (!(methodCallExpression.Arguments[0] is MemberExpression pathMember)) return null; + + return pathMember.Member.Name.ToLowerCamelCase(); + } + + return null; + } + + private bool IsPhrase(string searchText) + { + return searchText.Contains(" ") || searchText.Contains("\r\n") || searchText.Contains("\n"); + } + + private bool IsExactlyPhrase(string searchText) + { + return IsPhrase(searchText) && IsExactly(searchText); + } + + private bool IsExactly(string searchText) + { + return searchText.StartsWith("\"") && searchText.EndsWith("\""); + } + + private QueryContainer MultiMatch(Fields fields, string value) + { + var qcWildCard = new QueryContainer(); + + foreach (var field in fields) + { + var field1 = field; + qcWildCard = qcWildCard || Wrap(field1, (a, w) => w.Match(r => r.Field(a).Query(value.ToLower()))); + } + + return qcWildCard; + } + + private QueryContainer MultiWildCard(Fields fields, string value) + { + var qcWildCard = new QueryContainer(); + + foreach (var field in fields) + { + qcWildCard = qcWildCard || Wrap(field, (a, w) => w.Wildcard(r => r.Field(a).Value(value))); + } + + return qcWildCard; + } + + private QueryContainer MultiPhrase(Fields fields, string value) + { + var qcWildCard = new QueryContainer(); + + foreach (var field in fields) + { + qcWildCard = qcWildCard || Wrap(field, (a, w) => w.MatchPhrase(r => r.Field(a).Query(value.ToLower()))); + } + + return qcWildCard; + } + } + + internal static class StringExtension + { + private static bool Any(this string value, UnicodeCategory category) + { + return !string.IsNullOrWhiteSpace(value) + && value.Any(@char => char.GetUnicodeCategory(@char) == category); + } + + public static bool HasOtherLetter(this string value) + { + return value.Any(UnicodeCategory.OtherLetter); + } + + public static string WrapAsterisk(this string value) + { + var result = value; + + if (!value.Contains("*") && !value.Contains("?")) + { + result = "*" + result + "*"; + } + + return result; + } + + public static string ReplaceBackslash(this string value) + { + return value.Replace("\\", "\\\\"); + } + + public static string TrimQuotes(this string value) + { + return value.Trim('\"'); + } + + public static string PrepareToSearch(this string value) + { + return value.ReplaceBackslash().ToLowerInvariant().Replace('ё', 'е').Replace('Ё', 'Е'); + } + } +} diff --git a/common/services/ASC.ElasticSearch/Core/State.cs b/common/services/ASC.ElasticSearch/Core/State.cs new file mode 100644 index 0000000000..1cf8046222 --- /dev/null +++ b/common/services/ASC.ElasticSearch/Core/State.cs @@ -0,0 +1,15 @@ +using System; +using System.Runtime.Serialization; + +namespace ASC.ElasticSearch.Core +{ + [DataContract] + public class State + { + [DataMember] + public string Indexing { get; set; } + + [DataMember] + public DateTime? LastIndexed { get; set; } + } +} diff --git a/common/services/ASC.ElasticSearch/Core/Wrapper.cs b/common/services/ASC.ElasticSearch/Core/Wrapper.cs new file mode 100644 index 0000000000..bfd786a042 --- /dev/null +++ b/common/services/ASC.ElasticSearch/Core/Wrapper.cs @@ -0,0 +1,370 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using ASC.ElasticSearch.Core; +using Nest; +using Newtonsoft.Json.Linq; + +namespace ASC.ElasticSearch +{ + public abstract class Wrapper + { + protected internal abstract string Table { get; } + + protected internal virtual string IndexName + { + get { return Table; } + } + + [ColumnId("id")] + public virtual int Id { get; set; } + + [ColumnTenantId("tenant_id")] + public virtual int TenantId { get; set; } + + [ColumnLastModified("last_modified_on"), Date] + public virtual DateTime LastModifiedOn { get; set; } + + private List orderedProperties; + + private List OrderedProperties + { + get + { + return orderedProperties ?? (orderedProperties = GetType() + .GetProperties() + .Where(r => + { + var column = r.GetCustomAttribute(); + return column != null && !string.IsNullOrEmpty(column.ColumnName); + }) + .OrderBy(r => r.GetCustomAttribute().Order) + .ToList()); + } + } + + private List columns; + + private List Columns + { + get + { + return columns ?? (columns = OrderedProperties + .Select(r => r.GetCustomAttribute()) + .ToList()); + } + } + + internal Converter GetDataConverter(bool sub = false) + { + return data => + { + var result = Activator.CreateInstance(GetType()); + + var i = 0; + var props = OrderedProperties; + for (; i < props.Count; i++) + { + if (data[i] == null) continue; + object newValue; + if (props[i].PropertyType == typeof(Guid)) + { + newValue = Guid.Parse(data[i].ToString()); + } + else if (props[i].PropertyType == typeof(bool)) + { + try + { + newValue = Convert.ToBoolean(data[i]); + } + catch (Exception) + { + newValue = data[i] != null; + } + } + else + { + try + { + newValue = Convert.ChangeType(data[i], props[i].PropertyType); + } + catch (Exception) + { + newValue = Activator.CreateInstance(props[i].PropertyType); + } + } + props[i].SetValue(result, newValue); + } + + var joins = GetType() + .GetProperties() + .Where(r => r.GetCustomAttribute() != null) + .ToList(); + + if (!joins.Any()) return (Wrapper) result; + + data = data.Skip(i).ToArray(); + + foreach (var join in joins) + { + Wrapper joinWrapper; + + if (join.PropertyType.IsGenericType) + { + joinWrapper = Activator.CreateInstance(join.PropertyType.GenericTypeArguments[0]) as Wrapper; + } + else + { + joinWrapper = Activator.CreateInstance(join.PropertyType) as Wrapper; + } + + if (joinWrapper == null) continue; + + var joinAttr = join.GetCustomAttribute(); + var joinSub = sub || joinAttr.JoinType == JoinTypeEnum.Sub; + List newArray; + if (joinSub && join.PropertyType.IsGenericType) + { + newArray = data[0] == null + ? new List() + : JArray.Parse(data[0].ToString()).Select(r => ((JArray)r).Select(q => (object)q).ToArray()).ToList(); + } + else + { + newArray = new List {data}; + } + + var newArrayValue = newArray.ConvertAll(joinWrapper.GetDataConverter(joinSub)); + + object newValue; + + if (joinSub && join.PropertyType.IsGenericType) + { + var list = (IList)Activator.CreateInstance(join.PropertyType); + foreach (var item in newArrayValue) + { + list.Add(item); + } + + newValue = list; + } + else + { + newValue = Convert.ChangeType(newArrayValue.First(), join.PropertyType); + } + + join.SetValue(result, newValue); + + var skipCount = joinSub + ? 1 + : joinWrapper.OrderedProperties.Count; + + data = data.Skip(skipCount).ToArray(); + } + + return (Wrapper) result; + }; + } + + internal string GetColumnName(ColumnTypeEnum columnType, string alias) + { + var column = Columns.FirstOrDefault(r => r.ColumnType == columnType); + if (column == null || string.IsNullOrEmpty(column.ColumnName)) return null; + return alias + "." + column.ColumnName; + } + + internal bool TryGetColumnName(ColumnTypeEnum columnType, string alias, out string columnName) + { + columnName = GetColumnName(columnType, alias); + return columnName != null; + } + + internal bool TryGetColumnNames(List columnType, string alias, out List columnName) + { + columnName = new List(); + foreach (var cType in columnType) + { + var type = cType; + var column = Columns.Where(r => r.ColumnType == type); + columnName.AddRange(column.Select(r => alias + "." + r.ColumnName)); + } + + return columnName.Any(); + } + + internal string[] GetColumnNames(string alias) + { + return Columns + .Select(r => alias + "." + r.ColumnName) + .ToArray(); + } + + internal string[] GetContentProperties() + { + var result = OrderedProperties + .Where(r => r.GetCustomAttribute().ColumnType == ColumnTypeEnum.Content) + .Select(r => ToLowerCamelCase(r.Name)) + .ToList(); + + if (this is WrapperWithDoc) + { + result.Add("document.attachment.content"); + } + + foreach (var join in GetJoins()) + { + var joinType = join.Value.Name; + var joinAttr = join.Value.GetCustomAttribute().JoinType; + result.AddRange(join.Key.GetContentProperties() + .Select(r => (joinAttr == JoinTypeEnum.Sub ? joinAttr + ":" : "") + ToLowerCamelCase(joinType) + "." + r)); + } + + return result.ToArray(); + } + + internal Dictionary GetJoins() + { + var result = new Dictionary(); + + var joins = GetType() + .GetProperties() + .Where(r => r.GetCustomAttribute() != null) + .ToList(); + + foreach (var join in joins) + { + Wrapper joinWrapper; + if (join.PropertyType.IsGenericType) + { + joinWrapper = Activator.CreateInstance(join.PropertyType.GenericTypeArguments[0]) as Wrapper; + } + else + { + joinWrapper = Activator.CreateInstance(join.PropertyType) as Wrapper; + } + + if (joinWrapper == null) continue; + + result.Add(joinWrapper, join); + } + + return result; + } + + internal Dictionary GetConditions(string alias) + { + var result = new Dictionary(); + + var conditions = Columns.OfType(); + + foreach (var con in conditions.Where(r=> r.Value != null)) + { + result.Add(alias + "." + con.ColumnName, con.Value); + } + + return result; + } + + internal string ToLowerCamelCase(string value) + { + return char.ToLowerInvariant(value[0]) + value.Substring(1); + } + + internal Func, IPromise> GetProperties() where T : Wrapper + { + var analyzers = GetAnalyzers(); + + return p => + { + foreach (var c in analyzers) + { + var c1 = c; + string analyzer; + + if (c.Value.CharFilter != CharFilter.io) + { + analyzer = c.Key; + } + else + { + analyzer = c1.Value.Analyzer + "custom"; + } + + p.Text(s => s.Name(c1.Key).Analyzer(analyzer)); + } + if (this is WrapperWithDoc) + { + p.Object( + r => r.Name("document").Properties( + q => q.Object( + a => a.Name("attachment").Properties( + w => w.Text( + t => t.Name("content").Analyzer("document")))))); + } + return p; + }; + } + + internal Dictionary GetAnalyzers() + { + var result = new Dictionary(); + + foreach (var prop in OrderedProperties) + { + var column = prop.GetCustomAttribute(); + + if (column == null || column.ColumnType != ColumnTypeEnum.Content || column.Analyzer == Analyzer.standard) continue; + + result.Add(prop.Name.ToLowerInvariant()[0] + prop.Name.Substring(1), column); + } + + return result; + } + + internal Dictionary GetNested() + { + var result = new Dictionary(); + + var joins = GetType().GetProperties().Where(r => + { + var attr = r.GetCustomAttribute(); + return attr != null; + }); + + foreach (var prop in joins) + { + result.Add(prop.PropertyType, prop.Name); + } + + return result; + } + } +} \ No newline at end of file diff --git a/common/services/ASC.ElasticSearch/Core/WrapperWithDoc.cs b/common/services/ASC.ElasticSearch/Core/WrapperWithDoc.cs new file mode 100644 index 0000000000..c75881afda --- /dev/null +++ b/common/services/ASC.ElasticSearch/Core/WrapperWithDoc.cs @@ -0,0 +1,81 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.IO; +using System.Text; + +using ASC.Common.Logging; + +using Nest; + +using Newtonsoft.Json; + +namespace ASC.ElasticSearch.Core +{ + public abstract class WrapperWithDoc : Wrapper + { + public Document Document { get; set; } + + public const long MaxContentLength = 2 * 1024 * 1024 * 1024L; + + protected abstract Stream GetDocumentStream(); + + [Ignore, JsonIgnore] + public abstract string SettingsTitle { get; } + + internal void InitDocument(bool index, ILog log) + { + Document = new Document + { + Data = Convert.ToBase64String(Encoding.UTF8.GetBytes("")) + }; + + try + { + if (!index) return; + + using (var stream = GetDocumentStream()) + { + if (stream == null) return; + + Document = new Document + { + Data = Convert.ToBase64String(stream.GetCorrectBuffer()) + }; + } + } + catch (FileNotFoundException e) + { + log.Error("InitDocument FileNotFoundException", e); + } + catch (Exception e) + { + log.Error("InitDocument", e); + } + } + } +} diff --git a/common/services/ASC.ElasticSearch/Engine/BaseIndexer.cs b/common/services/ASC.ElasticSearch/Engine/BaseIndexer.cs new file mode 100644 index 0000000000..bac26c2901 --- /dev/null +++ b/common/services/ASC.ElasticSearch/Engine/BaseIndexer.cs @@ -0,0 +1,751 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.Core.Common.EF.Context; +using ASC.ElasticSearch.Core; +using ASC.ElasticSearch.Service; + +using Autofac; + +using Elasticsearch.Net; + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +using Nest; + +namespace ASC.ElasticSearch +{ + public class BaseIndexerHelper + { + public ConcurrentDictionary IsExist { get; set; } + private readonly ICacheNotify Notify; + + public BaseIndexerHelper(ICacheNotify cacheNotify) + { + IsExist = new ConcurrentDictionary(); + Notify = cacheNotify; + Notify.Subscribe((a) => + { + IsExist.AddOrUpdate(a.Id, false, (q, w) => false); + }, CacheNotifyAction.Any); + } + + public void Clear(T t) where T : Wrapper + { + Notify.Publish(new SearchItem() { Id = t.IndexName }, CacheNotifyAction.Any); + } + } + + public class BaseIndexer : IIndexer where T : Wrapper + { + private static readonly object Locker = new object(); + + protected internal T Wrapper { get { return ServiceProvider.GetService(); } } + + public string IndexName { get { return Wrapper.IndexName; } } + + public bool IsExist { get; set; } + public Client Client { get; } + public ILog Log { get; } + public TenantManager TenantManager { get; } + public SearchSettingsHelper SearchSettingsHelper { get; } + public BaseIndexerHelper BaseIndexerHelper { get; } + public IServiceProvider ServiceProvider { get; } + public WebstudioDbContext WebstudioDbContext { get; } + + public BaseIndexer( + Client client, + IOptionsMonitor log, + DbContextManager dbContextManager, + TenantManager tenantManager, + SearchSettingsHelper searchSettingsHelper, + BaseIndexerHelper baseIndexerHelper, + IServiceProvider serviceProvider) + { + Client = client; + Log = log.CurrentValue; + TenantManager = tenantManager; + SearchSettingsHelper = searchSettingsHelper; + BaseIndexerHelper = baseIndexerHelper; + ServiceProvider = serviceProvider; + WebstudioDbContext = dbContextManager.Value; + } + + internal void Index(T data, bool immediately = true) + { + BeforeIndex(data); + + Client.Instance.Index(data, idx => GetMeta(idx, data, immediately)); + } + + internal void Index(List data, bool immediately = true) + { + CreateIfNotExist(data[0]); + + if (typeof(T).IsSubclassOf(typeof(WrapperWithDoc))) + { + var currentLength = 0L; + var portion = new List(); + var portionStart = 0; + + for (var i = 0; i < data.Count; i++) + { + var t = data[i]; + var runBulk = i == data.Count - 1; + + BeforeIndex(t); + + + if (!(t is WrapperWithDoc wwd) || wwd.Document == null || string.IsNullOrEmpty(wwd.Document.Data)) + { + portion.Add(t); + } + else + { + var dLength = wwd.Document.Data.Length; + if (dLength >= Settings.Default.MemoryLimit) + { + try + { + Index(t, immediately); + } + catch (Exception e) + { + Log.Error(e); + } + + wwd.Document.Data = null; + wwd.Document = null; + GC.Collect(); + continue; + } + + if (currentLength + dLength < Settings.Default.MemoryLimit) + { + portion.Add(t); + currentLength += dLength; + } + else + { + runBulk = true; + i--; + } + } + + if (runBulk) + { + var portion1 = portion; + Client.Instance.Bulk(r => r.IndexMany(portion1, GetMeta)); + for (var j = portionStart; j < i; j++) + { + if (data[j] is WrapperWithDoc doc && doc.Document != null) + { + doc.Document.Data = null; + doc.Document = null; + } + } + + portionStart = i; + portion = new List(); + currentLength = 0L; + GC.Collect(); + } + } + } + else + { + foreach (var item in data) + { + BeforeIndex(item); + } + + Client.Instance.Bulk(r => r.IndexMany(data, GetMeta)); + } + } + + internal void Update(T data, bool immediately = true, params Expression>[] fields) + { + CreateIfNotExist(data); + Client.Instance.Update(DocumentPath.Id(data), r => GetMetaForUpdate(r, data, immediately, fields)); + } + + internal void Update(T data, UpdateAction action, Expression> fields, bool immediately = true) + { + CreateIfNotExist(data); + Client.Instance.Update(DocumentPath.Id(data), r => GetMetaForUpdate(r, data, action, fields, immediately)); + } + + internal void Update(T data, Expression, Selector>> expression, int tenantId, bool immediately = true, params Expression>[] fields) + { + CreateIfNotExist(data); + Client.Instance.UpdateByQuery(GetDescriptorForUpdate(data, expression, tenantId, immediately, fields)); + } + + internal void Update(T data, Expression, Selector>> expression, int tenantId, UpdateAction action, Expression> fields, bool immediately = true) + { + CreateIfNotExist(data); + Client.Instance.UpdateByQuery(GetDescriptorForUpdate(data, expression, tenantId, action, fields, immediately)); + } + + internal void Delete(T data, bool immediately = true) + { + Client.Instance.Delete(data, r => GetMetaForDelete(r, immediately)); + } + + internal void Delete(Expression, Selector>> expression, int tenantId, bool immediately = true) + { + Client.Instance.DeleteByQuery(GetDescriptorForDelete(expression, tenantId, immediately)); + } + + public void Flush() + { + Client.Instance.Flush(new FlushRequest(IndexName)); + } + + public void Refresh() + { + Client.Instance.Refresh(new RefreshRequest(IndexName)); + } + + internal bool CheckExist(T data) + { + try + { + var isExist = BaseIndexerHelper.IsExist.GetOrAdd(data.IndexName, (k) => Client.Instance.IndexExists(k).Exists); + if (isExist) return true; + + lock (Locker) + { + if (isExist) return true; + + isExist = Client.Instance.IndexExists(data.IndexName).Exists; + + _ = BaseIndexerHelper.IsExist.TryUpdate(data.IndexName, IsExist, false); + + if (isExist) return true; + } + } + catch (Exception e) + { + Log.Error("CheckExist " + data.IndexName, e); + } + return false; + } + + void IIndexer.Check() + { + var data = ServiceProvider.GetService(); + if (!CheckExist(data)) return; + + var result = false; + var currentMappings = Client.Instance.GetMapping(r => r.Index(data.IndexName)); + var newMappings = GetMappings(data).Invoke(new CreateIndexDescriptor(data.IndexName)); + + var newMappingDict = new Dictionary(); + var props = newMappings.Mappings.SelectMany(r => r.Value.Properties).ToList(); + foreach (var prop in props.Where(r => r.Key.Property != null && r.Key.Property.Name != "Document")) + { + var propKey = prop.Key.Property.Name.ToLowerCamelCase(); + var key = newMappings.Index.Name + "." + propKey; + if (prop.Key.Property.CustomAttributes.Any()) + { + newMappingDict.Add(key, props.Any(r => r.Key == propKey && r.Value is INestedProperty) ? FieldType.Nested.GetStringValue() : prop.Value.Type); + } + + + if (prop.Value is ObjectProperty obj) + { + foreach (var objProp in obj.Properties) + { + newMappingDict.Add(key + "." + objProp.Key.Property.Name.ToLowerCamelCase(), objProp.Value.Type); + } + } + } + + foreach (var ind in currentMappings.Indices) + { + foreach (var prop in ind.Value.Mappings.SelectMany(r => r.Value.Properties).Where(r => r.Key.Name != "document")) + { + var key = ind.Key.Name + "." + prop.Key.Name.ToLowerCamelCase(); + + if (!newMappingDict.Contains(new KeyValuePair(key, prop.Value.Type))) + { + result = true; + break; + } + + var nested = prop.Value as NestedProperty ?? prop.Value as ObjectProperty; + + if (nested != null) + { + if (nested.Properties.Any(nProp => !newMappingDict.Contains(new KeyValuePair(key + "." + nProp.Key.Name.ToLowerCamelCase(), nProp.Value.Type)))) + { + result = true; + } + } + } + } + + + if (result) + { + Clear(); + } + } + + async Task IIndexer.ReIndex() + { + Clear(); + //((IIndexer) this).IndexAll(); + } + + private void Clear() + { + var index = WebstudioDbContext.WebstudioIndex.Where(r => r.IndexName == Wrapper.IndexName).FirstOrDefault(); + + if (index != null) + { + WebstudioDbContext.WebstudioIndex.Remove(index); + } + + WebstudioDbContext.SaveChanges(); + + Log.DebugFormat("Delete {0}", Wrapper.IndexName); + Client.Instance.DeleteIndex(Wrapper.IndexName); + BaseIndexerHelper.Clear(Wrapper); + CreateIfNotExist(ServiceProvider.GetService()); + } + + internal IReadOnlyCollection Select(Expression, Selector>> expression, bool onlyId = false) + { + var func = expression.Compile(); + var selector = ServiceProvider.GetService>(); + var descriptor = func(selector).Where(r => r.TenantId, TenantManager.GetCurrentTenant().TenantId); + return Client.Instance.Search(descriptor.GetDescriptor(this, onlyId)).Documents; + } + + internal IReadOnlyCollection Select(Expression, Selector>> expression, bool onlyId, out long total) + { + var func = expression.Compile(); + var selector = ServiceProvider.GetService>(); + var descriptor = func(selector).Where(r => r.TenantId, TenantManager.GetCurrentTenant().TenantId); + var result = Client.Instance.Search(descriptor.GetDescriptor(this, onlyId)); + total = result.Total; + return result.Documents; + } + + private void BeforeIndex(T data) + { + CreateIfNotExist(data); + + if (data is WrapperWithDoc wrapperWithDoc) + { + wrapperWithDoc.InitDocument(SearchSettingsHelper.CanSearchByContent(data.TenantId), Log); + } + } + + private void CreateIfNotExist(T data) + { + try + { + if (CheckExist(data)) return; + + lock (Locker) + { + var columns = data.GetAnalyzers(); + var nestedColumns = data.GetNested(); + + if (!columns.Any() && !nestedColumns.Any()) + { + Client.Instance.CreateIndex(data.IndexName); + } + else + { + Client.Instance.CreateIndex(data.IndexName, GetMappings(data)); + } + + IsExist = true; + } + } + catch (Exception e) + { + Log.Error("CreateIfNotExist", e); + } + } + + public Func GetMappings(T data) + { + var columns = data.GetAnalyzers(); + var nestedColumns = data.GetNested(); + + Func> analyzers = b => + { + foreach (var c in Enum.GetNames(typeof(Analyzer))) + { + var c1 = c; + b.Custom(c1 + "custom", ca => ca.Tokenizer(c1).Filters(Filter.lowercase.ToString()).CharFilters(CharFilter.io.ToString())); + } + + foreach (var c in columns) + { + if (c.Value.CharFilter == CharFilter.io) continue; + var charFilters = new List(); + foreach (var r in Enum.GetValues(typeof(CharFilter))) + { + if ((c.Value.CharFilter & (CharFilter)r) == (CharFilter)r) charFilters.Add(r.ToString()); + } + + var c1 = c; + b.Custom(c1.Key, ca => ca.Tokenizer(c1.Value.Analyzer.ToString()).Filters(c1.Value.Filter.ToString()).CharFilters(charFilters)); + } + + if (data is WrapperWithDoc) + { + b.Custom("document", ca => ca.Tokenizer(Analyzer.whitespace.ToString()).Filters(Filter.lowercase.ToString()).CharFilters(CharFilter.io.ToString())); + } + + return b; + }; + + Func, IPromise> nestedSelector = p => + { + foreach (var c in nestedColumns) + { + var isNested = c.Key.IsGenericType; + Type prop; + MethodInfo nested; + Type typeDescriptor; + + if (isNested) + { + prop = c.Key.GenericTypeArguments[0]; + nested = p.GetType().GetMethod("Nested"); + typeDescriptor = typeof(NestedPropertyDescriptor<,>); + } + else + { + prop = c.Key; + nested = p.GetType().GetMethod("Object"); + typeDescriptor = typeof(ObjectTypeDescriptor<,>); + } + + var desc = typeDescriptor.MakeGenericType(typeof(T), prop); + + var methods = desc.GetMethods(); + var name = methods.FirstOrDefault(r => r.Name == "Name" && r.GetParameters().FirstOrDefault(q => q.ParameterType == typeof(PropertyName)) != null); + var autoMap = methods.FirstOrDefault(r => r.Name == "AutoMap" && r.GetParameters().Length == 2); + var props = methods.FirstOrDefault(r => r.Name == "Properties"); + if (name == null || autoMap == null || props == null) continue; + + var param = Expression.Parameter(desc, "a"); + var nameFunc = Expression.Call(param, name, Expression.Constant(new PropertyName(c.Value.ToLowerCamelCase()))); //a.Name(value(Nest.PropertyName)) + var autoMapFunc = Expression.Call(param, autoMap, Expression.Constant(null, typeof(IPropertyVisitor)), Expression.Constant(0)); //a.AutoMap() + + var inst = (Wrapper)Activator.CreateInstance(prop); + var instMethods = prop.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic); + var getProperties = instMethods.First(r => r.Name == "GetProperties").MakeGenericMethod(prop); + var propsFunc = Expression.Call(param, props, Expression.Constant(getProperties.Invoke(inst, null))); //a.AutoMap() + + var nestedFunc = Expression.Lambda(Expression.Block(nameFunc, autoMapFunc, propsFunc), param).Compile(); + var fooRef = nested.MakeGenericMethod(prop); + fooRef.Invoke(p, new object[] { nestedFunc });//p.Nested(r=> r.Name(c.Value.ToLowerCamelCase()).AutoMap().Properties(getProperties())) + } + + return p; + }; + + return c => + c.Settings(r => r.Analysis(a => a.Analyzers(analyzers).CharFilters(d => d.HtmlStrip(CharFilter.html.ToString()).Mapping(CharFilter.io.ToString(), m => m.Mappings("ё => е", "Ё => Е"))))) + .Mappings(r => r.Map(m => m.AutoMap().Properties(data.GetProperties()).Properties(nestedSelector))); + } + + private IIndexRequest GetMeta(IndexDescriptor request, T data, bool immediately = true) + { + var result = request.Index(data.IndexName).Id(data.Id); + + if (immediately) + { + result.Refresh(Elasticsearch.Net.Refresh.True); + } + + if (data is WrapperWithDoc) + { + result.Pipeline("attachments"); + } + + return result; + } + private IBulkIndexOperation GetMeta(BulkIndexDescriptor desc, T data) + { + var result = desc.Index(IndexName).Id(data.Id); + + if (data is WrapperWithDoc) + { + result.Pipeline("attachments"); + } + + return result; + } + + private IUpdateRequest GetMetaForUpdate(UpdateDescriptor request, T data, bool immediately = true, params Expression>[] fields) + { + var result = request.Index(IndexName); + + if (fields.Any()) + { + result.Script(GetScriptUpdateByQuery(data, fields)); + } + else + { + result.Doc(data); + } + + if (immediately) + { + result.Refresh(Elasticsearch.Net.Refresh.True); + } + + return result; + } + + private Func GetScriptUpdateByQuery(T data, params Expression>[] fields) + { + var source = new StringBuilder(); + var parameters = new Dictionary(); + + for (var i = 0; i < fields.Length; i++) + { + var func = fields[i].Compile(); + var newValue = func(data); + string name; + + var expression = fields[i].Body; + var isList = expression.Type.IsGenericType && expression.Type.GetGenericTypeDefinition() == typeof(List<>); + + + var sourceExprText = ""; + + while (!string.IsNullOrEmpty(name = TryGetName(expression, out var member))) + { + sourceExprText = "." + name + sourceExprText; + expression = member.Expression; + } + + if (isList) + { + UpdateByAction(UpdateAction.Add, (IList)newValue, sourceExprText, parameters, source); + } + else + { + if (newValue == default(T)) + { + source.AppendFormat("ctx._source.remove('{0}');", sourceExprText.Substring(1)); + } + else + { + var pkey = "p" + sourceExprText.Replace(".", ""); + source.AppendFormat("ctx._source{0} = params.{1};", sourceExprText, pkey); + parameters.Add(pkey, newValue); + } + } + } + + var sourceData = source.ToString(); + + return r => r.Source(sourceData).Params(parameters); + } + + private IUpdateRequest GetMetaForUpdate(UpdateDescriptor request, T data, UpdateAction action, Expression> fields, bool immediately = true) + { + var result = request.Index(IndexName).Script(GetScriptForUpdate(data, action, fields)); + + if (immediately) + { + result.Refresh(Elasticsearch.Net.Refresh.True); + } + + return result; + } + + private Func GetScriptForUpdate(T data, UpdateAction action, Expression> fields) + { + var source = new StringBuilder(); + + var func = fields.Compile(); + var newValue = func(data); + string name; + + var expression = fields.Body; + + MemberExpression member; + var sourceExprText = ""; + + while (!string.IsNullOrEmpty(name = TryGetName(expression, out member))) + { + sourceExprText = "." + name + sourceExprText; + expression = member.Expression; + } + + var parameters = new Dictionary(); + + UpdateByAction(action, newValue, sourceExprText, parameters, source); + + return r => r.Source(source.ToString()).Params(parameters); + } + + private void UpdateByAction(UpdateAction action, IList newValue, string key, Dictionary parameters, StringBuilder source) + { + var paramKey = "p" + key.Replace(".", ""); + switch (action) + { + case UpdateAction.Add: + for (var i = 0; i < newValue.Count; i++) + { + parameters.Add(paramKey + i, newValue[i]); + source.AppendFormat("if (!ctx._source{0}.contains(params.{1})){{ctx._source{0}.add(params.{1})}}", key, paramKey + i); + } + break; + case UpdateAction.Replace: + parameters.Add(paramKey, newValue); + source.AppendFormat("ctx._source{0} = params.{1};", key, paramKey); + break; + case UpdateAction.Remove: + for (var i = 0; i < newValue.Count; i++) + { + parameters.Add(paramKey + i, newValue[i]); + source.AppendFormat("ctx._source{0}.removeIf(item -> item.id == params.{1}.id)", key, paramKey + i); + } + break; + default: + throw new ArgumentOutOfRangeException("action", action, null); + } + } + + private string TryGetName(Expression expr, out MemberExpression member) + { + member = expr as MemberExpression; + if (member == null) + { + var unary = expr as UnaryExpression; + if (unary != null) + { + member = unary.Operand as MemberExpression; + } + } + + return member == null ? "" : member.Member.Name.ToLowerCamelCase(); + } + + private IDeleteRequest GetMetaForDelete(DeleteDescriptor request, bool immediately = true) + { + var result = request.Index(IndexName); + if (immediately) + { + result.Refresh(Elasticsearch.Net.Refresh.True); + } + return result; + } + + private Func, IDeleteByQueryRequest> GetDescriptorForDelete(Expression, Selector>> expression, int tenantId, bool immediately = true) + { + var func = expression.Compile(); + var selector = ServiceProvider.GetService>(); + var descriptor = func(selector).Where(r => r.TenantId, tenantId); + return descriptor.GetDescriptorForDelete(this, immediately); + } + + private Func, IUpdateByQueryRequest> GetDescriptorForUpdate(T data, Expression, Selector>> expression, int tenantId, bool immediately = true, params Expression>[] fields) + { + var func = expression.Compile(); + var selector = ServiceProvider.GetService>(); + var descriptor = func(selector).Where(r => r.TenantId, tenantId); + return descriptor.GetDescriptorForUpdate(this, GetScriptUpdateByQuery(data, fields), immediately); + } + + private Func, IUpdateByQueryRequest> GetDescriptorForUpdate(T data, Expression, Selector>> expression, int tenantId, UpdateAction action, Expression> fields, bool immediately = true) + { + var func = expression.Compile(); + var selector = ServiceProvider.GetService>(); + var descriptor = func(selector).Where(r => r.TenantId, tenantId); + return descriptor.GetDescriptorForUpdate(this, GetScriptForUpdate(data, action, fields), immediately); + } + } + + static class CamelCaseExtension + { + internal static string ToLowerCamelCase(this string str) + { + return str.ToLowerInvariant()[0] + str.Substring(1); + } + } + + + public enum UpdateAction + { + Add, + Replace, + Remove + } + + public static class BaseIndexerExtention + { + public static DIHelper AddBaseIndexerHelperService(this DIHelper services) + { + services.TryAddSingleton(); + return services.AddKafkaService(); + } + + public static DIHelper AddBaseIndexerService(this DIHelper services) where T : Wrapper + { + services.TryAddScoped>(); + + return services + .AddFactoryIndexerService() + .AddClientService() + .AddWebstudioDbContextService() + .AddTenantManagerService() + .AddSearchSettingsHelperService() + .AddBaseIndexerHelperService() + ; + } + } +} \ No newline at end of file diff --git a/common/services/ASC.ElasticSearch/Engine/Client.cs b/common/services/ASC.ElasticSearch/Engine/Client.cs new file mode 100644 index 0000000000..b47e1cd305 --- /dev/null +++ b/common/services/ASC.ElasticSearch/Engine/Client.cs @@ -0,0 +1,139 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Text; + +using ASC.Common; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Tenants; +using ASC.ElasticSearch.Service; + +using Elasticsearch.Net; + +using Microsoft.Extensions.Options; + +using Nest; + +namespace ASC.ElasticSearch +{ + public class Client + { + private static volatile ElasticClient client; + private bool IsInit; + private static readonly object Locker = new object(); + + public ILog Log { get; } + + public CoreConfiguration CoreConfiguration { get; } + + public Client(IOptionsMonitor option, CoreConfiguration coreConfiguration) + { + Log = option.Get("ASC.Indexer"); + CoreConfiguration = coreConfiguration; + } + + public ElasticClient Instance + { + get + { + if (client != null) return client; + + lock (Locker) + { + if (client != null) return client; + + var launchSettings = CoreConfiguration.GetSection(Tenant.DEFAULT_TENANT) ?? + Settings.Default; + + var uri = new Uri(string.Format("{0}://{1}:{2}", launchSettings.Scheme, launchSettings.Host, launchSettings.Port)); + var settings = new ConnectionSettings(new SingleNodeConnectionPool(uri)) + .RequestTimeout(TimeSpan.FromMinutes(5)) + .MaximumRetries(10) + .ThrowExceptions(); +#if DEBUG + if (Log.IsTraceEnabled) + { + settings.DisableDirectStreaming().PrettyJson().EnableDebugMode(r => + { + Log.Trace(r.DebugInformation); + + if (r.RequestBodyInBytes != null) + { + Log.TraceFormat("Request: {0}", Encoding.UTF8.GetString(r.RequestBodyInBytes)); + } + + if (r.ResponseBodyInBytes != null) + { + Log.TraceFormat("Response: {0}", Encoding.UTF8.GetString(r.ResponseBodyInBytes)); + } + }); + } +#endif + + client = new ElasticClient(settings); + + if (!IsInit) + { + try + { + var result = client.Ping(new PingRequest()); + + var isValid = result.IsValid; + + if (result.IsValid) + { + client.PutPipeline("attachments", p => p + .Processors(pp => pp + .Attachment(a => a.Field("document.data").TargetField("document.attachment")) + )); + } + + IsInit = true; + } + catch (Exception e) + { + Log.Error(e); + } + } + + return client; + } + } + } + } + + public static class ClientExtention + { + public static DIHelper AddClientService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddCoreConfigurationService(); + } + } +} diff --git a/common/services/ASC.ElasticSearch/Engine/FactoryIndexer.cs b/common/services/ASC.ElasticSearch/Engine/FactoryIndexer.cs new file mode 100644 index 0000000000..565b691daa --- /dev/null +++ b/common/services/ASC.ElasticSearch/Engine/FactoryIndexer.cs @@ -0,0 +1,588 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Common.Threading; +using ASC.Core; +using ASC.Core.Tenants; +using ASC.ElasticSearch.Core; + +using Autofac; + +using Elasticsearch.Net; + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +using Nest; + +namespace ASC.ElasticSearch +{ + public class FactoryIndexerHelper + { + public ICache Cache { get; } + public ILog Logger { get; } + public FactoryIndexer FactoryIndexer { get; } + + public FactoryIndexerHelper(IOptionsMonitor options, FactoryIndexer factoryIndexer) + { + Cache = AscCache.Memory; + Logger = options.Get("ASC.Indexer"); + FactoryIndexer = factoryIndexer; + } + + public bool Support(T t) where T : Wrapper + { + if (!FactoryIndexer.CheckState()) return false; + + var cacheTime = DateTime.UtcNow.AddMinutes(15); + var key = "elasticsearch " + t.IndexName; + try + { + var cacheValue = Cache.Get(key); + if (!string.IsNullOrEmpty(cacheValue)) + { + return Convert.ToBoolean(cacheValue); + } + + //TODO: + //var service = new Service.Service(); + + //var result = service.Support(t.IndexName); + + //Cache.Insert(key, result.ToString(CultureInfo.InvariantCulture).ToLower(), cacheTime); + + return true; + } + catch (Exception e) + { + Cache.Insert(key, "false", cacheTime); + Logger.Error("FactoryIndexer CheckState", e); + return false; + } + } + + } + + public class FactoryIndexer where T : Wrapper + { + private static readonly TaskScheduler Scheduler = new LimitedConcurrencyLevelTaskScheduler(10); + + public ILog Logger { get; } + + public FactoryIndexerHelper FactoryIndexerHelper { get; } + public TenantManager TenantManager { get; } + public SearchSettingsHelper SearchSettingsHelper { get; } + public FactoryIndexer FactoryIndexerCommon { get; } + public BaseIndexer Indexer { get; } + public Client Client { get; } + public IServiceProvider ServiceProvider { get; } + + public FactoryIndexer( + IOptionsMonitor options, + FactoryIndexerHelper factoryIndexerSupport, + TenantManager tenantManager, + SearchSettingsHelper searchSettingsHelper, + FactoryIndexer factoryIndexer, + BaseIndexer baseIndexer, + Client client, + IServiceProvider serviceProvider) + { + Logger = options.Get("ASC.Indexer"); + FactoryIndexerHelper = factoryIndexerSupport; + TenantManager = tenantManager; + SearchSettingsHelper = searchSettingsHelper; + FactoryIndexerCommon = factoryIndexer; + Indexer = baseIndexer; + Client = client; + ServiceProvider = serviceProvider; + } + + public bool TrySelect(Expression, Selector>> expression, out IReadOnlyCollection result) + { + var t = ServiceProvider.GetService(); + if (!FactoryIndexerHelper.Support(t) || !Indexer.CheckExist(t)) + { + result = new List(); + return false; + } + + try + { + result = Indexer.Select(expression); + } + catch (Exception e) + { + Logger.Error("Select", e); + result = new List(); + return false; + } + return true; + } + + public bool TrySelectIds(Expression, Selector>> expression, out List result) + { + var t = ServiceProvider.GetService(); + if (!FactoryIndexerHelper.Support(t) || !Indexer.CheckExist(t)) + { + result = new List(); + return false; + } + + try + { + result = Indexer.Select(expression, true).Select(r => r.Id).ToList(); + } + catch (Exception e) + { + Logger.Error("Select", e); + result = new List(); + return false; + } + + return true; + } + + public bool TrySelectIds(Expression, Selector>> expression, out List result, out long total) + { + var t = ServiceProvider.GetService(); + if (!FactoryIndexerHelper.Support(t) || !Indexer.CheckExist(t)) + { + result = new List(); + total = 0; + return false; + } + + try + { + result = Indexer.Select(expression, true, out total).Select(r => r.Id).ToList(); + } + catch (Exception e) + { + Logger.Error("Select", e); + total = 0; + result = new List(); + return false; + } + + return true; + } + + public bool CanSearchByContent() + { + return SearchSettingsHelper.CanSearchByContent(TenantManager.GetCurrentTenant().TenantId); + } + + public bool Index(T data, bool immediately = true) + { + var t = ServiceProvider.GetService(); + if (!FactoryIndexerHelper.Support(t)) return false; + + try + { + Indexer.Index(data, immediately); + return true; + } + catch (Exception e) + { + Logger.Error("Index", e); + } + return false; + } + + public void Index(List data, bool immediately = true) + { + var t = ServiceProvider.GetService(); + if (!FactoryIndexerHelper.Support(t) || !data.Any()) return; + + try + { + Indexer.Index(data, immediately); + } + catch (AggregateException e) + { + if (e.InnerExceptions.Count == 0) throw; + + var inner = e.InnerExceptions.OfType().FirstOrDefault(); + Logger.Error(inner); + + if (inner != null) + { + Logger.Error("inner", inner.Response.OriginalException); + + if (inner.Response.HttpStatusCode == 413) + { + data.ForEach(r => Index(r, immediately)); + } + } + else + { + throw; + } + } + } + + public void Update(T data, bool immediately = true, params Expression>[] fields) + { + var t = ServiceProvider.GetService(); + if (!FactoryIndexerHelper.Support(t)) return; + + try + { + Indexer.Update(data, immediately, fields); + } + catch (Exception e) + { + Logger.Error("Update", e); + } + } + + public void Update(T data, UpdateAction action, Expression> field, bool immediately = true) + { + var t = ServiceProvider.GetService(); + if (!FactoryIndexerHelper.Support(t)) return; + + try + { + Indexer.Update(data, action, field, immediately); + } + catch (Exception e) + { + Logger.Error("Update", e); + } + } + + public void Update(T data, Expression, Selector>> expression, bool immediately = true, params Expression>[] fields) + { + var t = ServiceProvider.GetService(); + if (!FactoryIndexerHelper.Support(t)) return; + + try + { + var tenant = TenantManager.GetCurrentTenant().TenantId; + Indexer.Update(data, expression, tenant, immediately, fields); + } + catch (Exception e) + { + Logger.Error("Update", e); + } + } + + public void Update(T data, Expression, Selector>> expression, UpdateAction action, Expression> fields, bool immediately = true) + { + var t = ServiceProvider.GetService(); + if (!FactoryIndexerHelper.Support(t)) return; + + try + { + var tenant = TenantManager.GetCurrentTenant().TenantId; + Indexer.Update(data, expression, tenant, action, fields, immediately); + } + catch (Exception e) + { + Logger.Error("Update", e); + } + } + + public void Delete(T data, bool immediately = true) + { + var t = ServiceProvider.GetService(); + if (!FactoryIndexerHelper.Support(t)) return; + + try + { + Indexer.Delete(data, immediately); + } + catch (Exception e) + { + Logger.Error("Delete", e); + } + } + + public void Delete(Expression, Selector>> expression, bool immediately = true) + { + var t = ServiceProvider.GetService(); + if (!FactoryIndexerHelper.Support(t)) return; + + var tenant = TenantManager.GetCurrentTenant().TenantId; + + try + { + Indexer.Delete(expression, tenant, immediately); + } + catch (Exception e) + { + Logger.Error("Index", e); + } + } + + public Task IndexAsync(T data, bool immediately = true) + { + var t = ServiceProvider.GetService(); + if (!FactoryIndexerHelper.Support(t)) return Task.FromResult(false); + return Queue(() => Indexer.Index(data, immediately)); + } + + public Task IndexAsync(List data, bool immediately = true) + { + var t = ServiceProvider.GetService(); + if (!FactoryIndexerHelper.Support(t)) return Task.FromResult(false); + return Queue(() => Indexer.Index(data, immediately)); + } + + public Task UpdateAsync(T data, bool immediately = true, params Expression>[] fields) + { + var t = ServiceProvider.GetService(); + if (!FactoryIndexerHelper.Support(t)) return Task.FromResult(false); + return Queue(() => Indexer.Update(data, immediately, fields)); + } + + public Task DeleteAsync(T data, bool immediately = true) + { + var t = ServiceProvider.GetService(); + if (!FactoryIndexerHelper.Support(t)) return Task.FromResult(false); + return Queue(() => Indexer.Delete(data, immediately)); + } + + public Task DeleteAsync(Expression, Selector>> expression, bool immediately = true) + { + var t = ServiceProvider.GetService(); + if (!FactoryIndexerHelper.Support(t)) return Task.FromResult(false); + var tenant = TenantManager.GetCurrentTenant().TenantId; + return Queue(() => Indexer.Delete(expression, tenant, immediately)); + } + + + public void Flush() + { + var t = ServiceProvider.GetService(); + if (!FactoryIndexerHelper.Support(t)) return; + Indexer.Flush(); + } + + public void Refresh() + { + var t = ServiceProvider.GetService(); + if (!FactoryIndexerHelper.Support(t)) return; + Indexer.Refresh(); + } + + private Task Queue(Action actionData) + { + var task = new Task(() => + { + try + { + actionData(); + return true; + } + catch (AggregateException agg) + { + foreach (var e in agg.InnerExceptions) + { + Logger.Error(e); + } + throw; + } + + }, TaskCreationOptions.LongRunning); + + task.ConfigureAwait(false); + task.Start(Scheduler); + return task; + } + } + + public class FactoryIndexer + { + private static ICache cache = AscCache.Memory; + internal IContainer Builder { get; set; } + internal static bool Init { get; set; } + public ILog Log { get; } + public Client Client { get; } + public CoreBaseSettings CoreBaseSettings { get; } + + public FactoryIndexer( + IContainer container, + Client client, + IOptionsMonitor options, + CoreBaseSettings coreBaseSettings) + { + Client = client; + CoreBaseSettings = coreBaseSettings; + + try + { + Log = options.Get("ASC.Indexer"); + + if (container != null) + { + Builder = container; + Init = true; + } + } + catch (Exception e) + { + Log.Fatal("FactoryIndexer", e); + } + } + + public bool CheckState(bool cacheState = true) + { + if (!Init) return false; + + const string key = "elasticsearch"; + + if (cacheState) + { + var cacheValue = cache.Get(key); + if (!string.IsNullOrEmpty(cacheValue)) + { + return Convert.ToBoolean(cacheValue); + } + } + + var cacheTime = DateTime.UtcNow.AddMinutes(15); + + try + { + var result = Client.Instance.Ping(new PingRequest()); + + var isValid = result.IsValid; + + Log.DebugFormat("CheckState ping {0}", result.DebugInformation); + + if (cacheState) + { + cache.Insert(key, isValid.ToString(CultureInfo.InvariantCulture).ToLower(), cacheTime); + } + + return isValid; + } + catch (Exception e) + { + if (cacheState) + { + cache.Insert(key, "false", cacheTime); + } + + Log.Error("Ping false", e); + return false; + } + } + + public object GetState(TenantUtil tenantUtil) + { + var indices = CoreBaseSettings.Standalone ? + Client.Instance.CatIndices(new CatIndicesRequest { SortByColumns = new[] { "index" } }).Records.Select(r => new + { + r.Index, + r.DocsCount, + r.StoreSize + }) : + null; + + State state = null; + + if (CoreBaseSettings.Standalone) + { + //TODO + //using (var service = new ServiceClient()) + //{ + // state = service.GetState(); + //} + + if (state.LastIndexed.HasValue) + { + state.LastIndexed = tenantUtil.DateTimeFromUtc(state.LastIndexed.Value); + } + } + + return new + { + state, + indices, + status = CheckState() + }; + } + + public void Reindex(string name) + { + if (!CoreBaseSettings.Standalone) return; + + var generic = typeof(BaseIndexer<>); + var indexers = Builder.Resolve>() + .Where(r => string.IsNullOrEmpty(name) || r.IndexName == name) + .Select(r => (IIndexer)Activator.CreateInstance(generic.MakeGenericType(r.GetType()), r)); + + foreach (var indexer in indexers) + { + indexer.ReIndex(); + } + } + } + + public static class FactoryIndexerExtention + { + public static DIHelper AddFactoryIndexerService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddClientService() + .AddCoreBaseSettingsService(); + } + + public static DIHelper AddFactoryIndexerHelperService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddFactoryIndexerService(); + } + + public static DIHelper AddFactoryIndexerService(this DIHelper services) where T : Wrapper + { + services.TryAddScoped>(); + + return services + .AddFactoryIndexerHelperService() + .AddTenantManagerService() + .AddFactoryIndexerService() + .AddClientService() + .AddSearchSettingsHelperService() + .AddBaseIndexerService(); + } + } +} \ No newline at end of file diff --git a/common/services/ASC.ElasticSearch/Engine/IIndexer.cs b/common/services/ASC.ElasticSearch/Engine/IIndexer.cs new file mode 100644 index 0000000000..b915eac522 --- /dev/null +++ b/common/services/ASC.ElasticSearch/Engine/IIndexer.cs @@ -0,0 +1,39 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.Threading.Tasks; + +namespace ASC.ElasticSearch +{ + internal interface IIndexer + { + string IndexName { get; } + + void Check(); + + Task ReIndex(); + } +} diff --git a/common/services/ASC.ElasticSearch/Service/Service.cs b/common/services/ASC.ElasticSearch/Service/Service.cs new file mode 100644 index 0000000000..d298605516 --- /dev/null +++ b/common/services/ASC.ElasticSearch/Service/Service.cs @@ -0,0 +1,94 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +using ASC.Core; +using ASC.Core.Common.Settings; +using ASC.ElasticSearch.Core; + +using Autofac; + +using Microsoft.Extensions.DependencyInjection; + +namespace ASC.ElasticSearch.Service +{ + public class Service + { + public FactoryIndexer FactoryIndexer { get; } + public IServiceProvider ServiceProvider { get; } + + public Service(FactoryIndexer factoryIndexer, IServiceProvider serviceProvider) + { + FactoryIndexer = factoryIndexer; + ServiceProvider = serviceProvider; + } + + public bool Support(string table) + { + return FactoryIndexer.Builder.Resolve>().Any(r => r.IndexName == table); + } + + public void ReIndex(List toReIndex, int tenant) + { + var allItems = FactoryIndexer.Builder.Resolve>().ToList(); + var tasks = new List(toReIndex.Count); + + foreach (var item in toReIndex) + { + var index = allItems.FirstOrDefault(r => r.IndexName == item); + if (index == null) continue; + + var generic = typeof(BaseIndexer<>); + var instance = (IIndexer)Activator.CreateInstance(generic.MakeGenericType(index.GetType()), index); + tasks.Add(instance.ReIndex()); + } + + Task.WhenAll(tasks).ContinueWith(r => + { + using var scope = ServiceProvider.CreateScope(); + + var tenantManager = scope.ServiceProvider.GetService(); + tenantManager.SetCurrentTenant(tenant); + + var settingsManager = scope.ServiceProvider.GetService(); + settingsManager.ClearCache(); + }); + } + + //public State GetState() + //{ + // return new State + // { + // Indexing = Launcher.Indexing, + // LastIndexed = Launcher.LastIndexed + // }; + //} + } +} diff --git a/common/services/ASC.ElasticSearch/Service/Settings.cs b/common/services/ASC.ElasticSearch/Service/Settings.cs new file mode 100644 index 0000000000..9b4d3cc8da --- /dev/null +++ b/common/services/ASC.ElasticSearch/Service/Settings.cs @@ -0,0 +1,77 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.Configuration; +using ASC.ElasticSearch.Config; + +namespace ASC.ElasticSearch.Service +{ + public class Settings + { + private static readonly Settings DefaultSettings; + + static Settings() + { + DefaultSettings = new Settings + { + Scheme = "http", + Host = "localhost", + Port = 9200, + Period = 1, + MemoryLimit = 10 * 1024 * 1024L + }; + + + var cfg = ConfigurationManager.GetSection("elastic") as ElasticSection; + if (cfg == null) return; + + DefaultSettings = new Settings + { + Scheme = cfg.Scheme, + Host = cfg.Host, + Port = cfg.Port, + Period = cfg.Period, + MemoryLimit = cfg.MemoryLimit + }; + + } + + public string Host { get; set; } + + public int Port { get; set; } + + public string Scheme { get; set; } + + public int Period { get; set; } + + public long MemoryLimit { get; set; } + + public static Settings Default + { + get { return DefaultSettings; } + } + } +} \ No newline at end of file diff --git a/common/services/ASC.ElasticSearch/protos/SearchItem.proto b/common/services/ASC.ElasticSearch/protos/SearchItem.proto new file mode 100644 index 0000000000..cc8dda6cf9 --- /dev/null +++ b/common/services/ASC.ElasticSearch/protos/SearchItem.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; + +package ASC.ElasticSearch; + +message SearchItem { + string Id = 1; +} \ No newline at end of file diff --git a/common/services/ASC.Notify/DbWorker.cs b/common/services/ASC.Notify/DbWorker.cs index 707018e6c4..a2d2c873d4 100644 --- a/common/services/ASC.Notify/DbWorker.cs +++ b/common/services/ASC.Notify/DbWorker.cs @@ -29,6 +29,7 @@ using System.Collections.Generic; using System.Data; using System.Linq; +using ASC.Common; using ASC.Core.Common.EF; using ASC.Core.Common.EF.Context; using ASC.Core.Common.EF.Model; @@ -39,7 +40,6 @@ using Google.Protobuf.Collections; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; using Newtonsoft.Json; @@ -222,7 +222,7 @@ namespace ASC.Notify public static class DbWorkerExtension { - public static IServiceCollection AddDbWorker(this IServiceCollection services) + public static DIHelper AddDbWorker(this DIHelper services) { services.TryAddSingleton(); diff --git a/common/services/ASC.Notify/NotifyCleaner.cs b/common/services/ASC.Notify/NotifyCleaner.cs index 907a84d7eb..ae1b1c15bd 100644 --- a/common/services/ASC.Notify/NotifyCleaner.cs +++ b/common/services/ASC.Notify/NotifyCleaner.cs @@ -25,17 +25,17 @@ using System; -using System.Linq; +using System.Linq; using System.Threading; -using System.Threading.Tasks; +using System.Threading.Tasks; +using ASC.Common; using ASC.Common.Logging; -using ASC.Core.Common.EF; -using ASC.Core.Common.EF.Context; -using ASC.Notify.Config; - +using ASC.Core.Common.EF; +using ASC.Core.Common.EF.Context; +using ASC.Notify.Config; + using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; namespace ASC.Notify @@ -75,21 +75,21 @@ namespace ASC.Notify { try { - var date = DateTime.UtcNow.AddDays(-NotifyServiceCfg.StoreMessagesDays); - + var date = DateTime.UtcNow.AddDays(-NotifyServiceCfg.StoreMessagesDays); + using var scope = ServiceProvider.CreateScope(); - using var dbContext = scope.ServiceProvider.GetService>().Get(NotifyServiceCfg.ConnectionStringName); - using var tx = dbContext.Database.BeginTransaction(); - - var info = dbContext.NotifyInfo.Where(r => r.ModifyDate < date && r.State == 4).ToList(); - var queue = dbContext.NotifyQueue.Where(r => r.CreationDate < date).ToList(); - dbContext.NotifyInfo.RemoveRange(info); - dbContext.NotifyQueue.RemoveRange(queue); + using var dbContext = scope.ServiceProvider.GetService>().Get(NotifyServiceCfg.ConnectionStringName); + using var tx = dbContext.Database.BeginTransaction(); - dbContext.SaveChanges(); - tx.Commit(); + var info = dbContext.NotifyInfo.Where(r => r.ModifyDate < date && r.State == 4).ToList(); + var queue = dbContext.NotifyQueue.Where(r => r.CreationDate < date).ToList(); + dbContext.NotifyInfo.RemoveRange(info); + dbContext.NotifyQueue.RemoveRange(queue); - log.InfoFormat("Clear notify messages: notify_info({0}), notify_queue ({1})", info.Count, queue.Count); + dbContext.SaveChanges(); + tx.Commit(); + + log.InfoFormat("Clear notify messages: notify_info({0}), notify_queue ({1})", info.Count, queue.Count); } catch (ThreadAbortException) @@ -110,7 +110,7 @@ namespace ASC.Notify public static class NotifyCleanerExtension { - public static IServiceCollection AddNotifyCleaner(this IServiceCollection services) + public static DIHelper AddNotifyCleaner(this DIHelper services) { services.TryAddSingleton(); diff --git a/common/services/ASC.Notify/NotifySender.cs b/common/services/ASC.Notify/NotifySender.cs index 43d135cdf7..c603ee74d5 100644 --- a/common/services/ASC.Notify/NotifySender.cs +++ b/common/services/ASC.Notify/NotifySender.cs @@ -29,12 +29,12 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; - + +using ASC.Common; using ASC.Common.Logging; using ASC.Notify.Config; -using ASC.Notify.Messages; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; +using ASC.Notify.Messages; + using Microsoft.Extensions.Options; namespace ASC.Notify @@ -150,7 +150,7 @@ namespace ASC.Notify public static class NotifySenderExtension { - public static IServiceCollection AddNotifySender(this IServiceCollection services) + public static DIHelper AddNotifySender(this DIHelper services) { services.TryAddSingleton(); diff --git a/common/services/ASC.Notify/NotifyService.cs b/common/services/ASC.Notify/NotifyService.cs index 6e928fe1ba..52e498de9e 100644 --- a/common/services/ASC.Notify/NotifyService.cs +++ b/common/services/ASC.Notify/NotifyService.cs @@ -26,14 +26,16 @@ using System; using System.Reflection; + +using ASC.Common; using ASC.Common.Caching; using ASC.Common.Logging; using ASC.Core; using ASC.Core.Common.Settings; using ASC.Notify.Messages; using ASC.Web.Core.WhiteLabel; + using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; namespace ASC.Notify @@ -112,7 +114,7 @@ namespace ASC.Notify public static class NotifyServiceExtension { - public static IServiceCollection AddNotifyService(this IServiceCollection services) + public static DIHelper AddNotifyService(this DIHelper services) { services.TryAddSingleton(); services.TryAddSingleton(typeof(ICacheNotify<>), typeof(KafkaCache<>)); diff --git a/common/services/ASC.Notify/NotifyServiceLauncher.cs b/common/services/ASC.Notify/NotifyServiceLauncher.cs index bec7fcfbec..4ae919cfce 100644 --- a/common/services/ASC.Notify/NotifyServiceLauncher.cs +++ b/common/services/ASC.Notify/NotifyServiceLauncher.cs @@ -28,13 +28,15 @@ using System; using System.Linq; using System.Threading; using System.Threading.Tasks; + +using ASC.Common; using ASC.Common.Logging; using ASC.Core.Common; using ASC.Notify.Config; using ASC.Web.Core; using ASC.Web.Studio.Core.Notify; using ASC.Web.Studio.Utility; -using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; @@ -129,7 +131,7 @@ namespace ASC.Notify public static class NotifyServiceLauncherExtension { - public static IServiceCollection AddNotifyServiceLauncher(this IServiceCollection services) + public static DIHelper AddNotifyServiceLauncher(this DIHelper services) { return services .AddCommonLinkUtilityService() diff --git a/common/services/ASC.Notify/Program.cs b/common/services/ASC.Notify/Program.cs index f80a7c7910..c5c97198db 100644 --- a/common/services/ASC.Notify/Program.cs +++ b/common/services/ASC.Notify/Program.cs @@ -2,6 +2,7 @@ using System.IO; using System.Threading.Tasks; +using ASC.Common; using ASC.Common.DependencyInjection; using ASC.Common.Logging; using ASC.Core.Common; @@ -10,7 +11,6 @@ using ASC.Notify.Config; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; @@ -48,18 +48,20 @@ namespace ASC.Notify }) .ConfigureServices((hostContext, services) => { - services.AddNLogManager("ASC.Notify", "ASC.Notify.Messages"); + var diHelper = new DIHelper(services); + + diHelper.AddNLogManager("ASC.Notify", "ASC.Notify.Messages"); services.Configure(hostContext.Configuration.GetSection("notify")); - services.AddSingleton, ConfigureNotifyServiceCfg>(); + diHelper.AddSingleton, ConfigureNotifyServiceCfg>(); - services.TryAddSingleton(); - services.AddSingleton, ConfigureCommonLinkUtilitySettings>(); + diHelper.TryAddSingleton(); + diHelper.AddSingleton, ConfigureCommonLinkUtilitySettings>(); - services.AddNotifyServiceLauncher(); + diHelper.AddNotifyServiceLauncher(); services.AddHostedService(); - services + diHelper .AddJabberSenderService() .AddSmtpSenderService() .AddAWSSenderService(); diff --git a/common/services/ASC.Studio.Notify/Program.cs b/common/services/ASC.Studio.Notify/Program.cs index 65bfc07ce5..3491800844 100644 --- a/common/services/ASC.Studio.Notify/Program.cs +++ b/common/services/ASC.Studio.Notify/Program.cs @@ -2,6 +2,7 @@ using System.IO; using System.Threading.Tasks; +using ASC.Common; using ASC.Common.DependencyInjection; using ASC.Common.Logging; using ASC.Notify; @@ -44,10 +45,10 @@ namespace ASC.Studio.Notify }) .ConfigureServices((hostContext, services) => { - services.AddHttpContextAccessor(); - services.AddNLogManager("ASC.Notify", "ASC.Notify.Messages"); + var diHelper = new DIHelper(services); + diHelper.AddNLogManager("ASC.Notify", "ASC.Notify.Messages"); services.AddHostedService(); - services.AddServiceLauncher(); + diHelper.AddServiceLauncher(); services.AddAutofac(hostContext.Configuration, hostContext.HostingEnvironment.ContentRootPath); }) diff --git a/common/services/ASC.Studio.Notify/ServiceLauncher.cs b/common/services/ASC.Studio.Notify/ServiceLauncher.cs index 16d2a96975..103b93e482 100644 --- a/common/services/ASC.Studio.Notify/ServiceLauncher.cs +++ b/common/services/ASC.Studio.Notify/ServiceLauncher.cs @@ -27,10 +27,11 @@ using System; using System.Threading; using System.Threading.Tasks; + +using ASC.Common; using ASC.Web.Core; using ASC.Web.Studio.Core.Notify; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; + using Microsoft.Extensions.Hosting; namespace ASC.Notify @@ -66,7 +67,7 @@ namespace ASC.Notify public static class ServiceLauncherExtension { - public static IServiceCollection AddServiceLauncher(this IServiceCollection services) + public static DIHelper AddServiceLauncher(this DIHelper services) { services.TryAddSingleton(); diff --git a/config/autofac.products.json b/config/autofac.products.json index 91b6125805..b848d76d82 100644 --- a/config/autofac.products.json +++ b/config/autofac.products.json @@ -8,6 +8,15 @@ } ], "instanceScope": "singleinstance" + }, + { + "type": "ASC.Web.Files.Configuration.ProductEntryPoint, ASC.Files", + "services": [ + { + "type": "ASC.Web.Core.IWebItem, ASC.Web.Core" + } + ], + "instanceScope": "singleinstance" } ] } diff --git a/config/nginx/onlyoffice-files.conf b/config/nginx/onlyoffice-files.conf new file mode 100644 index 0000000000..426ac2defa --- /dev/null +++ b/config/nginx/onlyoffice-files.conf @@ -0,0 +1,9 @@ +server { + listen 5008; + root /var/www/products/ASC.Files/client; + index index.html; + + location / { + try_files $uri /index.html =404; + } +} \ No newline at end of file diff --git a/config/nginx/onlyoffice.conf b/config/nginx/onlyoffice.conf index bca2caf67b..4d87fedec3 100644 --- a/config/nginx/onlyoffice.conf +++ b/config/nginx/onlyoffice.conf @@ -49,13 +49,13 @@ server { location / { proxy_pass http://localhost:5001; - + location ~* /(manifest.json|service-worker.js|appIcon.png|bg-error.png) { root $public_root; try_files /$basename /index.html =404; } } - + location /sockjs-node { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; @@ -77,7 +77,11 @@ server { location ~* /(people|group) { proxy_pass http://localhost:5004; proxy_set_header X-REWRITER-URL $X_REWRITER_URL; - + } + + location ~* /(files|encryption) { + proxy_pass http://localhost:5007; + proxy_set_header X-REWRITER-URL $X_REWRITER_URL; } } @@ -86,19 +90,45 @@ server { proxy_set_header X-REWRITER-URL $X_REWRITER_URL; } - location /products { + location /products { location ~* /people { #rewrite products/people/(.*) /$1 break; proxy_pass http://localhost:5002; proxy_set_header X-REWRITER-URL $X_REWRITER_URL; - + location ~* /(sockjs-node|locales) { rewrite products/people(.*)/(sockjs-node|locales)/(.*) /$2/$3 break; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header Host $host; - + proxy_pass http://localhost:5002; + + proxy_redirect off; + + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + + location ~* /(manifest.json|service-worker.js|appIcon.png|bg-error.png) { + root $public_root; + try_files /$basename /index.html =404; + } + } + + location ~* /files { + #rewrite products/files/(.*) /$1 break; + proxy_pass http://localhost:5008; + proxy_set_header X-REWRITER-URL $X_REWRITER_URL; + + location ~* /(sockjs-node|locales) { + rewrite products/files(.*)/(sockjs-node|locales)/(.*) /$2/$3 break; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header Host $host; + + proxy_pass http://localhost:5008; proxy_redirect off; diff --git a/config/storage.json b/config/storage.json index 12c87e5d9a..e3a1483d37 100644 --- a/config/storage.json +++ b/config/storage.json @@ -73,7 +73,7 @@ "visible": false, "data": "00000000-0000-0000-0000-000000000000", "type": "disc", - "path": "Products\\Files\\DocStore", + "path": "DocStore", "virtualpath": "~/products/files/docstore", "appendTenantId": false, "public": true, diff --git a/products/ASC.Files/Client/.env b/products/ASC.Files/Client/.env new file mode 100644 index 0000000000..28b97ae092 --- /dev/null +++ b/products/ASC.Files/Client/.env @@ -0,0 +1,3 @@ +PUBLIC_URL=/products/files +WDS_SOCKET_PATH=/products/files/sockjs-node +PORT=5008 \ No newline at end of file diff --git a/products/ASC.Files/Client/.gitignore b/products/ASC.Files/Client/.gitignore new file mode 100644 index 0000000000..d30f40ef44 --- /dev/null +++ b/products/ASC.Files/Client/.gitignore @@ -0,0 +1,21 @@ +# See https://help.github.com/ignore-files/ for more about ignoring files. + +# dependencies +/node_modules + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/products/ASC.Files/Client/config-overrides.js b/products/ASC.Files/Client/config-overrides.js new file mode 100644 index 0000000000..be23a12edd --- /dev/null +++ b/products/ASC.Files/Client/config-overrides.js @@ -0,0 +1,23 @@ +const CopyWebpackPlugin = require('copy-webpack-plugin'); +const path = require('path'); +module.exports = (config) => { + + config.plugins.push( + new CopyWebpackPlugin( + [ + { + from: path.join('src', path.sep, 'components', path.sep, '**', path.sep, 'locales', path.sep, '**'), + to: 'locales', + transformPath(targetPath) { + const reversedArrayOfFolders = path.dirname(targetPath).split(path.sep).reverse(); + const localePath = reversedArrayOfFolders.pop(); + const finalPath = path.join(path.sep, localePath, path.sep, reversedArrayOfFolders[2], path.sep, reversedArrayOfFolders[0], path.sep, path.basename(targetPath)); + return finalPath; + }, + } + ] + ) + ); + + return config; +} \ No newline at end of file diff --git a/products/ASC.Files/Client/package.json b/products/ASC.Files/Client/package.json new file mode 100644 index 0000000000..50866f3093 --- /dev/null +++ b/products/ASC.Files/Client/package.json @@ -0,0 +1,63 @@ +{ + "name": "ASC.Files", + "version": "0.1.0", + "private": true, + "homepage": "/products/files", + "dependencies": { + "asc-web-common": "file:../../../packages/asc-web-common", + "asc-web-components": "file:../../../packages/asc-web-components", + "connected-react-router": "6.6.1", + "copy-to-clipboard": "^3.2.0", + "history": "4.10.1", + "i18next": "19.0.3", + "i18next-browser-languagedetector": "4.0.1", + "i18next-xhr-backend": "3.2.2", + "lodash": "4.17.15", + "lodash-es": "4.17.15", + "merge": "^1.2.1", + "node-sass": "^4.13.0", + "oidc-client": "^1.10.1", + "prop-types": "^15.7.2", + "react": "^16.12.0", + "react-device-detect": "^1.11.14", + "react-dom": "^16.12.0", + "react-i18next": "11.3.0", + "react-redux": "7.1.3", + "react-router": "5.1.2", + "react-router-dom": "5.1.2", + "react-virtualized-auto-sizer": "^1.0.2", + "react-window": "^1.8.5", + "redux": "4.0.5", + "redux-thunk": "2.3.0", + "styled-components": "^5.0.0" + }, + "devDependencies": { + "copy-webpack-plugin": "^5.1.1", + "cross-env": "^6.0.3", + "react-app-rewired": "^2.1.5", + "react-scripts": "3.4.0", + "redux-devtools-extension": "^2.13.8", + "rimraf": "3.0.0" + }, + "eslintConfig": { + "extends": "react-app" + }, + "scripts": { + "start": "react-app-rewired start", + "build": "rimraf ./build && react-app-rewired build", + "test": "cross-env CI=true react-app-rewired test --env=jsdom", + "eject": "react-scripts eject" + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/products/ASC.Files/Client/public/favicon.ico b/products/ASC.Files/Client/public/favicon.ico new file mode 100644 index 0000000000..dcddd3efdf Binary files /dev/null and b/products/ASC.Files/Client/public/favicon.ico differ diff --git a/products/ASC.Files/Client/public/images/dark_general.png b/products/ASC.Files/Client/public/images/dark_general.png new file mode 100644 index 0000000000..9805aad2b0 Binary files /dev/null and b/products/ASC.Files/Client/public/images/dark_general.png differ diff --git a/products/ASC.Files/Client/public/images/empty_screen_filter.png b/products/ASC.Files/Client/public/images/empty_screen_filter.png new file mode 100644 index 0000000000..e79ba5e56f Binary files /dev/null and b/products/ASC.Files/Client/public/images/empty_screen_filter.png differ diff --git a/products/ASC.Files/Client/public/images/files.svg b/products/ASC.Files/Client/public/images/files.svg new file mode 100644 index 0000000000..6c04978b8f --- /dev/null +++ b/products/ASC.Files/Client/public/images/files.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/products/ASC.Files/Client/public/images/icon.svg b/products/ASC.Files/Client/public/images/icon.svg new file mode 100644 index 0000000000..ec9769b531 --- /dev/null +++ b/products/ASC.Files/Client/public/images/icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/products/ASC.Files/Client/public/images/nav.logo.opened.react.svg b/products/ASC.Files/Client/public/images/nav.logo.opened.react.svg new file mode 100644 index 0000000000..f46141f529 --- /dev/null +++ b/products/ASC.Files/Client/public/images/nav.logo.opened.react.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/products/ASC.Files/Client/public/images/nav.logo.react.svg b/products/ASC.Files/Client/public/images/nav.logo.react.svg new file mode 100644 index 0000000000..4d88e63c26 --- /dev/null +++ b/products/ASC.Files/Client/public/images/nav.logo.react.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/products/ASC.Files/Client/public/index.html b/products/ASC.Files/Client/public/index.html new file mode 100644 index 0000000000..e819d1a5cd --- /dev/null +++ b/products/ASC.Files/Client/public/index.html @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + ASC.Files + + + +
+ + + diff --git a/products/ASC.Files/Client/public/manifest.json b/products/ASC.Files/Client/public/manifest.json new file mode 100644 index 0000000000..a9b5861b4b --- /dev/null +++ b/products/ASC.Files/Client/public/manifest.json @@ -0,0 +1,15 @@ +{ + "short_name": "ASC.Files", + "name": "ASC.Files", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + } + ], + "start_url": "./index.html", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/products/ASC.Files/Client/src/App.js b/products/ASC.Files/Client/src/App.js new file mode 100644 index 0000000000..ee108f2615 --- /dev/null +++ b/products/ASC.Files/Client/src/App.js @@ -0,0 +1,36 @@ +import React, { Suspense } from "react"; +import { connect } from "react-redux"; +import { Router, Switch, Redirect } from "react-router-dom"; +import { Loader } from "asc-web-components"; +import Home from "./components/pages/Home"; +import { history, PrivateRoute, PublicRoute, Login, Error404, StudioLayout, Offline } from "asc-web-common"; + +const App = ({ settings }) => { + const { homepage } = settings; + return ( + navigator.onLine ? + + + } + > + + + + + + + + + + : + ); +}; + +function mapStateToProps(state) { + return { + settings: state.auth.settings + }; +} + +export default connect(mapStateToProps)(App); diff --git a/products/ASC.Files/Client/src/App.test.js b/products/ASC.Files/Client/src/App.test.js new file mode 100644 index 0000000000..304cc4af9f --- /dev/null +++ b/products/ASC.Files/Client/src/App.test.js @@ -0,0 +1,13 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { MemoryRouter } from 'react-router-dom'; +import App from './App'; + +it('renders without crashing', async () => { + const div = document.createElement('div'); + ReactDOM.render( + + + , div); + await new Promise(resolve => setTimeout(resolve, 1000)); +}); diff --git a/products/ASC.Files/Client/src/components/Article/Body/index.js b/products/ASC.Files/Client/src/components/Article/Body/index.js new file mode 100644 index 0000000000..b7c5070b06 --- /dev/null +++ b/products/ASC.Files/Client/src/components/Article/Body/index.js @@ -0,0 +1,135 @@ +import React from 'react'; +import { utils } from 'asc-web-components'; +import { connect } from 'react-redux'; +import { + TreeMenu, + TreeNode, + Icons +} from "asc-web-components"; +import { selectFolder } from '../../../store/files/actions'; + +const getItems = data => { + return data.map(item => { + if (item.children && item.children.length) { + return ( + + ) : ( + "" + ) + } + > + {getItems(item.children)} + + ); + } + return ( + + ) : ( + "" + ) + } + /> + ); + }); +}; + +class ArticleBodyContent extends React.Component { + + shouldComponentUpdate(nextProps) { + if(!utils.array.isArrayEqual(nextProps.selectedKeys, this.props.selectedKeys)) { + return true; + } + + if(!utils.array.isArrayEqual(nextProps.data, this.props.data)) { + return true; + } + + return false; + } + + onSelect = data => { + this.props.selectFolder(data && data.length === 1 && data[0] !== "root" ? data[0] : null); + }; + + switcherIcon = obj => { + if (obj.isLeaf) { + return null; + } + if (obj.expanded) { + return ( + + ); + } else { + return ( + + ); + } + }; + + render() { + const { data, selectedKeys } = this.props; + + //console.log("FilesTreeMenu", this.props); + + return ( + + {getItems(data)} + + ); + }; +}; + +const getTreeGroups = (groups, departments) => { + const treeData = [ + { + key: "root", + title: departments, + root: true, + children: groups.map(g => { + return { + key: g.id, title: g.name, root: false + }; + }) || [] + } + ]; + + return treeData; +}; + +function mapStateToProps(state) { + return { + data: getTreeGroups(state.files.folders, state.auth.settings.customNames.groupsCaption), + selectedKeys: state.files.selectedFolder ? [state.files.selectedFolder] : ["root"] + }; +} + +export default connect(mapStateToProps, { selectFolder })(ArticleBodyContent); \ No newline at end of file diff --git a/products/ASC.Files/Client/src/components/Article/Header/index.js b/products/ASC.Files/Client/src/components/Article/Header/index.js new file mode 100644 index 0000000000..1b8389bcf8 --- /dev/null +++ b/products/ASC.Files/Client/src/components/Article/Header/index.js @@ -0,0 +1,17 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { store, Headline } from 'asc-web-common'; +const { getCurrentModule } = store.auth.selectors; + +const ArticleHeaderContent = ({currentModuleName}) => { + return {currentModuleName}; +} + +const mapStateToProps = (state) => { + const currentModule = getCurrentModule(state.auth.modules, state.auth.settings.currentProductId); + return { + currentModuleName: (currentModule && currentModule.title) || "" + } +} + +export default connect(mapStateToProps)(ArticleHeaderContent); \ No newline at end of file diff --git a/products/ASC.Files/Client/src/components/Article/MainButton/index.js b/products/ASC.Files/Client/src/components/Article/MainButton/index.js new file mode 100644 index 0000000000..a15948608d --- /dev/null +++ b/products/ASC.Files/Client/src/components/Article/MainButton/index.js @@ -0,0 +1,67 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import PropTypes from 'prop-types'; +import { withRouter } from 'react-router'; +import { + MainButton, + DropDownItem, + toastr +} from "asc-web-components"; +import { withTranslation, I18nextProvider } from 'react-i18next'; +import i18n from '../i18n'; +import { store, utils } from 'asc-web-common'; +const { changeLanguage } = utils; +const { isAdmin } = store.auth.selectors; + +class PureArticleMainButtonContent extends React.Component { + + render() { + console.log("Files ArticleMainButtonContent render"); + const { isAdmin, t } = this.props; + + return ( + isAdmin ? + <> + + toastr.info("Create document click")} + /> + toastr.info("Create folder click")} + /> + + + : + <> + ); + }; +}; + +const ArticleMainButtonContentContainer = withTranslation()(PureArticleMainButtonContent); + +const ArticleMainButtonContent = (props) => { + changeLanguage(i18n); + return (); +}; + +ArticleMainButtonContent.propTypes = { + isAdmin: PropTypes.bool.isRequired, + history: PropTypes.object.isRequired +}; + +const mapStateToProps = (state) => { + return { + isAdmin: isAdmin(state.auth.user), + settings: state.auth.settings + } +} + +export default connect(mapStateToProps)(withRouter(ArticleMainButtonContent)); \ No newline at end of file diff --git a/products/ASC.Files/Client/src/components/Article/i18n.js b/products/ASC.Files/Client/src/components/Article/i18n.js new file mode 100644 index 0000000000..9c384193f4 --- /dev/null +++ b/products/ASC.Files/Client/src/components/Article/i18n.js @@ -0,0 +1,54 @@ +import i18n from "i18next"; +import Backend from "i18next-xhr-backend"; +import config from "../../../package.json"; +import { constants } from 'asc-web-common'; +const { LANGUAGE } = constants; + +const newInstance = i18n.createInstance(); + +if (process.env.NODE_ENV === "production") { + newInstance + .use(Backend) + .init({ + lng: localStorage.getItem(LANGUAGE) || 'en', + fallbackLng: "en", + + interpolation: { + escapeValue: false, // not needed for react as it escapes by default + }, + + react: { + useSuspense: true + }, + backend: { + loadPath: `${config.homepage}/locales/Article/{{lng}}/{{ns}}.json` + } + }); +} else if (process.env.NODE_ENV === "development") { + + const resources = { + en: { + translation: require("./locales/en/translation.json") + }, + ru: { + translation: require("./locales/ru/translation.json") + } + }; + + newInstance.init({ + resources: resources, + lng: localStorage.getItem(LANGUAGE) || 'en', + fallbackLng: "en", + debug: true, + + interpolation: { + escapeValue: false, // not needed for react as it escapes by default + }, + + react: { + useSuspense: false + } + }); +} + +export default newInstance; \ No newline at end of file diff --git a/products/ASC.Files/Client/src/components/Article/index.js b/products/ASC.Files/Client/src/components/Article/index.js new file mode 100644 index 0000000000..3cb994e0fe --- /dev/null +++ b/products/ASC.Files/Client/src/components/Article/index.js @@ -0,0 +1,3 @@ +export { default as ArticleHeaderContent } from './Header'; +export { default as ArticleBodyContent } from './Body'; +export { default as ArticleMainButtonContent } from './MainButton'; \ No newline at end of file diff --git a/products/ASC.Files/Client/src/components/Article/locales/en/translation.json b/products/ASC.Files/Client/src/components/Article/locales/en/translation.json new file mode 100644 index 0000000000..b00f5f5581 --- /dev/null +++ b/products/ASC.Files/Client/src/components/Article/locales/en/translation.json @@ -0,0 +1,6 @@ +{ + "InviteLinkTitle": "Invitation link", + "ImportPeople": "Import people", + "Actions": "Actions", + "LblInviteAgain": "Invite again" +} \ No newline at end of file diff --git a/products/ASC.Files/Client/src/components/Article/locales/ru/translation.json b/products/ASC.Files/Client/src/components/Article/locales/ru/translation.json new file mode 100644 index 0000000000..154db9d83a --- /dev/null +++ b/products/ASC.Files/Client/src/components/Article/locales/ru/translation.json @@ -0,0 +1,6 @@ +{ + "InviteLinkTitle": "Пригласительная ссылка", + "ImportPeople": "Импортировать людей", + "Actions": "Действия", + "LblInviteAgain": "Отправить приглашение ещё раз" +} \ No newline at end of file diff --git a/products/ASC.Files/Client/src/components/pages/Home/Section/Body/index.js b/products/ASC.Files/Client/src/components/pages/Home/Section/Body/index.js new file mode 100644 index 0000000000..7480494282 --- /dev/null +++ b/products/ASC.Files/Client/src/components/pages/Home/Section/Body/index.js @@ -0,0 +1,41 @@ +import React from "react"; +import { withRouter } from "react-router"; +import { connect } from "react-redux"; +import { withTranslation } from "react-i18next"; +import { + Text +} from "asc-web-components"; + +import i18n from '../../i18n'; + +class SectionBodyContent extends React.PureComponent { + + componentDidMount() { + + } + + render() { + console.log("Home SectionBodyContent render()"); + return There must be the list of files and folders + } +} + +SectionBodyContent.defaultProps = { + files: null +}; + +const mapStateToProps = state => { + return { + selection: state.files.selection, + selected: state.files.selected, + files: state.files.files, + viewer: state.auth.user, + settings: state.auth.settings, + //filter: state.people.filter + }; +}; + +export default connect( + mapStateToProps, + // { selectUser, deselectUser, setSelection, updateUserStatus, resetFilter, fetchPeople } +)(withRouter(withTranslation()(SectionBodyContent))); diff --git a/products/ASC.Files/Client/src/components/pages/Home/Section/Filter/index.js b/products/ASC.Files/Client/src/components/pages/Home/Section/Filter/index.js new file mode 100644 index 0000000000..c8001e1116 --- /dev/null +++ b/products/ASC.Files/Client/src/components/pages/Home/Section/Filter/index.js @@ -0,0 +1,274 @@ +import React from "react"; +import { connect } from "react-redux"; +import { FilterInput } from "asc-web-components"; +import { fetchFiles } from "../../../../../store/files/actions"; +import find from "lodash/find"; +import result from "lodash/result"; +import { withTranslation } from "react-i18next"; +import { withRouter } from "react-router"; +import { getFilterByLocation } from "../../../../../helpers/converters"; +import { store } from 'asc-web-common'; +const { isAdmin } = store.auth.selectors; + +const getEmployeeStatus = filterValues => { + const employeeStatus = result( + find(filterValues, value => { + return value.group === "filter-status"; + }), + "key" + ); + + return employeeStatus ? +employeeStatus : null; +}; + +const getActivationStatus = filterValues => { + const activationStatus = result( + find(filterValues, value => { + return value.group === "filter-email"; + }), + "key" + ); + + return activationStatus ? +activationStatus : null; +}; + +const getRole = filterValues => { + const employeeStatus = result( + find(filterValues, value => { + return value.group === "filter-type"; + }), + "key" + ); + + return employeeStatus || null; +}; + +const getGroup = filterValues => { + const groupId = result( + find(filterValues, value => { + return value.group === "filter-group"; + }), + "key" + ); + + return groupId || null; +}; + +class SectionFilterContent extends React.Component { + componentDidMount() { + const { location, filter, onLoading, fetchFiles } = this.props; + + const newFilter = getFilterByLocation(location); + + if (!newFilter || newFilter.equals(filter)) return; + + onLoading(true); + fetchFiles(newFilter).finally(() => onLoading(false)); + } + + onFilter = data => { + const { onLoading, fetchFiles, filter } = this.props; + + const employeeStatus = getEmployeeStatus(data.filterValues); + const activationStatus = getActivationStatus(data.filterValues); + const role = getRole(data.filterValues); + const group = getGroup(data.filterValues); + const search = data.inputValue || null; + const sortBy = data.sortId; + const sortOrder = + data.sortDirection === "desc" ? "descending" : "ascending"; + + const newFilter = filter.clone(); + newFilter.page = 0; + newFilter.sortBy = sortBy; + newFilter.sortOrder = sortOrder; + newFilter.employeeStatus = employeeStatus; + newFilter.activationStatus = activationStatus; + newFilter.role = role; + newFilter.search = search; + newFilter.group = group; + + onLoading(true); + fetchFiles(newFilter).finally(() => onLoading(false)); + }; + + getData = () => { + const { user, folders, t, settings } = this.props; + const { guestCaption, userCaption, groupCaption } = settings.customNames; + + const options = !isAdmin(user) + ? [] + : [ + { + key: "filter-status", + group: "filter-status", + label: t("UserStatus"), + isHeader: true + }, + { + key: "1", + group: "filter-status", + label: t("LblActive") + }, + { + key: "2", + group: "filter-status", + label: t("LblTerminated") + } + ]; + + const groupOptions = folders.map(group => { + return { + key: group.id, + inSubgroup: true, + group: "filter-group", + label: group.name + }; + }); + + const filterOptions = [ + ...options, + { + key: "filter-email", + group: "filter-email", + label: t("Email"), + isHeader: true + }, + { + key: "1", + group: "filter-email", + label: t("LblActive") + }, + { + key: "2", + group: "filter-email", + label: t("LblPending") + }, + { + key: "filter-type", + group: "filter-type", + label: t("UserType"), + isHeader: true + }, + { key: "admin", group: "filter-type", label: t("Administrator") }, + { + key: "user", + group: "filter-type", + label: userCaption + }, + { + key: "guest", + group: "filter-type", + label: guestCaption + }, + { + key: "filter-other", + group: "filter-other", + label: t("LblOther"), + isHeader: true + }, + { + key: "filter-type-group", + group: "filter-other", + subgroup: "filter-group", + label: groupCaption, + defaultSelectLabel: t("LblSelect") + }, + ...groupOptions + ]; + + //console.log("getData (filterOptions)", filterOptions); + + return filterOptions; + }; + + getSortData = () => { + const { t } = this.props; + + return [ + { key: "firstname", label: t("ByFirstNameSorting"), default: true }, + { key: "lastname", label: t("ByLastNameSorting"), default: true } + ]; + }; + + getSelectedFilterData = () => { + const { filter } = this.props; + const selectedFilterData = { + filterValues: [], + sortDirection: filter.sortOrder === "ascending" ? "asc" : "desc", + sortId: filter.sortBy + }; + + selectedFilterData.inputValue = filter.search; + + if (filter.employeeStatus) { + selectedFilterData.filterValues.push({ + key: `${filter.employeeStatus}`, + group: "filter-status" + }); + } + + if (filter.activationStatus) { + selectedFilterData.filterValues.push({ + key: `${filter.activationStatus}`, + group: "filter-email" + }); + } + + if (filter.role) { + selectedFilterData.filterValues.push({ + key: filter.role, + group: "filter-type" + }); + } + + if (filter.group) { + selectedFilterData.filterValues.push({ + key: filter.group, + group: "filter-group" + }); + } + + return selectedFilterData; + }; + + needForUpdate = (currentProps, nextProps) => { + if (currentProps.language !== nextProps.language) { + return true; + } + return false; + }; + + + render() { + const selectedFilterData = this.getSelectedFilterData(); + const { t, i18n } = this.props; + return ( + + ); + } +} + +function mapStateToProps(state) { + return { + user: state.auth.user, + folders: state.files.folders, + filter: state.files.filter, + settings: state.auth.settings + }; +} + +export default connect( + mapStateToProps, + { fetchFiles } +)(withRouter(withTranslation()(SectionFilterContent))); diff --git a/products/ASC.Files/Client/src/components/pages/Home/Section/Header/index.js b/products/ASC.Files/Client/src/components/pages/Home/Section/Header/index.js new file mode 100644 index 0000000000..42e22cfb3d --- /dev/null +++ b/products/ASC.Files/Client/src/components/pages/Home/Section/Header/index.js @@ -0,0 +1,69 @@ +import React from "react"; +import styled, { css } from "styled-components"; +import { withRouter } from "react-router"; +import { Headline } from 'asc-web-common'; +import { connect } from "react-redux"; +import { withTranslation } from "react-i18next"; + +const StyledContainer = styled.div` + + @media (min-width: 1024px) { + ${props => props.isHeaderVisible && css`width: calc(100% + 76px);`} + } + + .group-button-menu-container { + margin: 0 -16px; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + + @media (max-width: 1024px) { + & > div:first-child { + position: absolute; + top: 56px; + z-index: 180; + } + } + + @media (min-width: 1024px) { + margin: 0 -24px; + } + } + + .header-container { + position: relative; + + display: flex; + align-items: center; + max-width: calc(100vw - 32px); + + .action-button { + margin-left: 16px; + + @media (max-width: 1024px) { + margin-left: auto; + + & > div:first-child { + padding: 8px 16px 8px 16px; + margin-right: -16px; + } + } + } + } +`; + +const SectionHeaderContent = props => { + + return ( + + Files sample header + + ); +}; + +const mapStateToProps = state => { + return { + }; +}; + +export default connect( + mapStateToProps +)(withTranslation()(withRouter(SectionHeaderContent))); diff --git a/products/ASC.Files/Client/src/components/pages/Home/Section/Paging/index.js b/products/ASC.Files/Client/src/components/pages/Home/Section/Paging/index.js new file mode 100644 index 0000000000..a3d34a81ac --- /dev/null +++ b/products/ASC.Files/Client/src/components/pages/Home/Section/Paging/index.js @@ -0,0 +1,150 @@ +import React, { useCallback, useMemo } from "react"; +import { connect } from "react-redux"; +import { fetchFiles } from "../../../../../store/files/actions"; +import { Paging } from "asc-web-components"; +import { useTranslation } from 'react-i18next'; + +const SectionPagingContent = ({ + fetchFiles, + filter, + onLoading, + selectedCount +}) => { + const { t } = useTranslation(); + const onNextClick = useCallback( + e => { + if (!filter.hasNext()) { + e.preventDefault(); + return; + } + console.log("Next Clicked", e); + + const newFilter = filter.clone(); + newFilter.page++; + + onLoading(true); + fetchFiles(newFilter).finally(() => onLoading(false)); + }, + [filter, fetchFiles, onLoading] + ); + + const onPrevClick = useCallback( + e => { + if (!filter.hasPrev()) { + e.preventDefault(); + return; + } + + console.log("Prev Clicked", e); + + const newFilter = filter.clone(); + newFilter.page--; + + onLoading(true); + fetchFiles(newFilter).finally(() => onLoading(false)); + }, + [filter, fetchFiles, onLoading] + ); + + const onChangePageSize = useCallback( + pageItem => { + console.log("Paging onChangePageSize", pageItem); + + const newFilter = filter.clone(); + newFilter.page = 0; + newFilter.pageCount = pageItem.key; + + onLoading(true); + fetchFiles(newFilter).finally(() => onLoading(false)); + }, + [filter, fetchFiles, onLoading] + ); + + const onChangePage = useCallback( + pageItem => { + console.log("Paging onChangePage", pageItem); + + const newFilter = filter.clone(); + newFilter.page = pageItem.key; + + onLoading(true); + fetchFiles(newFilter).finally(() => onLoading(false)); + }, + [filter, fetchFiles, onLoading] + ); + + const countItems = useMemo( + () => [ + { + key: 25, + label: t('CountPerPage', { count: 25 }) + }, + { + key: 50, + label: t('CountPerPage', { count: 50 }) + }, + { + key: 100, + label: t('CountPerPage', { count: 100 }) + } + ], + [t] + ); + + const pageItems = useMemo(() => { + if (filter.total < filter.pageCount) return []; + const totalPages = Math.ceil(filter.total / filter.pageCount); + return [...Array(totalPages).keys()].map( + item => { + return { key: item, label: t('PageOfTotalPage', { page: item+1, totalPage: totalPages }) }; + } + ); + }, [filter.total, filter.pageCount, t]); + + const emptyPageSelection = { + key: 0, + label: t('PageOfTotalPage', { page: 1, totalPage: 1 }) + } + + const emptyCountSelection = { + key: 0, + label: t('CountPerPage', { count: 25 }) + }; + + const selectedPageItem = pageItems.find(x => x.key === filter.page) || emptyPageSelection; + const selectedCountItem = countItems.find(x => x.key === filter.pageCount) || emptyCountSelection; + + console.log("SectionPagingContent render", filter); + + return filter.total < filter.pageCount ? ( + <> + ) : ( + + ); +}; + +function mapStateToProps(state) { + return { + filter: state.files.filter + }; +} + +export default connect( + mapStateToProps, + { fetchFiles } +)(SectionPagingContent); diff --git a/products/ASC.Files/Client/src/components/pages/Home/Section/index.js b/products/ASC.Files/Client/src/components/pages/Home/Section/index.js new file mode 100644 index 0000000000..4a2ab45fa0 --- /dev/null +++ b/products/ASC.Files/Client/src/components/pages/Home/Section/index.js @@ -0,0 +1,4 @@ +export { default as SectionHeaderContent } from './Header'; +export { default as SectionBodyContent } from './Body'; +export { default as SectionFilterContent } from './Filter'; +export { default as SectionPagingContent } from './Paging'; \ No newline at end of file diff --git a/products/ASC.Files/Client/src/components/pages/Home/i18n.js b/products/ASC.Files/Client/src/components/pages/Home/i18n.js new file mode 100644 index 0000000000..6f0cfd1ad4 --- /dev/null +++ b/products/ASC.Files/Client/src/components/pages/Home/i18n.js @@ -0,0 +1,54 @@ +import i18n from "i18next"; +import Backend from "i18next-xhr-backend"; +import config from "../../../../package.json"; +import { constants } from 'asc-web-common'; +const { LANGUAGE } = constants; + +const newInstance = i18n.createInstance(); + +if (process.env.NODE_ENV === "production") { + newInstance + .use(Backend) + .init({ + lng: localStorage.getItem(LANGUAGE) || 'en', + fallbackLng: "en", + + interpolation: { + escapeValue: false, // not needed for react as it escapes by default + }, + + react: { + useSuspense: true + }, + backend: { + loadPath: `${config.homepage}/locales/Home/{{lng}}/{{ns}}.json` + } + }); +} else if (process.env.NODE_ENV === "development") { + + const resources = { + en: { + translation: require("./locales/en/translation.json") + }, + ru: { + translation: require("./locales/ru/translation.json") + } + }; + + newInstance.init({ + resources: resources, + lng: localStorage.getItem(LANGUAGE) || 'en', + fallbackLng: "en", + debug: true, + + interpolation: { + escapeValue: false, // not needed for react as it escapes by default + }, + + react: { + useSuspense: false + } + }); +} + +export default newInstance; \ No newline at end of file diff --git a/products/ASC.Files/Client/src/components/pages/Home/index.js b/products/ASC.Files/Client/src/components/pages/Home/index.js new file mode 100644 index 0000000000..e10ecac9f7 --- /dev/null +++ b/products/ASC.Files/Client/src/components/pages/Home/index.js @@ -0,0 +1,174 @@ +import React from "react"; +import { connect } from "react-redux"; +import PropTypes from "prop-types"; +import { withRouter } from "react-router"; +import { RequestLoader } from "asc-web-components"; +import { PageLayout, utils } from "asc-web-common"; +import { withTranslation, I18nextProvider } from 'react-i18next'; +import i18n from "./i18n"; + +import { + ArticleHeaderContent, + ArticleBodyContent, + ArticleMainButtonContent +} from "../../Article"; +import { + SectionHeaderContent, + SectionBodyContent, + SectionFilterContent, + SectionPagingContent +} from "./Section"; +import { setSelected } from "../../../store/files/actions"; +const { changeLanguage } = utils; + +class PureHome extends React.Component { + constructor(props) { + super(props); + + this.state = { + isHeaderVisible: false, + isHeaderIndeterminate: false, + isHeaderChecked: false, + isLoading: false + }; + } + + renderGroupButtonMenu = () => { + const { files, selection, selected, setSelected } = this.props; + + const headerVisible = selection.length > 0; + const headerIndeterminate = + headerVisible && selection.length > 0 && selection.length < files.length; + const headerChecked = headerVisible && selection.length === files.length; + + /*console.log(`renderGroupButtonMenu() + headerVisible=${headerVisible} + headerIndeterminate=${headerIndeterminate} + headerChecked=${headerChecked} + selection.length=${selection.length} + files.length=${files.length} + selected=${selected}`);*/ + + let newState = {}; + + if (headerVisible || selected === "close") { + newState.isHeaderVisible = headerVisible; + if (selected === "close") { + setSelected("none"); + } + } + + newState.isHeaderIndeterminate = headerIndeterminate; + newState.isHeaderChecked = headerChecked; + + this.setState(newState); + }; + + componentDidUpdate(prevProps) { + if (this.props.selection !== prevProps.selection) { + this.renderGroupButtonMenu(); + } + } + + onSectionHeaderContentCheck = checked => { + this.props.setSelected(checked ? "all" : "none"); + }; + + onSectionHeaderContentSelect = selected => { + this.props.setSelected(selected); + }; + + onClose = () => { + const { selection, setSelected } = this.props; + + if (!selection.length) { + setSelected("none"); + this.setState({ isHeaderVisible: false }); + } else { + setSelected("close"); + } + }; + + onLoading = status => { + this.setState({ isLoading: status }); + }; + + render() { + const { + isHeaderVisible, + isHeaderIndeterminate, + isHeaderChecked, + selected + } = this.state; + const { t } = this.props; + return ( + <> + + } + articleMainButtonContent={} + articleBodyContent={} + sectionHeaderContent={ + + } + sectionFilterContent={} + sectionBodyContent={ + + } + sectionPagingContent={ + + } + /> + + ); + } +} + +const HomeContainer = withTranslation()(PureHome); + +const Home = (props) => { + changeLanguage(i18n); + return (); +} + +Home.propTypes = { + files: PropTypes.array, + history: PropTypes.object.isRequired, + isLoaded: PropTypes.bool +}; + +function mapStateToProps(state) { + return { + files: state.files.files, + selection: state.files.selection, + selected: state.files.selected, + isLoaded: state.auth.isLoaded + }; +} + +export default connect( + mapStateToProps, + { setSelected } +)(withRouter(Home)); diff --git a/products/ASC.Files/Client/src/components/pages/Home/locales/en/translation.json b/products/ASC.Files/Client/src/components/pages/Home/locales/en/translation.json new file mode 100644 index 0000000000..b65a309a69 --- /dev/null +++ b/products/ASC.Files/Client/src/components/pages/Home/locales/en/translation.json @@ -0,0 +1,57 @@ +{ + "LoadingProcessing": "Loading...", + "LblSendEmail": "Send email", + "LblSendMessage": "Send message", + "EditButton": "Edit", + "PasswordChangeButton": "Change password", + "EmailChangeButton": "Change email", + "DisableUserButton": "Disable", + "EnableUserButton": "Enable", + "ReassignData": "Reassign data", + "RemoveData": "Delete personal data", + "DeleteSelfProfile": "Delete profile", + "LoadingDescription": "Please wait...", + "NotFoundDescription": "No people matching your filter can be displayed in this section. Please select other filter options or clear filter to view all the people in this section.", + "NotFoundTitle": "No results matching your search could be found", + "ClearButton": "Reset filter", + "UserStatus": "Status", + "LblActive": "Active", + "LblTerminated": "Disabled", + "Email": "Email", + "LblPending": "Pending", + "UserType": "Type", + "Administrator": "Administrator", + "LblOther": "Other", + "DeleteButton": "Delete", + "SuccessChangeUserStatus": "The user status was successfully changed", + "SuccessChangeUserType": "The user type was successfully changed", + "LblSelect": "Select", + "More": "More", + "CloseButton": "Close", + "Actions": "Actions", + "People": "People", + "PreviousPage": "Previous", + "NextPage": "Next", + "MessageEmailActivationInstuctionsSentOnEmail": "The email activation instructions have been sent to the <1>{{email}} email address", + "Search": "Search", + "SendInviteAgain": "Send invitation once again", + "SuccessSendInvitation": "The invitation was successfully sent", + + "CountPerPage": "{{count}} per page", + "PageOfTotalPage": "{{page}} of {{totalPage}}", + "ChangeToUser": "Change to {{userCaption}}", + "ChangeToGuest": "Change to {{guestCaption}}", + + "LblInviteAgain": "Invite again", + "ByFirstNameSorting": "By first name", + "ByLastNameSorting": "By last name", + "LblInvited": "Invited", + "LblSetActive": "Set active", + "LblSetDisabled": "Set disabled", + "DirectionAscLabel":"A-Z", + "DirectionDescLabel":"Z-A", + "MakeInvitationLink": "Make invitation link", + "SuccessfullyRemovedUsers": "Users have been removed successfully", + "SendEmailAction": "Send e-mail action", + "SuccessfullyRemovedGroup": "Group has been removed successfully" +} \ No newline at end of file diff --git a/products/ASC.Files/Client/src/components/pages/Home/locales/ru/translation.json b/products/ASC.Files/Client/src/components/pages/Home/locales/ru/translation.json new file mode 100644 index 0000000000..ef81a1391d --- /dev/null +++ b/products/ASC.Files/Client/src/components/pages/Home/locales/ru/translation.json @@ -0,0 +1,57 @@ +{ + "LoadingProcessing": "Загрузка...", + "LblSendEmail": "Отправить email", + "LblSendMessage": "Отправить сообщение", + "EditButton": "Редактировать", + "PasswordChangeButton": "Изменить пароль", + "EmailChangeButton": "Изменить email", + "DisableUserButton": "Заблокировать", + "EnableUserButton": "Разблокировать", + "ReassignData": "Передать данные", + "RemoveData": "Удалить личные данные", + "DeleteSelfProfile": "Удалить профиль", + "LoadingDescription": "Пожалуйста подождите...", + "NotFoundDescription": "В данном разделе нет людей, соответствующих фильтру. Пожалуйста, выберите другие параметры или очистите фильтр, чтобы просмотреть всех людей в этом разделе.", + "NotFoundTitle": "Результатов, соответствующих заданным критериям, не найдено", + "ClearButton": "Сбросить фильтр", + "UserStatus": "Статус", + "LblActive": "Активный", + "LblTerminated": "Заблокирован", + "Email": "Email", + "LblPending": "Ожидание", + "UserType": "Тип", + "Administrator": "Администратор", + "LblOther": "Другое", + "DeleteButton": "Удалить", + "SuccessChangeUserStatus": "Статус пользователя успешно изменен", + "SuccessChangeUserType": "Тип пользователя успешно изменен", + "LblSelect": "Выберите", + "More": "Больше", + "CloseButton": "Закрыть", + "Actions": "Действия", + "People": "Люди", + "PreviousPage": "Предыдущая", + "NextPage": "Следующая", + "MessageEmailActivationInstuctionsSentOnEmail": "Инструкции по активации электронной почты были отправлены на адрес <1>{{email}}", + "Search": "Поиск", + "SendInviteAgain": "Отправить приглашение ещё раз", + "SuccessSendInvitation": "Приглашение успешно отправлено", + + "PageOfTotalPage": "{{page}} из {{totalPage}}", + "CountPerPage": "{{count}} на странице", + "ChangeToUser": "Изменить на {{userCaption}}", + "ChangeToGuest": "Изменить на {{guestCaption}}", + + "LblInviteAgain": "Выслать прилашение ещё раз", + "ByFirstNameSorting": "По имени", + "ByLastNameSorting": "По фамилии", + "LblInvited": "Приглашен", + "LblSetActive": "Разблокировать", + "LblSetDisabled": "Заблокировать", + "DirectionAscLabel":"А-Я", + "DirectionDescLabel":"Я-А", + "MakeInvitationLink": "Создать пригласительную ссылку", + "SuccessfullyRemovedUsers": "Пользователи были успешно удалены", + "SendEmailAction": "Действие отправки e-mail", + "SuccessfullyRemovedGroup": "Группа была успешно удалена" +} \ No newline at end of file diff --git a/products/ASC.Files/Client/src/custom.scss b/products/ASC.Files/Client/src/custom.scss new file mode 100644 index 0000000000..d8c8ea66ac --- /dev/null +++ b/products/ASC.Files/Client/src/custom.scss @@ -0,0 +1,23 @@ +// Override default variables before the import +$font-family-base: 'Open Sans', sans-serif; + + + +html, body { + height: 100%; +} + +#root { + min-height: 100%; + position: relative; + + .pageLoader { + position: fixed; + left: calc(50% - 20px); + top: 35%; + } +} + +body { + margin: 0; +} diff --git a/products/ASC.Files/Client/src/helpers/constants.js b/products/ASC.Files/Client/src/helpers/constants.js new file mode 100644 index 0000000000..d7a84732ea --- /dev/null +++ b/products/ASC.Files/Client/src/helpers/constants.js @@ -0,0 +1,10 @@ +export const GUID_EMPTY = "00000000-0000-0000-0000-000000000000"; +export const EMPLOYEE_STATUS = "employeestatus"; +export const ACTIVATION_STATUS = "activationstatus"; +export const ROLE = "role"; +export const GROUP = "group"; +export const SEARCH = "search"; +export const SORT_BY = "sortby"; +export const SORT_ORDER = "sortorder"; +export const PAGE = "page"; +export const PAGE_COUNT = "pagecount"; \ No newline at end of file diff --git a/products/ASC.Files/Client/src/helpers/converters.js b/products/ASC.Files/Client/src/helpers/converters.js new file mode 100644 index 0000000000..27591b042a --- /dev/null +++ b/products/ASC.Files/Client/src/helpers/converters.js @@ -0,0 +1,53 @@ +import { + EMPLOYEE_STATUS, + ACTIVATION_STATUS, + ROLE, + GROUP, + SEARCH, + SORT_BY, + SORT_ORDER, + PAGE, + PAGE_COUNT +} from "./constants"; +import { api, utils } from "asc-web-common"; +const { Filter } = api; +const { getObjectByLocation } = utils; + +export function getFilterByLocation(location) { + const urlFilter = getObjectByLocation(location); + + if(!urlFilter) return null; + + const defaultFilter = Filter.getDefault(); + + const employeeStatus = + (urlFilter[EMPLOYEE_STATUS] && +urlFilter[EMPLOYEE_STATUS]) || + defaultFilter.employeeStatus; + const activationStatus = + (urlFilter[ACTIVATION_STATUS] && +urlFilter[ACTIVATION_STATUS]) || + defaultFilter.activationStatus; + const role = urlFilter[ROLE] || defaultFilter.role; + const group = urlFilter[GROUP] || defaultFilter.group; + const search = urlFilter[SEARCH] || defaultFilter.search; + const sortBy = urlFilter[SORT_BY] || defaultFilter.sortBy; + const sortOrder = urlFilter[SORT_ORDER] || defaultFilter.sortOrder; + const page = (urlFilter[PAGE] && (+urlFilter[PAGE]-1)) || defaultFilter.page; + const pageCount = + (urlFilter[PAGE_COUNT] && +urlFilter[PAGE_COUNT]) || + defaultFilter.pageCount; + + const newFilter = new Filter( + page, + pageCount, + defaultFilter.total, + sortBy, + sortOrder, + employeeStatus, + activationStatus, + role, + search, + group + ); + + return newFilter; +} diff --git a/products/ASC.Files/Client/src/index.js b/products/ASC.Files/Client/src/index.js new file mode 100644 index 0000000000..9e83cf8f2e --- /dev/null +++ b/products/ASC.Files/Client/src/index.js @@ -0,0 +1,56 @@ +import React from "react"; +import ReactDOM from "react-dom"; +import { Provider } from "react-redux"; +import store from "./store/store"; +//import { fetchGroups, fetchPeople } from "./store/files/actions"; +import config from "../package.json"; +import "./custom.scss"; +import App from "./App"; + +import * as serviceWorker from "./serviceWorker"; +import { store as commonStore, constants, ErrorBoundary} from "asc-web-common"; +//import { getFilterByLocation } from "./helpers/converters"; +const { setIsLoaded, getUserInfo, setCurrentProductId, setCurrentProductHomePage, getPortalPasswordSettings, getPortalCultures } = commonStore.auth.actions; +const { AUTH_KEY } = constants; + +const token = localStorage.getItem(AUTH_KEY); + +if (token) { + getUserInfo(store.dispatch) + .then(() => getPortalPasswordSettings(store.dispatch)) + .then(() => getPortalCultures(store.dispatch)) + // .then(() => fetchGroups(store.dispatch)) + .then(() => { + // var re = new RegExp(`${config.homepage}((/?)$|/filter)`, "gm"); + // const match = window.location.pathname.match(re); + + // if (match && match.length > 0) { + // const newFilter = getFilterByLocation(window.location); + // return fetchPeople(newFilter, store.dispatch); + // } + + return Promise.resolve(); + }) + .then(() => { + store.dispatch(setCurrentProductHomePage(config.homepage)); + store.dispatch(setCurrentProductId("e67be73d-f9ae-4ce1-8fec-1880cb518cb4")); + store.dispatch(setIsLoaded(true)); + }); +} +else { + store.dispatch(setIsLoaded(true)); +}; + +ReactDOM.render( + + + + + , + document.getElementById("root") +); + +// If you want your app to work offline and load faster, you can change +// unregister() to register() below. Note this comes with some pitfalls. +// Learn more about service workers: https://bit.ly/CRA-PWA +serviceWorker.register(); diff --git a/products/ASC.Files/Client/src/resourceConfig.json b/products/ASC.Files/Client/src/resourceConfig.json new file mode 100644 index 0000000000..4ec22bab85 --- /dev/null +++ b/products/ASC.Files/Client/src/resourceConfig.json @@ -0,0 +1,253 @@ +{ + "Article": { + "Resource":[ + "People", + "Actions", + "InviteLinkTitle" + ], + "PeopleResource": [ + "ImportPeople" + ] + }, + "dialogs": { + "InviteDialog": { + "Resource": [ + "HelpAnswerLinkInviteSettings", + "CopyToClipboard", + "CloseButton", + "GetShortenLink", + "LoadingProcessing", + "InviteUsersAsCollaborators", + "InviteLinkTitle" + ], + "ResourceJS": [ + "LinkCopySuccess" + ] + }, + "ChangeEmailDialog":{ + "Resource": [ + "EmailChangeTitle", + "EnterEmail", + "EmailActivationDescription", + "SendButton" + ] + }, + "DeleteSelfProfileDialog":{ + "Resource": [ + "DeleteProfileTitle", + "DeleteProfileInfo", + "CloseButton", + "SendButton" + ] + }, + "ChangePhoneDialog":{ + "Resource": [ + "SendButton", + "MobilePhoneChangeTitle", + "MobilePhoneEraseDescription" + ] + }, + "DeleteProfileEverDialog":{ + "Resource": [ + "Confirmation", + "DeleteUserConfirmation", + "Warning", + "DeleteUserDataConfirmation" + ], + "PeopleResource": [ + "SuccessfullyDeleteUserInfoMessage" + ], + "UserControlsCommonResource":[ + "NotBeUndone" + ] + }, + "ChangePasswordDialog":{ + "Resource": [ + "PasswordChangeTitle", + "MessageSendPasswordChangeInstructionsOnEmail", + "SendButton" + ] + } +}, + "pages": { + "Profile": { + "Resource": [ + "UserType", + "Email", + "Sex", + "Birthdate", + "Location", + "Language", + "EditUserDialogTitle", + "Subscriptions", + "Comments", + "ContactInformation", + "SocialProfiles", + "PendingTitle", + "EmailChangeButton", + "SendInviteAgain", + "EditPhoto", + "PasswordChangeButton", + "DisableUserButton", + "EnableUserButton", + "ReassignData", + "RemoveData", + "DeleteSelfProfile", + "EditButton", + "ChangeEmailSuccess", + "Actions", + "NotFoundLanguage", + "LearnMore", + "ErrorUnknownFileImageType", + "SaveButton", + "MessageEmailActivationInstuctionsSentOnEmail", + "Error" + ], + "ResourceJS": [ + "ChangesApplied" + ], + "PeopleJSResource": [ + "SuccessChangeUserStatus" + ] + }, + "ProfileAction": { + "Resource": [ + "EditPhoto", + "FirstName", + "LastName", + "Email", + "Password", + "Birthdate", + "Sex", + "Location", + "Comments", + "SaveButton", + "CopyEmailAndPassword", + "UserType", + "CancelButton", + "EmailPopupHelper", + "ProfileTypePopupHelper", + "ProductsAndInstruments_Products", + "GuestCaption", + "TermsOfUsePopupHelperLink", + "Mail", + "People", + "MaleSexStatus", + "FemaleSexStatus", + "EditUserDialogTitle", + "ErrorUnknownFileImageType", + "AccessRightsFullAccess", + "ErrorPasswordMessage", + "ErrorPasswordNoDigits", + "ErrorPasswordNoUpperCase", + "ErrorPasswordLength", + "ErrorPasswordNoSpecialSymbols", + "Error" + ], + "FeedResource": [ + "DocumentsProduct", + "ProjectsProduct", + "CommunityProduct", + "Message", + "WriteComment" + ], + "CustomModeResource": [ + "ReviewingCustomMode" + + ] + }, + "Home": { + "Resource": [ + "LoadingProcessing", + "LoadingDescription", + "PasswordChangeButton", + "EditButton", + "DisableUserButton", + "ReassignData", + "RemoveData", + "DeleteSelfProfile", + "EditButton", + "UserStatus", + "Email", + "UserType", + "Administrator", + "DeleteButton", + "More", + "CloseButton", + "Actions", + "People", + "MessageEmailActivationInstuctionsSentOnEmail", + "EnableUserButton", + "Search", + "SendInviteAgain" + ], + "PeopleResource": [ + "LblSendEmail", + "LblSendMessage", + "NotFoundDescription", + "NotFoundTitle", + "ClearButton", + "LblActive", + "LblTerminated", + "LblPending", + "LblOther", + "LblChangeEmail" + ], + "PeopleJSResource": [ + "SuccessChangeUserStatus", + "SuccessChangeUserType", + "SuccessSendInvitation" + ], + "UserControlsCommonResource": [ + "NextPage", + "PreviousPage", + "LblSelect" + + ] + } + }, + "Layout": { + "Resource": [ + "Profile", + "AboutCompanyTitle", + "LogoutButton" + ] + }, + "GroupAction": { + "Resource": [ + "SaveButton", + "CancelButton", + "Name" + ], + "PeopleResource": [ + "Members", + "AddMembers", + "AddButton" + ], + "UserControlsCommonResource": [ + "Members", + "AddMembers", + "LblSelect" + ] + }, + "Reassign":{ + "PeopleResource":[ + "ReassignmentData", + "ReassignsTransferedListHdr", + "ReassignsTransferedListItem1", + "ReassignsTransferedListItem2", + "ReassignsTransferedListItem3", + "ReassignsReadMore", + "DeleteProfileAfterReassignment", + "CancelButton", + "ReassignButton", + "ReassignsToUser" + ], + "UserControlsCommonResource":[ + "NotBeUndone" + ], + "PeopleJSResource":[ + "ChooseUser" + ] + } +} diff --git a/products/ASC.Files/Client/src/serviceWorker.js b/products/ASC.Files/Client/src/serviceWorker.js new file mode 100644 index 0000000000..825b155ab5 --- /dev/null +++ b/products/ASC.Files/Client/src/serviceWorker.js @@ -0,0 +1,145 @@ +// This optional code is used to register a service worker. +// register() is not called by default. + +// This lets the app load faster on subsequent visits in production, and gives +// it offline capabilities. However, it also means that developers (and users) +// will only see deployed updates on subsequent visits to a page, after all the +// existing tabs open on the page have been closed, since previously cached +// resources are updated in the background. + +// To learn more about the benefits of this model and instructions on how to +// opt-in, read https://bit.ly/CRA-PWA + +const isLocalhost = Boolean( + window.location.hostname === 'localhost' || + // [::1] is the IPv6 localhost address. + window.location.hostname === '[::1]' || + // 127.0.0.1/8 is considered localhost for IPv4. + window.location.hostname.match( + /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ + ) +); + +export function register(config) { + + if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { + // The URL constructor is available in all browsers that support SW. + const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); + if (publicUrl.origin !== window.location.origin) { + // Our service worker won't work if PUBLIC_URL is on a different origin + // from what our page is served on. This might happen if a CDN is used to + // serve assets; see https://github.com/facebook/create-react-app/issues/2374 + return; + } + + window.addEventListener('load', () => { + const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; + + if (isLocalhost) { + // This is running on localhost. Let's check if a service worker still exists or not. + checkValidServiceWorker(swUrl, config); + + // Add some additional logging to localhost, pointing developers to the + // service worker/PWA documentation. + navigator.serviceWorker.ready.then(() => { + console.log( + 'This web app is being served cache-first by a service ' + + 'worker. To learn more, visit https://bit.ly/CRA-PWA' + ); + }); + } else { + // Is not localhost. Just register service worker + registerValidSW(swUrl, config); + } + }); + } +} + +function registerValidSW(swUrl, config) { + navigator.serviceWorker + .register(swUrl) + .then(registration => { + registration.onupdatefound = () => { + const installingWorker = registration.installing; + if (installingWorker == null) { + return; + } + installingWorker.onstatechange = () => { + if (installingWorker.state === 'installed') { + if (navigator.serviceWorker.controller) { + // At this point, the updated precached content has been fetched, + // but the previous service worker will still serve the older + // content until all client tabs are closed. + console.log( + 'New content is available and will be used when all ' + + 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' + ); + + // Execute callback + if (config && config.onUpdate) { + config.onUpdate(registration); + } + } else { + // At this point, everything has been precached. + // It's the perfect time to display a + // "Content is cached for offline use." message. + console.log('Content is cached for offline use.'); + + // Execute callback + if (config && config.onSuccess) { + config.onSuccess(registration); + } + } + } + }; + setTimeout(function(){ + let precacheFiles = []; + for(let i = 0; i < document.scripts.length; i++){ + precacheFiles.push(document.scripts[i].src); + } + caches.open('precache-v1').then((cache) => { + return cache.addAll(precacheFiles); + }) + },1500); + }; + }) + .catch(error => { + console.error('Error during service worker registration:', error); + }); +} + +function checkValidServiceWorker(swUrl, config) { + // Check if the service worker can be found. If it can't reload the page. + fetch(swUrl) + .then(response => { + // Ensure service worker exists, and that we really are getting a JS file. + const contentType = response.headers.get('content-type'); + if ( + response.status === 404 || + (contentType != null && contentType.indexOf('javascript') === -1) + ) { + // No service worker found. Probably a different app. Reload the page. + navigator.serviceWorker.ready.then(registration => { + registration.unregister().then(() => { + window.location.reload(); + }); + }); + } else { + // Service worker found. Proceed as normal. + registerValidSW(swUrl, config); + } + }) + .catch(() => { + console.log( + 'No internet connection found. App is running in offline mode.' + ); + }); +} + +export function unregister() { + if ('serviceWorker' in navigator) { + navigator.serviceWorker.ready.then(registration => { + registration.unregister(); + }); + } +} diff --git a/products/ASC.Files/Client/src/store/files/actions.js b/products/ASC.Files/Client/src/store/files/actions.js new file mode 100644 index 0000000000..1ca200b7f9 --- /dev/null +++ b/products/ASC.Files/Client/src/store/files/actions.js @@ -0,0 +1,52 @@ +export const SET_FOLDERS = "SET_FOLDERS"; +export const SET_FILES = "SET_FILES"; +export const SET_SELECTION = "SET_SELECTION"; +export const SET_SELECTED = "SET_SELECTED"; +export const SET_SELECTED_FOLDER = "SET_SELECTED_FOLDER"; + +export function setFiles(files) { + return { + type: SET_FILES, + files + }; +} + +export function setFolders(folders) { + return { + type: SET_FOLDERS, + folders + }; +} + +export function setSelection(selection) { + return { + type: SET_SELECTION, + selection + }; +} + +export function setSelected(selected) { + return { + type: SET_SELECTED, + selected + }; +} + +export function setSelectedFolder(selectedFolder) { + return { + type: SET_SELECTED_FOLDER, + selectedFolder + }; +} + +export function fetchFiles() { + return Promise.resolve([]); +} + +export function fetchFolders() { + return Promise.resolve([]); +} + +export function selectFolder() { + return Promise.resolve([]); +} \ No newline at end of file diff --git a/products/ASC.Files/Client/src/store/files/reducers.js b/products/ASC.Files/Client/src/store/files/reducers.js new file mode 100644 index 0000000000..eee9147479 --- /dev/null +++ b/products/ASC.Files/Client/src/store/files/reducers.js @@ -0,0 +1,41 @@ +import { SET_FILES, SET_FOLDERS, SET_SELECTION, SET_SELECTED, SET_SELECTED_FOLDER } from "./actions"; +import { api } from "asc-web-common"; +const { Filter } = api; + +const initialState = { + files: [], + folders: [], + selection: [], + selected: "none", + selectedFolder: null, + filter: Filter.getDefault() //TODO: Replace to new FileFilter +}; + +const filesReducer = (state = initialState, action) => { + switch (action.type) { + case SET_FOLDERS: + return Object.assign({}, state, { + groups: action.folders + }); + case SET_FILES: + return Object.assign({}, state, { + users: action.files + }); + case SET_SELECTION: + return Object.assign({}, state, { + selection: action.selection + }); + case SET_SELECTED: + return Object.assign({}, state, { + selected: action.selected + }); + case SET_SELECTED_FOLDER: + return Object.assign({}, state, { + selectedFolder: action.selectedFolder + }); + default: + return state; + } +}; + +export default filesReducer; diff --git a/products/ASC.Files/Client/src/store/files/selectors.js b/products/ASC.Files/Client/src/store/files/selectors.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/products/ASC.Files/Client/src/store/rootReducer.js b/products/ASC.Files/Client/src/store/rootReducer.js new file mode 100644 index 0000000000..fec06e166e --- /dev/null +++ b/products/ASC.Files/Client/src/store/rootReducer.js @@ -0,0 +1,11 @@ +import { combineReducers } from 'redux'; +import filesReducer from './files/reducers'; +import { store } from 'asc-web-common'; +const { reducer: authReducer } = store.auth; + +const rootReducer = combineReducers({ + auth: authReducer, + files: filesReducer +}); + +export default rootReducer; \ No newline at end of file diff --git a/products/ASC.Files/Client/src/store/store.js b/products/ASC.Files/Client/src/store/store.js new file mode 100644 index 0000000000..3b9d27bd88 --- /dev/null +++ b/products/ASC.Files/Client/src/store/store.js @@ -0,0 +1,24 @@ +import { createStore, applyMiddleware } from 'redux'; +import { composeWithDevTools } from 'redux-devtools-extension/logOnlyInProduction'; +import rootReducer from './rootReducer'; +import thunk from 'redux-thunk'; + +/* eslint-disable no-underscore-dangle */ +const composeEnhancers = composeWithDevTools({ + // options like actionSanitizer, stateSanitizer +}); + +const configureStore = prelodedState => ( + createStore( + rootReducer, + prelodedState, + composeEnhancers( + applyMiddleware(thunk) + ) + ) +); +/* eslint-enable */ + +const store = configureStore({}); + +export default store; \ No newline at end of file diff --git a/products/ASC.Files/Client/yarn.lock b/products/ASC.Files/Client/yarn.lock new file mode 100644 index 0000000000..c29701a0fc --- /dev/null +++ b/products/ASC.Files/Client/yarn.lock @@ -0,0 +1,11758 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@ant-design/create-react-context@^0.2.4": + version "0.2.5" + resolved "https://registry.yarnpkg.com/@ant-design/create-react-context/-/create-react-context-0.2.5.tgz#f5f5a9163b4772097712837397ad30e22e79f858" + integrity sha512-1rMAa4qgP2lfl/QBH9i78+Gjxtj9FTMpMyDGZsEBW5Kih72EuUo9958mV8PgpRkh4uwPSQ7vVZWXeyNZXVAFDg== + dependencies: + gud "^1.0.0" + warning "^4.0.3" + +"@babel/code-frame@7.8.3", "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" + integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g== + dependencies: + "@babel/highlight" "^7.8.3" + +"@babel/compat-data@^7.8.4": + version "7.8.5" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.8.5.tgz#d28ce872778c23551cbb9432fc68d28495b613b9" + integrity sha512-jWYUqQX/ObOhG1UiEkbH5SANsE/8oKXiQWjj7p7xgj9Zmnt//aUvyz4dBkK0HNsS8/cbyC5NmmH87VekW+mXFg== + dependencies: + browserslist "^4.8.5" + invariant "^2.2.4" + semver "^5.5.0" + +"@babel/core@7.8.4", "@babel/core@^7.1.0", "@babel/core@^7.4.5": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.8.4.tgz#d496799e5c12195b3602d0fddd77294e3e38e80e" + integrity sha512-0LiLrB2PwrVI+a2/IEskBopDYSd8BCb3rOvH7D5tzoWd696TBEduBvuLVm4Nx6rltrLZqvI3MCalB2K2aVzQjA== + dependencies: + "@babel/code-frame" "^7.8.3" + "@babel/generator" "^7.8.4" + "@babel/helpers" "^7.8.4" + "@babel/parser" "^7.8.4" + "@babel/template" "^7.8.3" + "@babel/traverse" "^7.8.4" + "@babel/types" "^7.8.3" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.0" + lodash "^4.17.13" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + +"@babel/generator@^7.4.0", "@babel/generator@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.8.4.tgz#35bbc74486956fe4251829f9f6c48330e8d0985e" + integrity sha512-PwhclGdRpNAf3IxZb0YVuITPZmmrXz9zf6fH8lT4XbrmfQKr6ryBzhv593P5C6poJRciFCL/eHGW2NuGrgEyxA== + dependencies: + "@babel/types" "^7.8.3" + jsesc "^2.5.1" + lodash "^4.17.13" + source-map "^0.5.0" + +"@babel/helper-annotate-as-pure@^7.0.0", "@babel/helper-annotate-as-pure@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz#60bc0bc657f63a0924ff9a4b4a0b24a13cf4deee" + integrity sha512-6o+mJrZBxOoEX77Ezv9zwW7WV8DdluouRKNY/IR5u/YTMuKHgugHOzYWlYvYLpLA9nPsQCAAASpCIbjI9Mv+Uw== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-builder-binary-assignment-operator-visitor@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.8.3.tgz#c84097a427a061ac56a1c30ebf54b7b22d241503" + integrity sha512-5eFOm2SyFPK4Rh3XMMRDjN7lBH0orh3ss0g3rTYZnBQ+r6YPj7lgDyCvPphynHvUrobJmeMignBr6Acw9mAPlw== + dependencies: + "@babel/helper-explode-assignable-expression" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helper-builder-react-jsx@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.8.3.tgz#dee98d7d79cc1f003d80b76fe01c7f8945665ff6" + integrity sha512-JT8mfnpTkKNCboTqZsQTdGo3l3Ik3l7QIt9hh0O9DYiwVel37VoJpILKM4YFbP2euF32nkQSb+F9cUk9b7DDXQ== + dependencies: + "@babel/types" "^7.8.3" + esutils "^2.0.0" + +"@babel/helper-call-delegate@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-call-delegate/-/helper-call-delegate-7.8.3.tgz#de82619898aa605d409c42be6ffb8d7204579692" + integrity sha512-6Q05px0Eb+N4/GTyKPPvnkig7Lylw+QzihMpws9iiZQv7ZImf84ZsZpQH7QoWN4n4tm81SnSzPgHw2qtO0Zf3A== + dependencies: + "@babel/helper-hoist-variables" "^7.8.3" + "@babel/traverse" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helper-compilation-targets@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.8.4.tgz#03d7ecd454b7ebe19a254f76617e61770aed2c88" + integrity sha512-3k3BsKMvPp5bjxgMdrFyq0UaEO48HciVrOVF0+lon8pp95cyJ2ujAh0TrBHNMnJGT2rr0iKOJPFFbSqjDyf/Pg== + dependencies: + "@babel/compat-data" "^7.8.4" + browserslist "^4.8.5" + invariant "^2.2.4" + levenary "^1.1.1" + semver "^5.5.0" + +"@babel/helper-create-class-features-plugin@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.8.3.tgz#5b94be88c255f140fd2c10dd151e7f98f4bff397" + integrity sha512-qmp4pD7zeTxsv0JNecSBsEmG1ei2MqwJq4YQcK3ZWm/0t07QstWfvuV/vm3Qt5xNMFETn2SZqpMx2MQzbtq+KA== + dependencies: + "@babel/helper-function-name" "^7.8.3" + "@babel/helper-member-expression-to-functions" "^7.8.3" + "@babel/helper-optimise-call-expression" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-replace-supers" "^7.8.3" + "@babel/helper-split-export-declaration" "^7.8.3" + +"@babel/helper-create-regexp-features-plugin@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.3.tgz#c774268c95ec07ee92476a3862b75cc2839beb79" + integrity sha512-Gcsm1OHCUr9o9TcJln57xhWHtdXbA2pgQ58S0Lxlks0WMGNXuki4+GLfX0p+L2ZkINUGZvfkz8rzoqJQSthI+Q== + dependencies: + "@babel/helper-regex" "^7.8.3" + regexpu-core "^4.6.0" + +"@babel/helper-define-map@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.8.3.tgz#a0655cad5451c3760b726eba875f1cd8faa02c15" + integrity sha512-PoeBYtxoZGtct3md6xZOCWPcKuMuk3IHhgxsRRNtnNShebf4C8YonTSblsK4tvDbm+eJAw2HAPOfCr+Q/YRG/g== + dependencies: + "@babel/helper-function-name" "^7.8.3" + "@babel/types" "^7.8.3" + lodash "^4.17.13" + +"@babel/helper-explode-assignable-expression@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.8.3.tgz#a728dc5b4e89e30fc2dfc7d04fa28a930653f982" + integrity sha512-N+8eW86/Kj147bO9G2uclsg5pwfs/fqqY5rwgIL7eTBklgXjcOJ3btzS5iM6AitJcftnY7pm2lGsrJVYLGjzIw== + dependencies: + "@babel/traverse" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helper-function-name@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz#eeeb665a01b1f11068e9fb86ad56a1cb1a824cca" + integrity sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA== + dependencies: + "@babel/helper-get-function-arity" "^7.8.3" + "@babel/template" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helper-get-function-arity@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz#b894b947bd004381ce63ea1db9f08547e920abd5" + integrity sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-hoist-variables@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.8.3.tgz#1dbe9b6b55d78c9b4183fc8cdc6e30ceb83b7134" + integrity sha512-ky1JLOjcDUtSc+xkt0xhYff7Z6ILTAHKmZLHPxAhOP0Nd77O+3nCsd6uSVYur6nJnCI029CrNbYlc0LoPfAPQg== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-member-expression-to-functions@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz#659b710498ea6c1d9907e0c73f206eee7dadc24c" + integrity sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz#7fe39589b39c016331b6b8c3f441e8f0b1419498" + integrity sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-module-transforms@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.8.3.tgz#d305e35d02bee720fbc2c3c3623aa0c316c01590" + integrity sha512-C7NG6B7vfBa/pwCOshpMbOYUmrYQDfCpVL/JCRu0ek8B5p8kue1+BCXpg2vOYs7w5ACB9GTOBYQ5U6NwrMg+3Q== + dependencies: + "@babel/helper-module-imports" "^7.8.3" + "@babel/helper-simple-access" "^7.8.3" + "@babel/helper-split-export-declaration" "^7.8.3" + "@babel/template" "^7.8.3" + "@babel/types" "^7.8.3" + lodash "^4.17.13" + +"@babel/helper-optimise-call-expression@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz#7ed071813d09c75298ef4f208956006b6111ecb9" + integrity sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670" + integrity sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ== + +"@babel/helper-regex@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.8.3.tgz#139772607d51b93f23effe72105b319d2a4c6965" + integrity sha512-BWt0QtYv/cg/NecOAZMdcn/waj/5P26DR4mVLXfFtDokSR6fyuG0Pj+e2FqtSME+MqED1khnSMulkmGl8qWiUQ== + dependencies: + lodash "^4.17.13" + +"@babel/helper-remap-async-to-generator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.8.3.tgz#273c600d8b9bf5006142c1e35887d555c12edd86" + integrity sha512-kgwDmw4fCg7AVgS4DukQR/roGp+jP+XluJE5hsRZwxCYGg+Rv9wSGErDWhlI90FODdYfd4xG4AQRiMDjjN0GzA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.8.3" + "@babel/helper-wrap-function" "^7.8.3" + "@babel/template" "^7.8.3" + "@babel/traverse" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helper-replace-supers@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.8.3.tgz#91192d25f6abbcd41da8a989d4492574fb1530bc" + integrity sha512-xOUssL6ho41U81etpLoT2RTdvdus4VfHamCuAm4AHxGr+0it5fnwoVdwUJ7GFEqCsQYzJUhcbsN9wB9apcYKFA== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.8.3" + "@babel/helper-optimise-call-expression" "^7.8.3" + "@babel/traverse" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helper-simple-access@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz#7f8109928b4dab4654076986af575231deb639ae" + integrity sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw== + dependencies: + "@babel/template" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helper-split-export-declaration@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz#31a9f30070f91368a7182cf05f831781065fc7a9" + integrity sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-wrap-function@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz#9dbdb2bb55ef14aaa01fe8c99b629bd5352d8610" + integrity sha512-LACJrbUET9cQDzb6kG7EeD7+7doC3JNvUgTEQOx2qaO1fKlzE/Bf05qs9w1oXQMmXlPO65lC3Tq9S6gZpTErEQ== + dependencies: + "@babel/helper-function-name" "^7.8.3" + "@babel/template" "^7.8.3" + "@babel/traverse" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helpers@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.8.4.tgz#754eb3ee727c165e0a240d6c207de7c455f36f73" + integrity sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w== + dependencies: + "@babel/template" "^7.8.3" + "@babel/traverse" "^7.8.4" + "@babel/types" "^7.8.3" + +"@babel/highlight@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.8.3.tgz#28f173d04223eaaa59bc1d439a3836e6d1265797" + integrity sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg== + dependencies: + chalk "^2.0.0" + esutils "^2.0.2" + js-tokens "^4.0.0" + +"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.4.3", "@babel/parser@^7.8.3", "@babel/parser@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.4.tgz#d1dbe64691d60358a974295fa53da074dd2ce8e8" + integrity sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw== + +"@babel/plugin-proposal-async-generator-functions@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz#bad329c670b382589721b27540c7d288601c6e6f" + integrity sha512-NZ9zLv848JsV3hs8ryEh7Uaz/0KsmPLqv0+PdkDJL1cJy0K4kOCFa8zc1E3mp+RHPQcpdfb/6GovEsW4VDrOMw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-remap-async-to-generator" "^7.8.3" + "@babel/plugin-syntax-async-generators" "^7.8.0" + +"@babel/plugin-proposal-class-properties@7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.8.3.tgz#5e06654af5cd04b608915aada9b2a6788004464e" + integrity sha512-EqFhbo7IosdgPgZggHaNObkmO1kNUe3slaKu54d5OWvy+p9QIKOzK1GAEpAIsZtWVtPXUHSMcT4smvDrCfY4AA== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-proposal-decorators@7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.8.3.tgz#2156860ab65c5abf068c3f67042184041066543e" + integrity sha512-e3RvdvS4qPJVTe288DlXjwKflpfy1hr0j5dz5WpIYYeP7vQZg2WfAEIp8k5/Lwis/m5REXEteIz6rrcDtXXG7w== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-decorators" "^7.8.3" + +"@babel/plugin-proposal-dynamic-import@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.8.3.tgz#38c4fe555744826e97e2ae930b0fb4cc07e66054" + integrity sha512-NyaBbyLFXFLT9FP+zk0kYlUlA8XtCUbehs67F0nnEg7KICgMc2mNkIeu9TYhKzyXMkrapZFwAhXLdnt4IYHy1w== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-dynamic-import" "^7.8.0" + +"@babel/plugin-proposal-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.8.3.tgz#da5216b238a98b58a1e05d6852104b10f9a70d6b" + integrity sha512-KGhQNZ3TVCQG/MjRbAUwuH+14y9q0tpxs1nWWs3pbSleRdDro9SAMMDyye8HhY1gqZ7/NqIc8SKhya0wRDgP1Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.0" + +"@babel/plugin-proposal-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz#e4572253fdeed65cddeecfdab3f928afeb2fd5d2" + integrity sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" + +"@babel/plugin-proposal-numeric-separator@7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.8.3.tgz#5d6769409699ec9b3b68684cd8116cedff93bad8" + integrity sha512-jWioO1s6R/R+wEHizfaScNsAx+xKgwTLNXSh7tTC4Usj3ItsPEhYkEpU4h+lpnBwq7NBVOJXfO6cRFYcX69JUQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.8.3" + +"@babel/plugin-proposal-object-rest-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.8.3.tgz#eb5ae366118ddca67bed583b53d7554cad9951bb" + integrity sha512-8qvuPwU/xxUCt78HocNlv0mXXo0wdh9VT1R04WU8HGOfaOob26pF+9P5/lYjN/q7DHOX1bvX60hnhOvuQUJdbA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + +"@babel/plugin-proposal-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.8.3.tgz#9dee96ab1650eed88646ae9734ca167ac4a9c5c9" + integrity sha512-0gkX7J7E+AtAw9fcwlVQj8peP61qhdg/89D5swOkjYbkboA2CVckn3kiyum1DE0wskGb7KJJxBdyEBApDLLVdw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" + +"@babel/plugin-proposal-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.8.3.tgz#ae10b3214cb25f7adb1f3bc87ba42ca10b7e2543" + integrity sha512-QIoIR9abkVn+seDE3OjA08jWcs3eZ9+wJCKSRgo3WdEU2csFYgdScb+8qHB3+WXsGJD55u+5hWCISI7ejXS+kg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.0" + +"@babel/plugin-proposal-unicode-property-regex@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.3.tgz#b646c3adea5f98800c9ab45105ac34d06cd4a47f" + integrity sha512-1/1/rEZv2XGweRwwSkLpY+s60za9OZ1hJs4YDqFHCw0kYWYwL5IFljVY1MYBL+weT1l9pokDO2uhSTLVxzoHkQ== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-async-generators@^7.8.0": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-decorators@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.8.3.tgz#8d2c15a9f1af624b0025f961682a9d53d3001bda" + integrity sha512-8Hg4dNNT9/LcA1zQlfwuKR8BUc/if7Q7NkTam9sGTcJphLwpf2g4S42uhspQrIrR+dpzE0dtTqBVFoHl8GtnnQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-dynamic-import@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" + integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-flow@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.8.3.tgz#f2c883bd61a6316f2c89380ae5122f923ba4527f" + integrity sha512-innAx3bUbA0KSYj2E2MNFSn9hiCeowOFLxlsuhXzw8hMQnzkDomUr9QCD7E9VF60NmnG1sNTuuv6Qf4f8INYsg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-json-strings@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-jsx@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.8.3.tgz#521b06c83c40480f1e58b4fd33b92eceb1d6ea94" + integrity sha512-WxdW9xyLgBdefoo0Ynn3MRSkhe5tFVxxKNVdnZSh318WrG2e2jH+E9wd/++JsqcLJZPfz87njQJ8j2Upjm0M0A== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz#0e3fb63e09bea1b11e96467271c8308007e7c41f" + integrity sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-object-rest-spread@^7.0.0", "@babel/plugin-syntax-object-rest-spread@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-top-level-await@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.8.3.tgz#3acdece695e6b13aaf57fc291d1a800950c71391" + integrity sha512-kwj1j9lL/6Wd0hROD3b/OZZ7MSrZLqqn9RAZ5+cYYsflQ9HZBIKCUkr3+uL1MEJ1NePiUbf98jjiMQSv0NMR9g== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-typescript@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.8.3.tgz#c1f659dda97711a569cef75275f7e15dcaa6cabc" + integrity sha512-GO1MQ/SGGGoiEXY0e0bSpHimJvxqB7lktLLIq2pv8xG7WZ8IMEle74jIe1FhprHBWjwjZtXHkycDLZXIWM5Wfg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-arrow-functions@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz#82776c2ed0cd9e1a49956daeb896024c9473b8b6" + integrity sha512-0MRF+KC8EqH4dbuITCWwPSzsyO3HIWWlm30v8BbbpOrS1B++isGxPnnuq/IZvOX5J2D/p7DQalQm+/2PnlKGxg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-async-to-generator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.8.3.tgz#4308fad0d9409d71eafb9b1a6ee35f9d64b64086" + integrity sha512-imt9tFLD9ogt56Dd5CI/6XgpukMwd/fLGSrix2httihVe7LOGVPhyhMh1BU5kDM7iHD08i8uUtmV2sWaBFlHVQ== + dependencies: + "@babel/helper-module-imports" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-remap-async-to-generator" "^7.8.3" + +"@babel/plugin-transform-block-scoped-functions@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.8.3.tgz#437eec5b799b5852072084b3ae5ef66e8349e8a3" + integrity sha512-vo4F2OewqjbB1+yaJ7k2EJFHlTP3jR634Z9Cj9itpqNjuLXvhlVxgnjsHsdRgASR8xYDrx6onw4vW5H6We0Jmg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-block-scoping@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.8.3.tgz#97d35dab66857a437c166358b91d09050c868f3a" + integrity sha512-pGnYfm7RNRgYRi7bids5bHluENHqJhrV4bCZRwc5GamaWIIs07N4rZECcmJL6ZClwjDz1GbdMZFtPs27hTB06w== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + lodash "^4.17.13" + +"@babel/plugin-transform-classes@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.8.3.tgz#46fd7a9d2bb9ea89ce88720477979fe0d71b21b8" + integrity sha512-SjT0cwFJ+7Rbr1vQsvphAHwUHvSUPmMjMU/0P59G8U2HLFqSa082JO7zkbDNWs9kH/IUqpHI6xWNesGf8haF1w== + dependencies: + "@babel/helper-annotate-as-pure" "^7.8.3" + "@babel/helper-define-map" "^7.8.3" + "@babel/helper-function-name" "^7.8.3" + "@babel/helper-optimise-call-expression" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-replace-supers" "^7.8.3" + "@babel/helper-split-export-declaration" "^7.8.3" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.8.3.tgz#96d0d28b7f7ce4eb5b120bb2e0e943343c86f81b" + integrity sha512-O5hiIpSyOGdrQZRQ2ccwtTVkgUDBBiCuK//4RJ6UfePllUTCENOzKxfh6ulckXKc0DixTFLCfb2HVkNA7aDpzA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-destructuring@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.8.3.tgz#20ddfbd9e4676906b1056ee60af88590cc7aaa0b" + integrity sha512-H4X646nCkiEcHZUZaRkhE2XVsoz0J/1x3VVujnn96pSoGCtKPA99ZZA+va+gK+92Zycd6OBKCD8tDb/731bhgQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-dotall-regex@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.8.3.tgz#c3c6ec5ee6125c6993c5cbca20dc8621a9ea7a6e" + integrity sha512-kLs1j9Nn4MQoBYdRXH6AeaXMbEJFaFu/v1nQkvib6QzTj8MZI5OQzqmD83/2jEM1z0DLilra5aWO5YpyC0ALIw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-duplicate-keys@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.8.3.tgz#8d12df309aa537f272899c565ea1768e286e21f1" + integrity sha512-s8dHiBUbcbSgipS4SMFuWGqCvyge5V2ZeAWzR6INTVC3Ltjig/Vw1G2Gztv0vU/hRG9X8IvKvYdoksnUfgXOEQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-exponentiation-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.8.3.tgz#581a6d7f56970e06bf51560cd64f5e947b70d7b7" + integrity sha512-zwIpuIymb3ACcInbksHaNcR12S++0MDLKkiqXHl3AzpgdKlFNhog+z/K0+TGW+b0w5pgTq4H6IwV/WhxbGYSjQ== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-flow-strip-types@7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.8.3.tgz#da705a655466b2a9b36046b57bf0cbcd53551bd4" + integrity sha512-g/6WTWG/xbdd2exBBzMfygjX/zw4eyNC4X8pRaq7aRHRoDUCzAIu3kGYIXviOv8BjCuWm8vDBwjHcjiRNgXrPA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-flow" "^7.8.3" + +"@babel/plugin-transform-for-of@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.8.4.tgz#6fe8eae5d6875086ee185dd0b098a8513783b47d" + integrity sha512-iAXNlOWvcYUYoV8YIxwS7TxGRJcxyl8eQCfT+A5j8sKUzRFvJdcyjp97jL2IghWSRDaL2PU2O2tX8Cu9dTBq5A== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-function-name@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.8.3.tgz#279373cb27322aaad67c2683e776dfc47196ed8b" + integrity sha512-rO/OnDS78Eifbjn5Py9v8y0aR+aSYhDhqAwVfsTl0ERuMZyr05L1aFSCJnbv2mmsLkit/4ReeQ9N2BgLnOcPCQ== + dependencies: + "@babel/helper-function-name" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-literals@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.8.3.tgz#aef239823d91994ec7b68e55193525d76dbd5dc1" + integrity sha512-3Tqf8JJ/qB7TeldGl+TT55+uQei9JfYaregDcEAyBZ7akutriFrt6C/wLYIer6OYhleVQvH/ntEhjE/xMmy10A== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-member-expression-literals@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.8.3.tgz#963fed4b620ac7cbf6029c755424029fa3a40410" + integrity sha512-3Wk2EXhnw+rP+IDkK6BdtPKsUE5IeZ6QOGrPYvw52NwBStw9V1ZVzxgK6fSKSxqUvH9eQPR3tm3cOq79HlsKYA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-modules-amd@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.8.3.tgz#65606d44616b50225e76f5578f33c568a0b876a5" + integrity sha512-MadJiU3rLKclzT5kBH4yxdry96odTUwuqrZM+GllFI/VhxfPz+k9MshJM+MwhfkCdxxclSbSBbUGciBngR+kEQ== + dependencies: + "@babel/helper-module-transforms" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + babel-plugin-dynamic-import-node "^2.3.0" + +"@babel/plugin-transform-modules-commonjs@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.8.3.tgz#df251706ec331bd058a34bdd72613915f82928a5" + integrity sha512-JpdMEfA15HZ/1gNuB9XEDlZM1h/gF/YOH7zaZzQu2xCFRfwc01NXBMHHSTT6hRjlXJJs5x/bfODM3LiCk94Sxg== + dependencies: + "@babel/helper-module-transforms" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-simple-access" "^7.8.3" + babel-plugin-dynamic-import-node "^2.3.0" + +"@babel/plugin-transform-modules-systemjs@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.8.3.tgz#d8bbf222c1dbe3661f440f2f00c16e9bb7d0d420" + integrity sha512-8cESMCJjmArMYqa9AO5YuMEkE4ds28tMpZcGZB/jl3n0ZzlsxOAi3mC+SKypTfT8gjMupCnd3YiXCkMjj2jfOg== + dependencies: + "@babel/helper-hoist-variables" "^7.8.3" + "@babel/helper-module-transforms" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + babel-plugin-dynamic-import-node "^2.3.0" + +"@babel/plugin-transform-modules-umd@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.8.3.tgz#592d578ce06c52f5b98b02f913d653ffe972661a" + integrity sha512-evhTyWhbwbI3/U6dZAnx/ePoV7H6OUG+OjiJFHmhr9FPn0VShjwC2kdxqIuQ/+1P50TMrneGzMeyMTFOjKSnAw== + dependencies: + "@babel/helper-module-transforms" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-named-capturing-groups-regex@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz#a2a72bffa202ac0e2d0506afd0939c5ecbc48c6c" + integrity sha512-f+tF/8UVPU86TrCb06JoPWIdDpTNSGGcAtaD9mLP0aYGA0OS0j7j7DHJR0GTFrUZPUU6loZhbsVZgTh0N+Qdnw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.8.3" + +"@babel/plugin-transform-new-target@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.8.3.tgz#60cc2ae66d85c95ab540eb34babb6434d4c70c43" + integrity sha512-QuSGysibQpyxexRyui2vca+Cmbljo8bcRckgzYV4kRIsHpVeyeC3JDO63pY+xFZ6bWOBn7pfKZTqV4o/ix9sFw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-object-super@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.8.3.tgz#ebb6a1e7a86ffa96858bd6ac0102d65944261725" + integrity sha512-57FXk+gItG/GejofIyLIgBKTas4+pEU47IXKDBWFTxdPd7F80H8zybyAY7UoblVfBhBGs2EKM+bJUu2+iUYPDQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-replace-supers" "^7.8.3" + +"@babel/plugin-transform-parameters@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.8.4.tgz#1d5155de0b65db0ccf9971165745d3bb990d77d3" + integrity sha512-IsS3oTxeTsZlE5KqzTbcC2sV0P9pXdec53SU+Yxv7o/6dvGM5AkTotQKhoSffhNgZ/dftsSiOoxy7evCYJXzVA== + dependencies: + "@babel/helper-call-delegate" "^7.8.3" + "@babel/helper-get-function-arity" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-property-literals@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.8.3.tgz#33194300d8539c1ed28c62ad5087ba3807b98263" + integrity sha512-uGiiXAZMqEoQhRWMK17VospMZh5sXWg+dlh2soffpkAl96KAm+WZuJfa6lcELotSRmooLqg0MWdH6UUq85nmmg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-react-constant-elements@^7.0.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.8.3.tgz#784c25294bddaad2323eb4ff0c9f4a3f6c87d6bc" + integrity sha512-glrzN2U+egwRfkNFtL34xIBYTxbbUF2qJTP8HD3qETBBqzAWSeNB821X0GjU06+dNpq/UyCIjI72FmGE5NNkQQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-react-display-name@7.8.3", "@babel/plugin-transform-react-display-name@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.8.3.tgz#70ded987c91609f78353dd76d2fb2a0bb991e8e5" + integrity sha512-3Jy/PCw8Fe6uBKtEgz3M82ljt+lTg+xJaM4og+eyu83qLT87ZUSckn0wy7r31jflURWLO83TW6Ylf7lyXj3m5A== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-react-jsx-self@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.8.3.tgz#c4f178b2aa588ecfa8d077ea80d4194ee77ed702" + integrity sha512-01OT7s5oa0XTLf2I8XGsL8+KqV9lx3EZV+jxn/L2LQ97CGKila2YMroTkCEIE0HV/FF7CMSRsIAybopdN9NTdg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-jsx" "^7.8.3" + +"@babel/plugin-transform-react-jsx-source@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.8.3.tgz#951e75a8af47f9f120db731be095d2b2c34920e0" + integrity sha512-PLMgdMGuVDtRS/SzjNEQYUT8f4z1xb2BAT54vM1X5efkVuYBf5WyGUMbpmARcfq3NaglIwz08UVQK4HHHbC6ag== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-jsx" "^7.8.3" + +"@babel/plugin-transform-react-jsx@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.8.3.tgz#4220349c0390fdefa505365f68c103562ab2fc4a" + integrity sha512-r0h+mUiyL595ikykci+fbwm9YzmuOrUBi0b+FDIKmi3fPQyFokWVEMJnRWHJPPQEjyFJyna9WZC6Viv6UHSv1g== + dependencies: + "@babel/helper-builder-react-jsx" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-jsx" "^7.8.3" + +"@babel/plugin-transform-regenerator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.3.tgz#b31031e8059c07495bf23614c97f3d9698bc6ec8" + integrity sha512-qt/kcur/FxrQrzFR432FGZznkVAjiyFtCOANjkAKwCbt465L6ZCiUQh2oMYGU3Wo8LRFJxNDFwWn106S5wVUNA== + dependencies: + regenerator-transform "^0.14.0" + +"@babel/plugin-transform-reserved-words@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.8.3.tgz#9a0635ac4e665d29b162837dd3cc50745dfdf1f5" + integrity sha512-mwMxcycN3omKFDjDQUl+8zyMsBfjRFr0Zn/64I41pmjv4NJuqcYlEtezwYtw9TFd9WR1vN5kiM+O0gMZzO6L0A== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-runtime@7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.8.3.tgz#c0153bc0a5375ebc1f1591cb7eea223adea9f169" + integrity sha512-/vqUt5Yh+cgPZXXjmaG9NT8aVfThKk7G4OqkVhrXqwsC5soMn/qTCxs36rZ2QFhpfTJcjw4SNDIZ4RUb8OL4jQ== + dependencies: + "@babel/helper-module-imports" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + resolve "^1.8.1" + semver "^5.5.1" + +"@babel/plugin-transform-shorthand-properties@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.8.3.tgz#28545216e023a832d4d3a1185ed492bcfeac08c8" + integrity sha512-I9DI6Odg0JJwxCHzbzW08ggMdCezoWcuQRz3ptdudgwaHxTjxw5HgdFJmZIkIMlRymL6YiZcped4TTCB0JcC8w== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.8.3.tgz#9c8ffe8170fdfb88b114ecb920b82fb6e95fe5e8" + integrity sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-sticky-regex@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.8.3.tgz#be7a1290f81dae767475452199e1f76d6175b100" + integrity sha512-9Spq0vGCD5Bb4Z/ZXXSK5wbbLFMG085qd2vhL1JYu1WcQ5bXqZBAYRzU1d+p79GcHs2szYv5pVQCX13QgldaWw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-regex" "^7.8.3" + +"@babel/plugin-transform-template-literals@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.8.3.tgz#7bfa4732b455ea6a43130adc0ba767ec0e402a80" + integrity sha512-820QBtykIQOLFT8NZOcTRJ1UNuztIELe4p9DCgvj4NK+PwluSJ49we7s9FB1HIGNIYT7wFUJ0ar2QpCDj0escQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-typeof-symbol@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.4.tgz#ede4062315ce0aaf8a657a920858f1a2f35fc412" + integrity sha512-2QKyfjGdvuNfHsb7qnBBlKclbD4CfshH2KvDabiijLMGXPHJXGxtDzwIF7bQP+T0ysw8fYTtxPafgfs/c1Lrqg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-typescript@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.8.3.tgz#be6f01a7ef423be68e65ace1f04fc407e6d88917" + integrity sha512-Ebj230AxcrKGZPKIp4g4TdQLrqX95TobLUWKd/CwG7X1XHUH1ZpkpFvXuXqWbtGRWb7uuEWNlrl681wsOArAdQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-typescript" "^7.8.3" + +"@babel/plugin-transform-unicode-regex@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz#0cef36e3ba73e5c57273effb182f46b91a1ecaad" + integrity sha512-+ufgJjYdmWfSQ+6NS9VGUR2ns8cjJjYbrbi11mZBTaWm+Fui/ncTLFF28Ei1okavY+xkojGr1eJxNsWYeA5aZw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/preset-env@7.8.4", "@babel/preset-env@^7.4.5": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.8.4.tgz#9dac6df5f423015d3d49b6e9e5fa3413e4a72c4e" + integrity sha512-HihCgpr45AnSOHRbS5cWNTINs0TwaR8BS8xIIH+QwiW8cKL0llV91njQMpeMReEPVs+1Ao0x3RLEBLtt1hOq4w== + dependencies: + "@babel/compat-data" "^7.8.4" + "@babel/helper-compilation-targets" "^7.8.4" + "@babel/helper-module-imports" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-proposal-async-generator-functions" "^7.8.3" + "@babel/plugin-proposal-dynamic-import" "^7.8.3" + "@babel/plugin-proposal-json-strings" "^7.8.3" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-proposal-object-rest-spread" "^7.8.3" + "@babel/plugin-proposal-optional-catch-binding" "^7.8.3" + "@babel/plugin-proposal-optional-chaining" "^7.8.3" + "@babel/plugin-proposal-unicode-property-regex" "^7.8.3" + "@babel/plugin-syntax-async-generators" "^7.8.0" + "@babel/plugin-syntax-dynamic-import" "^7.8.0" + "@babel/plugin-syntax-json-strings" "^7.8.0" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.0" + "@babel/plugin-syntax-top-level-await" "^7.8.3" + "@babel/plugin-transform-arrow-functions" "^7.8.3" + "@babel/plugin-transform-async-to-generator" "^7.8.3" + "@babel/plugin-transform-block-scoped-functions" "^7.8.3" + "@babel/plugin-transform-block-scoping" "^7.8.3" + "@babel/plugin-transform-classes" "^7.8.3" + "@babel/plugin-transform-computed-properties" "^7.8.3" + "@babel/plugin-transform-destructuring" "^7.8.3" + "@babel/plugin-transform-dotall-regex" "^7.8.3" + "@babel/plugin-transform-duplicate-keys" "^7.8.3" + "@babel/plugin-transform-exponentiation-operator" "^7.8.3" + "@babel/plugin-transform-for-of" "^7.8.4" + "@babel/plugin-transform-function-name" "^7.8.3" + "@babel/plugin-transform-literals" "^7.8.3" + "@babel/plugin-transform-member-expression-literals" "^7.8.3" + "@babel/plugin-transform-modules-amd" "^7.8.3" + "@babel/plugin-transform-modules-commonjs" "^7.8.3" + "@babel/plugin-transform-modules-systemjs" "^7.8.3" + "@babel/plugin-transform-modules-umd" "^7.8.3" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.8.3" + "@babel/plugin-transform-new-target" "^7.8.3" + "@babel/plugin-transform-object-super" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.8.4" + "@babel/plugin-transform-property-literals" "^7.8.3" + "@babel/plugin-transform-regenerator" "^7.8.3" + "@babel/plugin-transform-reserved-words" "^7.8.3" + "@babel/plugin-transform-shorthand-properties" "^7.8.3" + "@babel/plugin-transform-spread" "^7.8.3" + "@babel/plugin-transform-sticky-regex" "^7.8.3" + "@babel/plugin-transform-template-literals" "^7.8.3" + "@babel/plugin-transform-typeof-symbol" "^7.8.4" + "@babel/plugin-transform-unicode-regex" "^7.8.3" + "@babel/types" "^7.8.3" + browserslist "^4.8.5" + core-js-compat "^3.6.2" + invariant "^2.2.2" + levenary "^1.1.1" + semver "^5.5.0" + +"@babel/preset-react@7.8.3", "@babel/preset-react@^7.0.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.8.3.tgz#23dc63f1b5b0751283e04252e78cf1d6589273d2" + integrity sha512-9hx0CwZg92jGb7iHYQVgi0tOEHP/kM60CtWJQnmbATSPIQQ2xYzfoCI3EdqAhFBeeJwYMdWQuDUHMsuDbH9hyQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-transform-react-display-name" "^7.8.3" + "@babel/plugin-transform-react-jsx" "^7.8.3" + "@babel/plugin-transform-react-jsx-self" "^7.8.3" + "@babel/plugin-transform-react-jsx-source" "^7.8.3" + +"@babel/preset-typescript@7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.8.3.tgz#90af8690121beecd9a75d0cc26c6be39d1595d13" + integrity sha512-qee5LgPGui9zQ0jR1TeU5/fP9L+ovoArklEqY12ek8P/wV5ZeM/VYSQYwICeoT6FfpJTekG9Ilay5PhwsOpMHA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-transform-typescript" "^7.8.3" + +"@babel/runtime@7.8.4", "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.4.tgz#d79f5a2040f7caa24d53e563aad49cbc05581308" + integrity sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ== + dependencies: + regenerator-runtime "^0.13.2" + +"@babel/template@^7.4.0", "@babel/template@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.3.tgz#e02ad04fe262a657809327f578056ca15fd4d1b8" + integrity sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ== + dependencies: + "@babel/code-frame" "^7.8.3" + "@babel/parser" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.4.3", "@babel/traverse@^7.4.5", "@babel/traverse@^7.8.3", "@babel/traverse@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.8.4.tgz#f0845822365f9d5b0e312ed3959d3f827f869e3c" + integrity sha512-NGLJPZwnVEyBPLI+bl9y9aSnxMhsKz42so7ApAv9D+b4vAFPpY013FTS9LdKxcABoIYFU52HcYga1pPlx454mg== + dependencies: + "@babel/code-frame" "^7.8.3" + "@babel/generator" "^7.8.4" + "@babel/helper-function-name" "^7.8.3" + "@babel/helper-split-export-declaration" "^7.8.3" + "@babel/parser" "^7.8.4" + "@babel/types" "^7.8.3" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.13" + +"@babel/types@^7.0.0", "@babel/types@^7.3.0", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.8.3.tgz#5a383dffa5416db1b73dedffd311ffd0788fb31c" + integrity sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg== + dependencies: + esutils "^2.0.2" + lodash "^4.17.13" + to-fast-properties "^2.0.0" + +"@cnakazawa/watch@^1.0.3": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" + integrity sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ== + dependencies: + exec-sh "^0.3.2" + minimist "^1.2.0" + +"@csstools/convert-colors@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@csstools/convert-colors/-/convert-colors-1.4.0.tgz#ad495dc41b12e75d588c6db8b9834f08fa131eb7" + integrity sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw== + +"@csstools/normalize.css@^10.1.0": + version "10.1.0" + resolved "https://registry.yarnpkg.com/@csstools/normalize.css/-/normalize.css-10.1.0.tgz#f0950bba18819512d42f7197e56c518aa491cf18" + integrity sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg== + +"@emotion/is-prop-valid@^0.8.3": + version "0.8.6" + resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.6.tgz#4757646f0a58e9dec614c47c838e7147d88c263c" + integrity sha512-mnZMho3Sq8BfzkYYRVc8ilQTnc8U02Ytp6J1AwM6taQStZ3AhsEJBX2LzhA/LJirNCwM2VtHL3VFIZ+sNJUgUQ== + dependencies: + "@emotion/memoize" "0.7.4" + +"@emotion/memoize@0.7.4": + version "0.7.4" + resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb" + integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw== + +"@emotion/stylis@^0.8.4": + version "0.8.5" + resolved "https://registry.yarnpkg.com/@emotion/stylis/-/stylis-0.8.5.tgz#deacb389bd6ee77d1e7fcaccce9e16c5c7e78e04" + integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ== + +"@emotion/unitless@^0.7.4": + version "0.7.5" + resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" + integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== + +"@hapi/address@2.x.x": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5" + integrity sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ== + +"@hapi/bourne@1.x.x": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@hapi/bourne/-/bourne-1.3.2.tgz#0a7095adea067243ce3283e1b56b8a8f453b242a" + integrity sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA== + +"@hapi/hoek@8.x.x", "@hapi/hoek@^8.3.0": + version "8.5.1" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.5.1.tgz#fde96064ca446dec8c55a8c2f130957b070c6e06" + integrity sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow== + +"@hapi/joi@^15.0.0": + version "15.1.1" + resolved "https://registry.yarnpkg.com/@hapi/joi/-/joi-15.1.1.tgz#c675b8a71296f02833f8d6d243b34c57b8ce19d7" + integrity sha512-entf8ZMOK8sc+8YfeOlM8pCfg3b5+WZIKBfUaaJT8UsjAAPjartzxIYm3TIbjvA4u+u++KbcXD38k682nVHDAQ== + dependencies: + "@hapi/address" "2.x.x" + "@hapi/bourne" "1.x.x" + "@hapi/hoek" "8.x.x" + "@hapi/topo" "3.x.x" + +"@hapi/topo@3.x.x": + version "3.1.6" + resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-3.1.6.tgz#68d935fa3eae7fdd5ab0d7f953f3205d8b2bfc29" + integrity sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ== + dependencies: + "@hapi/hoek" "^8.3.0" + +"@jest/console@^24.7.1", "@jest/console@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-24.9.0.tgz#79b1bc06fb74a8cfb01cbdedf945584b1b9707f0" + integrity sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ== + dependencies: + "@jest/source-map" "^24.9.0" + chalk "^2.0.1" + slash "^2.0.0" + +"@jest/core@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-24.9.0.tgz#2ceccd0b93181f9c4850e74f2a9ad43d351369c4" + integrity sha512-Fogg3s4wlAr1VX7q+rhV9RVnUv5tD7VuWfYy1+whMiWUrvl7U3QJSJyWcDio9Lq2prqYsZaeTv2Rz24pWGkJ2A== + dependencies: + "@jest/console" "^24.7.1" + "@jest/reporters" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" + ansi-escapes "^3.0.0" + chalk "^2.0.1" + exit "^0.1.2" + graceful-fs "^4.1.15" + jest-changed-files "^24.9.0" + jest-config "^24.9.0" + jest-haste-map "^24.9.0" + jest-message-util "^24.9.0" + jest-regex-util "^24.3.0" + jest-resolve "^24.9.0" + jest-resolve-dependencies "^24.9.0" + jest-runner "^24.9.0" + jest-runtime "^24.9.0" + jest-snapshot "^24.9.0" + jest-util "^24.9.0" + jest-validate "^24.9.0" + jest-watcher "^24.9.0" + micromatch "^3.1.10" + p-each-series "^1.0.0" + realpath-native "^1.1.0" + rimraf "^2.5.4" + slash "^2.0.0" + strip-ansi "^5.0.0" + +"@jest/environment@^24.3.0", "@jest/environment@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-24.9.0.tgz#21e3afa2d65c0586cbd6cbefe208bafade44ab18" + integrity sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ== + dependencies: + "@jest/fake-timers" "^24.9.0" + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" + jest-mock "^24.9.0" + +"@jest/fake-timers@^24.3.0", "@jest/fake-timers@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-24.9.0.tgz#ba3e6bf0eecd09a636049896434d306636540c93" + integrity sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A== + dependencies: + "@jest/types" "^24.9.0" + jest-message-util "^24.9.0" + jest-mock "^24.9.0" + +"@jest/reporters@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-24.9.0.tgz#86660eff8e2b9661d042a8e98a028b8d631a5b43" + integrity sha512-mu4X0yjaHrffOsWmVLzitKmmmWSQ3GGuefgNscUSWNiUNcEOSEQk9k3pERKEQVBb0Cnn88+UESIsZEMH3o88Gw== + dependencies: + "@jest/environment" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" + chalk "^2.0.1" + exit "^0.1.2" + glob "^7.1.2" + istanbul-lib-coverage "^2.0.2" + istanbul-lib-instrument "^3.0.1" + istanbul-lib-report "^2.0.4" + istanbul-lib-source-maps "^3.0.1" + istanbul-reports "^2.2.6" + jest-haste-map "^24.9.0" + jest-resolve "^24.9.0" + jest-runtime "^24.9.0" + jest-util "^24.9.0" + jest-worker "^24.6.0" + node-notifier "^5.4.2" + slash "^2.0.0" + source-map "^0.6.0" + string-length "^2.0.0" + +"@jest/source-map@^24.3.0", "@jest/source-map@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-24.9.0.tgz#0e263a94430be4b41da683ccc1e6bffe2a191714" + integrity sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg== + dependencies: + callsites "^3.0.0" + graceful-fs "^4.1.15" + source-map "^0.6.0" + +"@jest/test-result@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-24.9.0.tgz#11796e8aa9dbf88ea025757b3152595ad06ba0ca" + integrity sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA== + dependencies: + "@jest/console" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/istanbul-lib-coverage" "^2.0.0" + +"@jest/test-sequencer@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-24.9.0.tgz#f8f334f35b625a4f2f355f2fe7e6036dad2e6b31" + integrity sha512-6qqsU4o0kW1dvA95qfNog8v8gkRN9ph6Lz7r96IvZpHdNipP2cBcb07J1Z45mz/VIS01OHJ3pY8T5fUY38tg4A== + dependencies: + "@jest/test-result" "^24.9.0" + jest-haste-map "^24.9.0" + jest-runner "^24.9.0" + jest-runtime "^24.9.0" + +"@jest/transform@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-24.9.0.tgz#4ae2768b296553fadab09e9ec119543c90b16c56" + integrity sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ== + dependencies: + "@babel/core" "^7.1.0" + "@jest/types" "^24.9.0" + babel-plugin-istanbul "^5.1.0" + chalk "^2.0.1" + convert-source-map "^1.4.0" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.1.15" + jest-haste-map "^24.9.0" + jest-regex-util "^24.9.0" + jest-util "^24.9.0" + micromatch "^3.1.10" + pirates "^4.0.1" + realpath-native "^1.1.0" + slash "^2.0.0" + source-map "^0.6.1" + write-file-atomic "2.4.1" + +"@jest/types@^24.3.0", "@jest/types@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.9.0.tgz#63cb26cb7500d069e5a389441a7c6ab5e909fc59" + integrity sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^1.1.1" + "@types/yargs" "^13.0.0" + +"@mrmlnc/readdir-enhanced@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" + integrity sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g== + dependencies: + call-me-maybe "^1.0.1" + glob-to-regexp "^0.3.0" + +"@nodelib/fs.stat@^1.1.2": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" + integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== + +"@svgr/babel-plugin-add-jsx-attribute@^4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz#dadcb6218503532d6884b210e7f3c502caaa44b1" + integrity sha512-j7KnilGyZzYr/jhcrSYS3FGWMZVaqyCG0vzMCwzvei0coIkczuYMcniK07nI0aHJINciujjH11T72ICW5eL5Ig== + +"@svgr/babel-plugin-remove-jsx-attribute@^4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-4.2.0.tgz#297550b9a8c0c7337bea12bdfc8a80bb66f85abc" + integrity sha512-3XHLtJ+HbRCH4n28S7y/yZoEQnRpl0tvTZQsHqvaeNXPra+6vE5tbRliH3ox1yZYPCxrlqaJT/Mg+75GpDKlvQ== + +"@svgr/babel-plugin-remove-jsx-empty-expression@^4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-4.2.0.tgz#c196302f3e68eab6a05e98af9ca8570bc13131c7" + integrity sha512-yTr2iLdf6oEuUE9MsRdvt0NmdpMBAkgK8Bjhl6epb+eQWk6abBaX3d65UZ3E3FWaOwePyUgNyNCMVG61gGCQ7w== + +"@svgr/babel-plugin-replace-jsx-attribute-value@^4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-4.2.0.tgz#310ec0775de808a6a2e4fd4268c245fd734c1165" + integrity sha512-U9m870Kqm0ko8beHawRXLGLvSi/ZMrl89gJ5BNcT452fAjtF2p4uRzXkdzvGJJJYBgx7BmqlDjBN/eCp5AAX2w== + +"@svgr/babel-plugin-svg-dynamic-title@^4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-4.3.3.tgz#2cdedd747e5b1b29ed4c241e46256aac8110dd93" + integrity sha512-w3Be6xUNdwgParsvxkkeZb545VhXEwjGMwExMVBIdPQJeyMQHqm9Msnb2a1teHBqUYL66qtwfhNkbj1iarCG7w== + +"@svgr/babel-plugin-svg-em-dimensions@^4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-4.2.0.tgz#9a94791c9a288108d20a9d2cc64cac820f141391" + integrity sha512-C0Uy+BHolCHGOZ8Dnr1zXy/KgpBOkEUYY9kI/HseHVPeMbluaX3CijJr7D4C5uR8zrc1T64nnq/k63ydQuGt4w== + +"@svgr/babel-plugin-transform-react-native-svg@^4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-4.2.0.tgz#151487322843359a1ca86b21a3815fd21a88b717" + integrity sha512-7YvynOpZDpCOUoIVlaaOUU87J4Z6RdD6spYN4eUb5tfPoKGSF9OG2NuhgYnq4jSkAxcpMaXWPf1cePkzmqTPNw== + +"@svgr/babel-plugin-transform-svg-component@^4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-4.2.0.tgz#5f1e2f886b2c85c67e76da42f0f6be1b1767b697" + integrity sha512-hYfYuZhQPCBVotABsXKSCfel2slf/yvJY8heTVX1PCTaq/IgASq1IyxPPKJ0chWREEKewIU/JMSsIGBtK1KKxw== + +"@svgr/babel-preset@^4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-4.3.3.tgz#a75d8c2f202ac0e5774e6bfc165d028b39a1316c" + integrity sha512-6PG80tdz4eAlYUN3g5GZiUjg2FMcp+Wn6rtnz5WJG9ITGEF1pmFdzq02597Hn0OmnQuCVaBYQE1OVFAnwOl+0A== + dependencies: + "@svgr/babel-plugin-add-jsx-attribute" "^4.2.0" + "@svgr/babel-plugin-remove-jsx-attribute" "^4.2.0" + "@svgr/babel-plugin-remove-jsx-empty-expression" "^4.2.0" + "@svgr/babel-plugin-replace-jsx-attribute-value" "^4.2.0" + "@svgr/babel-plugin-svg-dynamic-title" "^4.3.3" + "@svgr/babel-plugin-svg-em-dimensions" "^4.2.0" + "@svgr/babel-plugin-transform-react-native-svg" "^4.2.0" + "@svgr/babel-plugin-transform-svg-component" "^4.2.0" + +"@svgr/core@^4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@svgr/core/-/core-4.3.3.tgz#b37b89d5b757dc66e8c74156d00c368338d24293" + integrity sha512-qNuGF1QON1626UCaZamWt5yedpgOytvLj5BQZe2j1k1B8DUG4OyugZyfEwBeXozCUwhLEpsrgPrE+eCu4fY17w== + dependencies: + "@svgr/plugin-jsx" "^4.3.3" + camelcase "^5.3.1" + cosmiconfig "^5.2.1" + +"@svgr/hast-util-to-babel-ast@^4.3.2": + version "4.3.2" + resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-4.3.2.tgz#1d5a082f7b929ef8f1f578950238f630e14532b8" + integrity sha512-JioXclZGhFIDL3ddn4Kiq8qEqYM2PyDKV0aYno8+IXTLuYt6TOgHUbUAAFvqtb0Xn37NwP0BTHglejFoYr8RZg== + dependencies: + "@babel/types" "^7.4.4" + +"@svgr/plugin-jsx@^4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-4.3.3.tgz#e2ba913dbdfbe85252a34db101abc7ebd50992fa" + integrity sha512-cLOCSpNWQnDB1/v+SUENHH7a0XY09bfuMKdq9+gYvtuwzC2rU4I0wKGFEp1i24holdQdwodCtDQdFtJiTCWc+w== + dependencies: + "@babel/core" "^7.4.5" + "@svgr/babel-preset" "^4.3.3" + "@svgr/hast-util-to-babel-ast" "^4.3.2" + svg-parser "^2.0.0" + +"@svgr/plugin-svgo@^4.3.1": + version "4.3.1" + resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-4.3.1.tgz#daac0a3d872e3f55935c6588dd370336865e9e32" + integrity sha512-PrMtEDUWjX3Ea65JsVCwTIXuSqa3CG9px+DluF1/eo9mlDrgrtFE7NE/DjdhjJgSM9wenlVBzkzneSIUgfUI/w== + dependencies: + cosmiconfig "^5.2.1" + merge-deep "^3.0.2" + svgo "^1.2.2" + +"@svgr/webpack@4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-4.3.3.tgz#13cc2423bf3dff2d494f16b17eb7eacb86895017" + integrity sha512-bjnWolZ6KVsHhgyCoYRFmbd26p8XVbulCzSG53BDQqAr+JOAderYK7CuYrB3bDjHJuF6LJ7Wrr42+goLRV9qIg== + dependencies: + "@babel/core" "^7.4.5" + "@babel/plugin-transform-react-constant-elements" "^7.0.0" + "@babel/preset-env" "^7.4.5" + "@babel/preset-react" "^7.0.0" + "@svgr/core" "^4.3.3" + "@svgr/plugin-jsx" "^4.3.3" + "@svgr/plugin-svgo" "^4.3.1" + loader-utils "^1.2.3" + +"@tanem/svg-injector@^8.0.40": + version "8.0.41" + resolved "https://registry.yarnpkg.com/@tanem/svg-injector/-/svg-injector-8.0.41.tgz#538a6f210ea49f173c1ccb9180cbd5c34c931873" + integrity sha512-vKomnKsycSS2eF6lNUPFKuPLPmh7UT93ISyrtQPb3HEd64Djp+dz6/rkImre5xTxuptTPwZs0NXztJV2ZIlTuw== + dependencies: + "@babel/runtime" "^7.8.4" + +"@types/babel__core@^7.1.0": + version "7.1.6" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.6.tgz#16ff42a5ae203c9af1c6e190ed1f30f83207b610" + integrity sha512-tTnhWszAqvXnhW7m5jQU9PomXSiKXk2sFxpahXvI20SZKu9ylPi8WtIxueZ6ehDWikPT0jeFujMj3X4ZHuf3Tg== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.6.1" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.1.tgz#4901767b397e8711aeb99df8d396d7ba7b7f0e04" + integrity sha512-bBKm+2VPJcMRVwNhxKu8W+5/zT7pwNEqeokFOmbvVSqGzFneNxYcEBro9Ac7/N9tlsaPYnZLK8J1LWKkMsLAew== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.0.2" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.0.2.tgz#4ff63d6b52eddac1de7b975a5223ed32ecea9307" + integrity sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": + version "7.0.9" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.9.tgz#be82fab304b141c3eee81a4ce3b034d0eba1590a" + integrity sha512-jEFQ8L1tuvPjOI8lnpaf73oCJe+aoxL6ygqSy6c8LcW98zaC+4mzWuQIRCEvKeCOu+lbqdXcg4Uqmm1S8AP1tw== + dependencies: + "@babel/types" "^7.3.0" + +"@types/eslint-visitor-keys@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" + integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== + +"@types/events@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" + integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== + +"@types/glob@^7.1.1": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575" + integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w== + dependencies: + "@types/events" "*" + "@types/minimatch" "*" + "@types/node" "*" + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff" + integrity sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg== + +"@types/istanbul-lib-report@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" + integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-1.1.1.tgz#7a8cbf6a406f36c8add871625b278eaf0b0d255a" + integrity sha512-UpYjBi8xefVChsCoBpKShdxTllC9pwISirfoZsUa2AAdQg/Jd2KQGtSbw+ya7GPo7x/wAPlH6JBhKhAsXUEZNA== + dependencies: + "@types/istanbul-lib-coverage" "*" + "@types/istanbul-lib-report" "*" + +"@types/json-schema@^7.0.3": + version "7.0.4" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339" + integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA== + +"@types/minimatch@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" + integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== + +"@types/node@*": + version "13.7.6" + resolved "https://registry.yarnpkg.com/@types/node/-/node-13.7.6.tgz#cb734a7c191472ae6a2b3a502b4dfffcea974113" + integrity sha512-eyK7MWD0R1HqVTp+PtwRgFeIsemzuj4gBFSQxfPHY5iMjS7474e5wq+VFgTcdpyHeNxyKSaetYAjdMLJlKoWqA== + +"@types/parse-json@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" + integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== + +"@types/q@^1.5.1": + version "1.5.2" + resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8" + integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw== + +"@types/stack-utils@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" + integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== + +"@types/yargs-parser@*": + version "15.0.0" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d" + integrity sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw== + +"@types/yargs@^13.0.0": + version "13.0.8" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-13.0.8.tgz#a38c22def2f1c2068f8971acb3ea734eb3c64a99" + integrity sha512-XAvHLwG7UQ+8M4caKIH0ZozIOYay5fQkAgyIXegXT9jPtdIGdhga+sUEdAr1CiG46aB+c64xQEYyEzlwWVTNzA== + dependencies: + "@types/yargs-parser" "*" + +"@typescript-eslint/eslint-plugin@^2.10.0": + version "2.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.21.0.tgz#a34de84a0791cae0357c4dda805c5b4e8203b6c6" + integrity sha512-b5jjjDMxzcjh/Sbjuo7WyhrQmVJg0WipTHQgXh5Xwx10uYm6nPWqN1WGOsaNq4HR3Zh4wUx4IRQdDkCHwyewyw== + dependencies: + "@typescript-eslint/experimental-utils" "2.21.0" + eslint-utils "^1.4.3" + functional-red-black-tree "^1.0.1" + regexpp "^3.0.0" + tsutils "^3.17.1" + +"@typescript-eslint/experimental-utils@2.21.0": + version "2.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.21.0.tgz#71de390a3ec00b280b69138d80733406e6e86bfa" + integrity sha512-olKw9JP/XUkav4lq0I7S1mhGgONJF9rHNhKFn9wJlpfRVjNo3PPjSvybxEldvCXnvD+WAshSzqH5cEjPp9CsBA== + dependencies: + "@types/json-schema" "^7.0.3" + "@typescript-eslint/typescript-estree" "2.21.0" + eslint-scope "^5.0.0" + +"@typescript-eslint/parser@^2.10.0": + version "2.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.21.0.tgz#4f200995517c3d5fc5ef51b17527bc948992e438" + integrity sha512-VrmbdrrrvvI6cPPOG7uOgGUFXNYTiSbnRq8ZMyuGa4+qmXJXVLEEz78hKuqupvkpwJQNk1Ucz1TenrRP90gmBg== + dependencies: + "@types/eslint-visitor-keys" "^1.0.0" + "@typescript-eslint/experimental-utils" "2.21.0" + "@typescript-eslint/typescript-estree" "2.21.0" + eslint-visitor-keys "^1.1.0" + +"@typescript-eslint/typescript-estree@2.21.0": + version "2.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.21.0.tgz#7e4be29f2e338195a2e8c818949ed0ff727cc943" + integrity sha512-NC/nogZNb9IK2MEFQqyDBAciOT8Lp8O3KgAfvHx2Skx6WBo+KmDqlU3R9KxHONaijfTIKtojRe3SZQyMjr3wBw== + dependencies: + debug "^4.1.1" + eslint-visitor-keys "^1.1.0" + glob "^7.1.6" + is-glob "^4.0.1" + lodash "^4.17.15" + semver "^6.3.0" + tsutils "^3.17.1" + +"@webassemblyjs/ast@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz#51b1c5fe6576a34953bf4b253df9f0d490d9e359" + integrity sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ== + dependencies: + "@webassemblyjs/helper-module-context" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/wast-parser" "1.8.5" + +"@webassemblyjs/floating-point-hex-parser@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz#1ba926a2923613edce496fd5b02e8ce8a5f49721" + integrity sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ== + +"@webassemblyjs/helper-api-error@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz#c49dad22f645227c5edb610bdb9697f1aab721f7" + integrity sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA== + +"@webassemblyjs/helper-buffer@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz#fea93e429863dd5e4338555f42292385a653f204" + integrity sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q== + +"@webassemblyjs/helper-code-frame@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz#9a740ff48e3faa3022b1dff54423df9aa293c25e" + integrity sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ== + dependencies: + "@webassemblyjs/wast-printer" "1.8.5" + +"@webassemblyjs/helper-fsm@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz#ba0b7d3b3f7e4733da6059c9332275d860702452" + integrity sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow== + +"@webassemblyjs/helper-module-context@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz#def4b9927b0101dc8cbbd8d1edb5b7b9c82eb245" + integrity sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g== + dependencies: + "@webassemblyjs/ast" "1.8.5" + mamacro "^0.0.3" + +"@webassemblyjs/helper-wasm-bytecode@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz#537a750eddf5c1e932f3744206551c91c1b93e61" + integrity sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ== + +"@webassemblyjs/helper-wasm-section@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz#74ca6a6bcbe19e50a3b6b462847e69503e6bfcbf" + integrity sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-buffer" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/wasm-gen" "1.8.5" + +"@webassemblyjs/ieee754@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz#712329dbef240f36bf57bd2f7b8fb9bf4154421e" + integrity sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.8.5.tgz#044edeb34ea679f3e04cd4fd9824d5e35767ae10" + integrity sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.8.5.tgz#a8bf3b5d8ffe986c7c1e373ccbdc2a0915f0cedc" + integrity sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw== + +"@webassemblyjs/wasm-edit@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz#962da12aa5acc1c131c81c4232991c82ce56e01a" + integrity sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-buffer" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/helper-wasm-section" "1.8.5" + "@webassemblyjs/wasm-gen" "1.8.5" + "@webassemblyjs/wasm-opt" "1.8.5" + "@webassemblyjs/wasm-parser" "1.8.5" + "@webassemblyjs/wast-printer" "1.8.5" + +"@webassemblyjs/wasm-gen@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz#54840766c2c1002eb64ed1abe720aded714f98bc" + integrity sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/ieee754" "1.8.5" + "@webassemblyjs/leb128" "1.8.5" + "@webassemblyjs/utf8" "1.8.5" + +"@webassemblyjs/wasm-opt@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz#b24d9f6ba50394af1349f510afa8ffcb8a63d264" + integrity sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-buffer" "1.8.5" + "@webassemblyjs/wasm-gen" "1.8.5" + "@webassemblyjs/wasm-parser" "1.8.5" + +"@webassemblyjs/wasm-parser@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz#21576f0ec88b91427357b8536383668ef7c66b8d" + integrity sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-api-error" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/ieee754" "1.8.5" + "@webassemblyjs/leb128" "1.8.5" + "@webassemblyjs/utf8" "1.8.5" + +"@webassemblyjs/wast-parser@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz#e10eecd542d0e7bd394f6827c49f3df6d4eefb8c" + integrity sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/floating-point-hex-parser" "1.8.5" + "@webassemblyjs/helper-api-error" "1.8.5" + "@webassemblyjs/helper-code-frame" "1.8.5" + "@webassemblyjs/helper-fsm" "1.8.5" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/wast-printer@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz#114bbc481fd10ca0e23b3560fa812748b0bae5bc" + integrity sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/wast-parser" "1.8.5" + "@xtuc/long" "4.2.2" + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +abab@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.3.tgz#623e2075e02eb2d3f2475e49f99c91846467907a" + integrity sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg== + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + +accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + +acorn-globals@^4.1.0, acorn-globals@^4.3.0: + version "4.3.4" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7" + integrity sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A== + dependencies: + acorn "^6.0.1" + acorn-walk "^6.0.1" + +acorn-jsx@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.1.0.tgz#294adb71b57398b0680015f0a38c563ee1db5384" + integrity sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw== + +acorn-walk@^6.0.1: + version "6.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" + integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA== + +acorn@^5.5.3: + version "5.7.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279" + integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw== + +acorn@^6.0.1, acorn@^6.0.4, acorn@^6.2.1: + version "6.4.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.0.tgz#b659d2ffbafa24baf5db1cdbb2c94a983ecd2784" + integrity sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw== + +acorn@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.0.tgz#949d36f2c292535da602283586c2477c57eb2d6c" + integrity sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ== + +add-dom-event-listener@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/add-dom-event-listener/-/add-dom-event-listener-1.1.0.tgz#6a92db3a0dd0abc254e095c0f1dc14acbbaae310" + integrity sha512-WCxx1ixHT0GQU9hb0KI/mhgRQhnU+U3GvwY6ZvVjYq8rsihIGoaIOUbY0yMPBxLH5MDtr0kz3fisWGNcbWW7Jw== + dependencies: + object-assign "4.x" + +add-px-to-style@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/add-px-to-style/-/add-px-to-style-1.0.0.tgz#d0c135441fa8014a8137904531096f67f28f263a" + integrity sha1-0ME1RB+oAUqBN5BFMQlvZ/KPJjo= + +address@1.1.2, address@^1.0.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" + integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA== + +adjust-sourcemap-loader@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/adjust-sourcemap-loader/-/adjust-sourcemap-loader-2.0.0.tgz#6471143af75ec02334b219f54bc7970c52fb29a4" + integrity sha512-4hFsTsn58+YjrU9qKzML2JSSDqKvN8mUGQ0nNIrfPi8hmIONT4L3uUaT6MKdMsZ9AjsU6D2xDkZxCkbQPxChrA== + dependencies: + assert "1.4.1" + camelcase "5.0.0" + loader-utils "1.2.3" + object-path "0.11.4" + regex-parser "2.2.10" + +aggregate-error@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.0.1.tgz#db2fe7246e536f40d9b5442a39e117d7dd6a24e0" + integrity sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + +ajv-errors@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" + integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== + +ajv-keywords@^3.1.0, ajv-keywords@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da" + integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ== + +ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.5.5: + version "6.12.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.0.tgz#06d60b96d87b8454a5adaba86e7854da629db4b7" + integrity sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +alphanum-sort@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" + integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM= + +amdefine@>=0.0.4: + version "1.0.1" + resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" + integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= + +ansi-colors@^3.0.0: + version "3.2.4" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" + integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== + +ansi-escapes@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== + +ansi-escapes@^4.2.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.0.tgz#a4ce2b33d6b214b7950d8595c212f12ac9cc569d" + integrity sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg== + dependencies: + type-fest "^0.8.1" + +ansi-html@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" + integrity sha1-gTWEAhliqenm/QOflA0S9WynhZ4= + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-regex@^4.0.0, ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +anymatch@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" + integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +aproba@^1.0.3, aproba@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== + +are-we-there-yet@~1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +aria-query@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-3.0.0.tgz#65b3fcc1ca1155a8c9ae64d6eee297f15d5133cc" + integrity sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w= + dependencies: + ast-types-flow "0.0.7" + commander "^2.11.0" + +arity-n@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/arity-n/-/arity-n-1.0.4.tgz#d9e76b11733e08569c0847ae7b39b2860b30b745" + integrity sha1-2edrEXM+CFacCEeuezmyhgswt0U= + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= + +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= + +array-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" + integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM= + +array-find-index@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + +array-flatten@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" + integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== + +array-includes@^3.0.3, array-includes@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.1.tgz#cdd67e6852bdf9c1215460786732255ed2459348" + integrity sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0" + is-string "^1.0.5" + +array-union@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" + integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk= + dependencies: + array-uniq "^1.0.1" + +array-uniq@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= + +array.prototype.flat@^1.2.1: + version "1.2.3" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz#0de82b426b0318dbfdb940089e38b043d37f6c7b" + integrity sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + +arrify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= + +asap@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= + +"asc-web-common@file:../../../packages/asc-web-common": + version "1.0.91" + dependencies: + axios "^0.19.1" + faker "^4.1.0" + history "4.10.1" + react-svg "^11.0.9" + react-window-infinite-loader "^1.0.5" + +"asc-web-components@file:../../../packages/asc-web-components": + version "1.0.360" + dependencies: + email-addresses "^3.1.0" + html-to-react "^1.4.2" + moment "^2.24.0" + prop-types "^15.7.2" + punycode "^2.1.1" + rc-tree "^2.1.3" + react-autosize-textarea "^7.0.0" + react-avatar-editor "^11.0.7" + react-custom-scrollbars "^4.2.1" + react-dropzone "^10.2.1" + react-onclickoutside "^6.9.0" + react-text-mask "^5.4.3" + react-toastify "^5.5.0" + react-tooltip "^3.11.2" + react-virtualized-auto-sizer "^1.0.2" + react-window "^1.8.5" + resize-image "^0.1.0" + +asn1.js@^4.0.0: + version "4.10.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" + integrity sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +assert@1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" + integrity sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE= + dependencies: + util "0.10.3" + +assert@^1.1.1: + version "1.5.0" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" + integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== + dependencies: + object-assign "^4.1.1" + util "0.10.3" + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= + +ast-types-flow@0.0.7, ast-types-flow@^0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" + integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0= + +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== + +async-each@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" + integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== + +async-foreach@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542" + integrity sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI= + +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + +async@^2.6.2: + version "2.6.3" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" + integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== + dependencies: + lodash "^4.17.14" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +attr-accept@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-2.0.0.tgz#8422fef5ee4a511c207796c888227ab5de03306f" + integrity sha512-I9SDP4Wvh2ItYYoafEg8hFpsBe96pfQ+eabceShXt3sw2fbIP96+Aoj9zZE0vkZNAkXXzHJATVRuWz+h9FxJxQ== + +autoprefixer@^9.6.1: + version "9.7.4" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.7.4.tgz#f8bf3e06707d047f0641d87aee8cfb174b2a5378" + integrity sha512-g0Ya30YrMBAEZk60lp+qfX5YQllG+S5W3GYCFvyHTvhOki0AEQJLPEcIuGRsqVwLi8FvXPVtwTGhfr38hVpm0g== + dependencies: + browserslist "^4.8.3" + caniuse-lite "^1.0.30001020" + chalk "^2.4.2" + normalize-range "^0.1.2" + num2fraction "^1.2.2" + postcss "^7.0.26" + postcss-value-parser "^4.0.2" + +autosize@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/autosize/-/autosize-4.0.2.tgz#073cfd07c8bf45da4b9fd153437f5bafbba1e4c9" + integrity sha512-jnSyH2d+qdfPGpWlcuhGiHmqBJ6g3X+8T+iRwFrHPLVcdoGJE/x6Qicm6aDHfTsbgZKxyV8UU/YB2p4cjKDRRA== + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e" + integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug== + +axios@^0.19.1: + version "0.19.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27" + integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA== + dependencies: + follow-redirects "1.5.10" + +axobject-query@^2.0.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.1.2.tgz#2bdffc0371e643e5f03ba99065d5179b9ca79799" + integrity sha512-ICt34ZmrVt8UQnvPl6TVyDTkmhXmAyAT4Jh5ugfGUX4MOrZ+U/ZY6/sdylRw3qGNr9Ub5AJsaHeDMzNLehRdOQ== + +babel-code-frame@^6.22.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= + dependencies: + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.0.2" + +babel-eslint@10.0.3: + version "10.0.3" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.3.tgz#81a2c669be0f205e19462fed2482d33e4687a88a" + integrity sha512-z3U7eMY6r/3f3/JB9mTsLjyxrv0Yb1zb8PCWCLpguxfCzBIZUwy23R1t/XKewP+8mEN2Ck8Dtr4q20z6ce6SoA== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/parser" "^7.0.0" + "@babel/traverse" "^7.0.0" + "@babel/types" "^7.0.0" + eslint-visitor-keys "^1.0.0" + resolve "^1.12.0" + +babel-extract-comments@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/babel-extract-comments/-/babel-extract-comments-1.0.0.tgz#0a2aedf81417ed391b85e18b4614e693a0351a21" + integrity sha512-qWWzi4TlddohA91bFwgt6zO/J0X+io7Qp184Fw0m2JYRSTZnJbFR8+07KmzudHCZgOiKRCrjhylwv9Xd8gfhVQ== + dependencies: + babylon "^6.18.0" + +babel-jest@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.9.0.tgz#3fc327cb8467b89d14d7bc70e315104a783ccd54" + integrity sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw== + dependencies: + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/babel__core" "^7.1.0" + babel-plugin-istanbul "^5.1.0" + babel-preset-jest "^24.9.0" + chalk "^2.4.2" + slash "^2.0.0" + +babel-loader@8.0.6: + version "8.0.6" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.0.6.tgz#e33bdb6f362b03f4bb141a0c21ab87c501b70dfb" + integrity sha512-4BmWKtBOBm13uoUwd08UwjZlaw3O9GWf456R9j+5YykFZ6LUIjIKLc0zEZf+hauxPOJs96C8k6FvYD09vWzhYw== + dependencies: + find-cache-dir "^2.0.0" + loader-utils "^1.0.2" + mkdirp "^0.5.1" + pify "^4.0.1" + +babel-plugin-dynamic-import-node@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz#f00f507bdaa3c3e3ff6e7e5e98d90a7acab96f7f" + integrity sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ== + dependencies: + object.assign "^4.1.0" + +babel-plugin-istanbul@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz#df4ade83d897a92df069c4d9a25cf2671293c854" + integrity sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + find-up "^3.0.0" + istanbul-lib-instrument "^3.3.0" + test-exclude "^5.2.3" + +babel-plugin-jest-hoist@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz#4f837091eb407e01447c8843cbec546d0002d756" + integrity sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw== + dependencies: + "@types/babel__traverse" "^7.0.6" + +babel-plugin-macros@2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz#0f958a7cc6556b1e65344465d99111a1e5e10138" + integrity sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg== + dependencies: + "@babel/runtime" "^7.7.2" + cosmiconfig "^6.0.0" + resolve "^1.12.0" + +babel-plugin-named-asset-import@^0.3.6: + version "0.3.6" + resolved "https://registry.yarnpkg.com/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.6.tgz#c9750a1b38d85112c9e166bf3ef7c5dbc605f4be" + integrity sha512-1aGDUfL1qOOIoqk9QKGIo2lANk+C7ko/fqH0uIyC71x3PEGz0uVP8ISgfEsFuG+FKmjHTvFK/nNM8dowpmUxLA== + +"babel-plugin-styled-components@>= 1": + version "1.10.7" + resolved "https://registry.yarnpkg.com/babel-plugin-styled-components/-/babel-plugin-styled-components-1.10.7.tgz#3494e77914e9989b33cc2d7b3b29527a949d635c" + integrity sha512-MBMHGcIA22996n9hZRf/UJLVVgkEOITuR2SvjHLb5dSTUyR4ZRGn+ngITapes36FI3WLxZHfRhkA1ffHxihOrg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.0.0" + "@babel/helper-module-imports" "^7.0.0" + babel-plugin-syntax-jsx "^6.18.0" + lodash "^4.17.11" + +babel-plugin-syntax-jsx@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" + integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY= + +babel-plugin-syntax-object-rest-spread@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5" + integrity sha1-/WU28rzhODb/o6VFjEkDpZe7O/U= + +babel-plugin-transform-object-rest-spread@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz#0f36692d50fef6b7e2d4b3ac1478137a963b7b06" + integrity sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY= + dependencies: + babel-plugin-syntax-object-rest-spread "^6.8.0" + babel-runtime "^6.26.0" + +babel-plugin-transform-react-remove-prop-types@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz#f2edaf9b4c6a5fbe5c1d678bfb531078c1555f3a" + integrity sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA== + +babel-preset-jest@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz#192b521e2217fb1d1f67cf73f70c336650ad3cdc" + integrity sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg== + dependencies: + "@babel/plugin-syntax-object-rest-spread" "^7.0.0" + babel-plugin-jest-hoist "^24.9.0" + +babel-preset-react-app@^9.1.1: + version "9.1.1" + resolved "https://registry.yarnpkg.com/babel-preset-react-app/-/babel-preset-react-app-9.1.1.tgz#d1ceb47cbe48b285fdd5c562c54c432ed5a41e0e" + integrity sha512-YkWP2UwY//TLltNlEBRngDOrYhvSLb+CA330G7T9M5UhGEMWe+JK/8IXJc5p2fDTSfSiETf+PY0+PYXFMix81Q== + dependencies: + "@babel/core" "7.8.4" + "@babel/plugin-proposal-class-properties" "7.8.3" + "@babel/plugin-proposal-decorators" "7.8.3" + "@babel/plugin-proposal-numeric-separator" "7.8.3" + "@babel/plugin-transform-flow-strip-types" "7.8.3" + "@babel/plugin-transform-react-display-name" "7.8.3" + "@babel/plugin-transform-runtime" "7.8.3" + "@babel/preset-env" "7.8.4" + "@babel/preset-react" "7.8.3" + "@babel/preset-typescript" "7.8.3" + "@babel/runtime" "7.8.4" + babel-plugin-macros "2.8.0" + babel-plugin-transform-react-remove-prop-types "0.4.24" + +babel-runtime@6.x, babel-runtime@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + +babylon@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" + integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +base64-js@^1.0.2, base64-js@^1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" + integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +batch@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" + integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY= + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +binary-extensions@^1.0.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" + integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== + +binary-extensions@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c" + integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow== + +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +block-stream@*: + version "0.0.9" + resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" + integrity sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo= + dependencies: + inherits "~2.0.0" + +bluebird@^3.5.5: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: + version "4.11.8" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" + integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== + +body-parser@1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== + dependencies: + bytes "3.1.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "1.7.2" + iconv-lite "0.4.24" + on-finished "~2.3.0" + qs "6.7.0" + raw-body "2.4.0" + type-is "~1.6.17" + +bonjour@^3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5" + integrity sha1-jokKGD2O6aI5OzhExpGkK897yfU= + dependencies: + array-flatten "^2.1.0" + deep-equal "^1.0.1" + dns-equal "^1.0.0" + dns-txt "^2.0.2" + multicast-dns "^6.0.1" + multicast-dns-service-types "^1.1.0" + +boolbase@^1.0.0, boolbase@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^2.3.1, braces@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +brorand@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= + +browser-process-hrtime@^0.1.2: + version "0.1.3" + resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz#616f00faef1df7ec1b5bf9cfe2bdc3170f26c7b4" + integrity sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw== + +browser-resolve@^1.11.3: + version "1.11.3" + resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6" + integrity sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ== + dependencies: + resolve "1.1.7" + +browserify-aes@^1.0.0, browserify-aes@^1.0.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-rsa@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" + integrity sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ= + dependencies: + bn.js "^4.1.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298" + integrity sha1-qk62jl17ZYuqa/alfmMMvXqT0pg= + dependencies: + bn.js "^4.1.1" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.2" + elliptic "^6.0.0" + inherits "^2.0.1" + parse-asn1 "^5.0.0" + +browserify-zlib@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" + integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== + dependencies: + pako "~1.0.5" + +browserslist@4.8.6: + version "4.8.6" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.8.6.tgz#96406f3f5f0755d272e27a66f4163ca821590a7e" + integrity sha512-ZHao85gf0eZ0ESxLfCp73GG9O/VTytYDIkIiZDlURppLTI9wErSM/5yAKEq6rcUdxBLjMELmrYUJGg5sxGKMHg== + dependencies: + caniuse-lite "^1.0.30001023" + electron-to-chromium "^1.3.341" + node-releases "^1.1.47" + +browserslist@^4.0.0, browserslist@^4.6.2, browserslist@^4.6.4, browserslist@^4.8.3, browserslist@^4.8.5: + version "4.8.7" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.8.7.tgz#ec8301ff415e6a42c949d0e66b405eb539c532d0" + integrity sha512-gFOnZNYBHrEyUML0xr5NJ6edFaaKbTFX9S9kQHlYfCP0Rit/boRIz4G+Avq6/4haEKJXdGGUnoolx+5MWW2BoA== + dependencies: + caniuse-lite "^1.0.30001027" + electron-to-chromium "^1.3.349" + node-releases "^1.1.49" + +bser@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== + dependencies: + node-int64 "^0.4.0" + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +buffer-indexof@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" + integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g== + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= + +buffer@^4.3.0: + version "4.9.2" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" + integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + isarray "^1.0.0" + +builtin-status-codes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= + +bytes@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + +cacache@^12.0.2, cacache@^12.0.3: + version "12.0.3" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.3.tgz#be99abba4e1bf5df461cd5a2c1071fc432573390" + integrity sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw== + dependencies: + bluebird "^3.5.5" + chownr "^1.1.1" + figgy-pudding "^3.5.1" + glob "^7.1.4" + graceful-fs "^4.1.15" + infer-owner "^1.0.3" + lru-cache "^5.1.1" + mississippi "^3.0.0" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + promise-inflight "^1.0.1" + rimraf "^2.6.3" + ssri "^6.0.1" + unique-filename "^1.1.1" + y18n "^4.0.0" + +cacache@^13.0.1: + version "13.0.1" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-13.0.1.tgz#a8000c21697089082f85287a1aec6e382024a71c" + integrity sha512-5ZvAxd05HDDU+y9BVvcqYu2LLXmPnQ0hW62h32g4xBTgL/MppR4/04NHfj/ycM2y6lmTnbw6HVi+1eN0Psba6w== + dependencies: + chownr "^1.1.2" + figgy-pudding "^3.5.1" + fs-minipass "^2.0.0" + glob "^7.1.4" + graceful-fs "^4.2.2" + infer-owner "^1.0.4" + lru-cache "^5.1.1" + minipass "^3.0.0" + minipass-collect "^1.0.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.2" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + p-map "^3.0.0" + promise-inflight "^1.0.1" + rimraf "^2.7.1" + ssri "^7.0.0" + unique-filename "^1.1.1" + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +call-me-maybe@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" + integrity sha1-JtII6onje1y95gJQoV8DHBak1ms= + +caller-callsite@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" + integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= + dependencies: + callsites "^2.0.0" + +caller-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" + integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= + dependencies: + caller-callsite "^2.0.0" + +callsites@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" + integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camel-case@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.1.tgz#1fc41c854f00e2f7d0139dfeba1542d6896fe547" + integrity sha512-7fa2WcG4fYFkclIvEmxBbTvmibwF2/agfEBc6q3lOpVu0A13ltLsA+Hr/8Hp6kp5f+G7hKi6t8lys6XxP+1K6Q== + dependencies: + pascal-case "^3.1.1" + tslib "^1.10.0" + +camelcase-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" + integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc= + dependencies: + camelcase "^2.0.0" + map-obj "^1.0.0" + +camelcase@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42" + integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA== + +camelcase@5.3.1, camelcase@^5.0.0, camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +camelcase@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= + +camelcase@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" + integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo= + +camelize@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.0.tgz#164a5483e630fa4321e5af07020e531831b2609b" + integrity sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs= + +caniuse-api@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" + integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== + dependencies: + browserslist "^4.0.0" + caniuse-lite "^1.0.0" + lodash.memoize "^4.1.2" + lodash.uniq "^4.5.0" + +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001020, caniuse-lite@^1.0.30001023, caniuse-lite@^1.0.30001027: + version "1.0.30001030" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001030.tgz#78076c4c6d67d3e41d6eb9399853fb27fe6e44ee" + integrity sha512-QGK0W4Ft/Ac+zTjEiRJfwDNATvS3fodDczBXrH42784kcfqcDKpEPfN08N0HQjrAp8He/Jw8QiSS9QRn7XAbUw== + +capture-exit@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" + integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g== + dependencies: + rsvp "^4.8.4" + +case-sensitive-paths-webpack-plugin@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.3.0.tgz#23ac613cc9a856e4f88ff8bb73bbb5e989825cf7" + integrity sha512-/4YgnZS8y1UXXmC02xD5rRrBEu6T5ub+mQHLNRj0fzTRbgdBYhsNo2V5EqwgqrExjxsjtF/OpAKAMkKsxbD5XQ== + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^1.1.1, chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +chokidar@^2.0.2, chokidar@^2.1.8: + version "2.1.8" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" + integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== + dependencies: + anymatch "^2.0.0" + async-each "^1.0.1" + braces "^2.3.2" + glob-parent "^3.1.0" + inherits "^2.0.3" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^3.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.2.1" + upath "^1.1.1" + optionalDependencies: + fsevents "^1.2.7" + +chokidar@^3.3.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.1.tgz#c84e5b3d18d9a4d77558fef466b1bf16bbeb3450" + integrity sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.3.0" + optionalDependencies: + fsevents "~2.1.2" + +chownr@^1.1.1, chownr@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +chrome-trace-event@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4" + integrity sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ== + dependencies: + tslib "^1.9.0" + +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +classnames@2.x, classnames@^2.2.6: + version "2.2.6" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" + integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q== + +clean-css@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz#507b5de7d97b48ee53d84adb0160ff6216380f78" + integrity sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA== + dependencies: + source-map "~0.6.0" + +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-width@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" + integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= + +cliui@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" + integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0= + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + wrap-ansi "^2.0.0" + +cliui@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" + integrity sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ== + dependencies: + string-width "^2.1.1" + strip-ansi "^4.0.0" + wrap-ansi "^2.0.0" + +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + +clone-deep@^0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-0.2.4.tgz#4e73dd09e9fb971cc38670c5dced9c1896481cc6" + integrity sha1-TnPdCen7lxzDhnDF3O2cGJZIHMY= + dependencies: + for-own "^0.1.3" + is-plain-object "^2.0.1" + kind-of "^3.0.2" + lazy-cache "^1.0.3" + shallow-clone "^0.1.2" + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= + +coa@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3" + integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA== + dependencies: + "@types/q" "^1.5.1" + chalk "^2.4.1" + q "^1.1.2" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0, color-convert@^1.9.1: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-string@^1.5.2: + version "1.5.3" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc" + integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/color/-/color-3.1.2.tgz#68148e7f85d41ad7649c5fa8c8106f098d229e10" + integrity sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg== + dependencies: + color-convert "^1.9.1" + color-string "^1.5.2" + +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@^2.11.0, commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" + integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== + +common-tags@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937" + integrity sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= + +component-classes@^1.2.5: + version "1.2.6" + resolved "https://registry.yarnpkg.com/component-classes/-/component-classes-1.2.6.tgz#c642394c3618a4d8b0b8919efccbbd930e5cd691" + integrity sha1-xkI5TDYYpNiwuJGe/Mu9kw5c1pE= + dependencies: + component-indexof "0.0.3" + +component-emitter@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + +component-indexof@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/component-indexof/-/component-indexof-0.0.3.tgz#11d091312239eb8f32c8f25ae9cb002ffe8d3c24" + integrity sha1-EdCRMSI5648yyPJa6csAL/6NPCQ= + +compose-function@3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/compose-function/-/compose-function-3.0.3.tgz#9ed675f13cc54501d30950a486ff6a7ba3ab185f" + integrity sha1-ntZ18TzFRQHTCVCkhv9qe6OrGF8= + dependencies: + arity-n "^1.0.4" + +compressible@~2.0.16: + version "2.0.18" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== + dependencies: + mime-db ">= 1.43.0 < 2" + +compression@^1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" + integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.16" + debug "2.6.9" + on-headers "~1.0.2" + safe-buffer "5.1.2" + vary "~1.1.2" + +computed-style@~0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/computed-style/-/computed-style-0.1.4.tgz#7f344fd8584b2e425bedca4a1afc0e300bb05d74" + integrity sha1-fzRP2FhLLkJb7cpKGvwOMAuwXXQ= + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +concat-stream@^1.5.0: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +confusing-browser-globals@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz#72bc13b483c0276801681871d4898516f8f54fdd" + integrity sha512-KbS1Y0jMtyPgIxjO7ZzMAuUpAKMt1SzCL9fsrKsX6b0zJPTaT0SiSPmewwVZg9UAO83HVIlEhZF84LIjZ0lmAw== + +connect-history-api-fallback@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" + integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== + +connected-react-router@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/connected-react-router/-/connected-react-router-6.6.1.tgz#f6b7717abf959393fab6756c8d43af1a57d622da" + integrity sha512-a/SE3HgpZABCxr083bfAMpgZwUzlv1RkmOV71+D4I77edoR/peg7uJMHOgqWnXXqGD7lo3Y2ZgUlXtMhcv8FeA== + dependencies: + immutable "^3.8.1" + prop-types "^15.7.2" + seamless-immutable "^7.1.3" + +console-browserify@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" + integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + +constants-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" + integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= + +contains-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" + integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= + +content-disposition@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== + dependencies: + safe-buffer "5.1.2" + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + +convert-source-map@1.7.0, convert-source-map@^1.4.0, convert-source-map@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" + integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== + dependencies: + safe-buffer "~5.1.1" + +convert-source-map@^0.3.3: + version "0.3.5" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-0.3.5.tgz#f1d802950af7dd2631a1febe0596550c86ab3190" + integrity sha1-8dgClQr33SYxof6+BZZVDIarMZA= + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + +cookie@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" + integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== + +copy-concurrently@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" + integrity sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A== + dependencies: + aproba "^1.1.1" + fs-write-stream-atomic "^1.0.8" + iferr "^0.1.5" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.0" + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= + +copy-to-clipboard@^3.2.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz#115aa1a9998ffab6196f93076ad6da3b913662ae" + integrity sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw== + dependencies: + toggle-selection "^1.0.6" + +copy-webpack-plugin@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-5.1.1.tgz#5481a03dea1123d88a988c6ff8b78247214f0b88" + integrity sha512-P15M5ZC8dyCjQHWwd4Ia/dm0SgVvZJMYeykVIVYXbGyqO4dWB5oyPHp9i7wjwo5LhtlhKbiBCdS2NvM07Wlybg== + dependencies: + cacache "^12.0.3" + find-cache-dir "^2.1.0" + glob-parent "^3.1.0" + globby "^7.1.1" + is-glob "^4.0.1" + loader-utils "^1.2.3" + minimatch "^3.0.4" + normalize-path "^3.0.0" + p-limit "^2.2.1" + schema-utils "^1.0.0" + serialize-javascript "^2.1.2" + webpack-log "^2.0.0" + +core-js-compat@^3.6.2: + version "3.6.4" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.6.4.tgz#938476569ebb6cda80d339bcf199fae4f16fff17" + integrity sha512-zAa3IZPvsJ0slViBQ2z+vgyyTuhd3MFn1rBQjZSKVEgB0UMYhUkCj9jJUVPgGTGqWvsBVmfnruXgTcNyTlEiSA== + dependencies: + browserslist "^4.8.3" + semver "7.0.0" + +core-js@^2.4.0, core-js@^2.6.4: + version "2.6.11" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c" + integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg== + +core-js@^3.5.0: + version "3.6.4" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.4.tgz#440a83536b458114b9cb2ac1580ba377dc470647" + integrity sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw== + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +cosmiconfig@^5.0.0, cosmiconfig@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" + integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== + dependencies: + import-fresh "^2.0.0" + is-directory "^0.3.1" + js-yaml "^3.13.1" + parse-json "^4.0.0" + +cosmiconfig@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982" + integrity sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.1.0" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.7.2" + +create-ecdh@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff" + integrity sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw== + dependencies: + bn.js "^4.1.0" + elliptic "^6.0.0" + +create-hash@^1.1.0, create-hash@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +cross-env@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-6.0.3.tgz#4256b71e49b3a40637a0ce70768a6ef5c72ae941" + integrity sha512-+KqxF6LCvfhWvADcDPqo64yVIB31gv/jQulX2NGzKS/g3GEVz6/pt4wjHFtFWsHMddebWD/sDthJemzM4MaAag== + dependencies: + cross-spawn "^7.0.0" + +cross-spawn@7.0.1, cross-spawn@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14" + integrity sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +cross-spawn@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982" + integrity sha1-ElYDfsufDF9549bvE14wdwGEuYI= + dependencies: + lru-cache "^4.0.1" + which "^1.2.9" + +cross-spawn@^6.0.0, cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +crypto-browserify@^3.11.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +crypto-js@^3.1.9-1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.3.0.tgz#846dd1cce2f68aacfa156c8578f926a609b7976b" + integrity sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q== + +css-animation@^1.3.2: + version "1.6.1" + resolved "https://registry.yarnpkg.com/css-animation/-/css-animation-1.6.1.tgz#162064a3b0d51f958b7ff37b3d6d4de18e17039e" + integrity sha512-/48+/BaEaHRY6kNQ2OIPzKf9A6g8WjZYjhiNDNuIVbsm5tXCGIAsHDjB4Xu1C4vXJtUWZo26O68OQkDpNBaPog== + dependencies: + babel-runtime "6.x" + component-classes "^1.2.5" + +css-blank-pseudo@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz#dfdefd3254bf8a82027993674ccf35483bfcb3c5" + integrity sha512-LHz35Hr83dnFeipc7oqFDmsjHdljj3TQtxGGiNWSOsTLIAubSm4TEz8qCaKFpk7idaQ1GfWscF4E6mgpBysA1w== + dependencies: + postcss "^7.0.5" + +css-color-keywords@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05" + integrity sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU= + +css-color-names@0.0.4, css-color-names@^0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" + integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA= + +css-declaration-sorter@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz#c198940f63a76d7e36c1e71018b001721054cb22" + integrity sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA== + dependencies: + postcss "^7.0.1" + timsort "^0.3.0" + +css-has-pseudo@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/css-has-pseudo/-/css-has-pseudo-0.10.0.tgz#3c642ab34ca242c59c41a125df9105841f6966ee" + integrity sha512-Z8hnfsZu4o/kt+AuFzeGpLVhFOGO9mluyHBaA2bA8aCGTwah5sT3WV/fTHH8UNZUytOIImuGPrl/prlb4oX4qQ== + dependencies: + postcss "^7.0.6" + postcss-selector-parser "^5.0.0-rc.4" + +css-loader@3.4.2: + version "3.4.2" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.4.2.tgz#d3fdb3358b43f233b78501c5ed7b1c6da6133202" + integrity sha512-jYq4zdZT0oS0Iykt+fqnzVLRIeiPWhka+7BqPn+oSIpWJAHak5tmB/WZrJ2a21JhCeFyNnnlroSl8c+MtVndzA== + dependencies: + camelcase "^5.3.1" + cssesc "^3.0.0" + icss-utils "^4.1.1" + loader-utils "^1.2.3" + normalize-path "^3.0.0" + postcss "^7.0.23" + postcss-modules-extract-imports "^2.0.0" + postcss-modules-local-by-default "^3.0.2" + postcss-modules-scope "^2.1.1" + postcss-modules-values "^3.0.0" + postcss-value-parser "^4.0.2" + schema-utils "^2.6.0" + +css-prefers-color-scheme@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/css-prefers-color-scheme/-/css-prefers-color-scheme-3.1.1.tgz#6f830a2714199d4f0d0d0bb8a27916ed65cff1f4" + integrity sha512-MTu6+tMs9S3EUqzmqLXEcgNRbNkkD/TGFvowpeoWJn5Vfq7FMgsmRQs9X5NXAURiOBmOxm/lLjsDNXDE6k9bhg== + dependencies: + postcss "^7.0.5" + +css-select-base-adapter@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" + integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== + +css-select@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" + integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg= + dependencies: + boolbase "~1.0.0" + css-what "2.1" + domutils "1.5.1" + nth-check "~1.0.1" + +css-select@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef" + integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ== + dependencies: + boolbase "^1.0.0" + css-what "^3.2.1" + domutils "^1.7.0" + nth-check "^1.0.2" + +css-to-react-native@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-3.0.0.tgz#62dbe678072a824a689bcfee011fc96e02a7d756" + integrity sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ== + dependencies: + camelize "^1.0.0" + css-color-keywords "^1.0.0" + postcss-value-parser "^4.0.2" + +css-tree@1.0.0-alpha.37: + version "1.0.0-alpha.37" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22" + integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg== + dependencies: + mdn-data "2.0.4" + source-map "^0.6.1" + +css-what@2.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" + integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== + +css-what@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.2.1.tgz#f4a8f12421064621b456755e34a03a2c22df5da1" + integrity sha512-WwOrosiQTvyms+Ti5ZC5vGEK0Vod3FTt1ca+payZqvKuGJF+dq7bG63DstxtN0dpm6FxY27a/zS3Wten+gEtGw== + +css@^2.0.0: + version "2.2.4" + resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929" + integrity sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw== + dependencies: + inherits "^2.0.3" + source-map "^0.6.1" + source-map-resolve "^0.5.2" + urix "^0.1.0" + +cssdb@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-4.4.0.tgz#3bf2f2a68c10f5c6a08abd92378331ee803cddb0" + integrity sha512-LsTAR1JPEM9TpGhl/0p3nQecC2LJ0kD8X5YARu1hk/9I1gril5vDtMZyNxcEpxxDj34YNck/ucjuoUd66K03oQ== + +cssesc@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-2.0.0.tgz#3b13bd1bb1cb36e1bcb5a4dcd27f54c5dcb35703" + integrity sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg== + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +cssnano-preset-default@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz#51ec662ccfca0f88b396dcd9679cdb931be17f76" + integrity sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA== + dependencies: + css-declaration-sorter "^4.0.1" + cssnano-util-raw-cache "^4.0.1" + postcss "^7.0.0" + postcss-calc "^7.0.1" + postcss-colormin "^4.0.3" + postcss-convert-values "^4.0.1" + postcss-discard-comments "^4.0.2" + postcss-discard-duplicates "^4.0.2" + postcss-discard-empty "^4.0.1" + postcss-discard-overridden "^4.0.1" + postcss-merge-longhand "^4.0.11" + postcss-merge-rules "^4.0.3" + postcss-minify-font-values "^4.0.2" + postcss-minify-gradients "^4.0.2" + postcss-minify-params "^4.0.2" + postcss-minify-selectors "^4.0.2" + postcss-normalize-charset "^4.0.1" + postcss-normalize-display-values "^4.0.2" + postcss-normalize-positions "^4.0.2" + postcss-normalize-repeat-style "^4.0.2" + postcss-normalize-string "^4.0.2" + postcss-normalize-timing-functions "^4.0.2" + postcss-normalize-unicode "^4.0.1" + postcss-normalize-url "^4.0.1" + postcss-normalize-whitespace "^4.0.2" + postcss-ordered-values "^4.1.2" + postcss-reduce-initial "^4.0.3" + postcss-reduce-transforms "^4.0.2" + postcss-svgo "^4.0.2" + postcss-unique-selectors "^4.0.1" + +cssnano-util-get-arguments@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz#ed3a08299f21d75741b20f3b81f194ed49cc150f" + integrity sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8= + +cssnano-util-get-match@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz#c0e4ca07f5386bb17ec5e52250b4f5961365156d" + integrity sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0= + +cssnano-util-raw-cache@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz#b26d5fd5f72a11dfe7a7846fb4c67260f96bf282" + integrity sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA== + dependencies: + postcss "^7.0.0" + +cssnano-util-same-parent@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz#574082fb2859d2db433855835d9a8456ea18bbf3" + integrity sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q== + +cssnano@^4.1.10: + version "4.1.10" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.1.10.tgz#0ac41f0b13d13d465487e111b778d42da631b8b2" + integrity sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ== + dependencies: + cosmiconfig "^5.0.0" + cssnano-preset-default "^4.0.7" + is-resolvable "^1.0.0" + postcss "^7.0.0" + +csso@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/csso/-/csso-4.0.2.tgz#e5f81ab3a56b8eefb7f0092ce7279329f454de3d" + integrity sha512-kS7/oeNVXkHWxby5tHVxlhjizRCSv8QdU7hB2FpdAibDU8FjTAolhNjKNTiLzXtUrKT6HwClE81yXwEk1309wg== + dependencies: + css-tree "1.0.0-alpha.37" + +cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0", cssom@^0.3.4: + version "0.3.8" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== + +cssstyle@^1.0.0, cssstyle@^1.1.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.4.0.tgz#9d31328229d3c565c61e586b02041a28fccdccf1" + integrity sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA== + dependencies: + cssom "0.3.x" + +csstype@^2.6.7: + version "2.6.9" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.9.tgz#05141d0cd557a56b8891394c1911c40c8a98d098" + integrity sha512-xz39Sb4+OaTsULgUERcCk+TJj8ylkL4aSVDQiX/ksxbELSqwkgt4d4RD7fovIdgJGSuNYqwZEiVjYY5l0ask+Q== + +currently-unhandled@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + integrity sha1-mI3zP+qxke95mmE2nddsF635V+o= + dependencies: + array-find-index "^1.0.1" + +cyclist@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" + integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= + +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + +damerau-levenshtein@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz#143c1641cb3d85c60c32329e26899adea8701791" + integrity sha512-JVrozIeElnj3QzfUIt8tB8YMluBJom4Vw9qTPpjGYQ9fYlB3D/rb6OordUxf3xeFB35LKWs0xqcO5U6ySvBtug== + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +data-urls@^1.0.0, data-urls@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe" + integrity sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ== + dependencies: + abab "^2.0.0" + whatwg-mimetype "^2.2.0" + whatwg-url "^7.0.0" + +debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@=3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + +debug@^3.0.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + +debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + +decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + +deep-equal@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" + integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== + dependencies: + is-arguments "^1.0.4" + is-date-object "^1.0.1" + is-regex "^1.0.4" + object-is "^1.0.1" + object-keys "^1.1.1" + regexp.prototype.flags "^1.2.0" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + +default-gateway@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" + integrity sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA== + dependencies: + execa "^1.0.0" + ip-regex "^2.1.0" + +define-properties@^1.1.2, define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +del@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/del/-/del-4.1.1.tgz#9e8f117222ea44a31ff3a156c049b99052a9f0b4" + integrity sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ== + dependencies: + "@types/glob" "^7.1.1" + globby "^6.1.0" + is-path-cwd "^2.0.0" + is-path-in-cwd "^2.0.0" + p-map "^2.0.0" + pify "^4.0.1" + rimraf "^2.6.3" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +des.js@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" + integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + +detect-libc@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= + +detect-newline@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" + integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I= + +detect-node@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c" + integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw== + +detect-port-alt@1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/detect-port-alt/-/detect-port-alt-1.1.6.tgz#24707deabe932d4a3cf621302027c2b266568275" + integrity sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q== + dependencies: + address "^1.0.1" + debug "^2.6.0" + +diff-sequences@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5" + integrity sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew== + +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +dir-glob@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.0.0.tgz#0b205d2b6aef98238ca286598a8204d29d0a0034" + integrity sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag== + dependencies: + arrify "^1.0.1" + path-type "^3.0.0" + +dir-glob@^2.0.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" + integrity sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw== + dependencies: + path-type "^3.0.0" + +dns-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" + integrity sha1-s55/HabrCnW6nBcySzR1PEfgZU0= + +dns-packet@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.1.tgz#12aa426981075be500b910eedcd0b47dd7deda5a" + integrity sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg== + dependencies: + ip "^1.1.0" + safe-buffer "^5.0.1" + +dns-txt@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6" + integrity sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY= + dependencies: + buffer-indexof "^1.0.0" + +doctrine@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" + integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo= + dependencies: + esutils "^2.0.2" + isarray "^1.0.0" + +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dom-converter@^0.2: + version "0.2.0" + resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" + integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA== + dependencies: + utila "~0.4" + +dom-css@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/dom-css/-/dom-css-2.1.0.tgz#fdbc2d5a015d0a3e1872e11472bbd0e7b9e6a202" + integrity sha1-/bwtWgFdCj4YcuEUcrvQ57nmogI= + dependencies: + add-px-to-style "1.0.0" + prefix-style "2.0.1" + to-camel-case "1.0.0" + +dom-helpers@^5.0.1: + version "5.1.3" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.1.3.tgz#7233248eb3a2d1f74aafca31e52c5299cc8ce821" + integrity sha512-nZD1OtwfWGRBWlpANxacBEZrEuLa16o1nh7YopFWeoF68Zt8GGEmzHu6Xv4F3XaFIC+YXtTLrzgqKxFgLEe4jw== + dependencies: + "@babel/runtime" "^7.6.3" + csstype "^2.6.7" + +dom-serializer@0, dom-serializer@^0.2.1: + version "0.2.2" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" + integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== + dependencies: + domelementtype "^2.0.1" + entities "^2.0.0" + +domain-browser@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" + integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== + +domelementtype@1, domelementtype@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" + integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== + +domelementtype@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.1.tgz#1f8bdfe91f5a78063274e803b4bdcedf6e94f94d" + integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ== + +domexception@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" + integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug== + dependencies: + webidl-conversions "^4.0.2" + +domhandler@^2.3.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" + integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA== + dependencies: + domelementtype "1" + +domhandler@^3.0, domhandler@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-3.0.0.tgz#51cd13efca31da95bbb0c5bee3a48300e333b3e9" + integrity sha512-eKLdI5v9m67kbXQbJSNn1zjh0SDzvzWVWtX+qEI3eMjZw8daH9k8rlj1FZY9memPwjiskQFbe7vHVVJIAqoEhw== + dependencies: + domelementtype "^2.0.1" + +domutils@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" + integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8= + dependencies: + dom-serializer "0" + domelementtype "1" + +domutils@^1.5.1, domutils@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" + integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== + dependencies: + dom-serializer "0" + domelementtype "1" + +domutils@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.0.0.tgz#15b8278e37bfa8468d157478c58c367718133c08" + integrity sha512-n5SelJ1axbO636c2yUtOGia/IcJtVtlhQbFiVDBZHKV5ReJO1ViX7sFEemtuyoAnBxk5meNSYgA8V4s0271efg== + dependencies: + dom-serializer "^0.2.1" + domelementtype "^2.0.1" + domhandler "^3.0.0" + +dot-case@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.3.tgz#21d3b52efaaba2ea5fda875bb1aa8124521cf4aa" + integrity sha512-7hwEmg6RiSQfm/GwPL4AAWXKy3YNNZA3oFv2Pdiey0mwkRCPZ9x6SZbkLcn8Ma5PYeVokzoD4Twv2n7LKp5WeA== + dependencies: + no-case "^3.0.3" + tslib "^1.10.0" + +dot-prop@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.2.0.tgz#c34ecc29556dc45f1f4c22697b6f4904e0cc4fcb" + integrity sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A== + dependencies: + is-obj "^2.0.0" + +dotenv-expand@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0" + integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA== + +dotenv@8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" + integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== + +duplexer@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" + integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E= + +duplexify@^3.4.2, duplexify@^3.6.0: + version "3.7.1" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" + integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== + dependencies: + end-of-stream "^1.0.0" + inherits "^2.0.1" + readable-stream "^2.0.0" + stream-shift "^1.0.0" + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + +electron-to-chromium@^1.3.341, electron-to-chromium@^1.3.349: + version "1.3.360" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.360.tgz#1db9cb8d43f4c772546d94ea9be8b677a8ecb483" + integrity sha512-RE1pv2sjQiDRRN1nI0fJ0eQHZ9le4oobu16OArnwEUV5ycAU5SNjFyvzjZ1gPUAqBa2Ud1XagtW8j3ZXfHuQHA== + +elliptic@^6.0.0: + version "6.5.2" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.2.tgz#05c5678d7173c049d8ca433552224a495d0e3762" + integrity sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw== + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + +email-addresses@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/email-addresses/-/email-addresses-3.1.0.tgz#cabf7e085cbdb63008a70319a74e6136188812fb" + integrity sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg== + +emoji-regex@^7.0.1, emoji-regex@^7.0.2: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emojis-list@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" + integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k= + +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + +end-of-stream@^1.0.0, end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +enhanced-resolve@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz#2937e2b8066cd0fe7ce0990a98f0d71a35189f66" + integrity sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA== + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.5.0" + tapable "^1.0.0" + +entities@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" + integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== + +entities@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4" + integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw== + +errno@^0.1.3, errno@~0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" + integrity sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg== + dependencies: + prr "~1.0.1" + +error-ex@^1.2.0, error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2: + version "1.17.4" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.4.tgz#e3aedf19706b20e7c2594c35fc0d57605a79e184" + integrity sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ== + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.1.5" + is-regex "^1.0.5" + object-inspect "^1.7.0" + object-keys "^1.1.1" + object.assign "^4.1.0" + string.prototype.trimleft "^2.1.1" + string.prototype.trimright "^2.1.1" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es5-ext@^0.10.35, es5-ext@^0.10.50: + version "0.10.53" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" + integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.3" + next-tick "~1.0.0" + +es6-iterator@2.0.3, es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-symbol@^3.1.1, es6-symbol@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + +escape-string-regexp@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +escodegen@^1.11.0, escodegen@^1.9.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.1.tgz#ba01d0c8278b5e95a9a45350142026659027a457" + integrity sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ== + dependencies: + esprima "^4.0.1" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +eslint-config-react-app@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/eslint-config-react-app/-/eslint-config-react-app-5.2.0.tgz#135110ba56a9e378f7acfe5f36e2ae76a2317899" + integrity sha512-WrHjoGpKr1kLLiWDD81tme9jMM0hk5cMxasLSdyno6DdPt+IfLOrDJBVo6jN7tn4y1nzhs43TmUaZWO6Sf0blw== + dependencies: + confusing-browser-globals "^1.0.9" + +eslint-import-resolver-node@^0.3.2: + version "0.3.3" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz#dbaa52b6b2816b50bc6711af75422de808e98404" + integrity sha512-b8crLDo0M5RSe5YG8Pu2DYBj71tSB6OvXkfzwbJU2w7y8P4/yo0MyF8jU26IEuEuHF2K5/gcAJE3LhQGqBBbVg== + dependencies: + debug "^2.6.9" + resolve "^1.13.1" + +eslint-loader@3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/eslint-loader/-/eslint-loader-3.0.3.tgz#e018e3d2722381d982b1201adb56819c73b480ca" + integrity sha512-+YRqB95PnNvxNp1HEjQmvf9KNvCin5HXYYseOXVC2U0KEcw4IkQ2IQEBG46j7+gW39bMzeu0GsUhVbBY3Votpw== + dependencies: + fs-extra "^8.1.0" + loader-fs-cache "^1.0.2" + loader-utils "^1.2.3" + object-hash "^2.0.1" + schema-utils "^2.6.1" + +eslint-module-utils@^2.4.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.5.2.tgz#7878f7504824e1b857dd2505b59a8e5eda26a708" + integrity sha512-LGScZ/JSlqGKiT8OC+cYRxseMjyqt6QO54nl281CK93unD89ijSeRV6An8Ci/2nvWVKe8K/Tqdm75RQoIOCr+Q== + dependencies: + debug "^2.6.9" + pkg-dir "^2.0.0" + +eslint-plugin-flowtype@4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-4.6.0.tgz#82b2bd6f21770e0e5deede0228e456cb35308451" + integrity sha512-W5hLjpFfZyZsXfo5anlu7HM970JBDqbEshAJUkeczP6BFCIfJXuiIBQXyberLRtOStT0OGPF8efeTbxlHk4LpQ== + dependencies: + lodash "^4.17.15" + +eslint-plugin-import@2.20.0: + version "2.20.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.20.0.tgz#d749a7263fb6c29980def8e960d380a6aa6aecaa" + integrity sha512-NK42oA0mUc8Ngn4kONOPsPB1XhbUvNHqF+g307dPV28aknPoiNnKLFd9em4nkswwepdF5ouieqv5Th/63U7YJQ== + dependencies: + array-includes "^3.0.3" + array.prototype.flat "^1.2.1" + contains-path "^0.1.0" + debug "^2.6.9" + doctrine "1.5.0" + eslint-import-resolver-node "^0.3.2" + eslint-module-utils "^2.4.1" + has "^1.0.3" + minimatch "^3.0.4" + object.values "^1.1.0" + read-pkg-up "^2.0.0" + resolve "^1.12.0" + +eslint-plugin-jsx-a11y@6.2.3: + version "6.2.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.3.tgz#b872a09d5de51af70a97db1eea7dc933043708aa" + integrity sha512-CawzfGt9w83tyuVekn0GDPU9ytYtxyxyFZ3aSWROmnRRFQFT2BiPJd7jvRdzNDi6oLWaS2asMeYSNMjWTV4eNg== + dependencies: + "@babel/runtime" "^7.4.5" + aria-query "^3.0.0" + array-includes "^3.0.3" + ast-types-flow "^0.0.7" + axobject-query "^2.0.2" + damerau-levenshtein "^1.0.4" + emoji-regex "^7.0.2" + has "^1.0.3" + jsx-ast-utils "^2.2.1" + +eslint-plugin-react-hooks@^1.6.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-1.7.0.tgz#6210b6d5a37205f0b92858f895a4e827020a7d04" + integrity sha512-iXTCFcOmlWvw4+TOE8CLWj6yX1GwzT0Y6cUfHHZqWnSk144VmVIRcVGtUAzrLES7C798lmvnt02C7rxaOX1HNA== + +eslint-plugin-react@7.18.0: + version "7.18.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.18.0.tgz#2317831284d005b30aff8afb7c4e906f13fa8e7e" + integrity sha512-p+PGoGeV4SaZRDsXqdj9OWcOrOpZn8gXoGPcIQTzo2IDMbAKhNDnME9myZWqO3Ic4R3YmwAZ1lDjWl2R2hMUVQ== + dependencies: + array-includes "^3.1.1" + doctrine "^2.1.0" + has "^1.0.3" + jsx-ast-utils "^2.2.3" + object.entries "^1.1.1" + object.fromentries "^2.0.2" + object.values "^1.1.1" + prop-types "^15.7.2" + resolve "^1.14.2" + +eslint-scope@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" + integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-scope@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" + integrity sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-utils@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" + integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" + integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== + +eslint@^6.6.0: + version "6.8.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb" + integrity sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig== + dependencies: + "@babel/code-frame" "^7.0.0" + ajv "^6.10.0" + chalk "^2.1.0" + cross-spawn "^6.0.5" + debug "^4.0.1" + doctrine "^3.0.0" + eslint-scope "^5.0.0" + eslint-utils "^1.4.3" + eslint-visitor-keys "^1.1.0" + espree "^6.1.2" + esquery "^1.0.1" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^5.0.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + inquirer "^7.0.0" + is-glob "^4.0.0" + js-yaml "^3.13.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.14" + minimatch "^3.0.4" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.3" + progress "^2.0.0" + regexpp "^2.0.1" + semver "^6.1.2" + strip-ansi "^5.2.0" + strip-json-comments "^3.0.1" + table "^5.2.3" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +espree@^6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/espree/-/espree-6.1.2.tgz#6c272650932b4f91c3714e5e7b5f5e2ecf47262d" + integrity sha512-2iUPuuPP+yW1PZaMSDM9eyVf8D5P0Hi8h83YtZ5bPc/zHYjII5khoixIUTMO794NOY8F/ThF1Bo8ncZILarUTA== + dependencies: + acorn "^7.1.0" + acorn-jsx "^5.1.0" + eslint-visitor-keys "^1.1.0" + +esprima@^4.0.0, esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.1.0.tgz#c5c0b66f383e7656404f86b31334d72524eddb48" + integrity sha512-MxYW9xKmROWF672KqjO75sszsA8Mxhw06YFeS5VHlB98KDHbOSurm3ArsjO60Eaf3QmGMCP1yn+0JQkNLo/97Q== + dependencies: + estraverse "^4.0.0" + +esrecurse@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" + integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== + dependencies: + estraverse "^4.1.0" + +estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +esutils@^2.0.0, esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +eventemitter3@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.0.tgz#d65176163887ee59f386d64c82610b696a4a74eb" + integrity sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg== + +events@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.1.0.tgz#84279af1b34cb75aa88bf5ff291f6d0bd9b31a59" + integrity sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg== + +eventsource@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.0.7.tgz#8fbc72c93fcd34088090bc0a4e64f4b5cee6d8d0" + integrity sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ== + dependencies: + original "^1.0.0" + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +exec-sh@^0.3.2: + version "0.3.4" + resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.4.tgz#3a018ceb526cc6f6df2bb504b2bfe8e3a4934ec5" + integrity sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A== + +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +exit@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +expect@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-24.9.0.tgz#b75165b4817074fa4a157794f46fe9f1ba15b6ca" + integrity sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q== + dependencies: + "@jest/types" "^24.9.0" + ansi-styles "^3.2.0" + jest-get-type "^24.9.0" + jest-matcher-utils "^24.9.0" + jest-message-util "^24.9.0" + jest-regex-util "^24.9.0" + +express@^4.17.1: + version "4.17.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" + integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== + dependencies: + accepts "~1.3.7" + array-flatten "1.1.1" + body-parser "1.19.0" + content-disposition "0.5.3" + content-type "~1.0.4" + cookie "0.4.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "~1.1.2" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.5" + qs "6.7.0" + range-parser "~1.2.1" + safe-buffer "5.1.2" + send "0.17.1" + serve-static "1.14.1" + setprototypeof "1.1.1" + statuses "~1.5.0" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +ext@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" + integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A== + dependencies: + type "^2.0.0" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +faker@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/faker/-/faker-4.1.0.tgz#1e45bbbecc6774b3c195fad2835109c6d748cc3f" + integrity sha1-HkW7vsxndLPBlfrSg1EJxtdIzD8= + +fast-deep-equal@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" + integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== + +fast-glob@^2.0.2: + version "2.2.7" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" + integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw== + dependencies: + "@mrmlnc/readdir-enhanced" "^2.2.1" + "@nodelib/fs.stat" "^1.1.2" + glob-parent "^3.1.0" + is-glob "^4.0.0" + merge2 "^1.2.3" + micromatch "^3.1.10" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + +faye-websocket@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" + integrity sha1-TkkvjQTftviQA1B/btvy1QHnxvQ= + dependencies: + websocket-driver ">=0.5.1" + +faye-websocket@~0.11.1: + version "0.11.3" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.3.tgz#5c0e9a8968e8912c286639fde977a8b209f2508e" + integrity sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA== + dependencies: + websocket-driver ">=0.5.1" + +fb-watchman@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" + integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg== + dependencies: + bser "2.1.1" + +figgy-pudding@^3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" + integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w== + +figures@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== + dependencies: + flat-cache "^2.0.1" + +file-loader@4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-4.3.0.tgz#780f040f729b3d18019f20605f723e844b8a58af" + integrity sha512-aKrYPYjF1yG3oX0kWRrqrSMfgftm7oJW5M+m4owoldH5C51C0RkIwB++JbRvEW3IU6/ZG5n8UvEcdgwOt2UOWA== + dependencies: + loader-utils "^1.2.3" + schema-utils "^2.5.0" + +file-selector@^0.1.12: + version "0.1.12" + resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.1.12.tgz#fe726547be219a787a9dcc640575a04a032b1fd0" + integrity sha512-Kx7RTzxyQipHuiqyZGf+Nz4vY9R1XGxuQl/hLoJwq+J4avk/9wxxgZyHKtbyIPJmbD4A66DWGYfyykWNpcYutQ== + dependencies: + tslib "^1.9.0" + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +filesize@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/filesize/-/filesize-6.0.1.tgz#f850b509909c7c86f7e450ea19006c31c2ed3d2f" + integrity sha512-u4AYWPgbI5GBhs6id1KdImZWn5yfyFrrQ8OWZdN7ZMfA8Bf4HcO0BGo9bmUIEV8yrp8I1xVfJ/dn90GtFNNJcg== + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +find-cache-dir@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-0.1.1.tgz#c8defae57c8a52a8a784f9e31c57c742e993a0b9" + integrity sha1-yN765XyKUqinhPnjHFfHQumToLk= + dependencies: + commondir "^1.0.1" + mkdirp "^0.5.1" + pkg-dir "^1.0.0" + +find-cache-dir@^2.0.0, find-cache-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" + integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== + dependencies: + commondir "^1.0.1" + make-dir "^2.0.0" + pkg-dir "^3.0.0" + +find-cache-dir@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.0.tgz#4d74ed1fe9ef1731467ca24378e8f8f5c8b6ed11" + integrity sha512-PtXtQb7IrD8O+h6Cq1dbpJH5NzD8+9keN1zZ0YlpDzl1PwXEJEBj6u1Xa92t1Hwluoozd9TNKul5Hi2iqpsWwg== + dependencies: + commondir "^1.0.1" + make-dir "^3.0.2" + pkg-dir "^4.1.0" + +find-up@4.1.0, find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +find-up@^2.0.0, find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= + dependencies: + locate-path "^2.0.0" + +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flatted@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08" + integrity sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg== + +flatten@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.3.tgz#c1283ac9f27b368abc1e36d1ff7b04501a30356b" + integrity sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg== + +flush-write-stream@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" + integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== + dependencies: + inherits "^2.0.3" + readable-stream "^2.3.6" + +follow-redirects@1.5.10: + version "1.5.10" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" + integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ== + dependencies: + debug "=3.1.0" + +follow-redirects@^1.0.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.10.0.tgz#01f5263aee921c6a54fb91667f08f4155ce169eb" + integrity sha512-4eyLK6s6lH32nOvLLwlIOnr9zrL8Sm+OvW4pVTJNoXeGzYIkHVf+pADQi+OJ0E67hiuSLezPVPyBcIZO50TmmQ== + dependencies: + debug "^3.0.0" + +for-in@^0.1.3: + version "0.1.8" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1" + integrity sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE= + +for-in@^1.0.1, for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= + +for-own@^0.1.3: + version "0.1.5" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" + integrity sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4= + dependencies: + for-in "^1.0.1" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +fork-ts-checker-webpack-plugin@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-3.1.1.tgz#a1642c0d3e65f50c2cc1742e9c0a80f441f86b19" + integrity sha512-DuVkPNrM12jR41KM2e+N+styka0EgLkTnXmNcXdgOM37vtGeY+oCBK/Jx0hzSeEU6memFCtWb4htrHPMDfwwUQ== + dependencies: + babel-code-frame "^6.22.0" + chalk "^2.4.1" + chokidar "^3.3.0" + micromatch "^3.1.10" + minimatch "^3.0.4" + semver "^5.6.0" + tapable "^1.0.0" + worker-rpc "^0.1.0" + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +forwarded@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= + dependencies: + map-cache "^0.2.2" + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + +from2@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" + integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8= + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.0" + +fs-extra@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" + integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-extra@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" + integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-minipass@^1.2.5: + version "1.2.7" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== + dependencies: + minipass "^2.6.0" + +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + +fs-write-stream-atomic@^1.0.8: + version "1.0.10" + resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" + integrity sha1-tH31NJPvkR33VzHnCp3tAYnbQMk= + dependencies: + graceful-fs "^4.1.2" + iferr "^0.1.5" + imurmurhash "^0.1.4" + readable-stream "1 || 2" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +fsevents@2.1.2, fsevents@~2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.2.tgz#4c0a1fb34bc68e543b4b82a9ec392bfbda840805" + integrity sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA== + +fsevents@^1.2.7: + version "1.2.11" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.11.tgz#67bf57f4758f02ede88fb2a1712fef4d15358be3" + integrity sha512-+ux3lx6peh0BpvY0JebGyZoiR4D+oYzdPZMKJwkZ+sFkNJzpL7tXc/wehS49gUAxg3tmMHPHZkA8JU2rhhgDHw== + dependencies: + bindings "^1.5.0" + nan "^2.12.1" + +fstream@^1.0.0, fstream@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" + integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg== + dependencies: + graceful-fs "^4.1.2" + inherits "~2.0.0" + mkdirp ">=0.5 0" + rimraf "2" + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +gaze@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a" + integrity sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g== + dependencies: + globule "^1.0.0" + +gensync@^1.0.0-beta.1: + version "1.0.0-beta.1" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" + integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== + +get-caller-file@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" + integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== + +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-own-enumerable-property-symbols@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" + integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== + +get-stdin@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= + +get-stream@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +glob-parent@^5.0.0, glob-parent@~5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2" + integrity sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw== + dependencies: + is-glob "^4.0.1" + +glob-to-regexp@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" + integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= + +glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.1: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global-modules@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" + integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== + dependencies: + global-prefix "^3.0.0" + +global-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" + integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== + dependencies: + ini "^1.3.5" + kind-of "^6.0.2" + which "^1.3.1" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^12.1.0: + version "12.3.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-12.3.0.tgz#1e564ee5c4dded2ab098b0f88f24702a3c56be13" + integrity sha512-wAfjdLgFsPZsklLJvOBUBmzYE8/CwhEqSBEMRXA3qxIiNtyqvjYurAtIfDh6chlEPUfmTY3MnZh5Hfh4q0UlIw== + dependencies: + type-fest "^0.8.1" + +globby@8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-8.0.2.tgz#5697619ccd95c5275dbb2d6faa42087c1a941d8d" + integrity sha512-yTzMmKygLp8RUpG1Ymu2VXPSJQZjNAZPD4ywgYEaG7e4tBJeUQBO8OpXrf1RCNcEs5alsoJYPAMiIHP0cmeC7w== + dependencies: + array-union "^1.0.1" + dir-glob "2.0.0" + fast-glob "^2.0.2" + glob "^7.1.2" + ignore "^3.3.5" + pify "^3.0.0" + slash "^1.0.0" + +globby@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" + integrity sha1-9abXDoOV4hyFj7BInWTfAkJNUGw= + dependencies: + array-union "^1.0.1" + glob "^7.0.3" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +globby@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680" + integrity sha1-+yzP+UAfhgCUXfral0QMypcrhoA= + dependencies: + array-union "^1.0.1" + dir-glob "^2.0.0" + glob "^7.1.2" + ignore "^3.3.5" + pify "^3.0.0" + slash "^1.0.0" + +globule@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/globule/-/globule-1.3.1.tgz#90a25338f22b7fbeb527cee63c629aea754d33b9" + integrity sha512-OVyWOHgw29yosRHCHo7NncwR1hW5ew0W/UrvtwvjefVJeQ26q4/8r8FmPsSF1hJ93IgWkyv16pCTz6WblMzm/g== + dependencies: + glob "~7.1.1" + lodash "~4.17.12" + minimatch "~3.0.2" + +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2: + version "4.2.3" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" + integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== + +growly@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" + integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= + +gud@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0" + integrity sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw== + +gzip-size@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.1.1.tgz#cb9bee692f87c0612b232840a873904e4c135274" + integrity sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA== + dependencies: + duplexer "^0.1.1" + pify "^4.0.1" + +handle-thing@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.0.tgz#0e039695ff50c93fc288557d696f3c1dc6776754" + integrity sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ== + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" + integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== + dependencies: + ajv "^6.5.5" + har-schema "^2.0.0" + +harmony-reflect@^1.4.6: + version "1.6.1" + resolved "https://registry.yarnpkg.com/harmony-reflect/-/harmony-reflect-1.6.1.tgz#c108d4f2bb451efef7a37861fdbdae72c9bdefa9" + integrity sha512-WJTeyp0JzGtHcuMsi7rw2VwtkvLa+JyfEKJCFyfcS0+CDkjQ5lHPu7zEhFZP+PDSRrEgXa5Ah0l1MbgbE41XjA== + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= + dependencies: + ansi-regex "^2.0.0" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbols@^1.0.0, has-symbols@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" + integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has@^1.0.0, has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hash-base@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" + integrity sha1-X8hoaEfs1zSZQDMZprCj8/auSRg= + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +he@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +hex-color-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" + integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== + +history@4.10.1, history@^4.9.0: + version "4.10.1" + resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" + integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew== + dependencies: + "@babel/runtime" "^7.1.2" + loose-envify "^1.2.0" + resolve-pathname "^3.0.0" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + value-equal "^1.0.1" + +hmac-drbg@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + dependencies: + react-is "^16.7.0" + +hosted-git-info@^2.1.4: + version "2.8.6" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.6.tgz#3a6e6d0324c5371fc8c7ba7175e1e5d14578724d" + integrity sha512-Kp6rShEsCHhF5dD3EWKdkgVA8ix90oSUJ0VY4g9goxxa0+f4lx63muTftn0mlJ/+8IESGWyKnP//V2D7S4ZbIQ== + +hpack.js@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" + integrity sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI= + dependencies: + inherits "^2.0.1" + obuf "^1.0.0" + readable-stream "^2.0.1" + wbuf "^1.1.0" + +hsl-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hsl-regex/-/hsl-regex-1.0.0.tgz#d49330c789ed819e276a4c0d272dffa30b18fe6e" + integrity sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4= + +hsla-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38" + integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg= + +html-comment-regex@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7" + integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== + +html-encoding-sniffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" + integrity sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw== + dependencies: + whatwg-encoding "^1.0.1" + +html-entities@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" + integrity sha1-DfKTUfByEWNRXfueVUPl9u7VFi8= + +html-escaper@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.0.tgz#71e87f931de3fe09e56661ab9a29aadec707b491" + integrity sha512-a4u9BeERWGu/S8JiWEAQcdrg9v4QArtP9keViQjGMdff20fBdd8waotXaNmODqBe6uZ3Nafi7K/ho4gCQHV3Ig== + +html-minifier-terser@^5.0.1: + version "5.0.4" + resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-5.0.4.tgz#e8cc02748acb983bd7912ea9660bd31c0702ec32" + integrity sha512-fHwmKQ+GzhlqdxEtwrqLT7MSuheiA+rif5/dZgbz3GjoMXJzcRzy1L9NXoiiyxrnap+q5guSiv8Tz5lrh9g42g== + dependencies: + camel-case "^4.1.1" + clean-css "^4.2.3" + commander "^4.1.1" + he "^1.2.0" + param-case "^3.0.3" + relateurl "^0.2.7" + terser "^4.6.3" + +html-parse-stringify2@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/html-parse-stringify2/-/html-parse-stringify2-2.0.1.tgz#dc5670b7292ca158b7bc916c9a6735ac8872834a" + integrity sha1-3FZwtyksoVi3vJFsmmc1rIhyg0o= + dependencies: + void-elements "^2.0.1" + +html-to-react@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/html-to-react/-/html-to-react-1.4.2.tgz#7b628ab56cd63a52f2d0b79d0fa838a51f088a57" + integrity sha512-TdTfxd95sRCo6QL8admCkE7mvNNrXtGoVr1dyS+7uvc8XCqAymnf/6ckclvnVbQNUo2Nh21VPwtfEHd0khiV7g== + dependencies: + domhandler "^3.0" + htmlparser2 "^4.0" + lodash.camelcase "^4.3.0" + ramda "^0.26" + +html-webpack-plugin@4.0.0-beta.11: + version "4.0.0-beta.11" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.0.0-beta.11.tgz#3059a69144b5aecef97708196ca32f9e68677715" + integrity sha512-4Xzepf0qWxf8CGg7/WQM5qBB2Lc/NFI7MhU59eUDTkuQp3skZczH4UA1d6oQyDEIoMDgERVhRyTdtUPZ5s5HBg== + dependencies: + html-minifier-terser "^5.0.1" + loader-utils "^1.2.3" + lodash "^4.17.15" + pretty-error "^2.1.1" + tapable "^1.1.3" + util.promisify "1.0.0" + +htmlparser2@^3.3.0: + version "3.10.1" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" + integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== + dependencies: + domelementtype "^1.3.1" + domhandler "^2.3.0" + domutils "^1.5.1" + entities "^1.1.1" + inherits "^2.0.1" + readable-stream "^3.1.1" + +htmlparser2@^4.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-4.1.0.tgz#9a4ef161f2e4625ebf7dfbe6c0a2f52d18a59e78" + integrity sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q== + dependencies: + domelementtype "^2.0.1" + domhandler "^3.0.0" + domutils "^2.0.0" + entities "^2.0.0" + +http-deceiver@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" + integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc= + +http-errors@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +"http-parser-js@>=0.4.0 <0.4.11": + version "0.4.10" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.10.tgz#92c9c1374c35085f75db359ec56cc257cbb93fa4" + integrity sha1-ksnBN0w1CF912zWexWzCV8u5P6Q= + +http-proxy-middleware@0.19.1: + version "0.19.1" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a" + integrity sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q== + dependencies: + http-proxy "^1.17.0" + is-glob "^4.0.0" + lodash "^4.17.11" + micromatch "^3.1.10" + +http-proxy@^1.17.0: + version "1.18.0" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.0.tgz#dbe55f63e75a347db7f3d99974f2692a314a6a3a" + integrity sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +https-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" + integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= + +i18next-browser-languagedetector@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-4.0.1.tgz#6a0b44a93835146287130da36ce3d04a1836879f" + integrity sha512-RxSoX6mB8cab0CTIQ+klCS764vYRj+Jk621cnFVsINvcdlb/cdi3vQFyrPwmnowB7ReUadjHovgZX+RPIzHVQQ== + dependencies: + "@babel/runtime" "^7.5.5" + +i18next-xhr-backend@3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/i18next-xhr-backend/-/i18next-xhr-backend-3.2.2.tgz#769124441461b085291f539d91864e3691199178" + integrity sha512-OtRf2Vo3IqAxsttQbpjYnmMML12IMB5e0fc5B7qKJFLScitYaXa1OhMX0n0X/3vrfFlpHL9Ro/H+ps4Ej2j7QQ== + dependencies: + "@babel/runtime" "^7.5.5" + +i18next@19.0.3: + version "19.0.3" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-19.0.3.tgz#31fd3165762d9802e08a2a86932db4eff5c862e9" + integrity sha512-Ru4afr++b4cUApsIBifcMYyWG9Nx8wlFdq4DuOF+UuoPoQKfuh0iAVMekTjs6w1CZLUOVb5QZEuoYRLmu17EIA== + dependencies: + "@babel/runtime" "^7.3.1" + +iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +icss-utils@^4.0.0, icss-utils@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" + integrity sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA== + dependencies: + postcss "^7.0.14" + +identity-obj-proxy@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz#94d2bda96084453ef36fbc5aaec37e0f79f1fc14" + integrity sha1-lNK9qWCERT7zb7xarsN+D3nx/BQ= + dependencies: + harmony-reflect "^1.4.6" + +ieee754@^1.1.4: + version "1.1.13" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" + integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== + +iferr@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" + integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= + +ignore-walk@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37" + integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw== + dependencies: + minimatch "^3.0.4" + +ignore@^3.3.5: + version "3.3.10" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" + integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== + +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +immer@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/immer/-/immer-1.10.0.tgz#bad67605ba9c810275d91e1c2a47d4582e98286d" + integrity sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg== + +immutable@^3.8.1: + version "3.8.2" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3" + integrity sha1-wkOZUUVbs5kT2vKBN28VMOEErfM= + +import-cwd@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9" + integrity sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk= + dependencies: + import-from "^2.1.0" + +import-fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" + integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= + dependencies: + caller-path "^2.0.0" + resolve-from "^3.0.0" + +import-fresh@^3.0.0, import-fresh@^3.1.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" + integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-from@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-from/-/import-from-2.1.0.tgz#335db7f2a7affd53aaa471d4b8021dee36b7f3b1" + integrity sha1-M1238qev/VOqpHHUuAId7ja387E= + dependencies: + resolve-from "^3.0.0" + +import-local@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" + integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ== + dependencies: + pkg-dir "^3.0.0" + resolve-cwd "^2.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +in-publish@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.0.tgz#e20ff5e3a2afc2690320b6dc552682a9c7fadf51" + integrity sha1-4g/146KvwmkDILbcVSaCqcf631E= + +indent-string@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= + dependencies: + repeating "^2.0.0" + +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +indexes-of@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" + integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= + +infer-owner@^1.0.3, infer-owner@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" + integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +ini@^1.3.5, ini@~1.3.0: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== + +inquirer@7.0.4, inquirer@^7.0.0: + version "7.0.4" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.0.4.tgz#99af5bde47153abca23f5c7fc30db247f39da703" + integrity sha512-Bu5Td5+j11sCkqfqmUTiwv+tWisMtP0L7Q8WrqA2C/BbBhy1YTdFrvjjlrKq8oagA/tLQBski2Gcx/Sqyi2qSQ== + dependencies: + ansi-escapes "^4.2.1" + chalk "^2.4.2" + cli-cursor "^3.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.15" + mute-stream "0.0.8" + run-async "^2.2.0" + rxjs "^6.5.3" + string-width "^4.1.0" + strip-ansi "^5.1.0" + through "^2.3.6" + +internal-ip@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907" + integrity sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg== + dependencies: + default-gateway "^4.2.0" + ipaddr.js "^1.9.0" + +invariant@^2.2.2, invariant@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + +invert-kv@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" + integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= + +invert-kv@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" + integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== + +ip-regex@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" + integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= + +ip@^1.1.0, ip@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= + +ipaddr.js@1.9.1, ipaddr.js@^1.9.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-absolute-url@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" + integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY= + +is-absolute-url@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698" + integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q== + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.0.0" + +is-arguments@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3" + integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA== + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= + dependencies: + binary-extensions "^1.0.0" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-buffer@^1.0.2, is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-callable@^1.1.4, is-callable@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab" + integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q== + +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + +is-color-stop@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345" + integrity sha1-z/9HGu5N1cnhWFmPvhKWe1za00U= + dependencies: + css-color-names "^0.0.4" + hex-color-regex "^1.1.0" + hsl-regex "^1.0.0" + hsla-regex "^1.0.0" + rgb-regex "^1.0.1" + rgba-regex "^1.0.0" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +is-date-object@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" + integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-directory@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= + +is-docker@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.0.0.tgz#2cb0df0e75e2d064fe1864c37cdeacb7b2dcf25b" + integrity sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ== + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^2.1.0, is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-finite@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" + integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-generator-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" + integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= + dependencies: + is-extglob "^2.1.0" + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= + dependencies: + kind-of "^3.0.2" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= + +is-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" + integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== + +is-path-cwd@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" + integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== + +is-path-in-cwd@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz#bfe2dca26c69f397265a4009963602935a053acb" + integrity sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ== + dependencies: + is-path-inside "^2.1.0" + +is-path-inside@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-2.1.0.tgz#7c9810587d659a40d27bcdb4d5616eab059494b2" + integrity sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg== + dependencies: + path-is-inside "^1.0.2" + +is-plain-obj@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= + +is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-promise@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" + integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= + +is-regex@^1.0.4, is-regex@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae" + integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ== + dependencies: + has "^1.0.3" + +is-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" + integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk= + +is-resolvable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" + integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== + +is-root@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c" + integrity sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg== + +is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +is-string@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" + integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== + +is-svg@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-3.0.0.tgz#9321dbd29c212e5ca99c4fa9794c714bcafa2f75" + integrity sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ== + dependencies: + html-comment-regex "^1.1.0" + +is-symbol@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" + integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== + dependencies: + has-symbols "^1.0.1" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= + +is-wsl@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.1.1.tgz#4a1c152d429df3d441669498e2486d3596ebaf1d" + integrity sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog== + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +istanbul-lib-coverage@^2.0.2, istanbul-lib-coverage@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz#675f0ab69503fad4b1d849f736baaca803344f49" + integrity sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA== + +istanbul-lib-instrument@^3.0.1, istanbul-lib-instrument@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz#a5f63d91f0bbc0c3e479ef4c5de027335ec6d630" + integrity sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA== + dependencies: + "@babel/generator" "^7.4.0" + "@babel/parser" "^7.4.3" + "@babel/template" "^7.4.0" + "@babel/traverse" "^7.4.3" + "@babel/types" "^7.4.0" + istanbul-lib-coverage "^2.0.5" + semver "^6.0.0" + +istanbul-lib-report@^2.0.4: + version "2.0.8" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz#5a8113cd746d43c4889eba36ab10e7d50c9b4f33" + integrity sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ== + dependencies: + istanbul-lib-coverage "^2.0.5" + make-dir "^2.1.0" + supports-color "^6.1.0" + +istanbul-lib-source-maps@^3.0.1: + version "3.0.6" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz#284997c48211752ec486253da97e3879defba8c8" + integrity sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^2.0.5" + make-dir "^2.1.0" + rimraf "^2.6.3" + source-map "^0.6.1" + +istanbul-reports@^2.2.6: + version "2.2.7" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-2.2.7.tgz#5d939f6237d7b48393cc0959eab40cd4fd056931" + integrity sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg== + dependencies: + html-escaper "^2.0.0" + +jest-changed-files@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-24.9.0.tgz#08d8c15eb79a7fa3fc98269bc14b451ee82f8039" + integrity sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg== + dependencies: + "@jest/types" "^24.9.0" + execa "^1.0.0" + throat "^4.0.0" + +jest-cli@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-24.9.0.tgz#ad2de62d07472d419c6abc301fc432b98b10d2af" + integrity sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg== + dependencies: + "@jest/core" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + chalk "^2.0.1" + exit "^0.1.2" + import-local "^2.0.0" + is-ci "^2.0.0" + jest-config "^24.9.0" + jest-util "^24.9.0" + jest-validate "^24.9.0" + prompts "^2.0.1" + realpath-native "^1.1.0" + yargs "^13.3.0" + +jest-config@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-24.9.0.tgz#fb1bbc60c73a46af03590719efa4825e6e4dd1b5" + integrity sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ== + dependencies: + "@babel/core" "^7.1.0" + "@jest/test-sequencer" "^24.9.0" + "@jest/types" "^24.9.0" + babel-jest "^24.9.0" + chalk "^2.0.1" + glob "^7.1.1" + jest-environment-jsdom "^24.9.0" + jest-environment-node "^24.9.0" + jest-get-type "^24.9.0" + jest-jasmine2 "^24.9.0" + jest-regex-util "^24.3.0" + jest-resolve "^24.9.0" + jest-util "^24.9.0" + jest-validate "^24.9.0" + micromatch "^3.1.10" + pretty-format "^24.9.0" + realpath-native "^1.1.0" + +jest-diff@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.9.0.tgz#931b7d0d5778a1baf7452cb816e325e3724055da" + integrity sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ== + dependencies: + chalk "^2.0.1" + diff-sequences "^24.9.0" + jest-get-type "^24.9.0" + pretty-format "^24.9.0" + +jest-docblock@^24.3.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-24.9.0.tgz#7970201802ba560e1c4092cc25cbedf5af5a8ce2" + integrity sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA== + dependencies: + detect-newline "^2.1.0" + +jest-each@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-24.9.0.tgz#eb2da602e2a610898dbc5f1f6df3ba86b55f8b05" + integrity sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog== + dependencies: + "@jest/types" "^24.9.0" + chalk "^2.0.1" + jest-get-type "^24.9.0" + jest-util "^24.9.0" + pretty-format "^24.9.0" + +jest-environment-jsdom-fourteen@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom-fourteen/-/jest-environment-jsdom-fourteen-1.0.1.tgz#4cd0042f58b4ab666950d96532ecb2fc188f96fb" + integrity sha512-DojMX1sY+at5Ep+O9yME34CdidZnO3/zfPh8UW+918C5fIZET5vCjfkegixmsi7AtdYfkr4bPlIzmWnlvQkP7Q== + dependencies: + "@jest/environment" "^24.3.0" + "@jest/fake-timers" "^24.3.0" + "@jest/types" "^24.3.0" + jest-mock "^24.0.0" + jest-util "^24.0.0" + jsdom "^14.1.0" + +jest-environment-jsdom@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz#4b0806c7fc94f95edb369a69cc2778eec2b7375b" + integrity sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA== + dependencies: + "@jest/environment" "^24.9.0" + "@jest/fake-timers" "^24.9.0" + "@jest/types" "^24.9.0" + jest-mock "^24.9.0" + jest-util "^24.9.0" + jsdom "^11.5.1" + +jest-environment-node@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-24.9.0.tgz#333d2d2796f9687f2aeebf0742b519f33c1cbfd3" + integrity sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA== + dependencies: + "@jest/environment" "^24.9.0" + "@jest/fake-timers" "^24.9.0" + "@jest/types" "^24.9.0" + jest-mock "^24.9.0" + jest-util "^24.9.0" + +jest-get-type@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.9.0.tgz#1684a0c8a50f2e4901b6644ae861f579eed2ef0e" + integrity sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q== + +jest-haste-map@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-24.9.0.tgz#b38a5d64274934e21fa417ae9a9fbeb77ceaac7d" + integrity sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ== + dependencies: + "@jest/types" "^24.9.0" + anymatch "^2.0.0" + fb-watchman "^2.0.0" + graceful-fs "^4.1.15" + invariant "^2.2.4" + jest-serializer "^24.9.0" + jest-util "^24.9.0" + jest-worker "^24.9.0" + micromatch "^3.1.10" + sane "^4.0.3" + walker "^1.0.7" + optionalDependencies: + fsevents "^1.2.7" + +jest-jasmine2@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz#1f7b1bd3242c1774e62acabb3646d96afc3be6a0" + integrity sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw== + dependencies: + "@babel/traverse" "^7.1.0" + "@jest/environment" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + chalk "^2.0.1" + co "^4.6.0" + expect "^24.9.0" + is-generator-fn "^2.0.0" + jest-each "^24.9.0" + jest-matcher-utils "^24.9.0" + jest-message-util "^24.9.0" + jest-runtime "^24.9.0" + jest-snapshot "^24.9.0" + jest-util "^24.9.0" + pretty-format "^24.9.0" + throat "^4.0.0" + +jest-leak-detector@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-24.9.0.tgz#b665dea7c77100c5c4f7dfcb153b65cf07dcf96a" + integrity sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA== + dependencies: + jest-get-type "^24.9.0" + pretty-format "^24.9.0" + +jest-matcher-utils@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz#f5b3661d5e628dffe6dd65251dfdae0e87c3a073" + integrity sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA== + dependencies: + chalk "^2.0.1" + jest-diff "^24.9.0" + jest-get-type "^24.9.0" + pretty-format "^24.9.0" + +jest-message-util@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.9.0.tgz#527f54a1e380f5e202a8d1149b0ec872f43119e3" + integrity sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw== + dependencies: + "@babel/code-frame" "^7.0.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/stack-utils" "^1.0.1" + chalk "^2.0.1" + micromatch "^3.1.10" + slash "^2.0.0" + stack-utils "^1.0.1" + +jest-mock@^24.0.0, jest-mock@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-24.9.0.tgz#c22835541ee379b908673ad51087a2185c13f1c6" + integrity sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w== + dependencies: + "@jest/types" "^24.9.0" + +jest-pnp-resolver@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz#ecdae604c077a7fbc70defb6d517c3c1c898923a" + integrity sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ== + +jest-regex-util@^24.3.0, jest-regex-util@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-24.9.0.tgz#c13fb3380bde22bf6575432c493ea8fe37965636" + integrity sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA== + +jest-resolve-dependencies@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-24.9.0.tgz#ad055198959c4cfba8a4f066c673a3f0786507ab" + integrity sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g== + dependencies: + "@jest/types" "^24.9.0" + jest-regex-util "^24.3.0" + jest-snapshot "^24.9.0" + +jest-resolve@24.9.0, jest-resolve@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-24.9.0.tgz#dff04c7687af34c4dd7e524892d9cf77e5d17321" + integrity sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ== + dependencies: + "@jest/types" "^24.9.0" + browser-resolve "^1.11.3" + chalk "^2.0.1" + jest-pnp-resolver "^1.2.1" + realpath-native "^1.1.0" + +jest-runner@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-24.9.0.tgz#574fafdbd54455c2b34b4bdf4365a23857fcdf42" + integrity sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg== + dependencies: + "@jest/console" "^24.7.1" + "@jest/environment" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + chalk "^2.4.2" + exit "^0.1.2" + graceful-fs "^4.1.15" + jest-config "^24.9.0" + jest-docblock "^24.3.0" + jest-haste-map "^24.9.0" + jest-jasmine2 "^24.9.0" + jest-leak-detector "^24.9.0" + jest-message-util "^24.9.0" + jest-resolve "^24.9.0" + jest-runtime "^24.9.0" + jest-util "^24.9.0" + jest-worker "^24.6.0" + source-map-support "^0.5.6" + throat "^4.0.0" + +jest-runtime@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-24.9.0.tgz#9f14583af6a4f7314a6a9d9f0226e1a781c8e4ac" + integrity sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw== + dependencies: + "@jest/console" "^24.7.1" + "@jest/environment" "^24.9.0" + "@jest/source-map" "^24.3.0" + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/yargs" "^13.0.0" + chalk "^2.0.1" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.1.15" + jest-config "^24.9.0" + jest-haste-map "^24.9.0" + jest-message-util "^24.9.0" + jest-mock "^24.9.0" + jest-regex-util "^24.3.0" + jest-resolve "^24.9.0" + jest-snapshot "^24.9.0" + jest-util "^24.9.0" + jest-validate "^24.9.0" + realpath-native "^1.1.0" + slash "^2.0.0" + strip-bom "^3.0.0" + yargs "^13.3.0" + +jest-serializer@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-24.9.0.tgz#e6d7d7ef96d31e8b9079a714754c5d5c58288e73" + integrity sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ== + +jest-snapshot@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-24.9.0.tgz#ec8e9ca4f2ec0c5c87ae8f925cf97497b0e951ba" + integrity sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew== + dependencies: + "@babel/types" "^7.0.0" + "@jest/types" "^24.9.0" + chalk "^2.0.1" + expect "^24.9.0" + jest-diff "^24.9.0" + jest-get-type "^24.9.0" + jest-matcher-utils "^24.9.0" + jest-message-util "^24.9.0" + jest-resolve "^24.9.0" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + pretty-format "^24.9.0" + semver "^6.2.0" + +jest-util@^24.0.0, jest-util@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-24.9.0.tgz#7396814e48536d2e85a37de3e4c431d7cb140162" + integrity sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg== + dependencies: + "@jest/console" "^24.9.0" + "@jest/fake-timers" "^24.9.0" + "@jest/source-map" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + callsites "^3.0.0" + chalk "^2.0.1" + graceful-fs "^4.1.15" + is-ci "^2.0.0" + mkdirp "^0.5.1" + slash "^2.0.0" + source-map "^0.6.0" + +jest-validate@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-24.9.0.tgz#0775c55360d173cd854e40180756d4ff52def8ab" + integrity sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ== + dependencies: + "@jest/types" "^24.9.0" + camelcase "^5.3.1" + chalk "^2.0.1" + jest-get-type "^24.9.0" + leven "^3.1.0" + pretty-format "^24.9.0" + +jest-watch-typeahead@0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/jest-watch-typeahead/-/jest-watch-typeahead-0.4.2.tgz#e5be959698a7fa2302229a5082c488c3c8780a4a" + integrity sha512-f7VpLebTdaXs81rg/oj4Vg/ObZy2QtGzAmGLNsqUS5G5KtSN68tFcIsbvNODfNyQxU78g7D8x77o3bgfBTR+2Q== + dependencies: + ansi-escapes "^4.2.1" + chalk "^2.4.1" + jest-regex-util "^24.9.0" + jest-watcher "^24.3.0" + slash "^3.0.0" + string-length "^3.1.0" + strip-ansi "^5.0.0" + +jest-watcher@^24.3.0, jest-watcher@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-24.9.0.tgz#4b56e5d1ceff005f5b88e528dc9afc8dd4ed2b3b" + integrity sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw== + dependencies: + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/yargs" "^13.0.0" + ansi-escapes "^3.0.0" + chalk "^2.0.1" + jest-util "^24.9.0" + string-length "^2.0.0" + +jest-worker@^24.6.0, jest-worker@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.9.0.tgz#5dbfdb5b2d322e98567898238a9697bcce67b3e5" + integrity sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw== + dependencies: + merge-stream "^2.0.0" + supports-color "^6.1.0" + +jest-worker@^25.1.0: + version "25.1.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-25.1.0.tgz#75d038bad6fdf58eba0d2ec1835856c497e3907a" + integrity sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg== + dependencies: + merge-stream "^2.0.0" + supports-color "^7.0.0" + +jest@24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-24.9.0.tgz#987d290c05a08b52c56188c1002e368edb007171" + integrity sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw== + dependencies: + import-local "^2.0.0" + jest-cli "^24.9.0" + +js-base64@^2.1.8: + version "2.5.2" + resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.5.2.tgz#313b6274dda718f714d00b3330bbae6e38e90209" + integrity sha512-Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ== + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-tokens@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= + +js-yaml@^3.13.1: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +jsdom@^11.5.1: + version "11.12.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.12.0.tgz#1a80d40ddd378a1de59656e9e6dc5a3ba8657bc8" + integrity sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw== + dependencies: + abab "^2.0.0" + acorn "^5.5.3" + acorn-globals "^4.1.0" + array-equal "^1.0.0" + cssom ">= 0.3.2 < 0.4.0" + cssstyle "^1.0.0" + data-urls "^1.0.0" + domexception "^1.0.1" + escodegen "^1.9.1" + html-encoding-sniffer "^1.0.2" + left-pad "^1.3.0" + nwsapi "^2.0.7" + parse5 "4.0.0" + pn "^1.1.0" + request "^2.87.0" + request-promise-native "^1.0.5" + sax "^1.2.4" + symbol-tree "^3.2.2" + tough-cookie "^2.3.4" + w3c-hr-time "^1.0.1" + webidl-conversions "^4.0.2" + whatwg-encoding "^1.0.3" + whatwg-mimetype "^2.1.0" + whatwg-url "^6.4.1" + ws "^5.2.0" + xml-name-validator "^3.0.0" + +jsdom@^14.1.0: + version "14.1.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-14.1.0.tgz#916463b6094956b0a6c1782c94e380cd30e1981b" + integrity sha512-O901mfJSuTdwU2w3Sn+74T+RnDVP+FuV5fH8tcPWyqrseRAb0s5xOtPgCFiPOtLcyK7CLIJwPyD83ZqQWvA5ng== + dependencies: + abab "^2.0.0" + acorn "^6.0.4" + acorn-globals "^4.3.0" + array-equal "^1.0.0" + cssom "^0.3.4" + cssstyle "^1.1.1" + data-urls "^1.1.0" + domexception "^1.0.1" + escodegen "^1.11.0" + html-encoding-sniffer "^1.0.2" + nwsapi "^2.1.3" + parse5 "5.1.0" + pn "^1.1.0" + request "^2.88.0" + request-promise-native "^1.0.5" + saxes "^3.1.9" + symbol-tree "^3.2.2" + tough-cookie "^2.5.0" + w3c-hr-time "^1.0.1" + w3c-xmlserializer "^1.1.2" + webidl-conversions "^4.0.2" + whatwg-encoding "^1.0.5" + whatwg-mimetype "^2.3.0" + whatwg-url "^7.0.0" + ws "^6.1.2" + xml-name-validator "^3.0.0" + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= + +json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + +json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= + dependencies: + jsonify "~0.0.0" + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +json3@^3.3.2: + version "3.3.3" + resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" + integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA== + +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + dependencies: + minimist "^1.2.0" + +json5@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.1.tgz#81b6cb04e9ba496f1c7005d07b4368a2638f90b6" + integrity sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ== + dependencies: + minimist "^1.2.0" + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +jsx-ast-utils@^2.2.1, jsx-ast-utils@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.2.3.tgz#8a9364e402448a3ce7f14d357738310d9248054f" + integrity sha512-EdIHFMm+1BPynpKOpdPqiOsvnIrInRGJD7bzPZdPkjitQEqpdpUuFpq4T0npZFKTiB3RhWFdGN+oqOJIdhDhQA== + dependencies: + array-includes "^3.0.3" + object.assign "^4.1.0" + +killable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" + integrity sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg== + +kind-of@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-2.0.1.tgz#018ec7a4ce7e3a86cb9141be519d24c8faa981b5" + integrity sha1-AY7HpM5+OobLkUG+UZ0kyPqpgbU= + dependencies: + is-buffer "^1.0.2" + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +last-call-webpack-plugin@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/last-call-webpack-plugin/-/last-call-webpack-plugin-3.0.0.tgz#9742df0e10e3cf46e5c0381c2de90d3a7a2d7555" + integrity sha512-7KI2l2GIZa9p2spzPIVZBYyNKkN+e/SQPpnjlTiPhdbDW3F86tdKKELxKpzJ5sgU19wQWsACULZmpTPYHeWO5w== + dependencies: + lodash "^4.17.5" + webpack-sources "^1.1.0" + +lazy-cache@^0.2.3: + version "0.2.7" + resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-0.2.7.tgz#7feddf2dcb6edb77d11ef1d117ab5ffdf0ab1b65" + integrity sha1-f+3fLctu23fRHvHRF6tf/fCrG2U= + +lazy-cache@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" + integrity sha1-odePw6UEdMuAhF07O24dpJpEbo4= + +lcid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" + integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU= + dependencies: + invert-kv "^1.0.0" + +lcid@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" + integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA== + dependencies: + invert-kv "^2.0.0" + +left-pad@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e" + integrity sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA== + +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +levenary@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/levenary/-/levenary-1.1.1.tgz#842a9ee98d2075aa7faeedbe32679e9205f46f77" + integrity sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ== + dependencies: + leven "^3.1.0" + +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +line-height@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/line-height/-/line-height-0.3.1.tgz#4b1205edde182872a5efa3c8f620b3187a9c54c9" + integrity sha1-SxIF7d4YKHKl76PI9iCzGHqcVMk= + dependencies: + computed-style "~0.1.3" + +lines-and-columns@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" + integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= + +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +load-json-file@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" + integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + strip-bom "^3.0.0" + +load-json-file@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" + integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= + dependencies: + graceful-fs "^4.1.2" + parse-json "^4.0.0" + pify "^3.0.0" + strip-bom "^3.0.0" + +loader-fs-cache@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/loader-fs-cache/-/loader-fs-cache-1.0.2.tgz#54cedf6b727e1779fd8f01205f05f6e88706f086" + integrity sha512-70IzT/0/L+M20jUlEqZhZyArTU6VKLRTYRDAYN26g4jfzpJqjipLL3/hgYpySqI9PwsVRHHFja0LfEmsx9X2Cw== + dependencies: + find-cache-dir "^0.1.1" + mkdirp "0.5.1" + +loader-runner@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" + integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== + +loader-utils@1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" + integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA== + dependencies: + big.js "^5.2.2" + emojis-list "^2.0.0" + json5 "^1.0.1" + +loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" + integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^1.0.1" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +lodash-es@4.17.15: + version "4.17.15" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78" + integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ== + +lodash._reinterpolate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" + integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= + +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= + +lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= + +lodash.sortby@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= + +lodash.template@^4.4.0, lodash.template@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" + integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== + dependencies: + lodash._reinterpolate "^3.0.0" + lodash.templatesettings "^4.0.0" + +lodash.templatesettings@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" + integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== + dependencies: + lodash._reinterpolate "^3.0.0" + +lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= + +lodash@4.17.15, "lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.5, lodash@~4.17.12: + version "4.17.15" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" + integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== + +loglevel@^1.6.6: + version "1.6.7" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.7.tgz#b3e034233188c68b889f5b862415306f565e2c56" + integrity sha512-cY2eLFrQSAfVPhCgH1s7JI73tMbg9YC3v3+ZHVW67sBS7UxWzNEk/ZBbSfLykBWHp33dqqtOv82gjhKEi81T/A== + +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +loud-rejection@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8= + dependencies: + currently-unhandled "^0.4.1" + signal-exit "^3.0.0" + +lower-case@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.1.tgz#39eeb36e396115cc05e29422eaea9e692c9408c7" + integrity sha512-LiWgfDLLb1dwbFQZsSglpRj+1ctGnayXz3Uv0/WO8n558JycT5fg6zkNcnW0G68Nn0aEldTFeEfmjCfmqry/rQ== + dependencies: + tslib "^1.10.0" + +lru-cache@^4.0.1: + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +make-dir@^2.0.0, make-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== + dependencies: + pify "^4.0.1" + semver "^5.6.0" + +make-dir@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.0.2.tgz#04a1acbf22221e1d6ef43559f43e05a90dbb4392" + integrity sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w== + dependencies: + semver "^6.0.0" + +makeerror@1.0.x: + version "1.0.11" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" + integrity sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw= + dependencies: + tmpl "1.0.x" + +mamacro@^0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/mamacro/-/mamacro-0.0.3.tgz#ad2c9576197c9f1abf308d0787865bd975a3f3e4" + integrity sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA== + +map-age-cleaner@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" + integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== + dependencies: + p-defer "^1.0.0" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= + +map-obj@^1.0.0, map-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= + dependencies: + object-visit "^1.0.0" + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +mdn-data@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" + integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + +mem@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178" + integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w== + dependencies: + map-age-cleaner "^0.1.1" + mimic-fn "^2.0.0" + p-is-promise "^2.0.0" + +"memoize-one@>=3.1.1 <6": + version "5.1.1" + resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.1.1.tgz#047b6e3199b508eaec03504de71229b8eb1d75c0" + integrity sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA== + +memory-fs@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" + integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI= + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +memory-fs@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.5.0.tgz#324c01288b88652966d161db77838720845a8e3c" + integrity sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA== + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +meow@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" + integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= + dependencies: + camelcase-keys "^2.0.0" + decamelize "^1.1.2" + loud-rejection "^1.0.0" + map-obj "^1.0.1" + minimist "^1.1.3" + normalize-package-data "^2.3.4" + object-assign "^4.0.1" + read-pkg-up "^1.0.1" + redent "^1.0.0" + trim-newlines "^1.0.0" + +merge-deep@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/merge-deep/-/merge-deep-3.0.2.tgz#f39fa100a4f1bd34ff29f7d2bf4508fbb8d83ad2" + integrity sha512-T7qC8kg4Zoti1cFd8Cr0M+qaZfOwjlPDEdZIIPPB2JZctjaPM4fX+i7HOId69tAti2fvO6X5ldfYUONDODsrkA== + dependencies: + arr-union "^3.1.0" + clone-deep "^0.2.4" + kind-of "^3.0.2" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.2.3: + version "1.3.0" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.3.0.tgz#5b366ee83b2f1582c48f87e47cf1a9352103ca81" + integrity sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw== + +merge@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.1.tgz#38bebf80c3220a8a487b6fcfb3941bb11720c145" + integrity sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + +microevent.ts@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/microevent.ts/-/microevent.ts-0.1.1.tgz#70b09b83f43df5172d0205a63025bce0f7357fa0" + integrity sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g== + +micromatch@^3.1.10, micromatch@^3.1.4: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mime-db@1.43.0, "mime-db@>= 1.43.0 < 2": + version "1.43.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58" + integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ== + +mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24: + version "2.1.26" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06" + integrity sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ== + dependencies: + mime-db "1.43.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mime@^2.4.4: + version "2.4.4" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5" + integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA== + +mimic-fn@^2.0.0, mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mini-create-react-context@^0.3.0: + version "0.3.2" + resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.3.2.tgz#79fc598f283dd623da8e088b05db8cddab250189" + integrity sha512-2v+OeetEyliMt5VHMXsBhABoJ0/M4RCe7fatd/fBy6SMiKazUSEt3gxxypfnk2SHMkdBYvorHRoQxuGoiwbzAw== + dependencies: + "@babel/runtime" "^7.4.0" + gud "^1.0.0" + tiny-warning "^1.0.2" + +mini-css-extract-plugin@0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz#47f2cf07aa165ab35733b1fc97d4c46c0564339e" + integrity sha512-lp3GeY7ygcgAmVIcRPBVhIkf8Us7FZjA+ILpal44qLdSu11wmjKQ3d9k15lfD7pO4esu9eUIAW7qiYIBppv40A== + dependencies: + loader-utils "^1.1.0" + normalize-url "1.9.1" + schema-utils "^1.0.0" + webpack-sources "^1.1.0" + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= + +minimatch@3.0.4, minimatch@^3.0.4, minimatch@~3.0.2: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= + +minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= + +minipass-collect@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" + integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== + dependencies: + minipass "^3.0.0" + +minipass-flush@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" + integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== + dependencies: + minipass "^3.0.0" + +minipass-pipeline@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.2.tgz#3dcb6bb4a546e32969c7ad710f2c79a86abba93a" + integrity sha512-3JS5A2DKhD2g0Gg8x3yamO0pj7YeKGwVlDS90pF++kxptwx/F+B//roxf9SqYil5tQo65bijy+dAuAFZmYOouA== + dependencies: + minipass "^3.0.0" + +minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minipass@^3.0.0, minipass@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.1.tgz#7607ce778472a185ad6d89082aa2070f79cedcd5" + integrity sha512-UFqVihv6PQgwj8/yTGvl9kPz7xIAY+R5z6XYjRInD3Gk3qx6QGSD6zEcpeG4Dy/lQnv1J6zv8ejV90hyYIKf3w== + dependencies: + yallist "^4.0.0" + +minizlib@^1.2.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== + dependencies: + minipass "^2.9.0" + +mississippi@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" + integrity sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA== + dependencies: + concat-stream "^1.5.0" + duplexify "^3.4.2" + end-of-stream "^1.1.0" + flush-write-stream "^1.0.0" + from2 "^2.1.0" + parallel-transform "^1.1.0" + pump "^3.0.0" + pumpify "^1.3.3" + stream-each "^1.1.0" + through2 "^2.0.0" + +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mixin-object@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/mixin-object/-/mixin-object-2.0.1.tgz#4fb949441dab182540f1fe035ba60e1947a5e57e" + integrity sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4= + dependencies: + for-in "^0.1.3" + is-extendable "^0.1.1" + +mkdirp@0.5.1, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= + dependencies: + minimist "0.0.8" + +moment@^2.24.0: + version "2.24.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" + integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== + +move-concurrently@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" + integrity sha1-viwAX9oy4LKa8fBdfEszIUxwH5I= + dependencies: + aproba "^1.1.1" + copy-concurrently "^1.0.0" + fs-write-stream-atomic "^1.0.8" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.3" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +multicast-dns-service-types@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" + integrity sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE= + +multicast-dns@^6.0.1: + version "6.2.3" + resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-6.2.3.tgz#a0ec7bd9055c4282f790c3c82f4e28db3b31b229" + integrity sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g== + dependencies: + dns-packet "^1.3.1" + thunky "^1.0.2" + +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + +nan@^2.12.1, nan@^2.13.2: + version "2.14.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" + integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + +needle@^2.2.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.3.2.tgz#3342dea100b7160960a450dc8c22160ac712a528" + integrity sha512-DUzITvPVDUy6vczKKYTnWc/pBZ0EnjMJnQ3y+Jo5zfKFimJs7S3HFCxCRZYB9FUZcrzUQr3WsmvZgddMEIZv6w== + dependencies: + debug "^3.2.6" + iconv-lite "^0.4.4" + sax "^1.2.4" + +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + +neo-async@^2.5.0, neo-async@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" + integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== + +next-tick@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +no-case@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.3.tgz#c21b434c1ffe48b39087e86cfb4d2582e9df18f8" + integrity sha512-ehY/mVQCf9BL0gKfsJBvFJen+1V//U+0HQMPrWct40ixE4jnv0bfvxDbWtAHL9EcaPEOJHVVYKoQn1TlZUB8Tw== + dependencies: + lower-case "^2.0.1" + tslib "^1.10.0" + +node-forge@0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579" + integrity sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ== + +node-gyp@^3.8.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.8.0.tgz#540304261c330e80d0d5edce253a68cb3964218c" + integrity sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA== + dependencies: + fstream "^1.0.0" + glob "^7.0.3" + graceful-fs "^4.1.2" + mkdirp "^0.5.0" + nopt "2 || 3" + npmlog "0 || 1 || 2 || 3 || 4" + osenv "0" + request "^2.87.0" + rimraf "2" + semver "~5.3.0" + tar "^2.0.0" + which "1" + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= + +node-libs-browser@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" + integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== + dependencies: + assert "^1.1.1" + browserify-zlib "^0.2.0" + buffer "^4.3.0" + console-browserify "^1.1.0" + constants-browserify "^1.0.0" + crypto-browserify "^3.11.0" + domain-browser "^1.1.1" + events "^3.0.0" + https-browserify "^1.0.0" + os-browserify "^0.3.0" + path-browserify "0.0.1" + process "^0.11.10" + punycode "^1.2.4" + querystring-es3 "^0.2.0" + readable-stream "^2.3.3" + stream-browserify "^2.0.1" + stream-http "^2.7.2" + string_decoder "^1.0.0" + timers-browserify "^2.0.4" + tty-browserify "0.0.0" + url "^0.11.0" + util "^0.11.0" + vm-browserify "^1.0.1" + +node-modules-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" + integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= + +node-notifier@^5.4.2: + version "5.4.3" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.4.3.tgz#cb72daf94c93904098e28b9c590fd866e464bd50" + integrity sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q== + dependencies: + growly "^1.3.0" + is-wsl "^1.1.0" + semver "^5.5.0" + shellwords "^0.1.1" + which "^1.3.0" + +node-pre-gyp@*: + version "0.14.0" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz#9a0596533b877289bcad4e143982ca3d904ddc83" + integrity sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA== + dependencies: + detect-libc "^1.0.2" + mkdirp "^0.5.1" + needle "^2.2.1" + nopt "^4.0.1" + npm-packlist "^1.1.6" + npmlog "^4.0.2" + rc "^1.2.7" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^4.4.2" + +node-releases@^1.1.47, node-releases@^1.1.49: + version "1.1.50" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.50.tgz#803c40d2c45db172d0410e4efec83aa8c6ad0592" + integrity sha512-lgAmPv9eYZ0bGwUYAKlr8MG6K4CvWliWqnkcT2P8mMAgVrH3lqfBPorFlxiG1pHQnqmavJZ9vbMXUTNyMLbrgQ== + dependencies: + semver "^6.3.0" + +node-sass@^4.13.0: + version "4.13.1" + resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.13.1.tgz#9db5689696bb2eec2c32b98bfea4c7a2e992d0a3" + integrity sha512-TTWFx+ZhyDx1Biiez2nB0L3YrCZ/8oHagaDalbuBSlqXgUPsdkUSzJsVxeDO9LtPB49+Fh3WQl3slABo6AotNw== + dependencies: + async-foreach "^0.1.3" + chalk "^1.1.1" + cross-spawn "^3.0.0" + gaze "^1.0.0" + get-stdin "^4.0.1" + glob "^7.0.3" + in-publish "^2.0.0" + lodash "^4.17.15" + meow "^3.7.0" + mkdirp "^0.5.1" + nan "^2.13.2" + node-gyp "^3.8.0" + npmlog "^4.0.0" + request "^2.88.0" + sass-graph "^2.2.4" + stdout-stream "^1.4.0" + "true-case-path" "^1.0.2" + +"nopt@2 || 3": + version "3.0.6" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k= + dependencies: + abbrev "1" + +nopt@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= + dependencies: + abbrev "1" + osenv "^0.1.4" + +normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= + +normalize-url@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" + integrity sha1-LMDWazHqIwNkWENuNiDYWVTGbDw= + dependencies: + object-assign "^4.0.1" + prepend-http "^1.0.0" + query-string "^4.1.0" + sort-keys "^1.0.0" + +normalize-url@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" + integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== + +npm-bundled@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b" + integrity sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA== + dependencies: + npm-normalize-package-bin "^1.0.1" + +npm-normalize-package-bin@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" + integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== + +npm-packlist@^1.1.6: + version "1.4.8" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e" + integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A== + dependencies: + ignore-walk "^3.0.1" + npm-bundled "^1.0.1" + npm-normalize-package-bin "^1.0.1" + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= + dependencies: + path-key "^2.0.0" + +"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +nth-check@^1.0.2, nth-check@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" + integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== + dependencies: + boolbase "~1.0.0" + +num2fraction@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" + integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + +nwsapi@^2.0.7, nwsapi@^2.1.3: + version "2.2.0" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" + integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +object-assign@4.x, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-hash@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.0.3.tgz#d12db044e03cd2ca3d77c0570d87225b02e1e6ea" + integrity sha512-JPKn0GMu+Fa3zt3Bmr66JhokJU5BaNBIh4ZeTlaCBzrBsOeXzwcKKAK1tbLiPKgvwmPXsDvvLHoWh5Bm7ofIYg== + +object-inspect@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" + integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== + +object-is@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.2.tgz#6b80eb84fe451498f65007982f035a5b445edec4" + integrity sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ== + +object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object-path@0.11.4: + version "0.11.4" + resolved "https://registry.yarnpkg.com/object-path/-/object-path-0.11.4.tgz#370ae752fbf37de3ea70a861c23bba8915691949" + integrity sha1-NwrnUvvzfePqcKhhwju6iRVpGUk= + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= + dependencies: + isobject "^3.0.0" + +object.assign@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" + +object.entries@^1.1.0, object.entries@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.1.tgz#ee1cf04153de02bb093fec33683900f57ce5399b" + integrity sha512-ilqR7BgdyZetJutmDPfXCDffGa0/Yzl2ivVNpbx/g4UeWrCdRnFDUBrKJGLhGieRHDATnyZXWBeCb29k9CJysQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + has "^1.0.3" + +object.fromentries@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.2.tgz#4a09c9b9bb3843dd0f89acdb517a794d4f355ac9" + integrity sha512-r3ZiBH7MQppDJVLx6fhD618GKNG40CZYH9wgwdhKxBDDbQgjeWGGd4AtkZad84d291YxvWe7bJGuE65Anh0dxQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + has "^1.0.3" + +object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649" + integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= + dependencies: + isobject "^3.0.1" + +object.values@^1.1.0, object.values@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e" + integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + has "^1.0.3" + +obuf@^1.0.0, obuf@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" + integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== + +oidc-client@^1.10.1: + version "1.10.1" + resolved "https://registry.yarnpkg.com/oidc-client/-/oidc-client-1.10.1.tgz#fe67ae54924fc1c338062f3fd733be362026192c" + integrity sha512-/QB5Nl7c9GmT9ir1E+OVY3+yZZnuk7Qa9ZEAJqSvDq0bAyAU9KAgeKipTEfKjGdGLTeOLy9FRWuNpULMkfZydQ== + dependencies: + base64-js "^1.3.0" + core-js "^2.6.4" + crypto-js "^3.1.9-1" + uuid "^3.3.2" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +onetime@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.0.tgz#fff0f3c91617fe62bb50189636e99ac8a6df7be5" + integrity sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q== + dependencies: + mimic-fn "^2.1.0" + +open@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/open/-/open-7.0.2.tgz#fb3681f11f157f2361d2392307548ca1792960e8" + integrity sha512-70E/pFTPr7nZ9nLDPNTcj3IVqnNvKuP4VsBmoKV9YGTnChe0mlS3C4qM7qKarhZ8rGaHKLfo+vBTHXDp6ZSyLQ== + dependencies: + is-docker "^2.0.0" + is-wsl "^2.1.1" + +opn@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" + integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA== + dependencies: + is-wsl "^1.1.0" + +optimize-css-assets-webpack-plugin@5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.3.tgz#e2f1d4d94ad8c0af8967ebd7cf138dcb1ef14572" + integrity sha512-q9fbvCRS6EYtUKKSwI87qm2IxlyJK5b4dygW1rKUBT6mMDhdG5e5bZT63v6tnJR9F9FB/H5a0HTmtw+laUBxKA== + dependencies: + cssnano "^4.1.10" + last-call-webpack-plugin "^3.0.0" + +optionator@^0.8.1, optionator@^0.8.3: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +original@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f" + integrity sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg== + dependencies: + url-parse "^1.4.3" + +os-browserify@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" + integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= + +os-locale@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" + integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk= + dependencies: + lcid "^1.0.0" + +os-locale@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" + integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== + dependencies: + execa "^1.0.0" + lcid "^2.0.0" + mem "^4.0.0" + +os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +osenv@0, osenv@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +p-defer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" + integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww= + +p-each-series@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-1.0.0.tgz#930f3d12dd1f50e7434457a22cd6f04ac6ad7f71" + integrity sha1-kw89Et0fUOdDRFeiLNbwSsatf3E= + dependencies: + p-reduce "^1.0.0" + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + +p-is-promise@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" + integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== + +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + +p-limit@^2.0.0, p-limit@^2.2.0, p-limit@^2.2.1, p-limit@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.2.tgz#61279b67721f5287aa1c13a9a7fbbc48c9291b1e" + integrity sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ== + dependencies: + p-try "^2.0.0" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= + dependencies: + p-limit "^1.1.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-map@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" + integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== + +p-map@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-3.0.0.tgz#d704d9af8a2ba684e2600d9a215983d4141a979d" + integrity sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ== + dependencies: + aggregate-error "^3.0.0" + +p-reduce@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa" + integrity sha1-GMKw3ZNqRpClKfgjH1ig/bakffo= + +p-retry@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-3.0.1.tgz#316b4c8893e2c8dc1cfa891f406c4b422bebf328" + integrity sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w== + dependencies: + retry "^0.12.0" + +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +pako@~1.0.5: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + +parallel-transform@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz#9049ca37d6cb2182c3b1d2c720be94d14a5814fc" + integrity sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg== + dependencies: + cyclist "^1.0.1" + inherits "^2.0.3" + readable-stream "^2.1.5" + +param-case@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.3.tgz#4be41f8399eff621c56eebb829a5e451d9801238" + integrity sha512-VWBVyimc1+QrzappRs7waeN2YmoZFCGXWASRYX1/rGHtXqEcrGEIDm+jqIwFa2fRXNgQEwrxaYuIrX0WcAguTA== + dependencies: + dot-case "^3.0.3" + tslib "^1.10.0" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-asn1@^5.0.0: + version "5.1.5" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.5.tgz#003271343da58dc94cace494faef3d2147ecea0e" + integrity sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ== + dependencies: + asn1.js "^4.0.0" + browserify-aes "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + safe-buffer "^5.1.1" + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= + dependencies: + error-ex "^1.2.0" + +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + +parse-json@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.0.0.tgz#73e5114c986d143efa3712d4ea24db9a4266f60f" + integrity sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + lines-and-columns "^1.1.6" + +parse5@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" + integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA== + +parse5@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2" + integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ== + +parseurl@~1.3.2, parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +pascal-case@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.1.tgz#5ac1975133ed619281e88920973d2cd1f279de5f" + integrity sha512-XIeHKqIrsquVTQL2crjq3NfJUxmdLasn3TYOU0VBM+UX2a6ztAWBlJQBePLGY7VHW8+2dRadeIPK5+KImwTxQA== + dependencies: + no-case "^3.0.3" + tslib "^1.10.0" + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= + +path-browserify@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" + integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== + +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= + dependencies: + pinkie-promise "^2.0.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-is-inside@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= + +path-key@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + +path-to-regexp@^1.7.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" + integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== + dependencies: + isarray "0.0.1" + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +path-type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" + integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM= + dependencies: + pify "^2.0.0" + +path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== + dependencies: + pify "^3.0.0" + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +pbkdf2@^3.0.3: + version "3.0.17" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" + integrity sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +picomatch@^2.0.4, picomatch@^2.0.7: + version "2.2.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.1.tgz#21bac888b6ed8601f831ce7816e335bc779f0a4a" + integrity sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA== + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= + +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= + +pirates@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" + integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== + dependencies: + node-modules-regexp "^1.0.0" + +pkg-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4" + integrity sha1-ektQio1bstYp1EcFb/TpyTFM89Q= + dependencies: + find-up "^1.0.0" + +pkg-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" + integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s= + dependencies: + find-up "^2.1.0" + +pkg-dir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" + integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== + dependencies: + find-up "^3.0.0" + +pkg-dir@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +pkg-up@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" + integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA== + dependencies: + find-up "^3.0.0" + +pn@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" + integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA== + +pnp-webpack-plugin@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.0.tgz#d5c068013a2fdc82224ca50ed179c8fba9036a8e" + integrity sha512-ZcMGn/xF/fCOq+9kWMP9vVVxjIkMCja72oy3lziR7UHy0hHFZ57iVpQ71OtveVbmzeCmphBg8pxNdk/hlK99aQ== + dependencies: + ts-pnp "^1.1.2" + +portfinder@^1.0.25: + version "1.0.25" + resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.25.tgz#254fd337ffba869f4b9d37edc298059cb4d35eca" + integrity sha512-6ElJnHBbxVA1XSLgBp7G1FiCkQdlqGzuF7DswL5tcea+E8UpuvPU7beVAjjRwCioTS9ZluNbu+ZyRvgTsmqEBg== + dependencies: + async "^2.6.2" + debug "^3.1.1" + mkdirp "^0.5.1" + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= + +postcss-attribute-case-insensitive@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz#d93e46b504589e94ac7277b0463226c68041a880" + integrity sha512-clkFxk/9pcdb4Vkn0hAHq3YnxBQ2p0CGD1dy24jN+reBck+EWxMbxSUqN4Yj7t0w8csl87K6p0gxBe1utkJsYA== + dependencies: + postcss "^7.0.2" + postcss-selector-parser "^6.0.2" + +postcss-browser-comments@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-browser-comments/-/postcss-browser-comments-3.0.0.tgz#1248d2d935fb72053c8e1f61a84a57292d9f65e9" + integrity sha512-qfVjLfq7HFd2e0HW4s1dvU8X080OZdG46fFbIBFjW7US7YPDcWfRvdElvwMJr2LI6hMmD+7LnH2HcmXTs+uOig== + dependencies: + postcss "^7" + +postcss-calc@^7.0.1: + version "7.0.2" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.2.tgz#504efcd008ca0273120568b0792b16cdcde8aac1" + integrity sha512-rofZFHUg6ZIrvRwPeFktv06GdbDYLcGqh9EwiMutZg+a0oePCCw1zHOEiji6LCpyRcjTREtPASuUqeAvYlEVvQ== + dependencies: + postcss "^7.0.27" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.0.2" + +postcss-color-functional-notation@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/postcss-color-functional-notation/-/postcss-color-functional-notation-2.0.1.tgz#5efd37a88fbabeb00a2966d1e53d98ced93f74e0" + integrity sha512-ZBARCypjEDofW4P6IdPVTLhDNXPRn8T2s1zHbZidW6rPaaZvcnCS2soYFIQJrMZSxiePJ2XIYTlcb2ztr/eT2g== + dependencies: + postcss "^7.0.2" + postcss-values-parser "^2.0.0" + +postcss-color-gray@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/postcss-color-gray/-/postcss-color-gray-5.0.0.tgz#532a31eb909f8da898ceffe296fdc1f864be8547" + integrity sha512-q6BuRnAGKM/ZRpfDascZlIZPjvwsRye7UDNalqVz3s7GDxMtqPY6+Q871liNxsonUw8oC61OG+PSaysYpl1bnw== + dependencies: + "@csstools/convert-colors" "^1.4.0" + postcss "^7.0.5" + postcss-values-parser "^2.0.0" + +postcss-color-hex-alpha@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/postcss-color-hex-alpha/-/postcss-color-hex-alpha-5.0.3.tgz#a8d9ca4c39d497c9661e374b9c51899ef0f87388" + integrity sha512-PF4GDel8q3kkreVXKLAGNpHKilXsZ6xuu+mOQMHWHLPNyjiUBOr75sp5ZKJfmv1MCus5/DWUGcK9hm6qHEnXYw== + dependencies: + postcss "^7.0.14" + postcss-values-parser "^2.0.1" + +postcss-color-mod-function@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/postcss-color-mod-function/-/postcss-color-mod-function-3.0.3.tgz#816ba145ac11cc3cb6baa905a75a49f903e4d31d" + integrity sha512-YP4VG+xufxaVtzV6ZmhEtc+/aTXH3d0JLpnYfxqTvwZPbJhWqp8bSY3nfNzNRFLgB4XSaBA82OE4VjOOKpCdVQ== + dependencies: + "@csstools/convert-colors" "^1.4.0" + postcss "^7.0.2" + postcss-values-parser "^2.0.0" + +postcss-color-rebeccapurple@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-4.0.1.tgz#c7a89be872bb74e45b1e3022bfe5748823e6de77" + integrity sha512-aAe3OhkS6qJXBbqzvZth2Au4V3KieR5sRQ4ptb2b2O8wgvB3SJBsdG+jsn2BZbbwekDG8nTfcCNKcSfe/lEy8g== + dependencies: + postcss "^7.0.2" + postcss-values-parser "^2.0.0" + +postcss-colormin@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-4.0.3.tgz#ae060bce93ed794ac71264f08132d550956bd381" + integrity sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw== + dependencies: + browserslist "^4.0.0" + color "^3.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-convert-values@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz#ca3813ed4da0f812f9d43703584e449ebe189a7f" + integrity sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ== + dependencies: + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-custom-media@^7.0.8: + version "7.0.8" + resolved "https://registry.yarnpkg.com/postcss-custom-media/-/postcss-custom-media-7.0.8.tgz#fffd13ffeffad73621be5f387076a28b00294e0c" + integrity sha512-c9s5iX0Ge15o00HKbuRuTqNndsJUbaXdiNsksnVH8H4gdc+zbLzr/UasOwNG6CTDpLFekVY4672eWdiiWu2GUg== + dependencies: + postcss "^7.0.14" + +postcss-custom-properties@^8.0.11: + version "8.0.11" + resolved "https://registry.yarnpkg.com/postcss-custom-properties/-/postcss-custom-properties-8.0.11.tgz#2d61772d6e92f22f5e0d52602df8fae46fa30d97" + integrity sha512-nm+o0eLdYqdnJ5abAJeXp4CEU1c1k+eB2yMCvhgzsds/e0umabFrN6HoTy/8Q4K5ilxERdl/JD1LO5ANoYBeMA== + dependencies: + postcss "^7.0.17" + postcss-values-parser "^2.0.1" + +postcss-custom-selectors@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/postcss-custom-selectors/-/postcss-custom-selectors-5.1.2.tgz#64858c6eb2ecff2fb41d0b28c9dd7b3db4de7fba" + integrity sha512-DSGDhqinCqXqlS4R7KGxL1OSycd1lydugJ1ky4iRXPHdBRiozyMHrdu0H3o7qNOCiZwySZTUI5MV0T8QhCLu+w== + dependencies: + postcss "^7.0.2" + postcss-selector-parser "^5.0.0-rc.3" + +postcss-dir-pseudo-class@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-5.0.0.tgz#6e3a4177d0edb3abcc85fdb6fbb1c26dabaeaba2" + integrity sha512-3pm4oq8HYWMZePJY+5ANriPs3P07q+LW6FAdTlkFH2XqDdP4HeeJYMOzn0HYLhRSjBO3fhiqSwwU9xEULSrPgw== + dependencies: + postcss "^7.0.2" + postcss-selector-parser "^5.0.0-rc.3" + +postcss-discard-comments@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz#1fbabd2c246bff6aaad7997b2b0918f4d7af4033" + integrity sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg== + dependencies: + postcss "^7.0.0" + +postcss-discard-duplicates@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz#3fe133cd3c82282e550fc9b239176a9207b784eb" + integrity sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ== + dependencies: + postcss "^7.0.0" + +postcss-discard-empty@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz#c8c951e9f73ed9428019458444a02ad90bb9f765" + integrity sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w== + dependencies: + postcss "^7.0.0" + +postcss-discard-overridden@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz#652aef8a96726f029f5e3e00146ee7a4e755ff57" + integrity sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg== + dependencies: + postcss "^7.0.0" + +postcss-double-position-gradients@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/postcss-double-position-gradients/-/postcss-double-position-gradients-1.0.0.tgz#fc927d52fddc896cb3a2812ebc5df147e110522e" + integrity sha512-G+nV8EnQq25fOI8CH/B6krEohGWnF5+3A6H/+JEpOncu5dCnkS1QQ6+ct3Jkaepw1NGVqqOZH6lqrm244mCftA== + dependencies: + postcss "^7.0.5" + postcss-values-parser "^2.0.0" + +postcss-env-function@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/postcss-env-function/-/postcss-env-function-2.0.2.tgz#0f3e3d3c57f094a92c2baf4b6241f0b0da5365d7" + integrity sha512-rwac4BuZlITeUbiBq60h/xbLzXY43qOsIErngWa4l7Mt+RaSkT7QBjXVGTcBHupykkblHMDrBFh30zchYPaOUw== + dependencies: + postcss "^7.0.2" + postcss-values-parser "^2.0.0" + +postcss-flexbugs-fixes@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-4.1.0.tgz#e094a9df1783e2200b7b19f875dcad3b3aff8b20" + integrity sha512-jr1LHxQvStNNAHlgco6PzY308zvLklh7SJVYuWUwyUQncofaAlD2l+P/gxKHOdqWKe7xJSkVLFF/2Tp+JqMSZA== + dependencies: + postcss "^7.0.0" + +postcss-focus-visible@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-focus-visible/-/postcss-focus-visible-4.0.0.tgz#477d107113ade6024b14128317ade2bd1e17046e" + integrity sha512-Z5CkWBw0+idJHSV6+Bgf2peDOFf/x4o+vX/pwcNYrWpXFrSfTkQ3JQ1ojrq9yS+upnAlNRHeg8uEwFTgorjI8g== + dependencies: + postcss "^7.0.2" + +postcss-focus-within@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-focus-within/-/postcss-focus-within-3.0.0.tgz#763b8788596cee9b874c999201cdde80659ef680" + integrity sha512-W0APui8jQeBKbCGZudW37EeMCjDeVxKgiYfIIEo8Bdh5SpB9sxds/Iq8SEuzS0Q4YFOlG7EPFulbbxujpkrV2w== + dependencies: + postcss "^7.0.2" + +postcss-font-variant@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-font-variant/-/postcss-font-variant-4.0.0.tgz#71dd3c6c10a0d846c5eda07803439617bbbabacc" + integrity sha512-M8BFYKOvCrI2aITzDad7kWuXXTm0YhGdP9Q8HanmN4EF1Hmcgs1KK5rSHylt/lUJe8yLxiSwWAHdScoEiIxztg== + dependencies: + postcss "^7.0.2" + +postcss-gap-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-gap-properties/-/postcss-gap-properties-2.0.0.tgz#431c192ab3ed96a3c3d09f2ff615960f902c1715" + integrity sha512-QZSqDaMgXCHuHTEzMsS2KfVDOq7ZFiknSpkrPJY6jmxbugUPTuSzs/vuE5I3zv0WAS+3vhrlqhijiprnuQfzmg== + dependencies: + postcss "^7.0.2" + +postcss-image-set-function@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/postcss-image-set-function/-/postcss-image-set-function-3.0.1.tgz#28920a2f29945bed4c3198d7df6496d410d3f288" + integrity sha512-oPTcFFip5LZy8Y/whto91L9xdRHCWEMs3e1MdJxhgt4jy2WYXfhkng59fH5qLXSCPN8k4n94p1Czrfe5IOkKUw== + dependencies: + postcss "^7.0.2" + postcss-values-parser "^2.0.0" + +postcss-initial@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/postcss-initial/-/postcss-initial-3.0.2.tgz#f018563694b3c16ae8eaabe3c585ac6319637b2d" + integrity sha512-ugA2wKonC0xeNHgirR4D3VWHs2JcU08WAi1KFLVcnb7IN89phID6Qtg2RIctWbnvp1TM2BOmDtX8GGLCKdR8YA== + dependencies: + lodash.template "^4.5.0" + postcss "^7.0.2" + +postcss-lab-function@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/postcss-lab-function/-/postcss-lab-function-2.0.1.tgz#bb51a6856cd12289ab4ae20db1e3821ef13d7d2e" + integrity sha512-whLy1IeZKY+3fYdqQFuDBf8Auw+qFuVnChWjmxm/UhHWqNHZx+B99EwxTvGYmUBqe3Fjxs4L1BoZTJmPu6usVg== + dependencies: + "@csstools/convert-colors" "^1.4.0" + postcss "^7.0.2" + postcss-values-parser "^2.0.0" + +postcss-load-config@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-2.1.0.tgz#c84d692b7bb7b41ddced94ee62e8ab31b417b003" + integrity sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q== + dependencies: + cosmiconfig "^5.0.0" + import-cwd "^2.0.0" + +postcss-loader@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-3.0.0.tgz#6b97943e47c72d845fa9e03f273773d4e8dd6c2d" + integrity sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA== + dependencies: + loader-utils "^1.1.0" + postcss "^7.0.0" + postcss-load-config "^2.0.0" + schema-utils "^1.0.0" + +postcss-logical@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-logical/-/postcss-logical-3.0.0.tgz#2495d0f8b82e9f262725f75f9401b34e7b45d5b5" + integrity sha512-1SUKdJc2vuMOmeItqGuNaC+N8MzBWFWEkAnRnLpFYj1tGGa7NqyVBujfRtgNa2gXR+6RkGUiB2O5Vmh7E2RmiA== + dependencies: + postcss "^7.0.2" + +postcss-media-minmax@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-media-minmax/-/postcss-media-minmax-4.0.0.tgz#b75bb6cbc217c8ac49433e12f22048814a4f5ed5" + integrity sha512-fo9moya6qyxsjbFAYl97qKO9gyre3qvbMnkOZeZwlsW6XYFsvs2DMGDlchVLfAd8LHPZDxivu/+qW2SMQeTHBw== + dependencies: + postcss "^7.0.2" + +postcss-merge-longhand@^4.0.11: + version "4.0.11" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz#62f49a13e4a0ee04e7b98f42bb16062ca2549e24" + integrity sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw== + dependencies: + css-color-names "0.0.4" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + stylehacks "^4.0.0" + +postcss-merge-rules@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz#362bea4ff5a1f98e4075a713c6cb25aefef9a650" + integrity sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ== + dependencies: + browserslist "^4.0.0" + caniuse-api "^3.0.0" + cssnano-util-same-parent "^4.0.0" + postcss "^7.0.0" + postcss-selector-parser "^3.0.0" + vendors "^1.0.0" + +postcss-minify-font-values@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz#cd4c344cce474343fac5d82206ab2cbcb8afd5a6" + integrity sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg== + dependencies: + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-minify-gradients@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz#93b29c2ff5099c535eecda56c4aa6e665a663471" + integrity sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q== + dependencies: + cssnano-util-get-arguments "^4.0.0" + is-color-stop "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-minify-params@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz#6b9cef030c11e35261f95f618c90036d680db874" + integrity sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg== + dependencies: + alphanum-sort "^1.0.0" + browserslist "^4.0.0" + cssnano-util-get-arguments "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + uniqs "^2.0.0" + +postcss-minify-selectors@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz#e2e5eb40bfee500d0cd9243500f5f8ea4262fbd8" + integrity sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g== + dependencies: + alphanum-sort "^1.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-selector-parser "^3.0.0" + +postcss-modules-extract-imports@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e" + integrity sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ== + dependencies: + postcss "^7.0.5" + +postcss-modules-local-by-default@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.2.tgz#e8a6561be914aaf3c052876377524ca90dbb7915" + integrity sha512-jM/V8eqM4oJ/22j0gx4jrp63GSvDH6v86OqyTHHUvk4/k1vceipZsaymiZ5PvocqZOl5SFHiFJqjs3la0wnfIQ== + dependencies: + icss-utils "^4.1.1" + postcss "^7.0.16" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.0.0" + +postcss-modules-scope@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.1.1.tgz#33d4fc946602eb5e9355c4165d68a10727689dba" + integrity sha512-OXRUPecnHCg8b9xWvldG/jUpRIGPNRka0r4D4j0ESUU2/5IOnpsjfPPmDprM3Ih8CgZ8FXjWqaniK5v4rWt3oQ== + dependencies: + postcss "^7.0.6" + postcss-selector-parser "^6.0.0" + +postcss-modules-values@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz#5b5000d6ebae29b4255301b4a3a54574423e7f10" + integrity sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg== + dependencies: + icss-utils "^4.0.0" + postcss "^7.0.6" + +postcss-nesting@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-7.0.1.tgz#b50ad7b7f0173e5b5e3880c3501344703e04c052" + integrity sha512-FrorPb0H3nuVq0Sff7W2rnc3SmIcruVC6YwpcS+k687VxyxO33iE1amna7wHuRVzM8vfiYofXSBHNAZ3QhLvYg== + dependencies: + postcss "^7.0.2" + +postcss-normalize-charset@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz#8b35add3aee83a136b0471e0d59be58a50285dd4" + integrity sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g== + dependencies: + postcss "^7.0.0" + +postcss-normalize-display-values@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz#0dbe04a4ce9063d4667ed2be476bb830c825935a" + integrity sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ== + dependencies: + cssnano-util-get-match "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-positions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz#05f757f84f260437378368a91f8932d4b102917f" + integrity sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA== + dependencies: + cssnano-util-get-arguments "^4.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-repeat-style@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz#c4ebbc289f3991a028d44751cbdd11918b17910c" + integrity sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q== + dependencies: + cssnano-util-get-arguments "^4.0.0" + cssnano-util-get-match "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-string@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz#cd44c40ab07a0c7a36dc5e99aace1eca4ec2690c" + integrity sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA== + dependencies: + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-timing-functions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz#8e009ca2a3949cdaf8ad23e6b6ab99cb5e7d28d9" + integrity sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A== + dependencies: + cssnano-util-get-match "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-unicode@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz#841bd48fdcf3019ad4baa7493a3d363b52ae1cfb" + integrity sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg== + dependencies: + browserslist "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-url@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz#10e437f86bc7c7e58f7b9652ed878daaa95faae1" + integrity sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA== + dependencies: + is-absolute-url "^2.0.0" + normalize-url "^3.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-whitespace@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz#bf1d4070fe4fcea87d1348e825d8cc0c5faa7d82" + integrity sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA== + dependencies: + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize@8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize/-/postcss-normalize-8.0.1.tgz#90e80a7763d7fdf2da6f2f0f82be832ce4f66776" + integrity sha512-rt9JMS/m9FHIRroDDBGSMsyW1c0fkvOJPy62ggxSHUldJO7B195TqFMqIf+lY5ezpDcYOV4j86aUp3/XbxzCCQ== + dependencies: + "@csstools/normalize.css" "^10.1.0" + browserslist "^4.6.2" + postcss "^7.0.17" + postcss-browser-comments "^3.0.0" + sanitize.css "^10.0.0" + +postcss-ordered-values@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz#0cf75c820ec7d5c4d280189559e0b571ebac0eee" + integrity sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw== + dependencies: + cssnano-util-get-arguments "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-overflow-shorthand@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-overflow-shorthand/-/postcss-overflow-shorthand-2.0.0.tgz#31ecf350e9c6f6ddc250a78f0c3e111f32dd4c30" + integrity sha512-aK0fHc9CBNx8jbzMYhshZcEv8LtYnBIRYQD5i7w/K/wS9c2+0NSR6B3OVMu5y0hBHYLcMGjfU+dmWYNKH0I85g== + dependencies: + postcss "^7.0.2" + +postcss-page-break@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-page-break/-/postcss-page-break-2.0.0.tgz#add52d0e0a528cabe6afee8b46e2abb277df46bf" + integrity sha512-tkpTSrLpfLfD9HvgOlJuigLuk39wVTbbd8RKcy8/ugV2bNBUW3xU+AIqyxhDrQr1VUj1RmyJrBn1YWrqUm9zAQ== + dependencies: + postcss "^7.0.2" + +postcss-place@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-place/-/postcss-place-4.0.1.tgz#e9f39d33d2dc584e46ee1db45adb77ca9d1dcc62" + integrity sha512-Zb6byCSLkgRKLODj/5mQugyuj9bvAAw9LqJJjgwz5cYryGeXfFZfSXoP1UfveccFmeq0b/2xxwcTEVScnqGxBg== + dependencies: + postcss "^7.0.2" + postcss-values-parser "^2.0.0" + +postcss-preset-env@6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/postcss-preset-env/-/postcss-preset-env-6.7.0.tgz#c34ddacf8f902383b35ad1e030f178f4cdf118a5" + integrity sha512-eU4/K5xzSFwUFJ8hTdTQzo2RBLbDVt83QZrAvI07TULOkmyQlnYlpwep+2yIK+K+0KlZO4BvFcleOCCcUtwchg== + dependencies: + autoprefixer "^9.6.1" + browserslist "^4.6.4" + caniuse-lite "^1.0.30000981" + css-blank-pseudo "^0.1.4" + css-has-pseudo "^0.10.0" + css-prefers-color-scheme "^3.1.1" + cssdb "^4.4.0" + postcss "^7.0.17" + postcss-attribute-case-insensitive "^4.0.1" + postcss-color-functional-notation "^2.0.1" + postcss-color-gray "^5.0.0" + postcss-color-hex-alpha "^5.0.3" + postcss-color-mod-function "^3.0.3" + postcss-color-rebeccapurple "^4.0.1" + postcss-custom-media "^7.0.8" + postcss-custom-properties "^8.0.11" + postcss-custom-selectors "^5.1.2" + postcss-dir-pseudo-class "^5.0.0" + postcss-double-position-gradients "^1.0.0" + postcss-env-function "^2.0.2" + postcss-focus-visible "^4.0.0" + postcss-focus-within "^3.0.0" + postcss-font-variant "^4.0.0" + postcss-gap-properties "^2.0.0" + postcss-image-set-function "^3.0.1" + postcss-initial "^3.0.0" + postcss-lab-function "^2.0.1" + postcss-logical "^3.0.0" + postcss-media-minmax "^4.0.0" + postcss-nesting "^7.0.0" + postcss-overflow-shorthand "^2.0.0" + postcss-page-break "^2.0.0" + postcss-place "^4.0.1" + postcss-pseudo-class-any-link "^6.0.0" + postcss-replace-overflow-wrap "^3.0.0" + postcss-selector-matches "^4.0.0" + postcss-selector-not "^4.0.0" + +postcss-pseudo-class-any-link@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-6.0.0.tgz#2ed3eed393b3702879dec4a87032b210daeb04d1" + integrity sha512-lgXW9sYJdLqtmw23otOzrtbDXofUdfYzNm4PIpNE322/swES3VU9XlXHeJS46zT2onFO7V1QFdD4Q9LiZj8mew== + dependencies: + postcss "^7.0.2" + postcss-selector-parser "^5.0.0-rc.3" + +postcss-reduce-initial@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz#7fd42ebea5e9c814609639e2c2e84ae270ba48df" + integrity sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA== + dependencies: + browserslist "^4.0.0" + caniuse-api "^3.0.0" + has "^1.0.0" + postcss "^7.0.0" + +postcss-reduce-transforms@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz#17efa405eacc6e07be3414a5ca2d1074681d4e29" + integrity sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg== + dependencies: + cssnano-util-get-match "^4.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-replace-overflow-wrap@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-3.0.0.tgz#61b360ffdaedca84c7c918d2b0f0d0ea559ab01c" + integrity sha512-2T5hcEHArDT6X9+9dVSPQdo7QHzG4XKclFT8rU5TzJPDN7RIRTbO9c4drUISOVemLj03aezStHCR2AIcr8XLpw== + dependencies: + postcss "^7.0.2" + +postcss-safe-parser@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-4.0.1.tgz#8756d9e4c36fdce2c72b091bbc8ca176ab1fcdea" + integrity sha512-xZsFA3uX8MO3yAda03QrG3/Eg1LN3EPfjjf07vke/46HERLZyHrTsQ9E1r1w1W//fWEhtYNndo2hQplN2cVpCQ== + dependencies: + postcss "^7.0.0" + +postcss-selector-matches@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-selector-matches/-/postcss-selector-matches-4.0.0.tgz#71c8248f917ba2cc93037c9637ee09c64436fcff" + integrity sha512-LgsHwQR/EsRYSqlwdGzeaPKVT0Ml7LAT6E75T8W8xLJY62CE4S/l03BWIt3jT8Taq22kXP08s2SfTSzaraoPww== + dependencies: + balanced-match "^1.0.0" + postcss "^7.0.2" + +postcss-selector-not@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-selector-not/-/postcss-selector-not-4.0.0.tgz#c68ff7ba96527499e832724a2674d65603b645c0" + integrity sha512-W+bkBZRhqJaYN8XAnbbZPLWMvZD1wKTu0UxtFKdhtGjWYmxhkUneoeOhRJKdAE5V7ZTlnbHfCR+6bNwK9e1dTQ== + dependencies: + balanced-match "^1.0.0" + postcss "^7.0.2" + +postcss-selector-parser@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz#b310f5c4c0fdaf76f94902bbaa30db6aa84f5270" + integrity sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA== + dependencies: + dot-prop "^5.2.0" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-selector-parser@^5.0.0-rc.3, postcss-selector-parser@^5.0.0-rc.4: + version "5.0.0" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz#249044356697b33b64f1a8f7c80922dddee7195c" + integrity sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ== + dependencies: + cssesc "^2.0.0" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c" + integrity sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg== + dependencies: + cssesc "^3.0.0" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-svgo@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.2.tgz#17b997bc711b333bab143aaed3b8d3d6e3d38258" + integrity sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw== + dependencies: + is-svg "^3.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + svgo "^1.0.0" + +postcss-unique-selectors@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz#9446911f3289bfd64c6d680f073c03b1f9ee4bac" + integrity sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg== + dependencies: + alphanum-sort "^1.0.0" + postcss "^7.0.0" + uniqs "^2.0.0" + +postcss-value-parser@^3.0.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" + integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== + +postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.3.tgz#651ff4593aa9eda8d5d0d66593a2417aeaeb325d" + integrity sha512-N7h4pG+Nnu5BEIzyeaaIYWs0LI5XC40OrRh5L60z0QjFsqGWcHcbkBvpe1WYpcIS9yQ8sOi/vIPt1ejQCrMVrg== + +postcss-values-parser@^2.0.0, postcss-values-parser@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz#da8b472d901da1e205b47bdc98637b9e9e550e5f" + integrity sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg== + dependencies: + flatten "^1.0.2" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss@7.0.21: + version "7.0.21" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.21.tgz#06bb07824c19c2021c5d056d5b10c35b989f7e17" + integrity sha512-uIFtJElxJo29QC753JzhidoAhvp/e/Exezkdhfmt8AymWT6/5B7W1WmponYWkHk2eg6sONyTch0A3nkMPun3SQ== + dependencies: + chalk "^2.4.2" + source-map "^0.6.1" + supports-color "^6.1.0" + +postcss@^7, postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.23, postcss@^7.0.26, postcss@^7.0.27, postcss@^7.0.5, postcss@^7.0.6: + version "7.0.27" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.27.tgz#cc67cdc6b0daa375105b7c424a85567345fc54d9" + integrity sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ== + dependencies: + chalk "^2.4.2" + source-map "^0.6.1" + supports-color "^6.1.0" + +prefix-style@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/prefix-style/-/prefix-style-2.0.1.tgz#66bba9a870cfda308a5dc20e85e9120932c95a06" + integrity sha1-ZrupqHDP2jCKXcIOhekSCTLJWgY= + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + +prepend-http@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= + +pretty-bytes@^5.1.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.3.0.tgz#f2849e27db79fb4d6cfe24764fc4134f165989f2" + integrity sha512-hjGrh+P926p4R4WbaB6OckyRtO0F0/lQBiT+0gnxjV+5kjPBrfVBFCsCLbMqVQeydvIoouYTCmmEURiH3R1Bdg== + +pretty-error@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.1.tgz#5f4f87c8f91e5ae3f3ba87ab4cf5e03b1a17f1a3" + integrity sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM= + dependencies: + renderkid "^2.0.1" + utila "~0.4" + +pretty-format@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.9.0.tgz#12fac31b37019a4eea3c11aa9a959eb7628aa7c9" + integrity sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA== + dependencies: + "@jest/types" "^24.9.0" + ansi-regex "^4.0.0" + ansi-styles "^3.2.0" + react-is "^16.8.4" + +private@^0.1.6: + version "0.1.8" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" + integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= + +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + +promise-inflight@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= + +promise@^8.0.3: + version "8.0.3" + resolved "https://registry.yarnpkg.com/promise/-/promise-8.0.3.tgz#f592e099c6cddc000d538ee7283bb190452b0bf6" + integrity sha512-HeRDUL1RJiLhyA0/grn+PTShlBAcLuh/1BJGtrvjwbvRDCTLLMEz9rOGCV+R3vHY4MixIuoMEd9Yq/XvsTPcjw== + dependencies: + asap "~2.0.6" + +prompts@^2.0.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.3.1.tgz#b63a9ce2809f106fa9ae1277c275b167af46ea05" + integrity sha512-qIP2lQyCwYbdzcqHIUi2HAxiWixhoM9OdLCWf8txXsapC/X9YdsCoeyRIXE/GP+Q0J37Q7+XN/MFqbUa7IzXNA== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.4" + +prop-types@15.x, prop-types@^15.5.10, prop-types@^15.5.6, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2: + version "15.7.2" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" + integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.8.1" + +proxy-addr@~2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" + integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw== + dependencies: + forwarded "~0.1.2" + ipaddr.js "1.9.1" + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= + +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= + +psl@^1.1.28: + version "1.7.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.7.0.tgz#f1c4c47a8ef97167dea5d6bbf4816d736e884a3c" + integrity sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ== + +public-encrypt@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + safe-buffer "^5.1.2" + +pump@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" + integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pumpify@^1.3.3: + version "1.5.1" + resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" + integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== + dependencies: + duplexify "^3.6.0" + inherits "^2.0.3" + pump "^2.0.0" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= + +punycode@^1.2.4: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= + +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +q@^1.1.2: + version "1.5.1" + resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" + integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= + +qs@6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +query-string@^4.1.0: + version "4.3.4" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" + integrity sha1-u7aTucqRXCMlFbIosaArYJBD2+s= + dependencies: + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + +querystring-es3@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= + +querystringify@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e" + integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA== + +raf@^3.1.0, raf@^3.4.0, raf@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" + integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== + dependencies: + performance-now "^2.1.0" + +ramda@^0.26: + version "0.26.1" + resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06" + integrity sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ== + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +range-parser@^1.2.1, range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== + dependencies: + bytes "3.1.0" + http-errors "1.7.2" + iconv-lite "0.4.24" + unpipe "1.0.0" + +rc-animate@^2.6.0: + version "2.10.3" + resolved "https://registry.yarnpkg.com/rc-animate/-/rc-animate-2.10.3.tgz#163d5e29281a4ff82d53ee7918eeeac856b756f9" + integrity sha512-A9qQ5Y8BLlM7EhuCO3fWb/dChndlbWtY/P5QvPqBU7h4r5Q2QsvsbpTGgdYZATRDZbTRnJXXfVk9UtlyS7MBLg== + dependencies: + babel-runtime "6.x" + classnames "^2.2.6" + css-animation "^1.3.2" + prop-types "15.x" + raf "^3.4.0" + rc-util "^4.15.3" + react-lifecycles-compat "^3.0.4" + +rc-tree@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-2.1.3.tgz#5214ab1b21a1848eb9a2ddcb919e3bc46d6d390b" + integrity sha512-COvV65spQ6omrHBUhHRKqKNL5+ddXjlS+qWZchaL9FFuQNvjM5pjp9RnmMWK4fJJ5kBhhpLneh6wh9Vh3kSMXQ== + dependencies: + "@ant-design/create-react-context" "^0.2.4" + classnames "2.x" + prop-types "^15.5.8" + rc-animate "^2.6.0" + rc-util "^4.5.1" + react-lifecycles-compat "^3.0.4" + warning "^4.0.3" + +rc-util@^4.15.3, rc-util@^4.5.1: + version "4.20.0" + resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-4.20.0.tgz#fb601c59bb48cbee38538e0d5b628addda6a8c30" + integrity sha512-rUqk4RqtDe4OfTsSk2GpbvIQNVtfmmebw4Rn7ZAA1TO1zLMLfyOF78ZyrEKqs8RDwoE3S1aXp0AX0ogLfSxXrQ== + dependencies: + add-dom-event-listener "^1.1.0" + babel-runtime "6.x" + prop-types "^15.5.10" + react-is "^16.12.0" + react-lifecycles-compat "^3.0.4" + shallowequal "^1.1.0" + +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +react-app-polyfill@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/react-app-polyfill/-/react-app-polyfill-1.0.6.tgz#890f8d7f2842ce6073f030b117de9130a5f385f0" + integrity sha512-OfBnObtnGgLGfweORmdZbyEz+3dgVePQBb3zipiaDsMHV1NpWm0rDFYIVXFV/AK+x4VIIfWHhrdMIeoTLyRr2g== + dependencies: + core-js "^3.5.0" + object-assign "^4.1.1" + promise "^8.0.3" + raf "^3.4.1" + regenerator-runtime "^0.13.3" + whatwg-fetch "^3.0.0" + +react-app-rewired@^2.1.5: + version "2.1.5" + resolved "https://registry.yarnpkg.com/react-app-rewired/-/react-app-rewired-2.1.5.tgz#592ec2eae5c3c5cd96c80930b5dc3f6c34da1dc6" + integrity sha512-Gr8KfCeL9/PTQs8Vvxc7v8wQ9vCFMnYPhcAkrMlzkLiMFXS+BgSwm11MoERjZm7dpA2WjTi+Pvbu/w7rujAV+A== + dependencies: + semver "^5.6.0" + +react-autosize-textarea@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/react-autosize-textarea/-/react-autosize-textarea-7.0.0.tgz#4f633e4238de7ba73c1da8fdc307353c50f1c5ab" + integrity sha512-rGQLpGUaELvzy3NKzp0kkcppaUtZTptsyR0PGuLotaJDjwRbT0DpD000yCzETpXseJQ/eMsyVGDDHXjXP93u8w== + dependencies: + autosize "^4.0.2" + line-height "^0.3.1" + prop-types "^15.5.6" + +react-avatar-editor@^11.0.7: + version "11.0.7" + resolved "https://registry.yarnpkg.com/react-avatar-editor/-/react-avatar-editor-11.0.7.tgz#021053cfeaa138407b79279ee5a0384f273f0c54" + integrity sha512-GbNYBd1/L1QyuU9VRvOW0hSkW1R0XSneOWZFgqI5phQf6dX+dF/G3/AjiJ0hv3JWh2irMQ7DL0oYDKzwtTnNBQ== + dependencies: + prop-types "^15.5.8" + +react-custom-scrollbars@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/react-custom-scrollbars/-/react-custom-scrollbars-4.2.1.tgz#830fd9502927e97e8a78c2086813899b2a8b66db" + integrity sha1-gw/ZUCkn6X6KeMIIaBOJmyqLZts= + dependencies: + dom-css "^2.0.0" + prop-types "^15.5.10" + raf "^3.1.0" + +react-dev-utils@^10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-10.2.0.tgz#b11cc48aa2be2502fb3c27a50d1dfa95cfa9dfe0" + integrity sha512-MwrvQW2TFjLblhqpDNeqCXHBkz3G5vc7k4wntgutAJZX4ia3o07eGKo6uYGhUOeJ0hfOxcpJFNFk7+4XCc1S8g== + dependencies: + "@babel/code-frame" "7.8.3" + address "1.1.2" + browserslist "4.8.6" + chalk "2.4.2" + cross-spawn "7.0.1" + detect-port-alt "1.1.6" + escape-string-regexp "2.0.0" + filesize "6.0.1" + find-up "4.1.0" + fork-ts-checker-webpack-plugin "3.1.1" + global-modules "2.0.0" + globby "8.0.2" + gzip-size "5.1.1" + immer "1.10.0" + inquirer "7.0.4" + is-root "2.1.0" + loader-utils "1.2.3" + open "^7.0.2" + pkg-up "3.1.0" + react-error-overlay "^6.0.6" + recursive-readdir "2.2.2" + shell-quote "1.7.2" + strip-ansi "6.0.0" + text-table "0.2.0" + +react-device-detect@^1.11.14: + version "1.11.14" + resolved "https://registry.yarnpkg.com/react-device-detect/-/react-device-detect-1.11.14.tgz#02ba2398e2ce81fb0eaed3e62a9ad713ab3870a7" + integrity sha512-WSjch241xI+rXHVtJaSYxNUT2WAykzfJgMI2Hg9xjNNTlIZdJu/fmWf4iedNH7qzFq+JaJ6fDJu3mrKFLerKBw== + dependencies: + ua-parser-js "^0.7.20" + +react-dom@^16.12.0: + version "16.12.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.12.0.tgz#0da4b714b8d13c2038c9396b54a92baea633fe11" + integrity sha512-LMxFfAGrcS3kETtQaCkTKjMiifahaMySFDn71fZUNpPHZQEzmk/GiAeIT8JSOrHB23fnuCOMruL2a8NYlw+8Gw== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + scheduler "^0.18.0" + +react-dropzone@^10.2.1: + version "10.2.1" + resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-10.2.1.tgz#b7520124c4a3b66f96d49f7879027c7a475eaa20" + integrity sha512-Me5nOu8hK9/Xyg5easpdfJ6SajwUquqYR/2YTdMotsCUgJ1pHIIwNsv0n+qcIno0tWR2V2rVQtj2r/hXYs2TnQ== + dependencies: + attr-accept "^2.0.0" + file-selector "^0.1.12" + prop-types "^15.7.2" + +react-error-overlay@^6.0.6: + version "6.0.6" + resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.6.tgz#ac4d9dc4c1b5c536c2c312bf66aa2b09bfa384e2" + integrity sha512-Yzpno3enVzSrSCnnljmr4b/2KUQSMZaPuqmS26t9k4nW7uwJk6STWmH9heNjPuvqUTO3jOSPkHoKgO4+Dw7uIw== + +react-i18next@11.3.0: + version "11.3.0" + resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.3.0.tgz#8c827b084708924fd2e8c787aca78e0f7966fa44" + integrity sha512-ahpEF2wYmTHkrZiz/Kgh6qW7KT0ZV2Kd0PEr7277V9+qBmBQIYXwsCWjKEDNY/nTxFpfz4DlLPdVvjerWt7uqQ== + dependencies: + "@babel/runtime" "^7.3.1" + html-parse-stringify2 "2.0.1" + +react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.9.0: + version "16.12.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c" + integrity sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q== + +react-lifecycles-compat@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" + integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== + +react-onclickoutside@^6.9.0: + version "6.9.0" + resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-6.9.0.tgz#a54bc317ae8cf6131a5d78acea55a11067f37a1f" + integrity sha512-8ltIY3bC7oGhj2nPAvWOGi+xGFybPNhJM0V1H8hY/whNcXgmDeaeoCMPPd8VatrpTsUWjb/vGzrmu6SrXVty3A== + +react-redux@7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.1.3.tgz#717a3d7bbe3a1b2d535c94885ce04cdc5a33fc79" + integrity sha512-uI1wca+ECG9RoVkWQFF4jDMqmaw0/qnvaSvOoL/GA4dNxf6LoV8sUAcNDvE5NWKs4hFpn0t6wswNQnY3f7HT3w== + dependencies: + "@babel/runtime" "^7.5.5" + hoist-non-react-statics "^3.3.0" + invariant "^2.2.4" + loose-envify "^1.4.0" + prop-types "^15.7.2" + react-is "^16.9.0" + +react-router-dom@5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.1.2.tgz#06701b834352f44d37fbb6311f870f84c76b9c18" + integrity sha512-7BPHAaIwWpZS074UKaw1FjVdZBSVWEk8IuDXdB+OkLb8vd/WRQIpA4ag9WQk61aEfQs47wHyjWUoUGGZxpQXew== + dependencies: + "@babel/runtime" "^7.1.2" + history "^4.9.0" + loose-envify "^1.3.1" + prop-types "^15.6.2" + react-router "5.1.2" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + +react-router@5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.1.2.tgz#6ea51d789cb36a6be1ba5f7c0d48dd9e817d3418" + integrity sha512-yjEuMFy1ONK246B+rsa0cUam5OeAQ8pyclRDgpxuSCrAlJ1qN9uZ5IgyKC7gQg0w8OM50NXHEegPh/ks9YuR2A== + dependencies: + "@babel/runtime" "^7.1.2" + history "^4.9.0" + hoist-non-react-statics "^3.1.0" + loose-envify "^1.3.1" + mini-create-react-context "^0.3.0" + path-to-regexp "^1.7.0" + prop-types "^15.6.2" + react-is "^16.6.0" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + +react-scripts@3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-3.4.0.tgz#f413680f0b5b937c8879ba1ffdae9b8c5b364bf5" + integrity sha512-pBqaAroFoHnFAkuX+uSK9Th1uEh2GYdGY2IG1I9/7HmuEf+ls3lLCk1p2GFYRSrLMz6ieQR/SyN6TLIGK3hKRg== + dependencies: + "@babel/core" "7.8.4" + "@svgr/webpack" "4.3.3" + "@typescript-eslint/eslint-plugin" "^2.10.0" + "@typescript-eslint/parser" "^2.10.0" + babel-eslint "10.0.3" + babel-jest "^24.9.0" + babel-loader "8.0.6" + babel-plugin-named-asset-import "^0.3.6" + babel-preset-react-app "^9.1.1" + camelcase "^5.3.1" + case-sensitive-paths-webpack-plugin "2.3.0" + css-loader "3.4.2" + dotenv "8.2.0" + dotenv-expand "5.1.0" + eslint "^6.6.0" + eslint-config-react-app "^5.2.0" + eslint-loader "3.0.3" + eslint-plugin-flowtype "4.6.0" + eslint-plugin-import "2.20.0" + eslint-plugin-jsx-a11y "6.2.3" + eslint-plugin-react "7.18.0" + eslint-plugin-react-hooks "^1.6.1" + file-loader "4.3.0" + fs-extra "^8.1.0" + html-webpack-plugin "4.0.0-beta.11" + identity-obj-proxy "3.0.0" + jest "24.9.0" + jest-environment-jsdom-fourteen "1.0.1" + jest-resolve "24.9.0" + jest-watch-typeahead "0.4.2" + mini-css-extract-plugin "0.9.0" + optimize-css-assets-webpack-plugin "5.0.3" + pnp-webpack-plugin "1.6.0" + postcss-flexbugs-fixes "4.1.0" + postcss-loader "3.0.0" + postcss-normalize "8.0.1" + postcss-preset-env "6.7.0" + postcss-safe-parser "4.0.1" + react-app-polyfill "^1.0.6" + react-dev-utils "^10.2.0" + resolve "1.15.0" + resolve-url-loader "3.1.1" + sass-loader "8.0.2" + semver "6.3.0" + style-loader "0.23.1" + terser-webpack-plugin "2.3.4" + ts-pnp "1.1.5" + url-loader "2.3.0" + webpack "4.41.5" + webpack-dev-server "3.10.2" + webpack-manifest-plugin "2.2.0" + workbox-webpack-plugin "4.3.1" + optionalDependencies: + fsevents "2.1.2" + +react-svg@^11.0.9: + version "11.0.11" + resolved "https://registry.yarnpkg.com/react-svg/-/react-svg-11.0.11.tgz#4a09fd1440d176fb857cd28c8b80c8568dcf6dd0" + integrity sha512-eNop/sMRImwSjS2Jsl7X36QN4J6wuFbh755xsLcRwE5Kcps3wZM71IJlA5zMyD8Mag//a3CsoH9TNP6E2rlxfw== + dependencies: + "@babel/runtime" "^7.8.4" + "@tanem/svg-injector" "^8.0.40" + prop-types "^15.7.2" + +react-text-mask@^5.4.3: + version "5.4.3" + resolved "https://registry.yarnpkg.com/react-text-mask/-/react-text-mask-5.4.3.tgz#991efb4299e30c2e6c2c46d13f617169463e0d2d" + integrity sha1-mR77QpnjDC5sLEbRP2FxaUY+DS0= + dependencies: + prop-types "^15.5.6" + +react-toastify@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/react-toastify/-/react-toastify-5.5.0.tgz#f55de44f6b5e3ce3b13b69e5bb4427f2c9404822" + integrity sha512-jsVme7jALIFGRyQsri/g4YTsRuaaGI70T6/ikjwZMB4mwTZaCWqj5NqxhGrRStKlJc5npXKKvKeqTiRGQl78LQ== + dependencies: + "@babel/runtime" "^7.4.2" + classnames "^2.2.6" + prop-types "^15.7.2" + react-transition-group "^4" + +react-tooltip@^3.11.2: + version "3.11.6" + resolved "https://registry.yarnpkg.com/react-tooltip/-/react-tooltip-3.11.6.tgz#4f9735a2a4aa50580af351ce23e74a56f41afc0c" + integrity sha512-nTc1yHHaPCHHURvMpf/VNF17pIZiU4zwUGFJBUVr1fZkezFC7E0VPMMVrCfDjt+IpwTHICyzlyx+1FiQ7lw5LQ== + dependencies: + prop-types "^15.6.0" + +react-transition-group@^4: + version "4.3.0" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.3.0.tgz#fea832e386cf8796c58b61874a3319704f5ce683" + integrity sha512-1qRV1ZuVSdxPlPf4O8t7inxUGpdyO5zG9IoNfJxSO0ImU2A1YWkEQvFPuIPZmMLkg5hYs7vv5mMOyfgSkvAwvw== + dependencies: + "@babel/runtime" "^7.5.5" + dom-helpers "^5.0.1" + loose-envify "^1.4.0" + prop-types "^15.6.2" + +react-virtualized-auto-sizer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.2.tgz#a61dd4f756458bbf63bd895a92379f9b70f803bd" + integrity sha512-MYXhTY1BZpdJFjUovvYHVBmkq79szK/k7V3MO+36gJkWGkrXKtyr4vCPtpphaTLRAdDNoYEYFZWE8LjN+PIHNg== + +react-window-infinite-loader@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/react-window-infinite-loader/-/react-window-infinite-loader-1.0.5.tgz#6fe094d538a88978c2c9b623052bc50cb28c2abc" + integrity sha512-IcPIq8lADK3zsAcqoLqQGyduicqR6jWkiK2VUX5sKSI9X/rou6OWlOEexnGyujdNTG7hSG8OVBFEhLSDs4qrxg== + +react-window@^1.8.5: + version "1.8.5" + resolved "https://registry.yarnpkg.com/react-window/-/react-window-1.8.5.tgz#a56b39307e79979721021f5d06a67742ecca52d1" + integrity sha512-HeTwlNa37AFa8MDZFZOKcNEkuF2YflA0hpGPiTT9vR7OawEt+GZbfM6wqkBahD3D3pUjIabQYzsnY/BSJbgq6Q== + dependencies: + "@babel/runtime" "^7.0.0" + memoize-one ">=3.1.1 <6" + +react@^16.12.0: + version "16.12.0" + resolved "https://registry.yarnpkg.com/react/-/react-16.12.0.tgz#0c0a9c6a142429e3614834d5a778e18aa78a0b83" + integrity sha512-fglqy3k5E+81pA8s+7K0/T3DBCF0ZDOher1elBFzF7O6arXJgzyu/FW+COxFvAWXJoJN9KIZbT2LXlukwphYTA== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" + integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4= + dependencies: + find-up "^2.0.0" + read-pkg "^2.0.0" + +read-pkg-up@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978" + integrity sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA== + dependencies: + find-up "^3.0.0" + read-pkg "^3.0.0" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +read-pkg@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" + integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg= + dependencies: + load-json-file "^2.0.0" + normalize-package-data "^2.3.2" + path-type "^2.0.0" + +read-pkg@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" + integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= + dependencies: + load-json-file "^4.0.0" + normalize-package-data "^2.3.2" + path-type "^3.0.0" + +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.0.6, readable-stream@^3.1.1: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" + integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== + dependencies: + graceful-fs "^4.1.11" + micromatch "^3.1.10" + readable-stream "^2.0.2" + +readdirp@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.3.0.tgz#984458d13a1e42e2e9f5841b129e162f369aff17" + integrity sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ== + dependencies: + picomatch "^2.0.7" + +realpath-native@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c" + integrity sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA== + dependencies: + util.promisify "^1.0.0" + +recursive-readdir@2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.2.tgz#9946fb3274e1628de6e36b2f6714953b4845094f" + integrity sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg== + dependencies: + minimatch "3.0.4" + +redent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" + integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94= + dependencies: + indent-string "^2.1.0" + strip-indent "^1.0.1" + +redux-devtools-extension@^2.13.8: + version "2.13.8" + resolved "https://registry.yarnpkg.com/redux-devtools-extension/-/redux-devtools-extension-2.13.8.tgz#37b982688626e5e4993ff87220c9bbb7cd2d96e1" + integrity sha512-8qlpooP2QqPtZHQZRhx3x3OP5skEV1py/zUdMY28WNAocbafxdG2tRD1MWE7sp8obGMNYuLWanhhQ7EQvT1FBg== + +redux-thunk@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622" + integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw== + +redux@4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f" + integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w== + dependencies: + loose-envify "^1.4.0" + symbol-observable "^1.2.0" + +regenerate-unicode-properties@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e" + integrity sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA== + dependencies: + regenerate "^1.4.0" + +regenerate@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" + integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg== + +regenerator-runtime@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" + integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== + +regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.3: + version "0.13.3" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5" + integrity sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw== + +regenerator-transform@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.1.tgz#3b2fce4e1ab7732c08f665dfdb314749c7ddd2fb" + integrity sha512-flVuee02C3FKRISbxhXl9mGzdbWUVHubl1SMaknjxkFB1/iqpJhArQUvRxOOPEc/9tAiX0BaQ28FJH10E4isSQ== + dependencies: + private "^0.1.6" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +regex-parser@2.2.10: + version "2.2.10" + resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.2.10.tgz#9e66a8f73d89a107616e63b39d4deddfee912b37" + integrity sha512-8t6074A68gHfU8Neftl0Le6KTDwfGAj7IyjPIMSfikI2wJUTHDMaIq42bUsfVnj8mhx0R+45rdUXHGpN164avA== + +regexp.prototype.flags@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75" + integrity sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + +regexpp@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== + +regexpp@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.0.0.tgz#dd63982ee3300e67b41c1956f850aa680d9d330e" + integrity sha512-Z+hNr7RAVWxznLPuA7DIh8UNX1j9CDrUQxskw9IrBE1Dxue2lyXT+shqEIeLUjrokxIP8CMy1WkjgG3rTsd5/g== + +regexpu-core@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.6.0.tgz#2037c18b327cfce8a6fea2a4ec441f2432afb8b6" + integrity sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg== + dependencies: + regenerate "^1.4.0" + regenerate-unicode-properties "^8.1.0" + regjsgen "^0.5.0" + regjsparser "^0.6.0" + unicode-match-property-ecmascript "^1.0.4" + unicode-match-property-value-ecmascript "^1.1.0" + +regjsgen@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.1.tgz#48f0bf1a5ea205196929c0d9798b42d1ed98443c" + integrity sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg== + +regjsparser@^0.6.0: + version "0.6.3" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.3.tgz#74192c5805d35e9f5ebe3c1fb5b40d40a8a38460" + integrity sha512-8uZvYbnfAtEm9Ab8NTb3hdLwL4g/LQzEYP7Xs27T96abJCCE2d6r3cPZPQEsLKy0vRSGVNG+/zVGtLr86HQduA== + dependencies: + jsesc "~0.5.0" + +relateurl@^0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" + integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= + +renderkid@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.3.tgz#380179c2ff5ae1365c522bf2fcfcff01c5b74149" + integrity sha512-z8CLQp7EZBPCwCnncgf9C4XAi3WR0dv+uWu/PjIyhhAb5d6IJ/QZqlHFprHeKT+59//V6BNUsLbvN8+2LarxGA== + dependencies: + css-select "^1.1.0" + dom-converter "^0.2" + htmlparser2 "^3.3.0" + strip-ansi "^3.0.0" + utila "^0.4.0" + +repeat-element@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" + integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== + +repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= + dependencies: + is-finite "^1.0.0" + +request-promise-core@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.3.tgz#e9a3c081b51380dfea677336061fea879a829ee9" + integrity sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ== + dependencies: + lodash "^4.17.15" + +request-promise-native@^1.0.5: + version "1.0.8" + resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.8.tgz#a455b960b826e44e2bf8999af64dff2bfe58cb36" + integrity sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ== + dependencies: + request-promise-core "1.1.3" + stealthy-require "^1.1.1" + tough-cookie "^2.3.3" + +request@^2.87.0, request@^2.88.0: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-main-filename@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= + +resize-image@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/resize-image/-/resize-image-0.1.0.tgz#033d5f499cb7095def7827d48b8046f7e1c5776c" + integrity sha512-78cWCEX/IupMCwKi5Gg5gxjOZKoN6UXinL2eVOB4xzyG2QNjq64z6pJBnyBKg5/BsjEosLz1co5e3DHr99TgFg== + +resolve-cwd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" + integrity sha1-AKn3OHVW4nA46uIyyqNypqWbZlo= + dependencies: + resolve-from "^3.0.0" + +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + integrity sha1-six699nWiBvItuZTM17rywoYh0g= + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-pathname@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd" + integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng== + +resolve-url-loader@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/resolve-url-loader/-/resolve-url-loader-3.1.1.tgz#28931895fa1eab9be0647d3b2958c100ae3c0bf0" + integrity sha512-K1N5xUjj7v0l2j/3Sgs5b8CjrrgtC70SmdCuZiJ8tSyb5J+uk3FoeZ4b7yTnH6j7ngI+Bc5bldHJIa8hYdu2gQ== + dependencies: + adjust-sourcemap-loader "2.0.0" + camelcase "5.3.1" + compose-function "3.0.3" + convert-source-map "1.7.0" + es6-iterator "2.0.3" + loader-utils "1.2.3" + postcss "7.0.21" + rework "1.0.1" + rework-visit "1.0.0" + source-map "0.6.1" + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= + +resolve@1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" + integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= + +resolve@1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.0.tgz#1b7ca96073ebb52e741ffd799f6b39ea462c67f5" + integrity sha512-+hTmAldEGE80U2wJJDC1lebb5jWqvTYAfm3YZ1ckk1gBr0MnCqUKlwK1e+anaFljIl+F5tR5IoZcm4ZDA1zMQw== + dependencies: + path-parse "^1.0.6" + +resolve@^1.10.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.3.2, resolve@^1.8.1: + version "1.15.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8" + integrity sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w== + dependencies: + path-parse "^1.0.6" + +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= + +rework-visit@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/rework-visit/-/rework-visit-1.0.0.tgz#9945b2803f219e2f7aca00adb8bc9f640f842c9a" + integrity sha1-mUWygD8hni96ygCtuLyfZA+ELJo= + +rework@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/rework/-/rework-1.0.1.tgz#30806a841342b54510aa4110850cd48534144aa7" + integrity sha1-MIBqhBNCtUUQqkEQhQzUhTQUSqc= + dependencies: + convert-source-map "^0.3.3" + css "^2.0.0" + +rgb-regex@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1" + integrity sha1-wODWiC3w4jviVKR16O3UGRX+rrE= + +rgba-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" + integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= + +rimraf@2, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.3, rimraf@^2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +rimraf@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + +rimraf@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.0.tgz#614176d4b3010b75e5c390eb0ee96f6dc0cebb9b" + integrity sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg== + dependencies: + glob "^7.1.3" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +rsvp@^4.8.4: + version "4.8.5" + resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" + integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== + +run-async@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" + integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA= + dependencies: + is-promise "^2.1.0" + +run-queue@^1.0.0, run-queue@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" + integrity sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec= + dependencies: + aproba "^1.1.1" + +rxjs@^6.5.3: + version "6.5.4" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.4.tgz#e0777fe0d184cec7872df147f303572d414e211c" + integrity sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q== + dependencies: + tslib "^1.9.0" + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" + integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sane@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" + integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA== + dependencies: + "@cnakazawa/watch" "^1.0.3" + anymatch "^2.0.0" + capture-exit "^2.0.0" + exec-sh "^0.3.2" + execa "^1.0.0" + fb-watchman "^2.0.0" + micromatch "^3.1.4" + minimist "^1.1.1" + walker "~1.0.5" + +sanitize.css@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/sanitize.css/-/sanitize.css-10.0.0.tgz#b5cb2547e96d8629a60947544665243b1dc3657a" + integrity sha512-vTxrZz4dX5W86M6oVWVdOVe72ZiPs41Oi7Z6Km4W5Turyz28mrXSJhhEBZoRtzJWIv3833WKVwLSDWWkEfupMg== + +sass-graph@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.4.tgz#13fbd63cd1caf0908b9fd93476ad43a51d1e0b49" + integrity sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k= + dependencies: + glob "^7.0.0" + lodash "^4.0.0" + scss-tokenizer "^0.2.3" + yargs "^7.0.0" + +sass-loader@8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-8.0.2.tgz#debecd8c3ce243c76454f2e8290482150380090d" + integrity sha512-7o4dbSK8/Ol2KflEmSco4jTjQoV988bM82P9CZdmo9hR3RLnvNc0ufMNdMrB0caq38JQ/FgF4/7RcbcfKzxoFQ== + dependencies: + clone-deep "^4.0.1" + loader-utils "^1.2.3" + neo-async "^2.6.1" + schema-utils "^2.6.1" + semver "^6.3.0" + +sax@^1.2.4, sax@~1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + +saxes@^3.1.9: + version "3.1.11" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-3.1.11.tgz#d59d1fd332ec92ad98a2e0b2ee644702384b1c5b" + integrity sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g== + dependencies: + xmlchars "^2.1.1" + +scheduler@^0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.18.0.tgz#5901ad6659bc1d8f3fdaf36eb7a67b0d6746b1c4" + integrity sha512-agTSHR1Nbfi6ulI0kYNK0203joW2Y5W4po4l+v03tOoiJKpTBbxpNhWDvqc/4IcOw+KLmSiQLTasZ4cab2/UWQ== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + +schema-utils@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" + integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g== + dependencies: + ajv "^6.1.0" + ajv-errors "^1.0.0" + ajv-keywords "^3.1.0" + +schema-utils@^2.5.0, schema-utils@^2.6.0, schema-utils@^2.6.1, schema-utils@^2.6.4: + version "2.6.4" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.6.4.tgz#a27efbf6e4e78689d91872ee3ccfa57d7bdd0f53" + integrity sha512-VNjcaUxVnEeun6B2fiiUDjXXBtD4ZSH7pdbfIu1pOFwgptDPLMo/z9jr4sUfsjFVPqDCEin/F7IYlq7/E6yDbQ== + dependencies: + ajv "^6.10.2" + ajv-keywords "^3.4.1" + +scss-tokenizer@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1" + integrity sha1-jrBtualyMzOCTT9VMGQRSYR85dE= + dependencies: + js-base64 "^2.1.8" + source-map "^0.4.2" + +seamless-immutable@^7.1.3: + version "7.1.4" + resolved "https://registry.yarnpkg.com/seamless-immutable/-/seamless-immutable-7.1.4.tgz#6e9536def083ddc4dea0207d722e0e80d0f372f8" + integrity sha512-XiUO1QP4ki4E2PHegiGAlu6r82o5A+6tRh7IkGGTVg/h+UoeX4nFBeCGPOhb4CYjvkqsfm/TUtvOMYC1xmV30A== + +select-hose@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" + integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo= + +selfsigned@^1.10.7: + version "1.10.7" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.7.tgz#da5819fd049d5574f28e88a9bcc6dbc6e6f3906b" + integrity sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA== + dependencies: + node-forge "0.9.0" + +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@6.3.0, semver@^6.0.0, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" + integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== + +semver@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" + integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8= + +send@0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + +serialize-javascript@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61" + integrity sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ== + +serve-index@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" + integrity sha1-03aNabHn2C5c4FD/9bRTvqEqkjk= + dependencies: + accepts "~1.3.4" + batch "0.6.1" + debug "2.6.9" + escape-html "~1.0.3" + http-errors "~1.6.2" + mime-types "~2.1.17" + parseurl "~1.3.2" + +serve-static@1.14.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.1" + +set-blocking@^2.0.0, set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +setimmediate@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== + +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +shallow-clone@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-0.1.2.tgz#5909e874ba77106d73ac414cfec1ffca87d97060" + integrity sha1-WQnodLp3EG1zrEFM/sH/yofZcGA= + dependencies: + is-extendable "^0.1.1" + kind-of "^2.0.1" + lazy-cache "^0.2.3" + mixin-object "^2.0.1" + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shallowequal@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" + integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ== + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shell-quote@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" + integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== + +shellwords@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" + integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== + +signal-exit@^3.0.0, signal-exit@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= + +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo= + dependencies: + is-arrayish "^0.3.1" + +sisteransi@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.4.tgz#386713f1ef688c7c0304dc4c0632898941cad2e3" + integrity sha512-/ekMoM4NJ59ivGSfKapeG+FWtrmWvA1p6FBZwXrqojw90vJu8lBmrTxCMuBCydKtkaUe2zt4PlxeTKpjwMbyig== + +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= + +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" + integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +sockjs-client@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.4.0.tgz#c9f2568e19c8fd8173b4997ea3420e0bb306c7d5" + integrity sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g== + dependencies: + debug "^3.2.5" + eventsource "^1.0.7" + faye-websocket "~0.11.1" + inherits "^2.0.3" + json3 "^3.3.2" + url-parse "^1.4.3" + +sockjs@0.3.19: + version "0.3.19" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.19.tgz#d976bbe800af7bd20ae08598d582393508993c0d" + integrity sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw== + dependencies: + faye-websocket "^0.10.0" + uuid "^3.0.1" + +sort-keys@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" + integrity sha1-RBttTTRnmPG05J6JIK37oOVD+a0= + dependencies: + is-plain-obj "^1.0.0" + +source-list-map@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" + integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== + +source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@^0.5.6, source-map-support@~0.5.12: + version "0.5.16" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" + integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= + +source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@^0.4.2: + version "0.4.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" + integrity sha1-66T12pwNyZneaAMti092FzZSA2s= + dependencies: + amdefine ">=0.0.4" + +source-map@^0.5.0, source-map@^0.5.6: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +spdx-correct@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4" + integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977" + integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA== + +spdx-expression-parse@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" + integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.5" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654" + integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q== + +spdy-transport@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" + integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== + dependencies: + debug "^4.1.0" + detect-node "^2.0.4" + hpack.js "^2.1.6" + obuf "^1.1.2" + readable-stream "^3.0.6" + wbuf "^1.7.3" + +spdy@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.1.tgz#6f12ed1c5db7ea4f24ebb8b89ba58c87c08257f2" + integrity sha512-HeZS3PBdMA+sZSu0qwpCxl3DeALD5ASx8pAX0jZdKXSpPWbQ6SYGnlg3BBmYLx5LtiZrmkAZfErCm2oECBcioA== + dependencies: + debug "^4.1.0" + handle-thing "^2.0.0" + http-deceiver "^1.2.7" + select-hose "^2.0.0" + spdy-transport "^3.0.0" + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +ssri@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8" + integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA== + dependencies: + figgy-pudding "^3.5.1" + +ssri@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-7.1.0.tgz#92c241bf6de82365b5c7fb4bd76e975522e1294d" + integrity sha512-77/WrDZUWocK0mvA5NTRQyveUf+wsrIc6vyrxpS8tVvYBcX215QbafrJR3KtkpskIzoFLqqNuuYQvxaMjXJ/0g== + dependencies: + figgy-pudding "^3.5.1" + minipass "^3.1.1" + +stable@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" + integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== + +stack-utils@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8" + integrity sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA== + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +stdout-stream@^1.4.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.1.tgz#5ac174cdd5cd726104aa0c0b2bd83815d8d535de" + integrity sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA== + dependencies: + readable-stream "^2.0.1" + +stealthy-require@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" + integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= + +stream-browserify@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" + integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== + dependencies: + inherits "~2.0.1" + readable-stream "^2.0.2" + +stream-each@^1.1.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae" + integrity sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw== + dependencies: + end-of-stream "^1.1.0" + stream-shift "^1.0.0" + +stream-http@^2.7.2: + version "2.8.3" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" + integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw== + dependencies: + builtin-status-codes "^3.0.0" + inherits "^2.0.1" + readable-stream "^2.3.6" + to-arraybuffer "^1.0.0" + xtend "^4.0.0" + +stream-shift@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" + integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== + +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= + +string-length@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed" + integrity sha1-1A27aGo6zpYMHP/KVivyxF+DY+0= + dependencies: + astral-regex "^1.0.0" + strip-ansi "^4.0.0" + +string-length@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-3.1.0.tgz#107ef8c23456e187a8abd4a61162ff4ac6e25837" + integrity sha512-Ttp5YvkGm5v9Ijagtaz1BnN+k9ObpvS0eIBblPMp2YWL8FBmi9qblQ9fexc2k/CXFgrTIteU3jAw3payCnwSTA== + dependencies: + astral-regex "^1.0.0" + strip-ansi "^5.2.0" + +string-width@^1.0.1, string-width@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" + integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + +string.prototype.trimleft@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz#9bdb8ac6abd6d602b17a4ed321870d2f8dcefc74" + integrity sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag== + dependencies: + define-properties "^1.1.3" + function-bind "^1.1.1" + +string.prototype.trimright@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz#440314b15996c866ce8a0341894d45186200c5d9" + integrity sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g== + dependencies: + define-properties "^1.1.3" + function-bind "^1.1.1" + +string_decoder@^1.0.0, string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +stringify-object@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" + integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== + dependencies: + get-own-enumerable-property-symbols "^3.0.0" + is-obj "^1.0.1" + is-regexp "^1.0.0" + +strip-ansi@6.0.0, strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= + dependencies: + is-utf8 "^0.2.0" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + +strip-comments@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/strip-comments/-/strip-comments-1.0.2.tgz#82b9c45e7f05873bee53f37168af930aa368679d" + integrity sha512-kL97alc47hoyIQSV165tTt9rG5dn4w1dNnBhOQ3bOU1Nc1hel09jnXANaHJ7vzHLd4Ju8kseDGzlev96pghLFw== + dependencies: + babel-extract-comments "^1.0.0" + babel-plugin-transform-object-rest-spread "^6.26.0" + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= + +strip-indent@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" + integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI= + dependencies: + get-stdin "^4.0.1" + +strip-json-comments@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" + integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +style-loader@0.23.1: + version "0.23.1" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.23.1.tgz#cb9154606f3e771ab6c4ab637026a1049174d925" + integrity sha512-XK+uv9kWwhZMZ1y7mysB+zoihsEj4wneFWAS5qoiLwzW0WzSqMrrsIy+a3zkQJq0ipFtBpX5W3MqyRIBF/WFGg== + dependencies: + loader-utils "^1.1.0" + schema-utils "^1.0.0" + +styled-components@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.0.1.tgz#57782a6471031abefb2db5820a1876ae853bc619" + integrity sha512-E0xKTRIjTs4DyvC1MHu/EcCXIj6+ENCP8hP01koyoADF++WdBUOrSGwU1scJRw7/YaYOhDvvoad6VlMG+0j53A== + dependencies: + "@babel/helper-module-imports" "^7.0.0" + "@babel/traverse" "^7.4.5" + "@emotion/is-prop-valid" "^0.8.3" + "@emotion/stylis" "^0.8.4" + "@emotion/unitless" "^0.7.4" + babel-plugin-styled-components ">= 1" + css-to-react-native "^3.0.0" + hoist-non-react-statics "^3.0.0" + shallowequal "^1.1.0" + supports-color "^5.5.0" + +stylehacks@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5" + integrity sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g== + dependencies: + browserslist "^4.0.0" + postcss "^7.0.0" + postcss-selector-parser "^3.0.0" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= + +supports-color@^5.3.0, supports-color@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" + integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== + dependencies: + has-flag "^4.0.0" + +svg-parser@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.3.tgz#a38f2e4e5442986f7ecb554c11f1411cfcf8c2b9" + integrity sha512-fnCWiifNhK8i2Z7b9R5tbNahpxrRdAaQbnoxKlT2KrSCj9Kq/yBSgulCRgBJRhy1dPnSY5slg5ehPUnzpEcHlg== + +svgo@^1.0.0, svgo@^1.2.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167" + integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw== + dependencies: + chalk "^2.4.1" + coa "^2.0.2" + css-select "^2.0.0" + css-select-base-adapter "^0.1.1" + css-tree "1.0.0-alpha.37" + csso "^4.0.2" + js-yaml "^3.13.1" + mkdirp "~0.5.1" + object.values "^1.1.0" + sax "~1.2.4" + stable "^0.1.8" + unquote "~1.1.1" + util.promisify "~1.0.0" + +symbol-observable@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" + integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== + +symbol-tree@^3.2.2: + version "3.2.4" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + +table@^5.2.3: + version "5.4.6" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" + integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== + dependencies: + ajv "^6.10.2" + lodash "^4.17.14" + slice-ansi "^2.1.0" + string-width "^3.0.0" + +tapable@^1.0.0, tapable@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" + integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== + +tar@^2.0.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.2.tgz#0ca8848562c7299b8b446ff6a4d60cdbb23edc40" + integrity sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA== + dependencies: + block-stream "*" + fstream "^1.0.12" + inherits "2" + +tar@^4.4.2: + version "4.4.13" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" + integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== + dependencies: + chownr "^1.1.1" + fs-minipass "^1.2.5" + minipass "^2.8.6" + minizlib "^1.2.1" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.3" + +terser-webpack-plugin@2.3.4: + version "2.3.4" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-2.3.4.tgz#ac045703bd8da0936ce910d8fb6350d0e1dee5fe" + integrity sha512-Nv96Nws2R2nrFOpbzF6IxRDpIkkIfmhvOws+IqMvYdFLO7o6wAILWFKONFgaYy8+T4LVz77DQW0f7wOeDEAjrg== + dependencies: + cacache "^13.0.1" + find-cache-dir "^3.2.0" + jest-worker "^25.1.0" + p-limit "^2.2.2" + schema-utils "^2.6.4" + serialize-javascript "^2.1.2" + source-map "^0.6.1" + terser "^4.4.3" + webpack-sources "^1.4.3" + +terser-webpack-plugin@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz#5ecaf2dbdc5fb99745fd06791f46fc9ddb1c9a7c" + integrity sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA== + dependencies: + cacache "^12.0.2" + find-cache-dir "^2.1.0" + is-wsl "^1.1.0" + schema-utils "^1.0.0" + serialize-javascript "^2.1.2" + source-map "^0.6.1" + terser "^4.1.2" + webpack-sources "^1.4.0" + worker-farm "^1.7.0" + +terser@^4.1.2, terser@^4.4.3, terser@^4.6.3: + version "4.6.4" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.4.tgz#40a0b37afbe5b57e494536815efa68326840fc00" + integrity sha512-5fqgBPLgVHZ/fVvqRhhUp9YUiGXhFJ9ZkrZWD9vQtFBR4QIGTnbsb+/kKqSqfgp3WnBwGWAFnedGTtmX1YTn0w== + dependencies: + commander "^2.20.0" + source-map "~0.6.1" + source-map-support "~0.5.12" + +test-exclude@^5.2.3: + version "5.2.3" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-5.2.3.tgz#c3d3e1e311eb7ee405e092dac10aefd09091eac0" + integrity sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g== + dependencies: + glob "^7.1.3" + minimatch "^3.0.4" + read-pkg-up "^4.0.0" + require-main-filename "^2.0.0" + +text-table@0.2.0, text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + +throat@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a" + integrity sha1-iQN8vJLFarGJJua6TLsgDhVnKmo= + +through2@^2.0.0: + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== + dependencies: + readable-stream "~2.3.6" + xtend "~4.0.1" + +through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +thunky@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" + integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== + +timers-browserify@^2.0.4: + version "2.0.11" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.11.tgz#800b1f3eee272e5bc53ee465a04d0e804c31211f" + integrity sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ== + dependencies: + setimmediate "^1.0.4" + +timsort@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" + integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= + +tiny-invariant@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875" + integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw== + +tiny-warning@^1.0.0, tiny-warning@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" + integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +tmpl@1.0.x: + version "1.0.4" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" + integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= + +to-arraybuffer@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" + integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M= + +to-camel-case@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-camel-case/-/to-camel-case-1.0.0.tgz#1a56054b2f9d696298ce66a60897322b6f423e46" + integrity sha1-GlYFSy+daWKYzmamCJcyK29CPkY= + dependencies: + to-space-case "^1.0.0" + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + +to-no-case@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/to-no-case/-/to-no-case-1.0.2.tgz#c722907164ef6b178132c8e69930212d1b4aa16a" + integrity sha1-xyKQcWTvaxeBMsjmmTAhLRtKoWo= + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +to-space-case@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-space-case/-/to-space-case-1.0.0.tgz#b052daafb1b2b29dc770cea0163e5ec0ebc9fc17" + integrity sha1-sFLar7Gysp3HcM6gFj5ewOvJ/Bc= + dependencies: + to-no-case "^1.0.0" + +toggle-selection@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" + integrity sha1-bkWxJj8gF/oKzH2J14sVuL932jI= + +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + +tough-cookie@^2.3.3, tough-cookie@^2.3.4, tough-cookie@^2.5.0, tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +tr46@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" + integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk= + dependencies: + punycode "^2.1.0" + +trim-newlines@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" + integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= + +"true-case-path@^1.0.2": + version "1.0.3" + resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-1.0.3.tgz#f813b5a8c86b40da59606722b144e3225799f47d" + integrity sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew== + dependencies: + glob "^7.1.2" + +ts-pnp@1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.1.5.tgz#840e0739c89fce5f3abd9037bb091dbff16d9dec" + integrity sha512-ti7OGMOUOzo66wLF3liskw6YQIaSsBgc4GOAlWRnIEj8htCxJUxskanMUoJOD6MDCRAXo36goXJZch+nOS0VMA== + +ts-pnp@^1.1.2: + version "1.1.6" + resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.1.6.tgz#389a24396d425a0d3162e96d2b4638900fdc289a" + integrity sha512-CrG5GqAAzMT7144Cl+UIFP7mz/iIhiy+xQ6GGcnjTezhALT02uPMRw7tgDSESgB5MsfKt55+GPWw4ir1kVtMIQ== + +tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.0.tgz#f1f3528301621a53220d58373ae510ff747a66bc" + integrity sha512-BmndXUtiTn/VDDrJzQE7Mm22Ix3PxgLltW9bSNLoeCY31gnG2OPx0QqJnuc9oMIKioYrz487i6K9o4Pdn0j+Kg== + +tsutils@^3.17.1: + version "3.17.1" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" + integrity sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g== + dependencies: + tslib "^1.8.1" + +tty-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" + integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + dependencies: + prelude-ls "~1.1.2" + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +type-is@~1.6.17, type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.0.0.tgz#5f16ff6ef2eb44f260494dae271033b29c09a9c3" + integrity sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow== + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + +ua-parser-js@^0.7.20: + version "0.7.21" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.21.tgz#853cf9ce93f642f67174273cc34565ae6f308777" + integrity sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ== + +unicode-canonical-property-names-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" + integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ== + +unicode-match-property-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" + integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg== + dependencies: + unicode-canonical-property-names-ecmascript "^1.0.4" + unicode-property-aliases-ecmascript "^1.0.4" + +unicode-match-property-value-ecmascript@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz#5b4b426e08d13a80365e0d657ac7a6c1ec46a277" + integrity sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g== + +unicode-property-aliases-ecmascript@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz#a9cc6cc7ce63a0a3023fc99e341b94431d405a57" + integrity sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw== + +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.1" + +uniq@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" + integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= + +uniqs@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" + integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI= + +unique-filename@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" + integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== + dependencies: + unique-slug "^2.0.0" + +unique-slug@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" + integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== + dependencies: + imurmurhash "^0.1.4" + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + +unquote@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" + integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ= + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +upath@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" + integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== + +uri-js@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== + dependencies: + punycode "^2.1.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= + +url-loader@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-2.3.0.tgz#e0e2ef658f003efb8ca41b0f3ffbf76bab88658b" + integrity sha512-goSdg8VY+7nPZKUEChZSEtW5gjbS66USIGCeSJ1OVOJ7Yfuh/36YxCwMi5HVEJh6mqUYOoy3NJ0vlOMrWsSHog== + dependencies: + loader-utils "^1.2.3" + mime "^2.4.4" + schema-utils "^2.5.0" + +url-parse@^1.4.3: + version "1.4.7" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" + integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + +url@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +util.promisify@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" + integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA== + dependencies: + define-properties "^1.1.2" + object.getownpropertydescriptors "^2.0.3" + +util.promisify@^1.0.0, util.promisify@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee" + integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.2" + has-symbols "^1.0.1" + object.getownpropertydescriptors "^2.1.0" + +util@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= + dependencies: + inherits "2.0.1" + +util@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" + integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ== + dependencies: + inherits "2.0.3" + +utila@^0.4.0, utila@~0.4: + version "0.4.0" + resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" + integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw= + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + +uuid@^3.0.1, uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +v8-compile-cache@^2.0.3: + version "2.1.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" + integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g== + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +value-equal@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c" + integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw== + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + +vendors@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.4.tgz#e2b800a53e7a29b93506c3cf41100d16c4c4ad8e" + integrity sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w== + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +vm-browserify@^1.0.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" + integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== + +void-elements@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" + integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w= + +w3c-hr-time@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz#82ac2bff63d950ea9e3189a58a65625fedf19045" + integrity sha1-gqwr/2PZUOqeMYmlimViX+3xkEU= + dependencies: + browser-process-hrtime "^0.1.2" + +w3c-xmlserializer@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz#30485ca7d70a6fd052420a3d12fd90e6339ce794" + integrity sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg== + dependencies: + domexception "^1.0.1" + webidl-conversions "^4.0.2" + xml-name-validator "^3.0.0" + +walker@^1.0.7, walker@~1.0.5: + version "1.0.7" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" + integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs= + dependencies: + makeerror "1.0.x" + +warning@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" + integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== + dependencies: + loose-envify "^1.0.0" + +watchpack@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00" + integrity sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA== + dependencies: + chokidar "^2.0.2" + graceful-fs "^4.1.2" + neo-async "^2.5.0" + +wbuf@^1.1.0, wbuf@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" + integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== + dependencies: + minimalistic-assert "^1.0.0" + +webidl-conversions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" + integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== + +webpack-dev-middleware@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz#0019c3db716e3fa5cecbf64f2ab88a74bab331f3" + integrity sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw== + dependencies: + memory-fs "^0.4.1" + mime "^2.4.4" + mkdirp "^0.5.1" + range-parser "^1.2.1" + webpack-log "^2.0.0" + +webpack-dev-server@3.10.2: + version "3.10.2" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.10.2.tgz#3403287d674c7407aab6d9b3f72259ecd0aa0874" + integrity sha512-pxZKPYb+n77UN8u9YxXT4IaIrGcNtijh/mi8TXbErHmczw0DtPnMTTjHj+eNjkqLOaAZM/qD7V59j/qJsEiaZA== + dependencies: + ansi-html "0.0.7" + bonjour "^3.5.0" + chokidar "^2.1.8" + compression "^1.7.4" + connect-history-api-fallback "^1.6.0" + debug "^4.1.1" + del "^4.1.1" + express "^4.17.1" + html-entities "^1.2.1" + http-proxy-middleware "0.19.1" + import-local "^2.0.0" + internal-ip "^4.3.0" + ip "^1.1.5" + is-absolute-url "^3.0.3" + killable "^1.0.1" + loglevel "^1.6.6" + opn "^5.5.0" + p-retry "^3.0.1" + portfinder "^1.0.25" + schema-utils "^1.0.0" + selfsigned "^1.10.7" + semver "^6.3.0" + serve-index "^1.9.1" + sockjs "0.3.19" + sockjs-client "1.4.0" + spdy "^4.0.1" + strip-ansi "^3.0.1" + supports-color "^6.1.0" + url "^0.11.0" + webpack-dev-middleware "^3.7.2" + webpack-log "^2.0.0" + ws "^6.2.1" + yargs "12.0.5" + +webpack-log@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f" + integrity sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg== + dependencies: + ansi-colors "^3.0.0" + uuid "^3.3.2" + +webpack-manifest-plugin@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/webpack-manifest-plugin/-/webpack-manifest-plugin-2.2.0.tgz#19ca69b435b0baec7e29fbe90fb4015de2de4f16" + integrity sha512-9S6YyKKKh/Oz/eryM1RyLVDVmy3NSPV0JXMRhZ18fJsq+AwGxUY34X54VNwkzYcEmEkDwNxuEOboCZEebJXBAQ== + dependencies: + fs-extra "^7.0.0" + lodash ">=3.5 <5" + object.entries "^1.1.0" + tapable "^1.0.0" + +webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" + integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== + dependencies: + source-list-map "^2.0.0" + source-map "~0.6.1" + +webpack@4.41.5: + version "4.41.5" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.41.5.tgz#3210f1886bce5310e62bb97204d18c263341b77c" + integrity sha512-wp0Co4vpyumnp3KlkmpM5LWuzvZYayDwM2n17EHFr4qxBBbRokC7DJawPJC7TfSFZ9HZ6GsdH40EBj4UV0nmpw== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-module-context" "1.8.5" + "@webassemblyjs/wasm-edit" "1.8.5" + "@webassemblyjs/wasm-parser" "1.8.5" + acorn "^6.2.1" + ajv "^6.10.2" + ajv-keywords "^3.4.1" + chrome-trace-event "^1.0.2" + enhanced-resolve "^4.1.0" + eslint-scope "^4.0.3" + json-parse-better-errors "^1.0.2" + loader-runner "^2.4.0" + loader-utils "^1.2.3" + memory-fs "^0.4.1" + micromatch "^3.1.10" + mkdirp "^0.5.1" + neo-async "^2.6.1" + node-libs-browser "^2.2.1" + schema-utils "^1.0.0" + tapable "^1.1.3" + terser-webpack-plugin "^1.4.3" + watchpack "^1.6.0" + webpack-sources "^1.4.1" + +websocket-driver@>=0.5.1: + version "0.7.3" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.3.tgz#a2d4e0d4f4f116f1e6297eba58b05d430100e9f9" + integrity sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg== + dependencies: + http-parser-js ">=0.4.0 <0.4.11" + safe-buffer ">=5.1.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29" + integrity sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg== + +whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3, whatwg-encoding@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" + integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== + dependencies: + iconv-lite "0.4.24" + +whatwg-fetch@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb" + integrity sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q== + +whatwg-mimetype@^2.1.0, whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" + integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== + +whatwg-url@^6.4.1: + version "6.5.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8" + integrity sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^1.0.1" + webidl-conversions "^4.0.2" + +whatwg-url@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" + integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^1.0.1" + webidl-conversions "^4.0.2" + +which-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" + integrity sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8= + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which@1, which@^1.2.9, which@^1.3.0, which@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wide-align@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + +word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +workbox-background-sync@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/workbox-background-sync/-/workbox-background-sync-4.3.1.tgz#26821b9bf16e9e37fd1d640289edddc08afd1950" + integrity sha512-1uFkvU8JXi7L7fCHVBEEnc3asPpiAL33kO495UMcD5+arew9IbKW2rV5lpzhoWcm/qhGB89YfO4PmB/0hQwPRg== + dependencies: + workbox-core "^4.3.1" + +workbox-broadcast-update@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/workbox-broadcast-update/-/workbox-broadcast-update-4.3.1.tgz#e2c0280b149e3a504983b757606ad041f332c35b" + integrity sha512-MTSfgzIljpKLTBPROo4IpKjESD86pPFlZwlvVG32Kb70hW+aob4Jxpblud8EhNb1/L5m43DUM4q7C+W6eQMMbA== + dependencies: + workbox-core "^4.3.1" + +workbox-build@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/workbox-build/-/workbox-build-4.3.1.tgz#414f70fb4d6de47f6538608b80ec52412d233e64" + integrity sha512-UHdwrN3FrDvicM3AqJS/J07X0KXj67R8Cg0waq1MKEOqzo89ap6zh6LmaLnRAjpB+bDIz+7OlPye9iii9KBnxw== + dependencies: + "@babel/runtime" "^7.3.4" + "@hapi/joi" "^15.0.0" + common-tags "^1.8.0" + fs-extra "^4.0.2" + glob "^7.1.3" + lodash.template "^4.4.0" + pretty-bytes "^5.1.0" + stringify-object "^3.3.0" + strip-comments "^1.0.2" + workbox-background-sync "^4.3.1" + workbox-broadcast-update "^4.3.1" + workbox-cacheable-response "^4.3.1" + workbox-core "^4.3.1" + workbox-expiration "^4.3.1" + workbox-google-analytics "^4.3.1" + workbox-navigation-preload "^4.3.1" + workbox-precaching "^4.3.1" + workbox-range-requests "^4.3.1" + workbox-routing "^4.3.1" + workbox-strategies "^4.3.1" + workbox-streams "^4.3.1" + workbox-sw "^4.3.1" + workbox-window "^4.3.1" + +workbox-cacheable-response@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/workbox-cacheable-response/-/workbox-cacheable-response-4.3.1.tgz#f53e079179c095a3f19e5313b284975c91428c91" + integrity sha512-Rp5qlzm6z8IOvnQNkCdO9qrDgDpoPNguovs0H8C+wswLuPgSzSp9p2afb5maUt9R1uTIwOXrVQMmPfPypv+npw== + dependencies: + workbox-core "^4.3.1" + +workbox-core@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/workbox-core/-/workbox-core-4.3.1.tgz#005d2c6a06a171437afd6ca2904a5727ecd73be6" + integrity sha512-I3C9jlLmMKPxAC1t0ExCq+QoAMd0vAAHULEgRZ7kieCdUd919n53WC0AfvokHNwqRhGn+tIIj7vcb5duCjs2Kg== + +workbox-expiration@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/workbox-expiration/-/workbox-expiration-4.3.1.tgz#d790433562029e56837f341d7f553c4a78ebe921" + integrity sha512-vsJLhgQsQouv9m0rpbXubT5jw0jMQdjpkum0uT+d9tTwhXcEZks7qLfQ9dGSaufTD2eimxbUOJfWLbNQpIDMPw== + dependencies: + workbox-core "^4.3.1" + +workbox-google-analytics@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/workbox-google-analytics/-/workbox-google-analytics-4.3.1.tgz#9eda0183b103890b5c256e6f4ea15a1f1548519a" + integrity sha512-xzCjAoKuOb55CBSwQrbyWBKqp35yg1vw9ohIlU2wTy06ZrYfJ8rKochb1MSGlnoBfXGWss3UPzxR5QL5guIFdg== + dependencies: + workbox-background-sync "^4.3.1" + workbox-core "^4.3.1" + workbox-routing "^4.3.1" + workbox-strategies "^4.3.1" + +workbox-navigation-preload@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/workbox-navigation-preload/-/workbox-navigation-preload-4.3.1.tgz#29c8e4db5843803b34cd96dc155f9ebd9afa453d" + integrity sha512-K076n3oFHYp16/C+F8CwrRqD25GitA6Rkd6+qAmLmMv1QHPI2jfDwYqrytOfKfYq42bYtW8Pr21ejZX7GvALOw== + dependencies: + workbox-core "^4.3.1" + +workbox-precaching@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/workbox-precaching/-/workbox-precaching-4.3.1.tgz#9fc45ed122d94bbe1f0ea9584ff5940960771cba" + integrity sha512-piSg/2csPoIi/vPpp48t1q5JLYjMkmg5gsXBQkh/QYapCdVwwmKlU9mHdmy52KsDGIjVaqEUMFvEzn2LRaigqQ== + dependencies: + workbox-core "^4.3.1" + +workbox-range-requests@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/workbox-range-requests/-/workbox-range-requests-4.3.1.tgz#f8a470188922145cbf0c09a9a2d5e35645244e74" + integrity sha512-S+HhL9+iTFypJZ/yQSl/x2Bf5pWnbXdd3j57xnb0V60FW1LVn9LRZkPtneODklzYuFZv7qK6riZ5BNyc0R0jZA== + dependencies: + workbox-core "^4.3.1" + +workbox-routing@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/workbox-routing/-/workbox-routing-4.3.1.tgz#a675841af623e0bb0c67ce4ed8e724ac0bed0cda" + integrity sha512-FkbtrODA4Imsi0p7TW9u9MXuQ5P4pVs1sWHK4dJMMChVROsbEltuE79fBoIk/BCztvOJ7yUpErMKa4z3uQLX+g== + dependencies: + workbox-core "^4.3.1" + +workbox-strategies@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/workbox-strategies/-/workbox-strategies-4.3.1.tgz#d2be03c4ef214c115e1ab29c9c759c9fe3e9e646" + integrity sha512-F/+E57BmVG8dX6dCCopBlkDvvhg/zj6VDs0PigYwSN23L8hseSRwljrceU2WzTvk/+BSYICsWmRq5qHS2UYzhw== + dependencies: + workbox-core "^4.3.1" + +workbox-streams@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/workbox-streams/-/workbox-streams-4.3.1.tgz#0b57da70e982572de09c8742dd0cb40a6b7c2cc3" + integrity sha512-4Kisis1f/y0ihf4l3u/+ndMkJkIT4/6UOacU3A4BwZSAC9pQ9vSvJpIi/WFGQRH/uPXvuVjF5c2RfIPQFSS2uA== + dependencies: + workbox-core "^4.3.1" + +workbox-sw@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/workbox-sw/-/workbox-sw-4.3.1.tgz#df69e395c479ef4d14499372bcd84c0f5e246164" + integrity sha512-0jXdusCL2uC5gM3yYFT6QMBzKfBr2XTk0g5TPAV4y8IZDyVNDyj1a8uSXy3/XrvkVTmQvLN4O5k3JawGReXr9w== + +workbox-webpack-plugin@4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/workbox-webpack-plugin/-/workbox-webpack-plugin-4.3.1.tgz#47ff5ea1cc074b6c40fb5a86108863a24120d4bd" + integrity sha512-gJ9jd8Mb8wHLbRz9ZvGN57IAmknOipD3W4XNE/Lk/4lqs5Htw4WOQgakQy/o/4CoXQlMCYldaqUg+EJ35l9MEQ== + dependencies: + "@babel/runtime" "^7.0.0" + json-stable-stringify "^1.0.1" + workbox-build "^4.3.1" + +workbox-window@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/workbox-window/-/workbox-window-4.3.1.tgz#ee6051bf10f06afa5483c9b8dfa0531994ede0f3" + integrity sha512-C5gWKh6I58w3GeSc0wp2Ne+rqVw8qwcmZnQGpjiek8A2wpbxSJb1FdCoQVO+jDJs35bFgo/WETgl1fqgsxN0Hg== + dependencies: + workbox-core "^4.3.1" + +worker-farm@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8" + integrity sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw== + dependencies: + errno "~0.1.7" + +worker-rpc@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/worker-rpc/-/worker-rpc-0.1.1.tgz#cb565bd6d7071a8f16660686051e969ad32f54d5" + integrity sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg== + dependencies: + microevent.ts "~0.1.1" + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +write-file-atomic@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.1.tgz#d0b05463c188ae804396fd5ab2a370062af87529" + integrity sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg== + dependencies: + graceful-fs "^4.1.11" + imurmurhash "^0.1.4" + signal-exit "^3.0.2" + +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== + dependencies: + mkdirp "^0.5.1" + +ws@^5.2.0: + version "5.2.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f" + integrity sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA== + dependencies: + async-limiter "~1.0.0" + +ws@^6.1.2, ws@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" + integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== + dependencies: + async-limiter "~1.0.0" + +xml-name-validator@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" + integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== + +xmlchars@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + +xtend@^4.0.0, xtend@~4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +y18n@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" + integrity sha1-bRX7qITAhnnA136I53WegR4H+kE= + +"y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= + +yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml@^1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.7.2.tgz#f26aabf738590ab61efaca502358e48dc9f348b2" + integrity sha512-qXROVp90sb83XtAoqE8bP9RwAkTTZbugRUTm5YeFCBfNRPEp2YzTeqWiz7m5OORHzEvrA/qcGS8hp/E+MMROYw== + dependencies: + "@babel/runtime" "^7.6.3" + +yargs-parser@^11.1.1: + version "11.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4" + integrity sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^13.1.1: + version "13.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0" + integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a" + integrity sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo= + dependencies: + camelcase "^3.0.0" + +yargs@12.0.5: + version "12.0.5" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13" + integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw== + dependencies: + cliui "^4.0.0" + decamelize "^1.2.0" + find-up "^3.0.0" + get-caller-file "^1.0.1" + os-locale "^3.0.0" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^2.0.0" + which-module "^2.0.0" + y18n "^3.2.1 || ^4.0.0" + yargs-parser "^11.1.1" + +yargs@^13.3.0: + version "13.3.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83" + integrity sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.1" + +yargs@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8" + integrity sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg= + dependencies: + camelcase "^3.0.0" + cliui "^3.2.0" + decamelize "^1.1.1" + get-caller-file "^1.0.1" + os-locale "^1.4.0" + read-pkg-up "^1.0.1" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^1.0.2" + which-module "^1.0.0" + y18n "^3.2.1" + yargs-parser "^5.0.0" diff --git a/products/ASC.Files/Server/ASC.Files.csproj b/products/ASC.Files/Server/ASC.Files.csproj new file mode 100644 index 0000000000..ce9caf50cb --- /dev/null +++ b/products/ASC.Files/Server/ASC.Files.csproj @@ -0,0 +1,68 @@ + + + 9.0.30729 + {77BA2F61-6155-4283-BB39-F8E42F46A0B0} + netcoreapp3.1 + + + ASC.Files + Ascensio System SIA + ASC.Files + (c) Ascensio System SIA. All rights reserved + false + + + full + + + none + true + + + + + + + + + + + + + + + + + + + + True + True + FilesCommonResource.resx + + + True + True + FilesJSResource.resx + + + True + True + FilesUCResource.resx + + + + + PublicResXFileCodeGenerator + FilesCommonResource.Designer.cs + + + PublicResXFileCodeGenerator + FilesJSResource.Designer.cs + + + PublicResXFileCodeGenerator + FilesUCResource.Designer.cs + + + \ No newline at end of file diff --git a/products/ASC.Files/Server/Configuration/FilesSettings.cs b/products/ASC.Files/Server/Configuration/FilesSettings.cs new file mode 100644 index 0000000000..bb199676da --- /dev/null +++ b/products/ASC.Files/Server/Configuration/FilesSettings.cs @@ -0,0 +1,235 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Runtime.Serialization; + +using ASC.Common; +using ASC.Core; +using ASC.Core.Common.Settings; +using ASC.Files.Core; + +namespace ASC.Web.Files.Classes +{ + [Serializable] + [DataContract] + public class FilesSettings : ISettings + { + [DataMember(Name = "EnableThirdpartySettings")] + public bool EnableThirdpartySetting { get; set; } + + [DataMember(Name = "FastDelete")] + public bool FastDeleteSetting { get; set; } + + [DataMember(Name = "StoreOriginalFiles")] + public bool StoreOriginalFilesSetting { get; set; } + + [DataMember(Name = "UpdateIfExist")] + public bool UpdateIfExistSetting { get; set; } + + [DataMember(Name = "ConvertNotify")] + public bool ConvertNotifySetting { get; set; } + + [DataMember(Name = "SortedBy")] + public SortedByType DefaultSortedBySetting { get; set; } + + [DataMember(Name = "SortedAsc")] + public bool DefaultSortedAscSetting { get; set; } + + [DataMember(Name = "HideConfirmConvertSave")] + public bool HideConfirmConvertSaveSetting { get; set; } + + [DataMember(Name = "HideConfirmConvertOpen")] + public bool HideConfirmConvertOpenSetting { get; set; } + + [DataMember(Name = "Forcesave")] + public bool ForcesaveSetting { get; set; } + + [DataMember(Name = "StoreForcesave")] + public bool StoreForcesaveSetting { get; set; } + + public ISettings GetDefault(IServiceProvider serviceProvider) + { + return new FilesSettings + { + FastDeleteSetting = false, + EnableThirdpartySetting = true, + StoreOriginalFilesSetting = true, + UpdateIfExistSetting = false, + ConvertNotifySetting = true, + DefaultSortedBySetting = SortedByType.DateAndTime, + DefaultSortedAscSetting = false, + HideConfirmConvertSaveSetting = false, + HideConfirmConvertOpenSetting = false, + ForcesaveSetting = false, + StoreForcesaveSetting = false + }; + } + + public Guid ID + { + get { return new Guid("{03B382BD-3C20-4f03-8AB9-5A33F016316E}"); } + } + } + + public class FilesSettingsHelper + { + public SettingsManager SettingsManager { get; } + public CoreBaseSettings CoreBaseSettings { get; } + + public FilesSettingsHelper(SettingsManager settingsManager, CoreBaseSettings coreBaseSettings) + { + SettingsManager = settingsManager; + CoreBaseSettings = coreBaseSettings; + } + + public bool ConfirmDelete + { + set + { + var setting = SettingsManager.LoadForCurrentUser(); + setting.FastDeleteSetting = !value; + SettingsManager.SaveForCurrentUser(setting); + } + get { return !SettingsManager.LoadForCurrentUser().FastDeleteSetting; } + } + + public bool EnableThirdParty + { + set + { + var setting = SettingsManager.Load(); + setting.EnableThirdpartySetting = value; + SettingsManager.Save(setting); + } + get { return SettingsManager.Load().EnableThirdpartySetting; } + } + + public bool StoreOriginalFiles + { + set + { + var setting = SettingsManager.LoadForCurrentUser(); + setting.StoreOriginalFilesSetting = value; + SettingsManager.SaveForCurrentUser(setting); + } + get { return SettingsManager.LoadForCurrentUser().StoreOriginalFilesSetting; } + } + + public bool UpdateIfExist + { + set + { + var setting = SettingsManager.LoadForCurrentUser(); + setting.UpdateIfExistSetting = value; + SettingsManager.SaveForCurrentUser(setting); + } + get { return SettingsManager.LoadForCurrentUser().UpdateIfExistSetting; } + } + + public bool ConvertNotify + { + set + { + var setting = SettingsManager.LoadForCurrentUser(); + setting.ConvertNotifySetting = value; + SettingsManager.SaveForCurrentUser(setting); + } + get { return SettingsManager.LoadForCurrentUser().ConvertNotifySetting; } + } + + public bool HideConfirmConvertSave + { + set + { + var setting = SettingsManager.LoadForCurrentUser(); + setting.HideConfirmConvertSaveSetting = value; + SettingsManager.SaveForCurrentUser(setting); + } + get { return SettingsManager.LoadForCurrentUser().HideConfirmConvertSaveSetting; } + } + + public bool HideConfirmConvertOpen + { + set + { + var setting = SettingsManager.LoadForCurrentUser(); + setting.HideConfirmConvertOpenSetting = value; + SettingsManager.SaveForCurrentUser(setting); + } + get { return SettingsManager.LoadForCurrentUser().HideConfirmConvertOpenSetting; } + } + + public OrderBy DefaultOrder + { + set + { + var setting = SettingsManager.LoadForCurrentUser(); + setting.DefaultSortedBySetting = value.SortedBy; + setting.DefaultSortedAscSetting = value.IsAsc; + SettingsManager.SaveForCurrentUser(setting); + } + get + { + var setting = SettingsManager.LoadForCurrentUser(); + return new OrderBy(setting.DefaultSortedBySetting, setting.DefaultSortedAscSetting); + } + } + + public bool Forcesave + { + set + { + var setting = SettingsManager.LoadForCurrentUser(); + setting.ForcesaveSetting = value; + SettingsManager.SaveForCurrentUser(setting); + } + get { return SettingsManager.LoadForCurrentUser().ForcesaveSetting; } + } + + public bool StoreForcesave + { + set + { + if (CoreBaseSettings.Personal) throw new NotSupportedException(); + var setting = SettingsManager.Load(); + setting.StoreForcesaveSetting = value; + SettingsManager.Save(setting); + } + get { return !CoreBaseSettings.Personal && SettingsManager.Load().StoreForcesaveSetting; } + } + } + public static class FilesSettingsHelperExtention + { + public static DIHelper AddFilesSettingsHelperService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddSettingsManagerService() + .AddCoreBaseSettingsService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Configuration/FilesSpaceUsageStatManager.cs b/products/ASC.Files/Server/Configuration/FilesSpaceUsageStatManager.cs new file mode 100644 index 0000000000..5b012ce60a --- /dev/null +++ b/products/ASC.Files/Server/Configuration/FilesSpaceUsageStatManager.cs @@ -0,0 +1,181 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Common; +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.Core.Common.EF.Context; +using ASC.Core.Users; +using ASC.Files.Core; +using ASC.Files.Resources; +using ASC.Web.Core; +using ASC.Web.Core.Users; +using ASC.Web.Files.Classes; +using ASC.Web.Studio.Utility; + +namespace ASC.Web.Files +{ + public class FilesSpaceUsageStatManager : SpaceUsageStatManager + { + public ASC.Files.Core.EF.FilesDbContext FilesDbContext { get; } + public TenantManager TenantManager { get; } + public UserManager UserManager { get; } + public UserPhotoManager UserPhotoManager { get; } + public DisplayUserSettingsHelper DisplayUserSettingsHelper { get; } + public CommonLinkUtility CommonLinkUtility { get; } + public GlobalFolderHelper GlobalFolderHelper { get; } + public PathProvider PathProvider { get; } + + public FilesSpaceUsageStatManager( + DbContextManager dbContextManager, + TenantManager tenantManager, + UserManager userManager, + UserPhotoManager userPhotoManager, + DisplayUserSettingsHelper displayUserSettingsHelper, + CommonLinkUtility commonLinkUtility, + GlobalFolderHelper globalFolderHelper, + PathProvider pathProvider) + { + FilesDbContext = dbContextManager.Get(FileConstant.DatabaseId); + TenantManager = tenantManager; + UserManager = userManager; + UserPhotoManager = userPhotoManager; + DisplayUserSettingsHelper = displayUserSettingsHelper; + CommonLinkUtility = commonLinkUtility; + GlobalFolderHelper = globalFolderHelper; + PathProvider = pathProvider; + } + + public override List GetStatData() + { + var myFiles = FilesDbContext.Files + .Join(FilesDbContext.Tree, a => a.FolderId, b => b.FolderId, (file, tree) => new { file, tree }) + .Join(FilesDbContext.BunchObjects, a => a.tree.ParentId.ToString(), b => b.LeftNode, (fileTree, bunch) => new { fileTree.file, fileTree.tree, bunch }) + .Where(r => r.file.TenantId == r.bunch.TenantId) + .Where(r => r.file.TenantId == TenantManager.GetCurrentTenant().TenantId) + .Where(r => r.bunch.RightNode.StartsWith("files/my/") | r.bunch.RightNode.StartsWith("files/trash/")) + .GroupBy(r => r.file.CreateBy) + .Select(r => new { CreateBy = r.Key, Size = r.Sum(a => a.file.ContentLength) }); + + var commonFiles = FilesDbContext.Files + .Join(FilesDbContext.Tree, a => a.FolderId, b => b.FolderId, (file, tree) => new { file, tree }) + .Join(FilesDbContext.BunchObjects, a => a.tree.ParentId.ToString(), b => b.LeftNode, (fileTree, bunch) => new { fileTree.file, fileTree.tree, bunch }) + .Where(r => r.file.TenantId == r.bunch.TenantId) + .Where(r => r.file.TenantId == TenantManager.GetCurrentTenant().TenantId) + .Where(r => r.bunch.RightNode.StartsWith("files/common/")) + .GroupBy(r => Constants.LostUser.ID) + .Select(r => new { CreateBy = Constants.LostUser.ID, Size = r.Sum(a => a.file.ContentLength) }); + + return myFiles.Union(commonFiles) + .ToList() + .GroupBy( + r => r.CreateBy, + r => r.Size, + (userId, items) => + { + var user = UserManager.GetUsers(userId); + var item = new UsageSpaceStatItem { SpaceUsage = items.Sum() }; + if (user.Equals(Constants.LostUser)) + { + item.Name = FilesUCResource.CorporateFiles; + item.ImgUrl = PathProvider.GetImagePath("corporatefiles_big.png"); + item.Url = PathProvider.GetFolderUrl(GlobalFolderHelper.FolderCommon); + } + else + { + item.Name = user.DisplayUserName(false, DisplayUserSettingsHelper); + item.ImgUrl = user.GetSmallPhotoURL(UserPhotoManager); + item.Url = user.GetUserProfilePageURL(CommonLinkUtility); + item.Disabled = user.Status == EmployeeStatus.Terminated; + } + return item; + }) + .OrderByDescending(i => i.SpaceUsage) + .ToList(); + + } + } + + public class FilesUserSpaceUsage : IUserSpaceUsage + { + public ASC.Files.Core.EF.FilesDbContext FilesDbContext { get; } + public TenantManager TenantManager { get; } + + public FilesUserSpaceUsage( + DbContextManager dbContextManager, + TenantManager tenantManager) + { + FilesDbContext = dbContextManager.Get(FileConstant.DatabaseId); + TenantManager = tenantManager; + } + + public long GetUserSpaceUsage(Guid userId) + { + return FilesDbContext.Files + .Join(FilesDbContext.Tree, a => a.FolderId, b => b.FolderId, (file, tree) => new { file, tree }) + .Join(FilesDbContext.BunchObjects, a => a.tree.ParentId.ToString(), b => b.LeftNode, (fileTree, bunch) => new { fileTree.file, fileTree.tree, bunch }) + .Where(r => r.file.TenantId == r.bunch.TenantId) + .Where(r => r.file.TenantId == TenantManager.GetCurrentTenant().TenantId) + .Where(r => r.file.CreateBy == userId) + .Where(r => r.bunch.RightNode.StartsWith("files/trash/") | r.bunch.RightNode.StartsWith("files/my/")) + .GroupBy(r => r.file.CreateBy) + .Select(r => r.Sum(f => f.file.ContentLength)) + .FirstOrDefault(); + } + } + + public static class FilesSpaceUsageStatManagerExtention + { + public static DIHelper AddFilesSpaceUsageStatManagerService(this DIHelper services) + { + services.TryAddScoped(); + /* + GlobalFolderHelper globalFolderHelper, + */ + return services + .AddTenantManagerService() + .AddUserManagerService() + .AddUserPhotoManagerService() + .AddDisplayUserSettingsService() + .AddCommonLinkUtilityService() + .AddFilesDbContextService() + .AddPathProviderService(); + } + + public static DIHelper AddFilesUserSpaceUsageService(this DIHelper services) + { + services.TryAddScoped(); + + return services + .AddTenantManagerService() + .AddFilesDbContextService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Configuration/ProductEntryPoint.cs b/products/ASC.Files/Server/Configuration/ProductEntryPoint.cs new file mode 100644 index 0000000000..c04aefcb2d --- /dev/null +++ b/products/ASC.Files/Server/Configuration/ProductEntryPoint.cs @@ -0,0 +1,183 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using ASC.Common; +using ASC.Core; +using ASC.Files.Resources; +using ASC.Web.Core; +using ASC.Web.Core.PublicResources; +using ASC.Web.Files.Classes; + +namespace ASC.Web.Files.Configuration +{ + public class ProductEntryPoint : Product + { + internal const string ProductPath = "/products/files/"; + + //public FilesSpaceUsageStatManager FilesSpaceUsageStatManager { get; } + public CoreBaseSettings CoreBaseSettings { get; } + public AuthContext AuthContext { get; } + public UserManager UserManager { get; } + //public SubscriptionManager SubscriptionManager { get; } + + public ProductEntryPoint() + { + + } + + public ProductEntryPoint( + // FilesSpaceUsageStatManager filesSpaceUsageStatManager, + CoreBaseSettings coreBaseSettings, + AuthContext authContext, + UserManager userManager + // SubscriptionManager subscriptionManager + ) + { + // FilesSpaceUsageStatManager = filesSpaceUsageStatManager; + CoreBaseSettings = coreBaseSettings; + AuthContext = authContext; + UserManager = userManager; + //SubscriptionManager = subscriptionManager; + } + + public static readonly Guid ID = WebItemManager.DocumentsProductID; + + private ProductContext _productContext; + + public override bool Visible { get { return true; } } + + public override void Init() + { + List adminOpportunities() => (CoreBaseSettings.CustomMode + ? CustomModeResource.ProductAdminOpportunitiesCustomMode + : FilesCommonResource.ProductAdminOpportunities).Split('|').ToList(); + + List userOpportunities() => (CoreBaseSettings.CustomMode + ? CustomModeResource.ProductUserOpportunitiesCustomMode + : FilesCommonResource.ProductUserOpportunities).Split('|').ToList(); + + _productContext = + new ProductContext + { + DisabledIconFileName = "product_disabled_logo.png", + IconFileName = "product_logo.png", + LargeIconFileName = "images/files.svg", + DefaultSortOrder = 10, + //SubscriptionManager = SubscriptionManager, + //SpaceUsageStatManager = FilesSpaceUsageStatManager, + AdminOpportunities = adminOpportunities, + UserOpportunities = userOpportunities, + CanNotBeDisabled = true, + }; + //SearchHandlerManager.Registry(new SearchHandler()); + } + + public string GetModuleResource(string ResourceClassTypeName, string ResourseKey) + { + if (string.IsNullOrEmpty(ResourseKey)) return string.Empty; + try + { + return (string)Type.GetType(ResourceClassTypeName).GetProperty(ResourseKey, BindingFlags.Static | BindingFlags.Public).GetValue(null, null); + } + catch (Exception) + { + return string.Empty; + } + } + + + public override Guid ProductID + { + get { return ID; } + } + + public override string Name + { + get { return FilesCommonResource.ProductName; } + } + + public override string Description + { + get + { + var id = AuthContext.CurrentAccount.ID; + + if (UserManager.IsUserInGroup(id, ASC.Core.Users.Constants.GroupVisitor.ID)) + return FilesCommonResource.ProductDescriptionShort; + + if (UserManager.IsUserInGroup(id, ASC.Core.Users.Constants.GroupAdmin.ID) || UserManager.IsUserInGroup(id, ID)) + return FilesCommonResource.ProductDescriptionEx; + + return FilesCommonResource.ProductDescription; + } + } + + public override string StartURL + { + get { return ProductPath; } + } + + public override string HelpURL + { + get { return PathProvider.StartURL; } + } + + public override string ProductClassName + { + get { return "documents"; } + } + + public override ProductContext Context + { + get { return _productContext; } + } + + public override string ApiURL + { + get => "api/2.0/files/info.json"; + } + } + public static class ProductEntryPointExtention + { + public static DIHelper AddProductEntryPointService(this DIHelper services) + { + services.TryAddScoped(); + + return services + .AddFilesSpaceUsageStatManagerService() + .AddCoreBaseSettingsService() + .AddAuthContextService() + .AddUserManagerService() + .AddGlobalService() + .AddFilesSubscriptionManagerService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Configuration/SubscriptionManager.cs b/products/ASC.Files/Server/Configuration/SubscriptionManager.cs new file mode 100644 index 0000000000..cd49f47182 --- /dev/null +++ b/products/ASC.Files/Server/Configuration/SubscriptionManager.cs @@ -0,0 +1,152 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; + +using ASC.Common; +using ASC.Core; +using ASC.Files.Resources; +using ASC.Notify.Model; +using ASC.Web.Core.Subscriptions; +using ASC.Web.Files.Services.NotifyService; + +namespace ASC.Web.Files.Classes +{ + public class SubscriptionManager : IProductSubscriptionManager + { + private readonly Guid _subscrTypeDocuSignComplete = new Guid("{0182E476-D63D-46ED-B928-104861507811}"); + private readonly Guid _subscrTypeDocuSignStatus = new Guid("{ED7F93CD-7575-40EB-86EB-82FBA23171D2}"); + private readonly Guid _subscrTypeShareDoc = new Guid("{552846EC-AC94-4408-AAC6-17C8989B8B38}"); + private readonly Guid _subscrTypeShareFolder = new Guid("{0292A4F4-0687-42a6-9CE4-E21215045ABE}"); + private readonly Guid _subscrTypeMailMerge = new Guid("{FB5858EC-046C-41E2-84C9-B44BF7884514}"); + private readonly Guid _subscrTypeEditorMentions = new Guid("{9D3CAB90-5718-4E82-959F-27EC83BFBC5F}"); + + public GroupByType GroupByType + { + get { return GroupByType.Simple; } + } + + public SubscriptionManager(CoreBaseSettings coreBaseSettings, NotifySource notifySource) + { + CoreBaseSettings = coreBaseSettings; + NotifySource = notifySource; + } + + public List GetSubscriptionObjects(Guid subItem) + { + return new List(); + } + + public List GetSubscriptionTypes() + { + var subscriptionTypes = new List + { + new SubscriptionType + { + ID = _subscrTypeShareDoc, + Name = FilesCommonResource.SubscriptForAccess, + NotifyAction = NotifyConstants.Event_ShareDocument, + Single = true, + CanSubscribe = true + }, + new SubscriptionType + { + ID = _subscrTypeShareFolder, + Name = FilesCommonResource.ShareFolder, + NotifyAction = NotifyConstants.Event_ShareFolder, + Single = true, + CanSubscribe = true + }, + new SubscriptionType + { + ID = _subscrTypeMailMerge, + Name = FilesCommonResource.SubscriptForMailMerge, + NotifyAction = NotifyConstants.Event_MailMergeEnd, + Single = true, + CanSubscribe = true + }, + new SubscriptionType + { + ID = _subscrTypeEditorMentions, + Name = FilesCommonResource.EditorMentions, + NotifyAction = NotifyConstants.Event_EditorMentions, + Single = true, + CanSubscribe = true + }, + }; + + if (CoreBaseSettings.CustomMode) return subscriptionTypes; + + subscriptionTypes.AddRange(new List + { + new SubscriptionType + { + ID = _subscrTypeDocuSignComplete, + Name = FilesCommonResource.SubscriptDocuSignComplete, + NotifyAction = NotifyConstants.Event_DocuSignComplete, + Single = true, + CanSubscribe = true + }, + new SubscriptionType + { + ID = _subscrTypeDocuSignStatus, + Name = FilesCommonResource.SubscriptDocuSignStatus, + NotifyAction = NotifyConstants.Event_DocuSignStatus, + Single = true, + CanSubscribe = true + } + }); + + return subscriptionTypes; + } + + public ISubscriptionProvider SubscriptionProvider + { + get { return NotifySource.GetSubscriptionProvider(); } + } + + public CoreBaseSettings CoreBaseSettings { get; } + public NotifySource NotifySource { get; } + + public List GetSubscriptionGroups() + { + return new List(); + } + } + + public static class FilesSubscriptionManagerExtention + { + public static DIHelper AddFilesSubscriptionManagerService(this DIHelper services) + { + services.TryAddScoped(); + + return services + .AddFilesNotifySourceService() + .AddCoreBaseSettingsService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Controllers/EncryptionController.cs b/products/ASC.Files/Server/Controllers/EncryptionController.cs new file mode 100644 index 0000000000..a80418266e --- /dev/null +++ b/products/ASC.Files/Server/Controllers/EncryptionController.cs @@ -0,0 +1,183 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Common; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Users; +using ASC.Files.Core.Data; +using ASC.Web.Api.Routing; +using ASC.Web.Files.Core.Entries; +using ASC.Web.Studio.Core; + +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; + +using Newtonsoft.Json; + +namespace ASC.Api.Documents +{ + [DefaultRoute] + [ApiController] + public class EncryptionController : ControllerBase + { + public PermissionContext PermissionContext { get; } + public AuthContext AuthContext { get; } + public EncryptionLoginProvider EncryptionLoginProvider { get; } + public EncryptionAddressHelper EncryptionAddressHelper { get; } + public EncryptedDataDao EncryptedDataDao { get; } + public ILog Log { get; } + + public EncryptionController( + PermissionContext permissionContext, + AuthContext authContext, + EncryptionLoginProvider encryptionLoginProvider, + IOptionsMonitor monitor, + EncryptionAddressHelper encryptionAddressHelper, + EncryptedDataDao encryptedDataDao) + { + PermissionContext = permissionContext; + AuthContext = authContext; + EncryptionLoginProvider = encryptionLoginProvider; + EncryptionAddressHelper = encryptionAddressHelper; + EncryptedDataDao = encryptedDataDao; + Log = monitor.Get("ASC.Api.Documents"); + } + + /// + /// + /// + /// false + [Update("address")] + public object UpdateAddress(string address, string publicKey) + { + PermissionContext.DemandPermissions(new UserSecurityProvider(AuthContext.CurrentAccount.ID), Constants.Action_EditUser); + + if (string.IsNullOrEmpty(address)) throw new ArgumentNullException("address"); + if (string.IsNullOrEmpty(publicKey)) throw new ArgumentNullException("publicKey"); + + var currentAddressString = EncryptionLoginProvider.GetAddress(); + if (!string.IsNullOrEmpty(currentAddressString)) + { + var currentAddress = JsonConvert.DeserializeObject(currentAddressString); + if (currentAddress != null + && !string.IsNullOrEmpty(currentAddress.PublicKey) + && currentAddress.PublicKey.Equals(publicKey)) + { + return new { isset = true }; + } + + Log.InfoFormat("User {0} updates address", AuthContext.CurrentAccount.ID); + } + + var account = new EncryptionAddress { Address = address, PublicKey = publicKey }; + var accountString = JsonConvert.SerializeObject(account); + EncryptionLoginProvider.UpdateAddress(accountString); + + return new + { + isset = !string.IsNullOrEmpty(EncryptionLoginProvider.GetAddress()) + }; + } + + /// + /// + /// + /// false + [Read("access/{fileId}")] + public IEnumerable GetAddressesWithAccess(string fileId) + { + var accountsString = EncryptionAddressHelper.GetAddresses(fileId); + + var accounts = accountsString.Select(JsonConvert.DeserializeObject); + return accounts; + } + + /// + /// + /// + /// + /// + /// + /// + /// false + [Create("store")] + public bool PutEncryptedData(string publicKey, string fileHash, string data) + { + if (string.IsNullOrEmpty(publicKey)) + throw new ArgumentNullException("Public Key require", publicKey); + + if (string.IsNullOrEmpty(fileHash)) + throw new ArgumentNullException("File hash require", fileHash); + + if (string.IsNullOrEmpty(data)) + throw new ArgumentNullException("Encrypted file password require", data); + + if (!string.IsNullOrEmpty(GetEncryptedData(publicKey, fileHash))) + throw new Exception("Encrypted file password already exist for this public key"); + + var encryptedDao = EncryptedDataDao; + var encryptedData = new EncryptedData + { + FileHash = fileHash, + PublicKey = publicKey, + Data = data + }; + + return encryptedDao.SaveEncryptedData(new[] { encryptedData }); + } + + /// + /// + /// + /// + /// + /// + /// false + [Read("store")] + public string GetEncryptedData(string publicKey, string fileHash) + { + var encryptedDao = EncryptedDataDao; + return encryptedDao.GetData(publicKey, fileHash); + } + } + public static class EncryptionControllerExtention + { + public static DIHelper AddEncryptionControllerService(this DIHelper services) + { + return services + .AddPermissionContextService() + .AddAuthContextService() + .AddEncryptionLoginProviderService() + .AddEncryptionAddressHelperService() + .AddEncryptedDataDaoService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Controllers/FilesController.cs b/products/ASC.Files/Server/Controllers/FilesController.cs new file mode 100644 index 0000000000..ddf13b45ca --- /dev/null +++ b/products/ASC.Files/Server/Controllers/FilesController.cs @@ -0,0 +1,1839 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using System.Web; + +using ASC.Api.Core; +using ASC.Api.Utils; +using ASC.Common; +using ASC.Common.Web; +using ASC.Core; +using ASC.Core.Common.Configuration; +using ASC.Core.Users; +using ASC.FederatedLogin.Helpers; +using ASC.FederatedLogin.LoginProviders; +using ASC.Files.Core; +using ASC.Files.Model; +using ASC.MessagingSystem; +using ASC.Web.Api.Routing; +using ASC.Web.Core; +using ASC.Web.Core.Files; +using ASC.Web.Files.Classes; +using ASC.Web.Files.Configuration; +using ASC.Web.Files.Helpers; +using ASC.Web.Files.Services.DocumentService; +using ASC.Web.Files.Services.WCFService; +using ASC.Web.Files.Services.WCFService.FileOperations; +using ASC.Web.Files.Utils; +using ASC.Web.Studio.Utility; + +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +using Newtonsoft.Json.Linq; + +using FileShare = ASC.Files.Core.Security.FileShare; +using MimeMapping = ASC.Common.Web.MimeMapping; +using SortedByType = ASC.Files.Core.SortedByType; + +namespace ASC.Api.Documents +{ + /// + /// Provides access to documents + /// + [DefaultRoute] + [ApiController] + public class FilesController : ControllerBase + { + private readonly ApiContext ApiContext; + private readonly FileStorageService FileStorageService; + + public GlobalFolderHelper GlobalFolderHelper { get; } + public FileWrapperHelper FileWrapperHelper { get; } + public FilesSettingsHelper FilesSettingsHelper { get; } + public FilesLinkUtility FilesLinkUtility { get; } + public FileUploader FileUploader { get; } + public DocumentServiceHelper DocumentServiceHelper { get; } + public TenantManager TenantManager { get; } + public SecurityContext SecurityContext { get; } + public FolderWrapperHelper FolderWrapperHelper { get; } + public FileOperationWraperHelper FileOperationWraperHelper { get; } + public FileShareWrapperHelper FileShareWrapperHelper { get; } + public FileShareParamsHelper FileShareParamsHelper { get; } + public EntryManager EntryManager { get; } + public UserManager UserManager { get; } + public WebItemSecurity WebItemSecurity { get; } + public CoreBaseSettings CoreBaseSettings { get; } + public ThirdpartyConfiguration ThirdpartyConfiguration { get; } + public BoxLoginProvider BoxLoginProvider { get; } + public DropboxLoginProvider DropboxLoginProvider { get; } + public GoogleLoginProvider GoogleLoginProvider { get; } + public OneDriveLoginProvider OneDriveLoginProvider { get; } + public MessageService MessageService { get; } + public CommonLinkUtility CommonLinkUtility { get; } + public DocumentServiceConnector DocumentServiceConnector { get; } + public FolderContentWrapperHelper FolderContentWrapperHelper { get; } + public WordpressToken WordpressToken { get; } + public WordpressHelper WordpressHelper { get; } + public ConsumerFactory ConsumerFactory { get; } + public EasyBibHelper EasyBibHelper { get; } + public ChunkedUploadSessionHelper ChunkedUploadSessionHelper { get; } + public ProductEntryPoint ProductEntryPoint { get; } + + /// + /// + /// + /// + public FilesController( + ApiContext context, + FileStorageService fileStorageService, + GlobalFolderHelper globalFolderHelper, + FileWrapperHelper fileWrapperHelper, + FilesSettingsHelper filesSettingsHelper, + FilesLinkUtility filesLinkUtility, + FileUploader fileUploader, + DocumentServiceHelper documentServiceHelper, + TenantManager tenantManager, + SecurityContext securityContext, + FolderWrapperHelper folderWrapperHelper, + FileOperationWraperHelper fileOperationWraperHelper, + FileShareWrapperHelper fileShareWrapperHelper, + FileShareParamsHelper fileShareParamsHelper, + EntryManager entryManager, + UserManager userManager, + WebItemSecurity webItemSecurity, + CoreBaseSettings coreBaseSettings, + ThirdpartyConfiguration thirdpartyConfiguration, + BoxLoginProvider boxLoginProvider, + DropboxLoginProvider dropboxLoginProvider, + GoogleLoginProvider googleLoginProvider, + OneDriveLoginProvider oneDriveLoginProvider, + MessageService messageService, + CommonLinkUtility commonLinkUtility, + DocumentServiceConnector documentServiceConnector, + FolderContentWrapperHelper folderContentWrapperHelper, + WordpressToken wordpressToken, + WordpressHelper wordpressHelper, + ConsumerFactory consumerFactory, + EasyBibHelper easyBibHelper, + ChunkedUploadSessionHelper chunkedUploadSessionHelper, + ProductEntryPoint productEntryPoint) + { + ApiContext = context; + FileStorageService = fileStorageService; + GlobalFolderHelper = globalFolderHelper; + FileWrapperHelper = fileWrapperHelper; + FilesSettingsHelper = filesSettingsHelper; + FilesLinkUtility = filesLinkUtility; + FileUploader = fileUploader; + DocumentServiceHelper = documentServiceHelper; + TenantManager = tenantManager; + SecurityContext = securityContext; + FolderWrapperHelper = folderWrapperHelper; + FileOperationWraperHelper = fileOperationWraperHelper; + FileShareWrapperHelper = fileShareWrapperHelper; + FileShareParamsHelper = fileShareParamsHelper; + EntryManager = entryManager; + UserManager = userManager; + WebItemSecurity = webItemSecurity; + CoreBaseSettings = coreBaseSettings; + ThirdpartyConfiguration = thirdpartyConfiguration; + BoxLoginProvider = boxLoginProvider; + DropboxLoginProvider = dropboxLoginProvider; + GoogleLoginProvider = googleLoginProvider; + OneDriveLoginProvider = oneDriveLoginProvider; + MessageService = messageService; + CommonLinkUtility = commonLinkUtility; + DocumentServiceConnector = documentServiceConnector; + FolderContentWrapperHelper = folderContentWrapperHelper; + WordpressToken = wordpressToken; + WordpressHelper = wordpressHelper; + ConsumerFactory = consumerFactory; + EasyBibHelper = easyBibHelper; + ChunkedUploadSessionHelper = chunkedUploadSessionHelper; + ProductEntryPoint = productEntryPoint; + } + + [Read("info")] + public Module GetModule() + { + ProductEntryPoint.Init(); + return new Module(ProductEntryPoint, true); + } + + /// + /// Returns the detailed list of files and folders located in the current user 'My Documents' section + /// + /// + /// My folder + /// + /// Folders + /// My folder contents + [Read("@my")] + public FolderContentWrapper GetMyFolder(Guid userIdOrGroupId, FilterType filterType) + { + return ToFolderContentWrapper(GlobalFolderHelper.FolderMy, userIdOrGroupId, filterType); + } + + /// + /// Returns the detailed list of files and folders located in the current user 'Projects Documents' section + /// + /// + /// Projects folder + /// + /// Folders + /// Projects folder contents + [Read("@projects")] + public FolderContentWrapper GetProjectsFolder(Guid userIdOrGroupId, FilterType filterType) + { + return ToFolderContentWrapper(GlobalFolderHelper.FolderProjects, userIdOrGroupId, filterType); + } + + + /// + /// Returns the detailed list of files and folders located in the 'Common Documents' section + /// + /// + /// Common folder + /// + /// Folders + /// Common folder contents + [Read("@common")] + public FolderContentWrapper GetCommonFolder(Guid userIdOrGroupId, FilterType filterType) + { + return ToFolderContentWrapper(GlobalFolderHelper.FolderCommon, userIdOrGroupId, filterType); + } + + /// + /// Returns the detailed list of files and folders located in the 'Shared with Me' section + /// + /// + /// Shared folder + /// + /// Folders + /// Shared folder contents + [Read("@share")] + public FolderContentWrapper GetShareFolder(Guid userIdOrGroupId, FilterType filterType) + { + return ToFolderContentWrapper(GlobalFolderHelper.FolderShare, userIdOrGroupId, filterType); + } + + /// + /// Returns the detailed list of files and folders located in the 'Recycle Bin' section + /// + /// + /// Trash folder + /// + /// Folders + /// Trash folder contents + [Read("@trash")] + public FolderContentWrapper GetTrashFolder(Guid userIdOrGroupId, FilterType filterType) + { + return ToFolderContentWrapper(GlobalFolderHelper.FolderTrash, userIdOrGroupId, filterType); + } + + /// + /// Returns the detailed list of files and folders located in the folder with the ID specified in the request + /// + /// + /// Folder by ID + /// + /// Folders + /// Folder ID + /// User or group ID + /// Filter type + /// Folder contents + [Read("{folderId}", order: int.MaxValue)] + public FolderContentWrapper GetFolder(string folderId, Guid userIdOrGroupId, FilterType filterType) + { + return ToFolderContentWrapper(folderId, userIdOrGroupId, filterType).NotFoundIfNull(); + + } + + /// + /// Uploads the file specified with single file upload or standart multipart/form-data method to 'My Documents' section + /// + /// Upload to My + /// Uploads + /// + /// + ///
  • Single file upload. You should set Content-Type & Content-Disposition header to specify filename and content type, and send file in request body
  • + ///
  • Using standart multipart/form-data method
  • + /// ]]> + ///
    + /// Request Input stream + /// Content-Type Header + /// Content-Disposition Header + /// List of files when posted as multipart/form-data + /// Uploaded file + [Create("@my/upload")] + public object UploadFileToMy(UploadModel uploadModel) + { + uploadModel.CreateNewIfExist = false; + return UploadFile(GlobalFolderHelper.FolderMy.ToString(), uploadModel); + } + + /// + /// Uploads the file specified with single file upload or standart multipart/form-data method to 'Common Documents' section + /// + /// Upload to Common + /// Uploads + /// + /// + ///
  • Single file upload. You should set Content-Type & Content-Disposition header to specify filename and content type, and send file in request body
  • + ///
  • Using standart multipart/form-data method
  • + /// ]]> + ///
    + /// Request Input stream + /// Content-Type Header + /// Content-Disposition Header + /// List of files when posted as multipart/form-data + /// Uploaded file + [Create("@common/upload")] + public object UploadFileToCommon(UploadModel uploadModel) + { + uploadModel.CreateNewIfExist = false; + return UploadFile(GlobalFolderHelper.FolderCommon.ToString(), uploadModel); + } + + + /// + /// Uploads the file specified with single file upload or standart multipart/form-data method to the selected folder + /// + /// Upload to folder + /// Uploads + /// + /// + ///
  • Single file upload. You should set Content-Type & Content-Disposition header to specify filename and content type, and send file in request body
  • + ///
  • Using standart multipart/form-data method
  • + /// ]]> + ///
    + /// Folder ID to upload to + /// Request Input stream + /// Content-Type Header + /// Content-Disposition Header + /// List of files when posted as multipart/form-data + /// Create New If Exist + /// If True, upload documents in original formats as well + /// Keep status conversation after finishing + /// Uploaded file + [Create("{folderId}/upload")] + public object UploadFile(string folderId, UploadModel uploadModel) + { + if (uploadModel.StoreOriginalFileFlag.HasValue) + { + FilesSettingsHelper.StoreOriginalFiles = uploadModel.StoreOriginalFileFlag.Value; + } + + if (uploadModel.Files != null && uploadModel.Files.Any()) + { + if (uploadModel.Files.Count() == 1) + { + //Only one file. return it + var postedFile = uploadModel.Files.First(); + return InsertFile(folderId, postedFile.OpenReadStream(), postedFile.FileName, uploadModel.CreateNewIfExist, uploadModel.KeepConvertStatus); + } + //For case with multiple files + return uploadModel.Files.Select(postedFile => InsertFile(folderId, postedFile.OpenReadStream(), postedFile.FileName, uploadModel.CreateNewIfExist, uploadModel.KeepConvertStatus)).ToList(); + } + if (uploadModel.File != null) + { + var fileName = "file" + MimeMapping.GetExtention(uploadModel.ContentType.MediaType); + if (uploadModel.ContentDisposition != null) + { + fileName = uploadModel.ContentDisposition.FileName; + } + + return InsertFile(folderId, uploadModel.File, fileName, uploadModel.CreateNewIfExist, uploadModel.KeepConvertStatus); + } + throw new InvalidOperationException("No input files"); + } + + /// + /// Uploads the file specified with single file upload to 'Common Documents' section + /// + /// Request Input stream + /// Name of file which has to be uploaded + /// Create New If Exist + /// Keep status conversation after finishing + /// Uploads + /// + [Create("@my/insert")] + public FileWrapper InsertFileToMy(Stream file, string title, bool? createNewIfExist, bool keepConvertStatus = false) + { + return InsertFile(GlobalFolderHelper.FolderMy.ToString(), file, title, createNewIfExist, keepConvertStatus); + } + + /// + /// Uploads the file specified with single file upload to 'Common Documents' section + /// + /// Request Input stream + /// Name of file which has to be uploaded + /// Create New If Exist + /// Keep status conversation after finishing + /// Uploads + /// + [Create("@common/insert")] + public FileWrapper InsertFileToCommon(Stream file, string title, bool? createNewIfExist, bool keepConvertStatus = false) + { + return InsertFile(GlobalFolderHelper.FolderCommon.ToString(), file, title, createNewIfExist, keepConvertStatus); + } + + /// + /// Uploads the file specified with single file upload + /// + /// Folder ID to upload to + /// Request Input stream + /// Name of file which has to be uploaded + /// Create New If Exist + /// Keep status conversation after finishing + /// Uploads + /// + [Create("{folderId}/insert")] + public FileWrapper InsertFile(string folderId, Stream file, string title, bool? createNewIfExist, bool keepConvertStatus = false) + { + try + { + var resultFile = FileUploader.Exec(folderId, title, file.Length, file, createNewIfExist ?? !FilesSettingsHelper.UpdateIfExist, !keepConvertStatus); + return FileWrapperHelper.Get(resultFile); + } + catch (FileNotFoundException e) + { + throw new ItemNotFoundException("File not found", e); + } + catch (DirectoryNotFoundException e) + { + throw new ItemNotFoundException("Folder not found", e); + } + } + + /// + /// + /// + /// + /// + /// + /// + /// false + [Update("{fileId}/update")] + public FileWrapper UpdateFileStream(Stream file, string fileId, bool encrypted = false) + { + try + { + var resultFile = FileStorageService.UpdateFileStream(fileId, file, encrypted); + return FileWrapperHelper.Get(resultFile); + } + catch (FileNotFoundException e) + { + throw new ItemNotFoundException("File not found", e); + } + } + + + /// + /// + /// + /// File ID + /// + /// + /// + /// + /// + /// Files + /// + [Update("file/{fileId}/saveediting")] + public FileWrapper SaveEditing(string fileId, string fileExtension, string downloadUri, Stream stream, string doc, bool forcesave) + { + return FileWrapperHelper.Get(FileStorageService.SaveEditing(fileId, fileExtension, downloadUri, stream, doc, forcesave)); + } + + /// + /// + /// + /// File ID + /// + /// + /// Files + /// + [Create("file/{fileId}/startedit")] + public string StartEdit(string fileId, bool editingAlone, string doc) + { + return FileStorageService.StartEdit(fileId, editingAlone, doc); + } + + /// + /// + /// + /// File ID + /// + /// + /// + /// + /// Files + /// + [Read("file/{fileId}/trackeditfile")] + public KeyValuePair TrackEditFile(string fileId, Guid tabId, string docKeyForTrack, string doc, bool isFinish) + { + return FileStorageService.TrackEditFile(fileId, tabId, docKeyForTrack, doc, isFinish); + } + + /// + /// + /// + /// File ID + /// + /// + /// Files + /// + [Read("file/{fileId}/openedit")] + public Configuration OpenEdit(string fileId, int version, string doc) + { + Configuration configuration; + DocumentServiceHelper.GetParams(fileId, version, doc, true, true, true, out configuration); + configuration.Type = Configuration.EditorType.External; + + configuration.Token = DocumentServiceHelper.GetSignature(configuration); + return configuration; + } + + + /// + /// Creates session to upload large files in multiple chunks. + /// + /// Chunked upload + /// Uploads + /// Id of the folder in which file will be uploaded + /// Name of file which has to be uploaded + /// Length in bytes of file which has to be uploaded + /// Relative folder from folderId + /// + /// + /// 512 and greater or equal than 5 mb. Last chunk can have any size. + /// After initial request respond with status 200 OK you must obtain value of 'location' field from the response. Send all your chunks to that location. + /// Each chunk must be sent in strict order in which chunks appears in file. + /// After receiving each chunk if no errors occured server will respond with current information about upload session. + /// When number of uploaded bytes equal to the number of bytes you send in initial request server will respond with 201 Created and will send you info about uploaded file. + /// ]]> + /// + /// + /// + ///
  • id: unique id of this upload session
  • + ///
  • created: UTC time when session was created
  • + ///
  • expired: UTC time when session will be expired if no chunks will be sent until that time
  • + ///
  • location: URL to which you must send your next chunk
  • + ///
  • bytes_uploaded: If exists contains number of bytes uploaded for specific upload id
  • + ///
  • bytes_total: Number of bytes which has to be uploaded
  • + /// + /// ]]> + ///
    + [Create("{folderId}/upload/create_session")] + public object CreateUploadSession(string folderId, string fileName, long fileSize, string relativePath, bool encrypted) + { + var file = FileUploader.VerifyChunkedUpload(folderId, fileName, fileSize, FilesSettingsHelper.UpdateIfExist, relativePath); + + if (FilesLinkUtility.IsLocalFileUploader) + { + var session = FileUploader.InitiateUpload(file.FolderID.ToString(), (file.ID ?? "").ToString(), file.Title, file.ContentLength, encrypted); + + var response = ChunkedUploadSessionHelper.ToResponseObject(session, true); + return new + { + success = true, + data = response + }; + } + + var createSessionUrl = FilesLinkUtility.GetInitiateUploadSessionUrl(TenantManager.GetCurrentTenant().TenantId, file.FolderID, file.ID, file.Title, file.ContentLength, encrypted, SecurityContext); + var request = (HttpWebRequest)WebRequest.Create(createSessionUrl); + request.Method = "POST"; + request.ContentLength = 0; + + // hack for uploader.onlyoffice.com in api requests + var rewriterHeader = ApiContext.HttpContext.Request.Headers[HttpRequestExtensions.UrlRewriterHeader]; + if (!string.IsNullOrEmpty(rewriterHeader)) + { + request.Headers[HttpRequestExtensions.UrlRewriterHeader] = rewriterHeader; + } + + // hack. http://ubuntuforums.org/showthread.php?t=1841740 + if (WorkContext.IsMono) + { + ServicePointManager.ServerCertificateValidationCallback += (s, ce, ca, p) => true; + } + + using (var response = request.GetResponse()) + using (var responseStream = response.GetResponseStream()) + { + return JObject.Parse(new StreamReader(responseStream).ReadToEnd()); //result is json string + } + } + + /// + /// Creates a text (.txt) file in 'My Documents' section with the title and contents sent in the request + /// + /// Create txt in 'My' + /// File Creation + /// File title + /// File contents + /// Folder contents + [Create("@my/text")] + public FileWrapper CreateTextFileInMy(string title, string content) + { + return CreateTextFile(GlobalFolderHelper.FolderMy.ToString(), title, content); + } + + /// + /// Creates a text (.txt) file in 'Common Documents' section with the title and contents sent in the request + /// + /// Create txt in 'Common' + /// File Creation + /// File title + /// File contents + /// Folder contents + [Create("@common/text")] + public FileWrapper CreateTextFileInCommon(string title, string content) + { + return CreateTextFile(GlobalFolderHelper.FolderCommon.ToString(), title, content); + } + + /// + /// Creates a text (.txt) file in the selected folder with the title and contents sent in the request + /// + /// Create txt + /// File Creation + /// Folder ID + /// File title + /// File contents + /// Folder contents + [Create("{folderId}/text")] + public FileWrapper CreateTextFile(string folderId, string title, string content) + { + if (title == null) throw new ArgumentNullException("title"); + //Try detect content + var extension = ".txt"; + if (!string.IsNullOrEmpty(content)) + { + if (Regex.IsMatch(content, @"<([^\s>]*)(\s[^<]*)>")) + { + extension = ".html"; + } + } + return CreateFile(folderId, title, content, extension); + } + + private FileWrapper CreateFile(string folderId, string title, string content, string extension) + { + using (var memStream = new MemoryStream(Encoding.UTF8.GetBytes(content))) + { + var file = FileUploader.Exec(folderId, + title.EndsWith(extension, StringComparison.OrdinalIgnoreCase) ? title : (title + extension), + memStream.Length, memStream); + return FileWrapperHelper.Get(file); + } + } + + /// + /// Creates an html (.html) file in the selected folder with the title and contents sent in the request + /// + /// Create html + /// File Creation + /// Folder ID + /// File title + /// File contents + /// Folder contents + [Create("{folderId}/html")] + public FileWrapper CreateHtmlFile(string folderId, string title, string content) + { + if (title == null) throw new ArgumentNullException("title"); + return CreateFile(folderId, title, content, ".html"); + } + + /// + /// Creates an html (.html) file in 'My Documents' section with the title and contents sent in the request + /// + /// Create html in 'My' + /// File Creation + /// File title + /// File contents + /// Folder contents + [Create("@my/html")] + public FileWrapper CreateHtmlFileInMy(string title, string content) + { + return CreateHtmlFile(GlobalFolderHelper.FolderMy.ToString(), title, content); + } + + + /// + /// Creates an html (.html) file in 'Common Documents' section with the title and contents sent in the request + /// + /// Create html in 'Common' + /// File Creation + /// File title + /// File contents + /// Folder contents + [Create("@common/html")] + public FileWrapper CreateHtmlFileInCommon(string title, string content) + { + return CreateHtmlFile(GlobalFolderHelper.FolderCommon.ToString(), title, content); + } + + + /// + /// Creates a new folder with the title sent in the request. The ID of a parent folder can be also specified. + /// + /// + /// New folder + /// + /// Folders + /// Parent folder ID + /// Title of new folder + /// New folder contents + [Create("folder/{folderId}")] + public FolderWrapper CreateFolder(string folderId, string title) + { + var folder = FileStorageService.CreateNewFolder(folderId, title); + return FolderWrapperHelper.Get(folder); + } + + /// + /// Creates a new file in the 'My Documents' section with the title sent in the request + /// + /// Create file + /// File Creation + /// File title + /// In case the extension for the file title differs from DOCX/XLSX/PPTX and belongs to one of the known text, spreadsheet or presentation formats, it will be changed to DOCX/XLSX/PPTX accordingly. If the file extension is not set or is unknown, the DOCX extension will be added to the file title. + /// New file info + [Create("@my/file")] + public FileWrapper CreateFile(string title) + { + return CreateFile(GlobalFolderHelper.FolderMy.ToString(), title); + } + + /// + /// Creates a new file in the specified folder with the title sent in the request + /// + /// Create file + /// File Creation + /// Folder ID + /// File title + /// In case the extension for the file title differs from DOCX/XLSX/PPTX and belongs to one of the known text, spreadsheet or presentation formats, it will be changed to DOCX/XLSX/PPTX accordingly. If the file extension is not set or is unknown, the DOCX extension will be added to the file title. + /// New file info + [Create("{folderId}/file")] + public FileWrapper CreateFile(string folderId, string title) + { + var file = FileStorageService.CreateNewFile(new FileModel { ParentId = folderId, Title = title }); + return FileWrapperHelper.Get(file); + } + + /// + /// Renames the selected folder to the new title specified in the request + /// + /// + /// Rename folder + /// + /// Folders + /// Folder ID + /// New title + /// Folder contents + [Update("folder/{folderId}")] + public FolderWrapper RenameFolder(string folderId, string title) + { + var folder = FileStorageService.FolderRename(folderId, title); + return FolderWrapperHelper.Get(folder); + } + + /// + /// Returns a detailed information about the folder with the ID specified in the request + /// + /// Folder information + /// Folders + /// Folder info + [Read("folder/{folderId}")] + public FolderWrapper GetFolderInfo(string folderId) + { + var folder = FileStorageService.GetFolder(folderId).NotFoundIfNull("Folder not found"); + + return FolderWrapperHelper.Get(folder); + } + + /// + /// Returns parent folders + /// + /// + /// Folders + /// Parent folders + [Read("folder/{folderId}/path")] + public IEnumerable GetFolderPath(string folderId) + { + return EntryManager.GetBreadCrumbs(folderId).Select(FolderWrapperHelper.Get); + } + + /// + /// Returns a detailed information about the file with the ID specified in the request + /// + /// File information + /// Files + /// File info + [Read("file/{fileId}")] + public FileWrapper GetFileInfo(string fileId, int version = -1) + { + var file = FileStorageService.GetFile(fileId, version).NotFoundIfNull("File not found"); + return FileWrapperHelper.Get(file); + } + + /// + /// Updates the information of the selected file with the parameters specified in the request + /// + /// Update file info + /// Files + /// File ID + /// New title + /// File last version number + /// File info + [Update("file/{fileId}")] + public FileWrapper UpdateFile(string fileId, string title, int lastVersion) + { + if (!string.IsNullOrEmpty(title)) + FileStorageService.FileRename(fileId.ToString(CultureInfo.InvariantCulture), title); + + if (lastVersion > 0) + FileStorageService.UpdateToVersion(fileId.ToString(CultureInfo.InvariantCulture), lastVersion); + + return GetFileInfo(fileId); + } + + /// + /// Deletes the file with the ID specified in the request + /// + /// Delete file + /// Files + /// File ID + /// Delete after finished + /// Don't move to the Recycle Bin + /// Operation result + [Delete("file/{fileId}")] + public IEnumerable DeleteFile(string fileId, bool deleteAfter, bool immediately) + { + var model = new DeleteBatchModel + { + FileIds = new List { fileId }, + DeleteAfter = deleteAfter, + Immediately = immediately + }; + + return DeleteBatchItems(model); + } + + /// + /// Start conversion + /// + /// Convert + /// File operations + /// + /// Operation result + [Update("file/{fileId}/checkconversion")] + public IEnumerable StartConversion(string fileId) + { + return CheckConversion(fileId, true); + } + + /// + /// Check conversion status + /// + /// Convert + /// File operations + /// + /// + /// Operation result + [Read("file/{fileId}/checkconversion")] + public IEnumerable CheckConversion(string fileId, bool start) + { + return FileStorageService.CheckConversion(new ItemList> + { + new ItemList { fileId, "0", start.ToString() } + }) + .Select(r => + { + var o = new ConversationResult + { + Id = r.Id, + Error = r.Error, + OperationType = r.OperationType, + Processed = r.Processed, + Progress = r.Progress, + Source = r.Source, + }; + if (!string.IsNullOrEmpty(r.Result)) + { + var jResult = JObject.Parse(r.Result); + o.File = GetFileInfo(jResult.Value("id"), jResult.Value("version")); + } + return o; + }); + } + + /// + /// Deletes the folder with the ID specified in the request + /// + /// Delete folder + /// Folders + /// Folder ID + /// Delete after finished + /// Don't move to the Recycle Bin + /// Operation result + [Delete("folder/{folderId}")] + public IEnumerable DeleteFolder(string folderId, bool deleteAfter, bool immediately) + { + var model = new DeleteBatchModel + { + FolderIds = new List { folderId }, + DeleteAfter = deleteAfter, + Immediately = immediately + }; + + return DeleteBatchItems(model); + } + + /// + /// Checking for conflicts + /// + /// File operations + /// Destination folder ID + /// Folder ID list + /// File ID list + /// Conflicts file ids + [Read("fileops/move")] + public IEnumerable MoveOrCopyBatchCheck(BatchModel batchModel) + { + var itemList = new ItemList(); + + itemList.AddRange((batchModel.FolderIds ?? new List()).Select(x => "folder_" + x)); + itemList.AddRange((batchModel.FileIds ?? new List()).Select(x => "file_" + x)); + + var ids = FileStorageService.MoveOrCopyFilesCheck(itemList, batchModel.DestFolderId).Keys.Select(id => "file_" + id); + + var entries = FileStorageService.GetItems(new ItemList(ids), FilterType.FilesOnly, false, "", ""); + return entries.Select(x => FileWrapperHelper.Get((Files.Core.File)x)); + } + + /// + /// Moves all the selected files and folders to the folder with the ID specified in the request + /// + /// Move to folder + /// File operations + /// Destination folder ID + /// Folder ID list + /// File ID list + /// Overwriting behavior: skip(0), overwrite(1) or duplicate(2) + /// Delete after finished + /// Operation result + [Update("fileops/move")] + public IEnumerable MoveBatchItems(BatchModel batchModel) + { + var itemList = new ItemList(); + + itemList.AddRange((batchModel.FolderIds ?? new List()).Select(x => "folder_" + x)); + itemList.AddRange((batchModel.FileIds ?? new List()).Select(x => "file_" + x)); + + return FileStorageService.MoveOrCopyItems(itemList, batchModel.DestFolderId, batchModel.ConflictResolveType, false, batchModel.DeleteAfter).Select(FileOperationWraperHelper.Get); + } + + /// + /// Copies all the selected files and folders to the folder with the ID specified in the request + /// + /// Copy to folder + /// File operations + /// Destination folder ID + /// Folder ID list + /// File ID list + /// Overwriting behavior: skip(0), overwrite(1) or duplicate(2) + /// Delete after finished + /// Operation result + [Update("fileops/copy")] + public IEnumerable CopyBatchItems(BatchModel batchModel) + { + var itemList = new ItemList(); + + itemList.AddRange((batchModel.FolderIds ?? new List()).Select(x => "folder_" + x)); + itemList.AddRange((batchModel.FileIds ?? new List()).Select(x => "file_" + x)); + + return FileStorageService.MoveOrCopyItems(itemList, batchModel.DestFolderId, batchModel.ConflictResolveType, true, batchModel.DeleteAfter).Select(FileOperationWraperHelper.Get); + } + + /// + /// Marks all files and folders as read + /// + /// Mark as read + /// File operations + /// Operation result + [Update("fileops/markasread")] + public IEnumerable MarkAsRead(BaseBatchModel model) + { + var itemList = new ItemList(); + + itemList.AddRange((model.FolderIds ?? new List()).Select(x => "folder_" + x)); + itemList.AddRange((model.FileIds ?? new List()).Select(x => "file_" + x)); + + return FileStorageService.MarkAsRead(itemList).Select(FileOperationWraperHelper.Get); + } + + /// + /// Finishes all the active file operations + /// + /// Finish all + /// File operations + /// Operation result + [Update("fileops/terminate")] + public IEnumerable TerminateTasks() + { + return FileStorageService.TerminateTasks().Select(FileOperationWraperHelper.Get); + } + + + /// + /// Returns the list of all active file operations + /// + /// Get file operations list + /// File operations + /// Operation result + [Read("fileops")] + public IEnumerable GetOperationStatuses() + { + return FileStorageService.GetTasksStatuses().Select(FileOperationWraperHelper.Get); + } + + /// + /// Start downlaod process of files and folders with ID + /// + /// Finish file operations + /// File ID list for download with convert to format + /// File ID list + /// Folder ID list + /// File operations + /// Operation result + [Update("fileops/bulkdownload")] + public IEnumerable BulkDownload(DownloadModel model) + { + var itemList = new Dictionary(); + + foreach (var fileId in model.FileConvertIds.Where(fileId => !itemList.ContainsKey(fileId.Key))) + { + itemList.Add("file_" + fileId.Key, fileId.Value); + } + + foreach (var fileId in model.FileIds.Where(fileId => !itemList.ContainsKey(fileId))) + { + itemList.Add("file_" + fileId, string.Empty); + } + + foreach (var folderId in model.FolderIds.Where(folderId => !itemList.ContainsKey(folderId))) + { + itemList.Add("folder_" + folderId, string.Empty); + } + + return FileStorageService.BulkDownload(itemList).Select(FileOperationWraperHelper.Get); + } + + /// + /// Deletes the files and folders with the IDs specified in the request + /// + /// Folder ID list + /// File ID list + /// Delete after finished + /// Don't move to the Recycle Bin + /// Delete files and folders + /// File operations + /// Operation result + [Update("fileops/delete")] + public IEnumerable DeleteBatchItems(DeleteBatchModel batch) + { + var itemList = new ItemList(); + + itemList.AddRange((batch.FolderIds ?? new List()).Select(x => "folder_" + x)); + itemList.AddRange((batch.FileIds ?? new List()).Select(x => "file_" + x)); + + return FileStorageService.DeleteItems("delete", itemList, false, batch.DeleteAfter, batch.Immediately).Select(FileOperationWraperHelper.Get); + } + + /// + /// Deletes all files and folders from the recycle bin + /// + /// Clear recycle bin + /// File operations + /// Operation result + [Update("fileops/emptytrash")] + public IEnumerable EmptyTrash() + { + return FileStorageService.EmptyTrash().Select(FileOperationWraperHelper.Get); + } + + /// + /// Returns the detailed information about all the available file versions with the ID specified in the request + /// + /// File versions + /// Files + /// File ID + /// File information + [Read("file/{fileId}/history")] + public IEnumerable GetFileVersionInfo(string fileId) + { + var files = FileStorageService.GetFileHistory(fileId); + return files.Select(FileWrapperHelper.Get); + } + + /// + /// Change version history + /// + /// File ID + /// Version of history + /// Mark as version or revision + /// Files + /// + [Update("file/{fileId}/history")] + public IEnumerable ChangeHistory(string fileId, int version, bool continueVersion) + { + var history = FileStorageService.CompleteVersion(fileId, version, continueVersion).Value; + return history.Select(FileWrapperHelper.Get); + } + + /// + /// Returns the detailed information about shared file with the ID specified in the request + /// + /// File sharing + /// Sharing + /// File ID + /// Shared file information + [Read("file/{fileId}/share")] + public IEnumerable GetFileSecurityInfo(string fileId) + { + var fileShares = FileStorageService.GetSharedInfo(new ItemList { string.Format("file_{0}", fileId) }); + return fileShares.Select(FileShareWrapperHelper.Get); + } + + /// + /// Returns the detailed information about shared folder with the ID specified in the request + /// + /// Folder sharing + /// Folder ID + /// Sharing + /// Shared folder information + [Read("folder/{folderId}/share")] + public IEnumerable GetFolderSecurityInfo(string folderId) + { + var fileShares = FileStorageService.GetSharedInfo(new ItemList { string.Format("folder_{0}", folderId) }); + return fileShares.Select(FileShareWrapperHelper.Get); + } + + /// + /// Sets sharing settings for the file with the ID specified in the request + /// + /// File ID + /// Collection of sharing rights + /// Should notify people + /// Sharing message to send when notifying + /// Share file + /// Sharing + /// + /// Each of the FileShareParams must contain two parameters: 'ShareTo' - ID of the user with whom we want to share and 'Access' - access type which we want to grant to the user (Read, ReadWrite, etc) + /// + /// Shared file information + [Update("file/{fileId}/share")] + public IEnumerable SetFileSecurityInfo(string fileId, IEnumerable share, bool notify, string sharingMessage) + { + if (share != null && share.Any()) + { + var list = new ItemList(share.Select(FileShareParamsHelper.ToAceObject)); + var aceCollection = new AceCollection + { + Entries = new ItemList { "file_" + fileId }, + Aces = list, + Message = sharingMessage + }; + FileStorageService.SetAceObject(aceCollection, notify); + } + return GetFileSecurityInfo(fileId); + } + + /// + /// Sets sharing settings for the folder with the ID specified in the request + /// + /// Share folder + /// Folder ID + /// Collection of sharing rights + /// Should notify people + /// Sharing message to send when notifying + /// + /// Each of the FileShareParams must contain two parameters: 'ShareTo' - ID of the user with whom we want to share and 'Access' - access type which we want to grant to the user (Read, ReadWrite, etc) + /// + /// Sharing + /// Shared folder information + [Update("folder/{folderId}/share")] + public IEnumerable SetFolderSecurityInfo(string folderId, IEnumerable share, bool notify, string sharingMessage) + { + if (share != null && share.Any()) + { + var list = new ItemList(share.Select(FileShareParamsHelper.ToAceObject)); + var aceCollection = new AceCollection + { + Entries = new ItemList { "folder_" + folderId }, + Aces = list, + Message = sharingMessage + }; + FileStorageService.SetAceObject(aceCollection, notify); + } + + return GetFolderSecurityInfo(folderId); + } + + /// + /// Removes sharing rights for the group with the ID specified in the request + /// + /// Folders ID + /// Files ID + /// Remove group sharing rights + /// Sharing + /// Shared file information + [Delete("share")] + public bool RemoveSecurityInfo(BaseBatchModel model) + { + var itemList = new ItemList(); + + itemList.AddRange((model.FolderIds ?? new List()).Select(x => "folder_" + x)); + itemList.AddRange((model.FileIds ?? new List()).Select(x => "file_" + x)); + + FileStorageService.RemoveAce(itemList); + + return true; + } + + /// + /// Returns the external link to the shared file with the ID specified in the request + /// + /// + /// File external link + /// + /// File ID + /// Access right + /// Files + /// Shared file link + [Update("{fileId}/sharedlink")] + public string GenerateSharedLink(string fileId, FileShare share) + { + var file = GetFileInfo(fileId); + + var objectId = "file_" + file.Id; + var sharedInfo = FileStorageService.GetSharedInfo(new ItemList { objectId }).Find(r => r.SubjectId == FileConstant.ShareLinkId); + if (sharedInfo == null || sharedInfo.Share != share) + { + var list = new ItemList + { + new AceWrapper + { + SubjectId = FileConstant.ShareLinkId, + SubjectGroup = true, + Share = share + } + }; + var aceCollection = new AceCollection + { + Entries = new ItemList { objectId }, + Aces = list + }; + FileStorageService.SetAceObject(aceCollection, false); + sharedInfo = FileStorageService.GetSharedInfo(new ItemList { objectId }).Find(r => r.SubjectId == FileConstant.ShareLinkId); + } + + return sharedInfo.Link; + } + + /// + /// Get a list of available providers + /// + /// Third-Party Integration + /// List of provider key + /// List of provider key: DropboxV2, Box, WebDav, Yandex, OneDrive, SharePoint, GoogleDrive + /// + [Read("thirdparty/capabilities")] + public List> Capabilities() + { + var result = new List>(); + + if (UserManager.GetUsers(SecurityContext.CurrentAccount.ID).IsVisitor(UserManager) + || (!UserManager.IsUserInGroup(SecurityContext.CurrentAccount.ID, Constants.GroupAdmin.ID) + && !WebItemSecurity.IsProductAdministrator(ProductEntryPoint.ID, SecurityContext.CurrentAccount.ID) + && !FilesSettingsHelper.EnableThirdParty + && !CoreBaseSettings.Personal)) + { + return result; + } + + if (ThirdpartyConfiguration.SupportBoxInclusion) + { + result.Add(new List { "Box", BoxLoginProvider.Instance.ClientID, BoxLoginProvider.Instance.RedirectUri }); + } + if (ThirdpartyConfiguration.SupportDropboxInclusion) + { + result.Add(new List { "DropboxV2", DropboxLoginProvider.Instance.ClientID, DropboxLoginProvider.Instance.RedirectUri }); + } + if (ThirdpartyConfiguration.SupportGoogleDriveInclusion) + { + result.Add(new List { "GoogleDrive", GoogleLoginProvider.Instance.ClientID, GoogleLoginProvider.Instance.RedirectUri }); + } + if (ThirdpartyConfiguration.SupportOneDriveInclusion) + { + result.Add(new List { "OneDrive", OneDriveLoginProvider.Instance.ClientID, OneDriveLoginProvider.Instance.RedirectUri }); + } + if (ThirdpartyConfiguration.SupportSharePointInclusion) + { + result.Add(new List { "SharePoint" }); + } + if (ThirdpartyConfiguration.SupportYandexInclusion) + { + result.Add(new List { "Yandex" }); + } + if (ThirdpartyConfiguration.SupportWebDavInclusion) + { + result.Add(new List { "WebDav" }); + } + + //Obsolete BoxNet, DropBox, Google, SkyDrive, + + return result; + } + + /// + /// Saves the third party file storage service account + /// + /// Save third party account + /// Connection url for SharePoint + /// Login + /// Password + /// Authentication token + /// + /// Title + /// Provider Key + /// Provider ID + /// Third-Party Integration + /// Folder contents + /// List of provider key: DropboxV2, Box, WebDav, Yandex, OneDrive, SharePoint, GoogleDrive + /// + [Create("thirdparty")] + public FolderWrapper SaveThirdParty( + string url, + string login, + string password, + string token, + bool isCorporate, + string customerTitle, + string providerKey, + string providerId) + { + var thirdPartyParams = new ThirdPartyParams + { + AuthData = new AuthData(url, login, password, token), + Corporate = isCorporate, + CustomerTitle = customerTitle, + ProviderId = providerId, + ProviderKey = providerKey, + }; + + var folder = FileStorageService.SaveThirdParty(thirdPartyParams); + + return FolderWrapperHelper.Get(folder); + } + + /// + /// Returns the list of all connected third party services + /// + /// Third-Party Integration + /// Get third party list + /// Connected providers + [Read("thirdparty")] + public IEnumerable GetThirdPartyAccounts() + { + return FileStorageService.GetThirdParty(); + } + + /// + /// Returns the list of third party services connected in the 'Common Documents' section + /// + /// Third-Party Integration + /// Get third party folder + /// Connected providers folder + [Read("thirdparty/common")] + public IEnumerable GetCommonThirdPartyFolders() + { + var parent = FileStorageService.GetFolder(GlobalFolderHelper.FolderCommon.ToString()); + return EntryManager.GetThirpartyFolders(parent); + } + + /// + /// Removes the third party file storage service account with the ID specified in the request + /// + /// Provider ID. Provider id is part of folder id. + /// Example, folder id is "sbox-123", then provider id is "123" + /// + /// Remove third party account + /// Third-Party Integration + /// Folder id + /// + [Delete("thirdparty/{providerId:int}")] + public object DeleteThirdParty(int providerId) + { + return FileStorageService.DeleteThirdParty(providerId.ToString(CultureInfo.InvariantCulture)); + + } + + ///// + ///// + ///// + ///// + ///// + //[Read(@"@search/{query}")] + //public IEnumerable Search(string query) + //{ + // var searcher = new SearchHandler(); + // var files = searcher.SearchFiles(query).Select(r => (FileEntryWrapper)FileWrapperHelper.Get(r)); + // var folders = searcher.SearchFolders(query).Select(f => (FileEntryWrapper)FolderWrapperHelper.Get(f)); + + // return files.Concat(folders); + //} + + + /// + /// + /// + /// + /// + [Update(@"storeoriginal")] + public bool StoreOriginal(bool set) + { + return FileStorageService.StoreOriginal(set); + } + + /// + /// + /// + /// + /// false + /// + [Update(@"hideconfirmconvert")] + public bool HideConfirmConvert(bool save) + { + return FileStorageService.HideConfirmConvert(save); + } + + /// + /// + /// + /// + /// + [Update(@"updateifexist")] + public bool UpdateIfExist(bool set) + { + return FileStorageService.UpdateIfExist(set); + } + + /// + /// Checking document service location + /// + /// Document editing service Domain + /// Document command service Domain + /// Community Server Address + /// + [Update("docservice")] + public IEnumerable CheckDocServiceUrl(string docServiceUrl, string docServiceUrlInternal, string docServiceUrlPortal) + { + FilesLinkUtility.DocServiceUrl = docServiceUrl; + FilesLinkUtility.DocServiceUrlInternal = docServiceUrlInternal; + FilesLinkUtility.DocServicePortalUrl = docServiceUrlPortal; + + MessageService.Send(MessageAction.DocumentServiceLocationSetting); + + var https = new Regex(@"^https://", RegexOptions.IgnoreCase); + var http = new Regex(@"^http://", RegexOptions.IgnoreCase); + if (https.IsMatch(CommonLinkUtility.GetFullAbsolutePath("")) && http.IsMatch(FilesLinkUtility.DocServiceUrl)) + { + throw new Exception("Mixed Active Content is not allowed. HTTPS address for Document Server is required."); + } + + DocumentServiceConnector.CheckDocServiceUrl(); + + return new[] + { + FilesLinkUtility.DocServiceUrl, + FilesLinkUtility.DocServiceUrlInternal, + FilesLinkUtility.DocServicePortalUrl + }; + } + + /// false + [Read("docservice")] + public object GetDocServiceUrl(bool version) + { + var url = CommonLinkUtility.GetFullAbsolutePath(FilesLinkUtility.DocServiceApiUrl); + if (!version) + { + return url; + } + + var dsVersion = DocumentServiceConnector.GetVersion(); + + return new + { + version = dsVersion, + docServiceUrlApi = url, + }; + } + + + private FolderContentWrapper ToFolderContentWrapper(object folderId, Guid userIdOrGroupId, FilterType filterType) + { + SortedByType sortBy; + if (!Enum.TryParse(ApiContext.SortBy, true, out sortBy)) + sortBy = SortedByType.AZ; + var startIndex = Convert.ToInt32(ApiContext.StartIndex); + return FolderContentWrapperHelper.Get(FileStorageService.GetFolderItems(folderId.ToString(), + startIndex, + Convert.ToInt32(ApiContext.Count) - 1, //NOTE: in ApiContext +1 + filterType, + filterType == FilterType.ByUser, + userIdOrGroupId.ToString(), + ApiContext.FilterValue, + false, + false, + new OrderBy(sortBy, !ApiContext.SortDescending)), + startIndex); + } + + #region wordpress + + /// false + [Read("wordpress-info")] + public object GetWordpressInfo() + { + var token = WordpressToken.GetToken(); + if (token != null) + { + var meInfo = WordpressHelper.GetWordpressMeInfo(token.AccessToken); + var blogId = JObject.Parse(meInfo).Value("token_site_id"); + var wordpressUserName = JObject.Parse(meInfo).Value("username"); + + var blogInfo = RequestHelper.PerformRequest(WordpressLoginProvider.WordpressSites + blogId, "", "GET", ""); + var jsonBlogInfo = JObject.Parse(blogInfo); + jsonBlogInfo.Add("username", wordpressUserName); + + blogInfo = jsonBlogInfo.ToString(); + return new + { + success = true, + data = blogInfo + }; + } + return new + { + success = false + }; + } + + /// false + [Read("wordpress-delete")] + public object DeleteWordpressInfo() + { + var token = WordpressToken.GetToken(); + if (token != null) + { + WordpressToken.DeleteToken(token); + return new + { + success = true + }; + } + return new + { + success = false + }; + } + + /// false + [Create("wordpress-save")] + public object WordpressSave(string code) + { + if (code == "") + { + return new + { + success = false + }; + } + try + { + var token = OAuth20TokenHelper.GetAccessToken(ConsumerFactory, code); + WordpressToken.SaveToken(token); + var meInfo = WordpressHelper.GetWordpressMeInfo(token.AccessToken); + var blogId = JObject.Parse(meInfo).Value("token_site_id"); + + var wordpressUserName = JObject.Parse(meInfo).Value("username"); + + var blogInfo = RequestHelper.PerformRequest(WordpressLoginProvider.WordpressSites + blogId, "", "GET", ""); + var jsonBlogInfo = JObject.Parse(blogInfo); + jsonBlogInfo.Add("username", wordpressUserName); + + blogInfo = jsonBlogInfo.ToString(); + return new + { + success = true, + data = blogInfo + }; + } + catch (Exception) + { + return new + { + success = false + }; + } + } + + /// false + [Create("wordpress")] + public bool CreateWordpressPost(string code, string title, string content, int status) + { + try + { + var token = WordpressToken.GetToken(); + var meInfo = WordpressHelper.GetWordpressMeInfo(token.AccessToken); + var parser = JObject.Parse(meInfo); + if (parser == null) return false; + var blogId = parser.Value("token_site_id"); + + if (blogId != null) + { + var createPost = WordpressHelper.CreateWordpressPost(title, content, status, blogId, token); + return createPost; + } + return false; + } + catch (Exception) + { + return false; + } + } + + #endregion + + #region easybib + + /// false + [Read("easybib-citation-list")] + public object GetEasybibCitationList(int source, string data) + { + try + { + var citationList = EasyBibHelper.GetEasyBibCitationsList(source, data); + return new + { + success = true, + citations = citationList + }; + } + catch (Exception) + { + return new + { + success = false + }; + } + + } + + /// false + [Read("easybib-styles")] + public object GetEasybibStyles() + { + try + { + var data = EasyBibHelper.GetEasyBibStyles(); + return new + { + success = true, + styles = data + }; + } + catch (Exception) + { + return new + { + success = false + }; + } + } + + /// false + [Create("easybib-citation")] + public object EasyBibCitationBook(string citationData) + { + try + { + var citat = EasyBibHelper.GetEasyBibCitation(citationData); + if (citat != null) + { + return new + { + success = true, + citation = citat + }; + } + else + { + return new + { + success = false + }; + } + + } + catch (Exception) + { + return new + { + success = false + }; + } + } + + #endregion + + /// + /// Result of file conversation operation. + /// + [DataContract(Name = "operation_result", Namespace = "")] + public class ConversationResult + { + /// + /// Operation Id. + /// + [DataMember(Name = "id")] + public string Id { get; set; } + + /// + /// Operation type. + /// + [DataMember(Name = "operation")] + public FileOperationType OperationType { get; set; } + + /// + /// Operation progress. + /// + [DataMember(Name = "progress")] + public int Progress { get; set; } + + /// + /// Source files for operation. + /// + [DataMember(Name = "source")] + public string Source { get; set; } + + /// + /// Result file of operation. + /// + [DataMember(Name = "result")] + public FileWrapper File { get; set; } + + /// + /// Error during conversation. + /// + [DataMember(Name = "error")] + public string Error { get; set; } + + /// + /// Is operation processed. + /// + [DataMember(Name = "processed")] + public string Processed { get; set; } + } + } + + public static class DocumentsControllerExtention + { + public static DIHelper AddDocumentsControllerService(this DIHelper services) + { + return services + .AddEasyBibHelperService() + .AddWordpressTokenService() + .AddWordpressHelperService() + .AddFolderContentWrapperHelperService() + .AddFileUploaderService() + .AddFileShareParamsService() + .AddFileShareWrapperService() + .AddFileOperationWraperHelperService() + .AddFileWrapperHelperService() + .AddFolderWrapperHelperService() + .AddConsumerFactoryService() + .AddDocumentServiceConnectorService() + .AddCommonLinkUtilityService() + .AddMessageServiceService() + .AddThirdpartyConfigurationService() + .AddCoreBaseSettingsService() + .AddWebItemSecurity() + .AddUserManagerService() + .AddEntryManagerService() + .AddTenantManagerService() + .AddSecurityContextService() + .AddDocumentServiceHelperService() + .AddFilesLinkUtilityService() + .AddApiContextService() + .AddFileStorageService() + .AddGlobalFolderHelperService() + .AddFilesSettingsHelperService() + .AddBoxLoginProviderService() + .AddDropboxLoginProviderService() + .AddOneDriveLoginProviderService() + .AddGoogleLoginProviderService() + .AddChunkedUploadSessionHelperService() + .AddProductEntryPointService() + ; + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/Dao/EncryptedDataDao.cs b/products/ASC.Files/Server/Core/Dao/EncryptedDataDao.cs new file mode 100644 index 0000000000..70478ed7a1 --- /dev/null +++ b/products/ASC.Files/Server/Core/Dao/EncryptedDataDao.cs @@ -0,0 +1,144 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Common; +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.Core.Common.Settings; +using ASC.Core.Tenants; +using ASC.Files.Core.EF; +using ASC.Web.Files.Core.Entries; +using ASC.Web.Studio.Core; +using ASC.Web.Studio.UserControls.Statistics; +using ASC.Web.Studio.Utility; + +namespace ASC.Files.Core.Data +{ + public class EncryptedDataDao : AbstractDao + { + private static readonly object SyncRoot = new object(); + + public EncryptedDataDao( + DbContextManager dbContextManager, + UserManager userManager, + TenantManager tenantManager, + TenantUtil tenantUtil, + SetupInfo setupInfo, + TenantExtra tenantExtra, + TenantStatisticsProvider tenantStatisticProvider, + CoreBaseSettings coreBaseSettings, + CoreConfiguration coreConfiguration, + SettingsManager settingsManager, + AuthContext authContext, + IServiceProvider serviceProvider + ) + : base(dbContextManager, + userManager, + tenantManager, + tenantUtil, + setupInfo, + tenantExtra, + tenantStatisticProvider, + coreBaseSettings, + coreConfiguration, + settingsManager, + authContext, + serviceProvider) + { + } + + public bool SaveEncryptedData(IEnumerable ecnryptedDatas) + { + if (ecnryptedDatas == null) + { + throw new ArgumentNullException("ecnryptedDatas"); + } + + lock (SyncRoot) + { + using var tx = FilesDbContext.Database.BeginTransaction(); + foreach (var test in ecnryptedDatas) + { + if (string.IsNullOrEmpty(test.PublicKey) + || string.IsNullOrEmpty(test.FileHash) + || string.IsNullOrEmpty(test.Data)) + { + continue; + } + var encryptedData = new DbEncryptedData + { + PublicKey = test.PublicKey, + FileHash = test.FileHash, + Data = test.Data + }; + + FilesDbContext.AddOrUpdate(r => r.EncryptedData, encryptedData); + } + + tx.Commit(); + } + return true; + } + + public string GetData(string publicKey, string fileHash) + { + if (string.IsNullOrEmpty(publicKey)) throw new ArgumentException("publicKey is empty", "publicKey"); + if (string.IsNullOrEmpty(fileHash)) throw new ArgumentException("fileHash is empty", "hash"); + + lock (SyncRoot) + { + return FilesDbContext.EncryptedData + .Where(r => r.PublicKey == publicKey) + .Where(r => r.FileHash == fileHash) + .Select(r => r.Data) + .SingleOrDefault(); + } + } + } + public static class EncryptedDataDaoExtention + { + public static DIHelper AddEncryptedDataDaoService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddFilesDbContextService() + .AddUserManagerService() + .AddTenantManagerService() + .AddTenantUtilService() + .AddSetupInfo() + .AddTenantExtraService() + .AddTenantStatisticsProviderService() + .AddCoreBaseSettingsService() + .AddCoreConfigurationService() + .AddSettingsManagerService() + .AddAuthContextService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/Dao/Interfaces/IDaoFactory.cs b/products/ASC.Files/Server/Core/Dao/Interfaces/IDaoFactory.cs new file mode 100644 index 0000000000..35ad8670d7 --- /dev/null +++ b/products/ASC.Files/Server/Core/Dao/Interfaces/IDaoFactory.cs @@ -0,0 +1,43 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using ASC.Files.Core.Security; + +namespace ASC.Files.Core +{ + public interface IDaoFactory + { + IFolderDao FolderDao { get; } + + IFileDao FileDao { get; } + + ITagDao TagDao { get; } + + ISecurityDao SecurityDao { get; } + + IProviderDao ProviderDao { get; } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/Dao/Interfaces/IFileDao.cs b/products/ASC.Files/Server/Core/Dao/Interfaces/IFileDao.cs new file mode 100644 index 0000000000..c2f3df23d1 --- /dev/null +++ b/products/ASC.Files/Server/Core/Dao/Interfaces/IFileDao.cs @@ -0,0 +1,303 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.IO; + +using ASC.Web.Files.Services.DocumentService; + +namespace ASC.Files.Core +{ + /// + /// Interface encapsulates access toFolderId files + /// + public interface IFileDao + { + /// + /// Clear the application cache for the specific file + /// + void InvalidateCache(object fileId); + + /// + /// Receive file + /// + /// file id + /// + File GetFile(object fileId); + + /// + /// Receive file + /// + /// file id + /// file version + /// + File GetFile(object fileId, int fileVersion); + + /// + /// Receive file + /// + /// folder id + /// file name + /// + /// file + /// + File GetFile(object parentId, string title); + + /// + /// Receive last file without forcesave + /// + /// file id + /// + /// + File GetFileStable(object fileId, int fileVersion = -1); + + /// + /// Returns all versions of the file + /// + /// + /// + List GetFileHistory(object fileId); + + /// + /// Gets the file (s) by ID (s) + /// + /// id file + /// + List GetFiles(object[] fileIds); + + /// + /// Gets the file (s) by ID (s) for share + /// + /// id file + /// + /// + /// + /// + /// + /// + List GetFilesForShare(object[] fileIds, FilterType filterType, bool subjectGroup, Guid subjectID, string searchText, bool searchInContent); + + /// + /// + /// + /// + /// + List GetFiles(object parentId); + + /// + /// Get files in folder + /// + /// folder id + /// + /// filterType type + /// + /// + /// + /// + /// + /// list of files + /// + /// Return only the latest versions of files of a folder + /// + List GetFiles(object parentId, OrderBy orderBy, FilterType filterType, bool subjectGroup, Guid subjectID, string searchText, bool searchInContent, bool withSubfolders = false); + + /// + /// Get stream of file + /// + /// + /// Stream + Stream GetFileStream(File file); + + /// + /// Get stream of file + /// + /// + /// + /// Stream + Stream GetFileStream(File file, long offset); + + /// + /// Get presigned uri + /// + /// + /// + /// Stream uri + Uri GetPreSignedUri(File file, TimeSpan expires); + + /// + /// Check is supported PreSignedUri + /// + /// + /// Stream uri + bool IsSupportedPreSignedUri(File file); + + /// + /// Saves / updates the version of the file + /// and save stream of file + /// + /// + /// + /// + /// + /// Updates the file if: + /// - The file comes with the given id + /// - The file with that name in the folder / container exists + /// + /// Save in all other cases + /// + File SaveFile(File file, Stream fileStream); + + /// + /// + /// + /// + /// + /// + File ReplaceFileVersion(File file, Stream fileStream); + + /// + /// Deletes a file including all previous versions + /// + /// file id + void DeleteFile(object fileId); + + /// + /// Checks whether or not file + /// + /// file name + /// folder id + /// Returns true if the file exists, otherwise false + bool IsExist(string title, object folderId); + + /// + /// Moves a file or set of files in a folder + /// + /// file id + /// The ID of the destination folder + object MoveFile(object fileId, object toFolderId); + + /// + /// Copy the files in a folder + /// + /// file id + /// The ID of the destination folder + File CopyFile(object fileId, object toFolderId); + + /// + /// Rename file + /// + /// + /// new name + object FileRename(File file, string newTitle); + + /// + /// Update comment file + /// + /// file id + /// file version + /// new comment + string UpdateComment(object fileId, int fileVersion, string comment); + + /// + /// Complete file version + /// + /// file id + /// file version + void CompleteVersion(object fileId, int fileVersion); + + /// + /// Continue file version + /// + /// file id + /// file version + void ContinueVersion(object fileId, int fileVersion); + + /// + /// Check the need to use the trash before removing + /// + /// + /// + bool UseTrashForRemove(File file); + + #region chunking + + ChunkedUploadSession CreateUploadSession(File file, long contentLength); + + void UploadChunk(ChunkedUploadSession uploadSession, Stream chunkStream, long chunkLength); + + void AbortUploadSession(ChunkedUploadSession uploadSession); + + #endregion + + #region Only in TMFileDao + + /// + /// Set created by + /// + /// + /// + void ReassignFiles(object[] fileIds, Guid newOwnerId); + + /// + /// Search files in SharedWithMe & Projects + /// + /// + /// + /// + /// + /// + /// + /// + List GetFiles(object[] parentIds, FilterType filterType, bool subjectGroup, Guid subjectID, string searchText, bool searchInContent); + + /// + /// Search the list of files containing text + /// Only in TMFileDao + /// + /// search text + /// + /// list of files + IEnumerable Search(string text, bool bunch = false); + + /// + /// Checks whether file exists on storage + /// + /// file + /// + bool IsExistOnStorage(File file); + + void SaveEditHistory(File file, string changes, Stream differenceStream); + + List GetEditHistory(DocumentServiceHelper documentServiceHelper, object fileId, int fileVersion = 0); + + Stream GetDifferenceStream(File file); + + bool ContainChanges(object fileId, int fileVersion); + + #endregion + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/Dao/Interfaces/IFolderDao.cs b/products/ASC.Files/Server/Core/Dao/Interfaces/IFolderDao.cs new file mode 100644 index 0000000000..106bd0414b --- /dev/null +++ b/products/ASC.Files/Server/Core/Dao/Interfaces/IFolderDao.cs @@ -0,0 +1,285 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Threading; + +namespace ASC.Files.Core +{ + public interface IFolderDao + { + /// + /// Get folder by id. + /// + /// folder id + /// folder + Folder GetFolder(object folderId); + + /// + /// Returns the folder with the given name and id of the root + /// + /// + /// + /// + Folder GetFolder(string title, object parentId); + + /// + /// Gets the root folder + /// + /// folder id + /// root folder + Folder GetRootFolder(object folderId); + + /// + /// Gets the root folder + /// + /// file id + /// root folder + Folder GetRootFolderByFile(object fileId); + + /// + /// Get a list of folders in current folder. + /// + /// + List GetFolders(object parentId); + + /// + /// Get a list of folders. + /// + /// + /// + /// + /// + /// + /// + /// + /// + List GetFolders(object parentId, OrderBy orderBy, FilterType filterType, bool subjectGroup, Guid subjectID, string searchText, bool withSubfolders = false); + + /// + /// Gets the folder (s) by ID (s) + /// + /// + /// + /// + /// + /// + /// + /// + /// + List GetFolders(object[] folderIds, FilterType filterType = FilterType.None, bool subjectGroup = false, Guid? subjectID = null, string searchText = "", bool searchSubfolders = false, bool checkShare = true); + + /// + /// Get folder, contains folder with id + /// + /// folder id + /// + List GetParentFolders(object folderId); + + /// + /// save or update folder + /// + /// + /// + object SaveFolder(Folder folder); + + /// + /// delete folder + /// + /// folder id + void DeleteFolder(object folderId); + + /// + /// move folder + /// + /// folder id + /// destination folder id + /// + object MoveFolder(object folderId, object toFolderId, CancellationToken? cancellationToken); + + /// + /// copy folder + /// + /// + /// + /// + /// + /// + Folder CopyFolder(object folderId, object toFolderId, CancellationToken? cancellationToken); + + /// + /// Validate the transfer operation directory to another directory. + /// + /// + /// + /// + /// Returns pair of file ID, file name, in which the same name. + /// + IDictionary CanMoveOrCopy(object[] folderIds, object to); + + /// + /// Rename folder + /// + /// + /// new name + object RenameFolder(Folder folder, string newTitle); + + /// + /// Gets the number of files and folders to the container in your + /// + /// folder id + /// + int GetItemsCount(object folderId); + + /// + /// Check folder on emptiness + /// + /// folder id + /// + bool IsEmpty(object folderId); + + /// + /// Check the need to use the trash before removing + /// + /// + /// + bool UseTrashForRemove(Folder folder); + + /// + /// Check the need to use recursion for operations + /// + /// + /// + /// + bool UseRecursiveOperation(object folderId, object toRootFolderId); + + /// + /// Check the possibility to calculate the number of subitems + /// + /// + /// + bool CanCalculateSubitems(object entryId); + + /// + /// Returns maximum size of file which can be uploaded to specific folder + /// + /// Id of the folder + /// Determines whenever supposed upload will be chunked (true) or not (false) + /// Maximum size of file which can be uploaded to folder + long GetMaxUploadSize(object folderId, bool chunkedUpload = false); + + #region Only for TMFolderDao + + /// + /// Set created by + /// + /// + /// + void ReassignFolders(object[] folderIds, Guid newOwnerId); + + /// + /// Search the list of folders containing text in title + /// Only in TMFolderDao + /// + /// + /// + /// + IEnumerable Search(string text, bool bunch = false); + + /// + /// Only in TMFolderDao + /// + /// + /// + /// + /// + /// + object GetFolderID(string module, string bunch, string data, bool createIfNotExists); + + IEnumerable GetFolderIDs(string module, string bunch, IEnumerable data, bool createIfNotExists); + + /// + /// Returns id folder "Shared Documents" + /// Only in TMFolderDao + /// + /// + object GetFolderIDCommon(bool createIfNotExists); + + /// + /// Returns id folder "My Documents" + /// Only in TMFolderDao + /// + /// + /// + /// + object GetFolderIDUser(bool createIfNotExists, Guid? userId = null); + + /// + /// Returns id folder "Shared with me" + /// Only in TMFolderDao + /// + /// + /// + object GetFolderIDShare(bool createIfNotExists); + + /// + /// Returns id folder "Trash" + /// Only in TMFolderDao + /// + /// + /// + /// + object GetFolderIDTrash(bool createIfNotExists, Guid? userId = null); + + /// + /// Returns id folder "Projects" + /// Only in TMFolderDao + /// + /// + /// + object GetFolderIDProjects(bool createIfNotExists); + + + /// + /// Return id of related object + /// Only in TMFolderDao + /// + /// + /// + string GetBunchObjectID(object folderID); + + /// + /// Return ids of related objects + /// Only in TMFolderDao + /// + /// + /// + Dictionary GetBunchObjectIDs(List folderIDs); + + #endregion + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/Dao/Interfaces/IProviderDao.cs b/products/ASC.Files/Server/Core/Dao/Interfaces/IProviderDao.cs new file mode 100644 index 0000000000..984dafc8e9 --- /dev/null +++ b/products/ASC.Files/Server/Core/Dao/Interfaces/IProviderDao.cs @@ -0,0 +1,42 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; + +namespace ASC.Files.Core +{ + public interface IProviderDao + { + IProviderInfo GetProviderInfo(int linkId); + List GetProvidersInfo(); + List GetProvidersInfo(FolderType folderType, string searchText = null); + List GetProvidersInfo(Guid userId); + int SaveProviderInfo(string providerKey, string customerTitle, AuthData authData, FolderType folderType); + int UpdateProviderInfo(int linkId, string customerTitle, AuthData authData, FolderType folderType, Guid? userId = null); + void RemoveProviderInfo(int linkId); + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/Dao/Interfaces/IProviderInfo.cs b/products/ASC.Files/Server/Core/Dao/Interfaces/IProviderInfo.cs new file mode 100644 index 0000000000..2a5904546a --- /dev/null +++ b/products/ASC.Files/Server/Core/Dao/Interfaces/IProviderInfo.cs @@ -0,0 +1,45 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; + +namespace ASC.Files.Core +{ + public interface IProviderInfo + { + int ID { get; set; } + string ProviderKey { get; } + Guid Owner { get; } + FolderType RootFolderType { get; } + DateTime CreateOn { get; } + string CustomerTitle { get; } + + object RootFolderId { get; } + + bool CheckAccess(); + void InvalidateStorage(); + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/Dao/Interfaces/ITagDao.cs b/products/ASC.Files/Server/Core/Dao/Interfaces/ITagDao.cs new file mode 100644 index 0000000000..a1e092a612 --- /dev/null +++ b/products/ASC.Files/Server/Core/Dao/Interfaces/ITagDao.cs @@ -0,0 +1,62 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; + +namespace ASC.Files.Core +{ + public interface ITagDao + { + IEnumerable GetTags(TagType tagType, IEnumerable fileEntries); + + IEnumerable GetTags(Guid owner, TagType tagType); + + IEnumerable GetTags(string name, TagType tagType); + + IEnumerable GetTags(string[] names, TagType tagType); + + IEnumerable GetNewTags(Guid subject, Folder parentFolder, bool deepSearch); + + IEnumerable GetNewTags(Guid subject, IEnumerable fileEntries); + + IEnumerable GetNewTags(Guid subject, FileEntry fileEntry); + + IEnumerable SaveTags(IEnumerable tag); + + IEnumerable SaveTags(Tag tag); + + void UpdateNewTags(IEnumerable tag); + + void UpdateNewTags(Tag tag); + + void RemoveTags(IEnumerable tag); + + void RemoveTags(Tag tag); + + IEnumerable GetTags(object entryID, FileEntryType entryType, TagType tagType); + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/Dao/TeamlabDao/AbstractDao.cs b/products/ASC.Files/Server/Core/Dao/TeamlabDao/AbstractDao.cs new file mode 100644 index 0000000000..ff303c011a --- /dev/null +++ b/products/ASC.Files/Server/Core/Dao/TeamlabDao/AbstractDao.cs @@ -0,0 +1,197 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text.RegularExpressions; + +using ASC.Common.Caching; +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.Core.Common.Settings; +using ASC.Core.Tenants; +using ASC.Files.Core.EF; +using ASC.Security.Cryptography; +using ASC.Web.Studio.Core; +using ASC.Web.Studio.UserControls.Statistics; +using ASC.Web.Studio.Utility; + +using Autofac; + +using Microsoft.EntityFrameworkCore; + +namespace ASC.Files.Core.Data +{ + public class AbstractDao + { + protected readonly ICache cache; + + public FilesDbContext FilesDbContext { get; } + + protected internal int TenantID { get; } + public UserManager UserManager { get; } + public TenantUtil TenantUtil { get; } + public SetupInfo SetupInfo { get; } + public TenantExtra TenantExtra { get; } + public TenantStatisticsProvider TenantStatisticProvider { get; } + public CoreBaseSettings CoreBaseSettings { get; } + public CoreConfiguration CoreConfiguration { get; } + public SettingsManager SettingsManager { get; } + public AuthContext AuthContext { get; } + public IServiceProvider ServiceProvider { get; } + + protected AbstractDao( + DbContextManager dbContextManager, + UserManager userManager, + TenantManager tenantManager, + TenantUtil tenantUtil, + SetupInfo setupInfo, + TenantExtra tenantExtra, + TenantStatisticsProvider tenantStatisticProvider, + CoreBaseSettings coreBaseSettings, + CoreConfiguration coreConfiguration, + SettingsManager settingsManager, + AuthContext authContext, + IServiceProvider serviceProvider) + { + cache = AscCache.Memory; + FilesDbContext = dbContextManager.Get(FileConstant.DatabaseId); + TenantID = tenantManager.GetCurrentTenant().TenantId; + UserManager = userManager; + TenantUtil = tenantUtil; + SetupInfo = setupInfo; + TenantExtra = tenantExtra; + TenantStatisticProvider = tenantStatisticProvider; + CoreBaseSettings = coreBaseSettings; + CoreConfiguration = coreConfiguration; + SettingsManager = settingsManager; + AuthContext = authContext; + ServiceProvider = serviceProvider; + } + + + protected IQueryable Query(DbSet set) where T : class, IDbFile + { + return set.Where(r => r.TenantId == TenantID); + } + + protected IQueryable GetFileQuery(Expression> where) + { + return Query(FilesDbContext.Files) + .Where(where); + } + + protected void GetRecalculateFilesCountUpdate(object folderId) + { + var folders = FilesDbContext.Folders + .Where(r => r.TenantId == TenantID) + .Where(r => FilesDbContext.Tree.Where(r => r.FolderId.ToString() == folderId.ToString()).Select(r => r.ParentId).Any(a => a == r.Id)) + .ToList(); + + foreach (var f in folders) + { + var filesCount = + FilesDbContext.Files + .Join(FilesDbContext.Tree, a => a.FolderId, b => b.FolderId, (file, tree) => new { file, tree }) + .Where(r => r.file.TenantId == f.TenantId) + .Where(r => r.tree.ParentId == f.Id) + .Count(); + + f.FilesCount = filesCount; + } + + FilesDbContext.SaveChanges(); + } + + protected object MappingID(object id, bool saveIfNotExist) + { + if (id == null) return null; + + var isNumeric = int.TryParse(id.ToString(), out var n); + + if (isNumeric) return n; + + object result; + + if (id.ToString().StartsWith("sbox") + || id.ToString().StartsWith("box") + || id.ToString().StartsWith("dropbox") + || id.ToString().StartsWith("spoint") + || id.ToString().StartsWith("drive") + || id.ToString().StartsWith("onedrive")) + { + result = Regex.Replace(BitConverter.ToString(Hasher.Hash(id.ToString(), HashAlg.MD5)), "-", "").ToLower(); + } + else + { + result = Query(FilesDbContext.ThirdpartyIdMapping) + .Where(r => r.HashId == id.ToString()) + .Select(r => r.Id) + .FirstOrDefault(); + } + + if (saveIfNotExist) + { + var newItem = new DbFilesThirdpartyIdMapping + { + Id = id.ToString(), + HashId = result.ToString() + }; + + FilesDbContext.AddOrUpdate(r => r.ThirdpartyIdMapping, newItem); + } + + return result; + } + + protected object MappingID(object id) + { + return MappingID(id, false); + } + + internal static bool BuildSearch(IDbSearch dbSearch, string text, SearhTypeEnum searhTypeEnum) + { + var lowerTitle = dbSearch.Title.ToLower(); + var lowerText = text.ToLower().Trim().Replace("%", "\\%").Replace("_", "\\_"); + return searhTypeEnum switch + { + SearhTypeEnum.Start => lowerTitle.StartsWith(lowerText), + SearhTypeEnum.End => lowerTitle.EndsWith(lowerText), + SearhTypeEnum.Any => lowerTitle.Contains(lowerText), + _ => lowerTitle.EndsWith(lowerText), + }; + } + + internal enum SearhTypeEnum + { + Start, + End, + Any + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/Dao/TeamlabDao/DaoFactory.cs b/products/ASC.Files/Server/Core/Dao/TeamlabDao/DaoFactory.cs new file mode 100644 index 0000000000..561c729d5b --- /dev/null +++ b/products/ASC.Files/Server/Core/Dao/TeamlabDao/DaoFactory.cs @@ -0,0 +1,62 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using ASC.Common; +using ASC.Files.Core.Security; + +namespace ASC.Files.Core.Data +{ + public class DaoFactory : IDaoFactory + { + public IFileDao FileDao { get; } + public IFolderDao FolderDao { get; } + public ITagDao TagDao { get; } + public ISecurityDao SecurityDao { get; } + public IProviderDao ProviderDao { get; } + + public DaoFactory(IFileDao fileDao, IFolderDao folderDao, ITagDao tagDao, ISecurityDao securityDao) + { + FileDao = fileDao; + FolderDao = folderDao; + TagDao = tagDao; + SecurityDao = securityDao; + } + } + + public static class DaoFactoryExtention + { + public static DIHelper AddDaoFactoryService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddFileDaoService() + .AddFolderDaoService() + .AddTagDaoService() + .AddSecurityDaoService() + ; + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/Dao/TeamlabDao/FileDao.cs b/products/ASC.Files/Server/Core/Dao/TeamlabDao/FileDao.cs new file mode 100644 index 0000000000..7fadd13663 --- /dev/null +++ b/products/ASC.Files/Server/Core/Dao/TeamlabDao/FileDao.cs @@ -0,0 +1,1280 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Linq.Expressions; + +using ASC.Common; +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.Core.Common.Settings; +using ASC.Core.Tenants; +using ASC.ElasticSearch; +using ASC.Files.Core.EF; +using ASC.Files.Resources; +using ASC.Web.Core.Files; +using ASC.Web.Files.Classes; +using ASC.Web.Files.Core.Search; +using ASC.Web.Files.Services.DocumentService; +using ASC.Web.Files.Utils; +using ASC.Web.Studio.Core; +using ASC.Web.Studio.UserControls.Statistics; +using ASC.Web.Studio.Utility; + +using Microsoft.Extensions.DependencyInjection; + +namespace ASC.Files.Core.Data +{ + public class FileDao : AbstractDao, IFileDao + { + private static readonly object syncRoot = new object(); + public FactoryIndexer FactoryIndexer { get; } + public GlobalStore GlobalStore { get; } + public GlobalSpace GlobalSpace { get; } + public GlobalFolder GlobalFolder { get; } + public IFolderDao FolderDao { get; } + public ChunkedUploadSessionHolder ChunkedUploadSessionHolder { get; } + + public FileDao( + FactoryIndexer factoryIndexer, + UserManager userManager, + DbContextManager dbContextManager, + TenantManager tenantManager, + TenantUtil tenantUtil, + SetupInfo setupInfo, + TenantExtra tenantExtra, + TenantStatisticsProvider tenantStatisticProvider, + CoreBaseSettings coreBaseSettings, + CoreConfiguration coreConfiguration, + SettingsManager settingsManager, + AuthContext authContext, + IServiceProvider serviceProvider, + GlobalStore globalStore, + GlobalSpace globalSpace, + GlobalFolder globalFolder, + IFolderDao folderDao, + ChunkedUploadSessionHolder chunkedUploadSessionHolder) + : base( + dbContextManager, + userManager, + tenantManager, + tenantUtil, + setupInfo, + tenantExtra, + tenantStatisticProvider, + coreBaseSettings, + coreConfiguration, + settingsManager, + authContext, + serviceProvider) + { + FactoryIndexer = factoryIndexer; + GlobalStore = globalStore; + GlobalSpace = globalSpace; + GlobalFolder = globalFolder; + FolderDao = folderDao; + ChunkedUploadSessionHolder = chunkedUploadSessionHolder; + } + + public void InvalidateCache(object fileId) + { + } + + public File GetFile(object fileId) + { + var query = GetFileQuery(r => r.Id.ToString() == fileId.ToString() && r.CurrentVersion); + return FromQueryWithShared(query).SingleOrDefault(); + } + + public File GetFile(object fileId, int fileVersion) + { + var query = GetFileQuery(r => r.Id.ToString() == fileId.ToString() && r.Version == fileVersion); + return FromQueryWithShared(query).SingleOrDefault(); + } + + public File GetFile(object parentId, string title) + { + if (string.IsNullOrEmpty(title)) throw new ArgumentNullException(title); + + var query = GetFileQuery(r => r.Title == title && r.CurrentVersion == true && r.FolderId.ToString() == parentId.ToString()) + .OrderBy(r => r.CreateOn); + + return FromQueryWithShared(query).FirstOrDefault(); + } + + public File GetFileStable(object fileId, int fileVersion = -1) + { + var query = GetFileQuery(r => r.Id.ToString() == fileId.ToString() && r.Forcesave == ForcesaveType.None); + + if (fileVersion >= 0) + { + query = query.Where(r => r.Version <= fileVersion); + } + + query = query.OrderByDescending(r => r.Version); + + return FromQueryWithShared(query).SingleOrDefault(); + } + + public List GetFileHistory(object fileId) + { + var query = GetFileQuery(r => r.Id.ToString() == fileId.ToString()).OrderByDescending(r => r.Version); + + return FromQueryWithShared(query); + } + + public List GetFiles(object[] fileIds) + { + if (fileIds == null || fileIds.Length == 0) return new List(); + + var query = GetFileQuery(r => fileIds.Any(a => a.ToString() == r.Id.ToString()) && r.CurrentVersion); + + return FromQueryWithShared(query); + } + + public List GetFilesForShare(object[] fileIds, FilterType filterType, bool subjectGroup, Guid subjectID, string searchText, bool searchInContent) + { + if (fileIds == null || fileIds.Length == 0 || filterType == FilterType.FoldersOnly) return new List(); + + var query = GetFileQuery(r => fileIds.Any(a => a.ToString() == r.Id.ToString()) && r.CurrentVersion); + + if (!string.IsNullOrEmpty(searchText)) + { + var func = GetFuncForSearch(null, null, filterType, subjectGroup, subjectID, searchText, searchInContent, false); + + if (FactoryIndexer.TrySelectIds(s => func(s).In(r => r.Id, fileIds), out var searchIds)) + { + query = query.Where(r => searchIds.Any(b => b == r.Id)); + } + else + { + query = query.Where(r => BuildSearch(r, searchText, SearhTypeEnum.Any)); + } + } + + if (subjectID != Guid.Empty) + { + if (subjectGroup) + { + var users = UserManager.GetUsersByGroup(subjectID).Select(u => u.ID).ToArray(); + query = query.Where(r => users.Any(b => b == r.CreateBy)); + } + else + { + query = query.Where(r => r.CreateBy == subjectID); + } + } + + switch (filterType) + { + case FilterType.DocumentsOnly: + case FilterType.ImagesOnly: + case FilterType.PresentationsOnly: + case FilterType.SpreadsheetsOnly: + case FilterType.ArchiveOnly: + case FilterType.MediaOnly: + query = query.Where(r => r.Category == (int)filterType); + break; + case FilterType.ByExtension: + if (!string.IsNullOrEmpty(searchText)) + { + query = query.Where(r => BuildSearch(r, searchText, SearhTypeEnum.End)); + } + break; + } + + return FromQuery(query); + } + + public List GetFiles(object parentId) + { + var query = GetFileQuery(r => r.FolderId.ToString() == parentId.ToString() && r.CurrentVersion).Select(r => r.Id); + + return Query(FilesDbContext.Files) + .Where(r => r.FolderId.ToString() == parentId.ToString() && r.CurrentVersion) + .Select(r => r.Id) + .ToList() + .ConvertAll(r => (object)r); + } + + public List GetFiles(object parentId, OrderBy orderBy, FilterType filterType, bool subjectGroup, Guid subjectID, string searchText, bool searchInContent, bool withSubfolders = false) + { + if (filterType == FilterType.FoldersOnly) return new List(); + + if (orderBy == null) orderBy = new OrderBy(SortedByType.DateAndTime, false); + + var q = GetFileQuery(r => r.FolderId.ToString() == parentId.ToString() && r.CurrentVersion); + + + if (withSubfolders) + { + q = GetFileQuery(r => r.CurrentVersion) + .Join(FilesDbContext.Tree, r => r.FolderId, a => a.FolderId, (file, tree) => new { file, tree }) + .Where(r => r.tree.ParentId.ToString() == parentId.ToString()) + .Select(r => r.file); + } + + if (!string.IsNullOrEmpty(searchText)) + { + + var func = GetFuncForSearch(parentId, orderBy, filterType, subjectGroup, subjectID, searchText, searchInContent, withSubfolders); + + Expression, Selector>> expression = s => func(s); + + if (FactoryIndexer.TrySelectIds(expression, out var searchIds)) + { + q = q.Where(r => searchIds.Any(a => a == r.Id)); + } + else + { + q = q.Where(r => BuildSearch(r, searchText, SearhTypeEnum.Any)); + } + } + + switch (orderBy.SortedBy) + { + case SortedByType.Author: + q = orderBy.IsAsc ? q.OrderBy(r => r.CreateBy) : q.OrderByDescending(r => r.CreateBy); + break; + case SortedByType.Size: + q = orderBy.IsAsc ? q.OrderBy(r => r.ContentLength) : q.OrderByDescending(r => r.ContentLength); + break; + case SortedByType.AZ: + q = orderBy.IsAsc ? q.OrderBy(r => r.Title) : q.OrderByDescending(r => r.Title); + break; + case SortedByType.DateAndTime: + q = orderBy.IsAsc ? q.OrderBy(r => r.ModifiedOn) : q.OrderByDescending(r => r.ModifiedOn); + break; + case SortedByType.DateAndTimeCreation: + q = orderBy.IsAsc ? q.OrderBy(r => r.CreateOn) : q.OrderByDescending(r => r.CreateOn); + break; + default: + q = q.OrderBy(r => r.Title); + break; + } + + if (subjectID != Guid.Empty) + { + if (subjectGroup) + { + var users = UserManager.GetUsersByGroup(subjectID).Select(u => u.ID).ToArray(); + q = q.Where(r => users.Any(a => a == r.CreateBy)); + } + else + { + q = q.Where(r => r.CreateBy == subjectID); + } + } + + switch (filterType) + { + case FilterType.DocumentsOnly: + case FilterType.ImagesOnly: + case FilterType.PresentationsOnly: + case FilterType.SpreadsheetsOnly: + case FilterType.ArchiveOnly: + case FilterType.MediaOnly: + q = q.Where(r => r.Category == (int)filterType); + break; + case FilterType.ByExtension: + if (!string.IsNullOrEmpty(searchText)) + { + q = q.Where(r => BuildSearch(r, searchText, SearhTypeEnum.End)); + } + break; + } + + return FromQueryWithShared(q); + } + + public Stream GetFileStream(File file, long offset) + { + return GlobalStore.GetStore().GetReadStream(string.Empty, GetUniqFilePath(file), (int)offset); + } + + public Uri GetPreSignedUri(File file, TimeSpan expires) + { + return GlobalStore.GetStore().GetPreSignedUri(string.Empty, GetUniqFilePath(file), expires, + new List + { + string.Concat("Content-Disposition:", ContentDispositionUtil.GetHeaderValue(file.Title, withoutBase: true)) + }); + } + + public bool IsSupportedPreSignedUri(File file) + { + return GlobalStore.GetStore().IsSupportedPreSignedUri; + } + + public Stream GetFileStream(File file) + { + return GetFileStream(file, 0); + } + + public File SaveFile(File file, Stream fileStream) + { + if (file == null) + { + throw new ArgumentNullException("file"); + } + + var maxChunkedUploadSize = SetupInfo.MaxChunkedUploadSize(TenantExtra, TenantStatisticProvider); + if (maxChunkedUploadSize < file.ContentLength) + { + throw FileSizeComment.GetFileSizeException(maxChunkedUploadSize); + } + + if (CoreBaseSettings.Personal && SetupInfo.IsVisibleSettings("PersonalMaxSpace")) + { + var personalMaxSpace = CoreConfiguration.PersonalMaxSpace(SettingsManager); + if (personalMaxSpace - GlobalSpace.GetUserUsedSpace(file.ID == null ? AuthContext.CurrentAccount.ID : file.CreateBy) < file.ContentLength) + { + throw FileSizeComment.GetPersonalFreeSpaceException(personalMaxSpace); + } + } + + var isNew = false; + List parentFoldersIds; + lock (syncRoot) + { + using var tx = FilesDbContext.Database.BeginTransaction(); + + if (file.ID == null) + { + file.ID = FilesDbContext.Files.Max(r => r.Id) + 1; + file.Version = 1; + file.VersionGroup = 1; + isNew = true; + } + + file.Title = Global.ReplaceInvalidCharsAndTruncate(file.Title); + //make lowerCase + file.Title = FileUtility.ReplaceFileExtension(file.Title, FileUtility.GetFileExtension(file.Title)); + + file.ModifiedBy = AuthContext.CurrentAccount.ID; + file.ModifiedOn = TenantUtil.DateTimeNow(); + if (file.CreateBy == default) file.CreateBy = AuthContext.CurrentAccount.ID; + if (file.CreateOn == default) file.CreateOn = TenantUtil.DateTimeNow(); + + var toUpdate = FilesDbContext.Files + .Where(r => r.Id.ToString() == file.ID.ToString() && r.CurrentVersion && r.TenantId == TenantID) + .FirstOrDefault(); + + if (toUpdate != null) + { + toUpdate.CurrentVersion = false; + FilesDbContext.SaveChanges(); + } + + var toInsert = new DbFile + { + Id = (int)file.ID, + Version = file.Version, + VersionGroup = file.VersionGroup, + CurrentVersion = true, + FolderId = (int)file.FolderID, + Title = file.Title, + ContentLength = file.ContentLength, + Category = (int)file.FilterType, + CreateBy = file.CreateBy, + CreateOn = TenantUtil.DateTimeToUtc(file.CreateOn), + ModifiedBy = file.ModifiedBy, + ModifiedOn = TenantUtil.DateTimeToUtc(file.ModifiedOn), + ConvertedType = file.ConvertedType, + Comment = file.Comment, + Encrypted = file.Encrypted, + Forcesave = file.Forcesave, + TenantId = TenantID + }; + + FilesDbContext.Files.Add(toInsert); + FilesDbContext.SaveChanges(); + + tx.Commit(); + + file.PureTitle = file.Title; + + parentFoldersIds = + FilesDbContext.Tree + .Where(r => r.FolderId == (int)file.FolderID) + .OrderByDescending(r => r.Level) + .Select(r => r.ParentId) + .ToList(); + + if (parentFoldersIds.Count > 0) + { + var folderToUpdate = FilesDbContext.Folders + .Where(r => parentFoldersIds.Any(a => a == r.Id)); + + foreach (var f in folderToUpdate) + { + f.ModifiedOn = TenantUtil.DateTimeToUtc(file.ModifiedOn); + f.ModifiedBy = file.ModifiedBy; + } + + FilesDbContext.SaveChanges(); + } + + if (isNew) + { + RecalculateFilesCount(file.FolderID); + } + } + + if (fileStream != null) + { + try + { + SaveFileStream(file, fileStream); + } + catch + { + if (isNew) + { + var stored = GlobalStore.GetStore().IsDirectory(GetUniqFileDirectory(file.ID)); + DeleteFile(file.ID, stored); + } + else if (!IsExistOnStorage(file)) + { + DeleteVersion(file); + } + throw; + } + } + + FactoryIndexer.IndexAsync(FilesWrapper.GetFilesWrapper(ServiceProvider, file, parentFoldersIds)); + + return GetFile(file.ID); + } + + public File ReplaceFileVersion(File file, Stream fileStream) + { + if (file == null) throw new ArgumentNullException("file"); + if (file.ID == null) throw new ArgumentException("No file id or folder id toFolderId determine provider"); + + var maxChunkedUploadSize = SetupInfo.MaxChunkedUploadSize(TenantExtra, TenantStatisticProvider); + + if (maxChunkedUploadSize < file.ContentLength) + { + throw FileSizeComment.GetFileSizeException(maxChunkedUploadSize); + } + + if (CoreBaseSettings.Personal && SetupInfo.IsVisibleSettings("PersonalMaxSpace")) + { + var personalMaxSpace = CoreConfiguration.PersonalMaxSpace(SettingsManager); + if (personalMaxSpace - GlobalSpace.GetUserUsedSpace(file.ID == null ? AuthContext.CurrentAccount.ID : file.CreateBy) < file.ContentLength) + { + throw FileSizeComment.GetPersonalFreeSpaceException(personalMaxSpace); + } + } + + List parentFoldersIds; + lock (syncRoot) + { + using var tx = FilesDbContext.Database.BeginTransaction(); + + file.Title = Global.ReplaceInvalidCharsAndTruncate(file.Title); + //make lowerCase + file.Title = FileUtility.ReplaceFileExtension(file.Title, FileUtility.GetFileExtension(file.Title)); + + file.ModifiedBy = AuthContext.CurrentAccount.ID; + file.ModifiedOn = TenantUtil.DateTimeNow(); + if (file.CreateBy == default) file.CreateBy = AuthContext.CurrentAccount.ID; + if (file.CreateOn == default) file.CreateOn = TenantUtil.DateTimeNow(); + + var toUpdate = FilesDbContext.Files + .Where(r => r.Id.ToString() == file.ID.ToString() && r.Version == file.Version) + .FirstOrDefault(); + + toUpdate.Version = file.Version; + toUpdate.VersionGroup = file.VersionGroup; + toUpdate.FolderId = (int)file.FolderID; + toUpdate.Title = file.Title; + toUpdate.ContentLength = file.ContentLength; + toUpdate.Category = (int)file.FilterType; + toUpdate.CreateBy = file.CreateBy; + toUpdate.CreateOn = TenantUtil.DateTimeToUtc(file.CreateOn); + toUpdate.ModifiedBy = file.ModifiedBy; + toUpdate.ModifiedOn = TenantUtil.DateTimeToUtc(file.ModifiedOn); + toUpdate.ConvertedType = file.ConvertedType; + toUpdate.Comment = file.Comment; + toUpdate.Encrypted = file.Encrypted; + toUpdate.Forcesave = file.Forcesave; + + FilesDbContext.SaveChanges(); + + tx.Commit(); + + file.PureTitle = file.Title; + + parentFoldersIds = FilesDbContext.Tree + .Where(r => r.FolderId == (int)file.FolderID) + .OrderByDescending(r => r.Level) + .Select(r => r.ParentId) + .ToList(); + + if (parentFoldersIds.Count > 0) + { + var folderToUpdate = FilesDbContext.Folders + .Where(r => parentFoldersIds.Any(a => a == r.Id)); + + foreach (var f in folderToUpdate) + { + f.ModifiedOn = TenantUtil.DateTimeToUtc(file.ModifiedOn); + f.ModifiedBy = file.ModifiedBy; + } + + FilesDbContext.SaveChanges(); + } + } + + if (fileStream != null) + { + try + { + DeleteVersionStream(file); + SaveFileStream(file, fileStream); + } + catch + { + if (!IsExistOnStorage(file)) + { + DeleteVersion(file); + } + throw; + } + } + + FactoryIndexer.IndexAsync(FilesWrapper.GetFilesWrapper(ServiceProvider, file, parentFoldersIds)); + + return GetFile(file.ID); + } + + private void DeleteVersion(File file) + { + if (file == null + || file.ID == null + || file.Version <= 1) return; + + var toDelete = Query(FilesDbContext.Files) + .Where(r => r.Id == (int)file.ID) + .Where(r => r.Version == file.Version) + .FirstOrDefault(); + + if (toDelete != null) + { + FilesDbContext.Files.Remove(toDelete); + } + FilesDbContext.SaveChanges(); + + var toUpdate = Query(FilesDbContext.Files) + .Where(r => r.Id == (int)file.ID) + .Where(r => r.Version == file.Version - 1) + .FirstOrDefault(); + + toUpdate.CurrentVersion = true; + FilesDbContext.SaveChanges(); + } + + private void DeleteVersionStream(File file) + { + GlobalStore.GetStore().DeleteDirectory(GetUniqFileVersionPath(file.ID, file.Version)); + } + + private void SaveFileStream(File file, Stream stream) + { + GlobalStore.GetStore().Save(string.Empty, GetUniqFilePath(file), stream, file.Title); + } + + public void DeleteFile(object fileId) + { + DeleteFile(fileId, true); + } + + private void DeleteFile(object fileId, bool deleteFolder) + { + if (fileId == null) return; + using var tx = FilesDbContext.Database.BeginTransaction(); + + var fromFolders = Query(FilesDbContext.Files).Where(r => r.Id == (int)fileId).GroupBy(r => r.Id).SelectMany(r => r.Select(a => a.FolderId)).Distinct().ToList(); + + var toDeleteFiles = Query(FilesDbContext.Files).Where(r => r.Id == (int)fileId); + FilesDbContext.RemoveRange(toDeleteFiles); + + var toDeleteLinks = Query(FilesDbContext.TagLink).Where(r => r.EntryId == fileId.ToString()).Where(r => r.EntryType == FileEntryType.File); + FilesDbContext.RemoveRange(toDeleteFiles); + + var tagsToRemove = Query(FilesDbContext.Tag) + .Where(r => !Query(FilesDbContext.TagLink).Where(a => a.TagId == r.Id).Any()); + + FilesDbContext.Tag.RemoveRange(tagsToRemove); + + var securityToDelete = Query(FilesDbContext.Security) + .Where(r => r.EntryId == fileId.ToString()) + .Where(r => r.EntryType == FileEntryType.File); + + FilesDbContext.Security.RemoveRange(securityToDelete); + FilesDbContext.SaveChanges(); + + tx.Commit(); + + fromFolders.ForEach(folderId => RecalculateFilesCount(folderId)); + + if (deleteFolder) + DeleteFolder(fileId); + + var wrapper = ServiceProvider.GetService(); + wrapper.Id = (int)fileId; + FactoryIndexer.DeleteAsync(wrapper); + } + + public bool IsExist(string title, object folderId) + { + return Query(FilesDbContext.Files) + .Where(r => r.Title == title) + .Where(r => r.FolderId == (int)folderId) + .Where(r => r.CurrentVersion) + .Any(); + } + + public object MoveFile(object fileId, object toFolderId) + { + if (fileId == null) return null; + + using (var tx = FilesDbContext.Database.BeginTransaction()) + { + var fromFolders = Query(FilesDbContext.Files) + .Where(r => r.Id == (int)fileId) + .GroupBy(r => r.Id) + .SelectMany(r => r.Select(a => a.FolderId)) + .Distinct() + .ToList(); + + var toUpdate = Query(FilesDbContext.Files) + .Where(r => r.Id == (int)fileId); + + foreach (var f in toUpdate) + { + f.FolderId = (int)toFolderId; + + if (GlobalFolder.GetFolderTrash(FolderDao).Equals(toFolderId)) + { + f.ModifiedBy = AuthContext.CurrentAccount.ID; + f.ModifiedOn = DateTime.UtcNow; + } + } + + FilesDbContext.SaveChanges(); + tx.Commit(); + + fromFolders.ForEach(folderId => RecalculateFilesCount(folderId)); + RecalculateFilesCount(toFolderId); + } + + var parentFoldersIds = + FilesDbContext.Tree + .Where(r => r.FolderId == (int)toFolderId) + .OrderByDescending(r => r.Level) + .Select(r => r.ParentId) + .ToList(); + + var wrapper = ServiceProvider.GetService(); + wrapper.Id = (int)fileId; + wrapper.Folders = parentFoldersIds.Select(r => new FilesFoldersWrapper() { FolderId = r.ToString() }).ToList(); + + FactoryIndexer.Update(wrapper, + UpdateAction.Replace, + w => w.Folders); + + return fileId; + } + + public File CopyFile(object fileId, object toFolderId) + { + var file = GetFile(fileId); + if (file != null) + { + var copy = ServiceProvider.GetService(); + copy.FileStatus = file.FileStatus; + copy.FolderID = toFolderId; + copy.Title = file.Title; + copy.ConvertedType = file.ConvertedType; + copy.Comment = FilesCommonResource.CommentCopy; + copy.Encrypted = file.Encrypted; + + using (var stream = GetFileStream(file)) + { + copy.ContentLength = stream.CanSeek ? stream.Length : file.ContentLength; + copy = SaveFile(copy, stream); + } + + return copy; + } + return null; + } + + public object FileRename(File file, string newTitle) + { + newTitle = Global.ReplaceInvalidCharsAndTruncate(newTitle); + var toUpdate = Query(FilesDbContext.Files) + .Where(r => r.Id == (int)file.ID) + .Where(r => r.CurrentVersion) + .FirstOrDefault(); + + toUpdate.Title = newTitle; + toUpdate.ModifiedOn = DateTime.UtcNow; + toUpdate.ModifiedBy = AuthContext.CurrentAccount.ID; + + FilesDbContext.SaveChanges(); + + return file.ID; + } + + public string UpdateComment(object fileId, int fileVersion, string comment) + { + comment ??= string.Empty; + comment = comment.Substring(0, Math.Min(comment.Length, 255)); + + var toUpdate = Query(FilesDbContext.Files) + .Where(r => r.Id == (int)fileId) + .Where(r => r.Version == fileVersion) + .FirstOrDefault(); + + toUpdate.Comment = comment; + + FilesDbContext.SaveChanges(); + + return comment; + } + + public void CompleteVersion(object fileId, int fileVersion) + { + var toUpdate = Query(FilesDbContext.Files) + .Where(r => r.Id == (int)fileId) + .Where(r => r.Version >= fileVersion); + + foreach (var f in toUpdate) + { + f.VersionGroup += 1; + } + + FilesDbContext.SaveChanges(); + } + + public void ContinueVersion(object fileId, int fileVersion) + { + using var tx = FilesDbContext.Database.BeginTransaction(); + + var versionGroup = Query(FilesDbContext.Files) + .Where(r => r.Id == (int)fileId) + .Where(r => r.Version == fileVersion) + .Select(r => r.VersionGroup) + .FirstOrDefault(); + + var toUpdate = Query(FilesDbContext.Files) + .Where(r => r.Id == (int)fileId) + .Where(r => r.Version >= fileVersion) + .Where(r => r.VersionGroup >= versionGroup); + + foreach (var f in toUpdate) + { + f.VersionGroup -= 1; + } + + FilesDbContext.SaveChanges(); + + tx.Commit(); + } + + public bool UseTrashForRemove(File file) + { + return file.RootFolderType != FolderType.TRASH; + } + + public static string GetUniqFileDirectory(object fileIdObject) + { + if (fileIdObject == null) throw new ArgumentNullException("fileIdObject"); + var fileIdInt = Convert.ToInt32(Convert.ToString(fileIdObject)); + return string.Format("folder_{0}/file_{1}", (fileIdInt / 1000 + 1) * 1000, fileIdInt); + } + + public static string GetUniqFilePath(File file) + { + return file != null + ? GetUniqFilePath(file, "content" + FileUtility.GetFileExtension(file.PureTitle)) + : null; + } + + public static string GetUniqFilePath(File file, string fileTitle) + { + return file != null + ? string.Format("{0}/{1}", GetUniqFileVersionPath(file.ID, file.Version), fileTitle) + : null; + } + + public static string GetUniqFileVersionPath(object fileIdObject, int version) + { + return fileIdObject != null + ? string.Format("{0}/v{1}", GetUniqFileDirectory(fileIdObject), version) + : null; + } + + private void RecalculateFilesCount(object folderId) + { + GetRecalculateFilesCountUpdate(folderId); + } + + #region chunking + + public ChunkedUploadSession CreateUploadSession(File file, long contentLength) + { + return ChunkedUploadSessionHolder.CreateUploadSession(file, contentLength); + } + + public void UploadChunk(ChunkedUploadSession uploadSession, Stream stream, long chunkLength) + { + if (!uploadSession.UseChunks) + { + using (var streamToSave = ChunkedUploadSessionHolder.UploadSingleChunk(uploadSession, stream, chunkLength)) + { + if (streamToSave != Stream.Null) + { + uploadSession.File = SaveFile(GetFileForCommit(uploadSession), streamToSave); + } + } + + return; + } + + ChunkedUploadSessionHolder.UploadChunk(uploadSession, stream, chunkLength); + + if (uploadSession.BytesUploaded == uploadSession.BytesTotal) + { + uploadSession.File = FinalizeUploadSession(uploadSession); + } + } + + private File FinalizeUploadSession(ChunkedUploadSession uploadSession) + { + ChunkedUploadSessionHolder.FinalizeUploadSession(uploadSession); + + var file = GetFileForCommit(uploadSession); + SaveFile(file, null); + ChunkedUploadSessionHolder.Move(uploadSession, GetUniqFilePath(file)); + + return file; + } + + public void AbortUploadSession(ChunkedUploadSession uploadSession) + { + ChunkedUploadSessionHolder.AbortUploadSession(uploadSession); + } + + private File GetFileForCommit(ChunkedUploadSession uploadSession) + { + if (uploadSession.File.ID != null) + { + var file = GetFile(uploadSession.File.ID); + file.Version++; + file.ContentLength = uploadSession.BytesTotal; + file.ConvertedType = null; + file.Comment = FilesCommonResource.CommentUpload; + file.Encrypted = uploadSession.Encrypted; + return file; + } + var result = ServiceProvider.GetService(); + result.FolderID = uploadSession.File.FolderID; + result.Title = uploadSession.File.Title; + result.ContentLength = uploadSession.BytesTotal; + result.Comment = FilesCommonResource.CommentUpload; + result.Encrypted = uploadSession.Encrypted; + + return result; + } + + #endregion + + #region Only in TMFileDao + + public void ReassignFiles(object[] fileIds, Guid newOwnerId) + { + var toUpdate = Query(FilesDbContext.Files) + .Where(r => r.CurrentVersion) + .Where(r => fileIds.Any(a => a.ToString() == r.Id.ToString())); + + foreach (var f in toUpdate) + { + f.CreateBy = newOwnerId; + } + + FilesDbContext.SaveChanges(); + } + + public List GetFiles(object[] parentIds, FilterType filterType, bool subjectGroup, Guid subjectID, string searchText, bool searchInContent) + { + if (parentIds == null || parentIds.Length == 0 || filterType == FilterType.FoldersOnly) return new List(); + + var q = GetFileQuery(r => r.CurrentVersion) + .Join(FilesDbContext.Tree, a => a.FolderId, t => t.FolderId, (file, tree) => new { file, tree }) + .Where(r => parentIds.Any(a => a.ToString() == r.tree.ParentId.ToString())) + .Select(r => r.file); + + if (!string.IsNullOrEmpty(searchText)) + { + var func = GetFuncForSearch(null, null, filterType, subjectGroup, subjectID, searchText, searchInContent, false); + + if (FactoryIndexer.TrySelectIds(s => func(s), out var searchIds)) + { + q = q.Where(r => searchIds.Any(b => b == r.Id)); + } + else + { + q = q.Where(r => BuildSearch(r, searchText, SearhTypeEnum.Any)); + } + } + + if (subjectID != Guid.Empty) + { + if (subjectGroup) + { + var users = UserManager.GetUsersByGroup(subjectID).Select(u => u.ID).ToArray(); + q = q.Where(r => users.Any(u => u == r.CreateBy)); + } + else + { + q = q.Where(r => r.CreateBy == subjectID); + } + } + + switch (filterType) + { + case FilterType.DocumentsOnly: + case FilterType.ImagesOnly: + case FilterType.PresentationsOnly: + case FilterType.SpreadsheetsOnly: + case FilterType.ArchiveOnly: + case FilterType.MediaOnly: + q = q.Where(r => r.Category == (int)filterType); + break; + case FilterType.ByExtension: + if (!string.IsNullOrEmpty(searchText)) + { + q = q.Where(r => BuildSearch(r, searchText, SearhTypeEnum.End)); + } + break; + } + + return FromQueryWithShared(q); + } + + public IEnumerable Search(string searchText, bool bunch) + { + if (FactoryIndexer.TrySelectIds(s => s.MatchAll(searchText), out var ids)) + { + var query = GetFileQuery(r => r.CurrentVersion && ids.Any(i => i == r.Id)); + return FromQueryWithShared(query) + .Where( + f => + bunch + ? f.RootFolderType == FolderType.BUNCH + : f.RootFolderType == FolderType.USER || f.RootFolderType == FolderType.COMMON) + .ToList(); + } + else + { + var query = GetFileQuery(r => r.CurrentVersion && BuildSearch(r, searchText, SearhTypeEnum.Any)); + return FromQueryWithShared(query) + .Where(f => + bunch + ? f.RootFolderType == FolderType.BUNCH + : f.RootFolderType == FolderType.USER || f.RootFolderType == FolderType.COMMON) + .ToList(); + } + } + + private void DeleteFolder(object fileId) + { + GlobalStore.GetStore().DeleteDirectory(GetUniqFileDirectory(fileId)); + } + + public bool IsExistOnStorage(File file) + { + return GlobalStore.GetStore().IsFile(GetUniqFilePath(file)); + } + + private const string DiffTitle = "diff.zip"; + + public void SaveEditHistory(File file, string changes, Stream differenceStream) + { + if (file == null) throw new ArgumentNullException("file"); + if (string.IsNullOrEmpty(changes)) throw new ArgumentNullException("changes"); + if (differenceStream == null) throw new ArgumentNullException("differenceStream"); + + changes = changes.Trim(); + + var toUpdate = Query(FilesDbContext.Files) + .Where(r => r.Id == (int)file.ID) + .Where(r => r.Version == file.Version); + + foreach (var f in toUpdate) + { + f.Changes = changes; + } + + FilesDbContext.SaveChanges(); + + GlobalStore.GetStore().Save(string.Empty, GetUniqFilePath(file, DiffTitle), differenceStream, DiffTitle); + } + + public List GetEditHistory(DocumentServiceHelper documentServiceHelper, object fileId, int fileVersion = 0) + { + var query = Query(FilesDbContext.Files) + .Where(r => r.Id == (int)fileId) + .Where(r => r.Forcesave == ForcesaveType.None); + + if (fileVersion > 0) + { + query = query.Where(r => r.Version == fileVersion); + } + + query = query.OrderBy(r => r.Version); + + return query + .ToList() + .Select(r => + { + var item = ServiceProvider.GetService(); + var editHistoryAuthor = ServiceProvider.GetService(); + + editHistoryAuthor.Id = r.ModifiedBy; + item.ID = r.Id; + item.Version = r.Version; + item.VersionGroup = r.VersionGroup; + item.ModifiedOn = TenantUtil.DateTimeFromUtc(r.ModifiedOn); + item.ModifiedBy = editHistoryAuthor; + item.ChangesString = r.Changes; + item.Key = documentServiceHelper.GetDocKey(item.ID, item.Version, TenantUtil.DateTimeFromUtc(r.CreateOn)); + + return item; + }) + .ToList(); + } + + public Stream GetDifferenceStream(File file) + { + return GlobalStore.GetStore().GetReadStream(string.Empty, GetUniqFilePath(file, DiffTitle)); + } + + public bool ContainChanges(object fileId, int fileVersion) + { + return Query(FilesDbContext.Files) + .Where(r => r.Id == (int)fileId) + .Where(r => r.Version == fileVersion) + .Where(r => r.Changes != null) + .Any(); + } + + #endregion + + private Func, Selector> GetFuncForSearch(object parentId, OrderBy orderBy, FilterType filterType, bool subjectGroup, Guid subjectID, string searchText, bool searchInContent, bool withSubfolders = false) + { + return s => + { + var result = !searchInContent || filterType == FilterType.ByExtension + ? s.Match(r => r.Title, searchText) + : s.MatchAll(searchText); + + if (parentId != null) + { + if (withSubfolders) + { + result.In(a => a.Folders.Select(r => r.FolderId), new[] { parentId.ToString() }); + } + else + { + result.InAll(a => a.Folders.Select(r => r.FolderId), new[] { parentId.ToString() }); + } + } + + if (orderBy != null) + { + switch (orderBy.SortedBy) + { + case SortedByType.Author: + result.Sort(r => r.CreateBy, orderBy.IsAsc); + break; + case SortedByType.Size: + result.Sort(r => r.ContentLength, orderBy.IsAsc); + break; + //case SortedByType.AZ: + // result.Sort(r => r.Title, orderBy.IsAsc); + // break; + case SortedByType.DateAndTime: + result.Sort(r => r.LastModifiedOn, orderBy.IsAsc); + break; + case SortedByType.DateAndTimeCreation: + result.Sort(r => r.CreateOn, orderBy.IsAsc); + break; + } + } + + if (subjectID != Guid.Empty) + { + if (subjectGroup) + { + var users = UserManager.GetUsersByGroup(subjectID).Select(u => u.ID.ToString()).ToArray(); + result.In(r => r.CreateBy, users); + } + else + { + result.Where(r => r.CreateBy, subjectID); + } + } + + switch (filterType) + { + case FilterType.DocumentsOnly: + case FilterType.ImagesOnly: + case FilterType.PresentationsOnly: + case FilterType.SpreadsheetsOnly: + case FilterType.ArchiveOnly: + case FilterType.MediaOnly: + result.Where(r => r.Category, (int)filterType); + break; + } + + return result; + }; + } + + protected List FromQueryWithShared(IQueryable dbFiles) + { + return dbFiles + .Select(r => new DbFileQuery + { + file = r, + root = + FilesDbContext.Folders + .Join(FilesDbContext.Tree, a => a.Id, b => b.ParentId, (folder, tree) => new { folder, tree }) + .Where(x => x.folder.TenantId == r.TenantId) + .Where(x => x.tree.FolderId == r.FolderId) + .OrderByDescending(r => r.tree.Level) + .Select(r => r.folder) + .FirstOrDefault(), + shared = + FilesDbContext.Security + .Where(x => x.EntryType == FileEntryType.File) + .Where(x => x.EntryId == r.Id.ToString()) + .Any() + }) + .ToList() + .Select(ToFile) + .ToList(); + } + + protected List FromQuery(IQueryable dbFiles) + { + return dbFiles + .Select(r => new DbFileQuery + { + file = r, + root = FilesDbContext.Folders + .Join(FilesDbContext.Tree, a => a.Id, b => b.ParentId, (folder, tree) => new { folder, tree }) + .Where(x => x.folder.TenantId == r.TenantId) + .Where(x => x.tree.FolderId == r.FolderId) + .OrderByDescending(r => r.tree.Level) + .Select(r => r.folder) + .FirstOrDefault(), + shared = true + }) + .ToList() + .Select(ToFile) + .ToList(); + } + + public File ToFile(DbFileQuery r) + { + var file = ServiceProvider.GetService(); + file.ID = r.file.Id; + file.Title = r.file.Title; + file.FolderID = r.file.FolderId; + file.CreateOn = TenantUtil.DateTimeFromUtc(r.file.CreateOn); + file.CreateBy = r.file.CreateBy; + file.Version = r.file.Version; + file.VersionGroup = r.file.VersionGroup; + file.ContentLength = r.file.ContentLength; + file.ModifiedOn = TenantUtil.DateTimeFromUtc(r.file.ModifiedOn); + file.ModifiedBy = r.file.ModifiedBy; + file.RootFolderType = r.root?.FolderType ?? default; + file.RootFolderCreator = r.root?.CreateBy ?? default; + file.RootFolderId = r.root?.Id ?? default; + file.Shared = r.shared; + file.ConvertedType = r.file.ConvertedType; + file.Comment = r.file.Comment; + file.Encrypted = r.file.Encrypted; + file.Forcesave = r.file.Forcesave; + return file; + } + } + + public class DbFileQuery + { + public DbFile file { get; set; } + public DbFolder root { get; set; } + public bool shared { get; set; } + } + + public static class FileDaoExtention + { + public static DIHelper AddFileDaoService(this DIHelper services) + { + services.TryAddScoped(); + services.TryAddTransient(); + + return services + .AddFilesDbContextService() + .AddUserManagerService() + .AddTenantManagerService() + .AddTenantUtilService() + .AddSetupInfo() + .AddTenantExtraService() + .AddTenantStatisticsProviderService() + .AddCoreBaseSettingsService() + .AddCoreConfigurationService() + .AddSettingsManagerService() + .AddAuthContextService() + .AddGlobalStoreService() + .AddGlobalSpaceService() + .AddFactoryIndexerService() + .AddGlobalFolderService() + .AddChunkedUploadSessionHolderService() + .AddFolderDaoService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/Dao/TeamlabDao/FolderDao.cs b/products/ASC.Files/Server/Core/Dao/TeamlabDao/FolderDao.cs new file mode 100644 index 0000000000..946b326e7f --- /dev/null +++ b/products/ASC.Files/Server/Core/Dao/TeamlabDao/FolderDao.cs @@ -0,0 +1,1059 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; + +using ASC.Common; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.Core.Common.Settings; +using ASC.Core.Tenants; +using ASC.ElasticSearch; +using ASC.Files.Core.EF; +using ASC.Files.Resources; +using ASC.Web.Files.Classes; +using ASC.Web.Files.Core.Search; +using ASC.Web.Studio.Core; +using ASC.Web.Studio.UserControls.Statistics; +using ASC.Web.Studio.Utility; + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace ASC.Files.Core.Data +{ + public class FolderDao : AbstractDao, IFolderDao + { + private const string my = "my"; + private const string common = "common"; + private const string share = "share"; + private const string trash = "trash"; + private const string projects = "projects"; + + public FactoryIndexer FactoryIndexer { get; } + public GlobalSpace GlobalSpace { get; } + public ILog Logger { get; } + + public FolderDao( + FactoryIndexer factoryIndexer, + UserManager userManager, + DbContextManager dbContextManager, + TenantManager tenantManager, + TenantUtil tenantUtil, + SetupInfo setupInfo, + TenantExtra tenantExtra, + TenantStatisticsProvider tenantStatisticProvider, + CoreBaseSettings coreBaseSettings, + CoreConfiguration coreConfiguration, + SettingsManager settingsManager, + AuthContext authContext, + IServiceProvider serviceProvider, + GlobalSpace globalSpace, + IOptionsMonitor options) + : base( + dbContextManager, + userManager, + tenantManager, + tenantUtil, + setupInfo, + tenantExtra, + tenantStatisticProvider, + coreBaseSettings, + coreConfiguration, + settingsManager, + authContext, + serviceProvider) + { + FactoryIndexer = factoryIndexer; + GlobalSpace = globalSpace; + Logger = options.Get("ASC.Files"); + } + + public Folder GetFolder(object folderId) + { + var query = GetFolderQuery(r => r.Id.ToString() == folderId.ToString()); + return FromQueryWithShared(query).SingleOrDefault(); + } + + public Folder GetFolder(string title, object parentId) + { + if (string.IsNullOrEmpty(title)) throw new ArgumentNullException(title); + + var query = GetFolderQuery(r => r.Title == title && r.ParentId.ToString() == parentId.ToString()) + .OrderBy(r => r.CreateOn); + + return FromQueryWithShared(query).FirstOrDefault(); + } + + public Folder GetRootFolder(object folderId) + { + var id = FilesDbContext.Tree + .Where(r => r.FolderId == (int)folderId) + .OrderByDescending(r => r.Level) + .Select(r => r.ParentId) + .FirstOrDefault(); + + var query = GetFolderQuery(r => r.Id == id); + + return FromQueryWithShared(query).SingleOrDefault(); + } + + public Folder GetRootFolderByFile(object fileId) + { + var subq = Query(FilesDbContext.Files) + .Where(r => r.Id == (int)fileId && r.CurrentVersion) + .Select(r => r.FolderId) + .Distinct(); + + var q = FilesDbContext.Tree + .Where(r => subq.Any(q => q == r.FolderId)) + .OrderByDescending(r => r.Level) + .Select(r => r.ParentId) + .FirstOrDefault(); + + var query = GetFolderQuery(r => r.Id == q); + return FromQueryWithShared(query).SingleOrDefault(); + } + + public List GetFolders(object parentId) + { + return GetFolders(parentId, default, default, false, default, string.Empty); + } + + public List GetFolders(object parentId, OrderBy orderBy, FilterType filterType, bool subjectGroup, Guid subjectID, string searchText, bool withSubfolders = false) + { + if (filterType == FilterType.FilesOnly || filterType == FilterType.ByExtension + || filterType == FilterType.DocumentsOnly || filterType == FilterType.ImagesOnly + || filterType == FilterType.PresentationsOnly || filterType == FilterType.SpreadsheetsOnly + || filterType == FilterType.ArchiveOnly || filterType == FilterType.MediaOnly) + return new List(); + + if (orderBy == null) orderBy = new OrderBy(SortedByType.DateAndTime, false); + + var q = GetFolderQuery(r => r.ParentId.ToString() == parentId.ToString()); + + if (withSubfolders) + { + q = GetFolderQuery() + .Join(FilesDbContext.Tree, r => r.Id, a => a.FolderId, (folder, tree) => new { folder, tree }) + .Where(r => r.tree.ParentId.ToString() == parentId.ToString() && r.tree.Level != 0) + .Select(r => r.folder); + } + + if (!string.IsNullOrEmpty(searchText)) + { + if (FactoryIndexer.TrySelectIds(s => s.MatchAll(searchText), out var searchIds)) + { + q = q.Where(r => searchIds.Any(a => a == r.Id)); + } + else + { + q = q.Where(r => BuildSearch(r, searchText, SearhTypeEnum.Any)); + } + } + + switch (orderBy.SortedBy) + { + case SortedByType.Author: + q = orderBy.IsAsc ? q.OrderBy(r => r.CreateBy) : q.OrderByDescending(r => r.CreateBy); + break; + case SortedByType.AZ: + q = orderBy.IsAsc ? q.OrderBy(r => r.Title) : q.OrderByDescending(r => r.Title); + break; + case SortedByType.DateAndTime: + q = orderBy.IsAsc ? q.OrderBy(r => r.ModifiedOn) : q.OrderByDescending(r => r.ModifiedOn); + break; + case SortedByType.DateAndTimeCreation: + q = orderBy.IsAsc ? q.OrderBy(r => r.CreateOn) : q.OrderByDescending(r => r.CreateOn); + break; + default: + q = q.OrderBy(r => r.Title); + break; + } + + if (subjectID != Guid.Empty) + { + if (subjectGroup) + { + var users = UserManager.GetUsersByGroup(subjectID).Select(u => u.ID).ToArray(); + q = q.Where(r => users.Any(a => a == r.CreateBy)); + } + else + { + q = q.Where(r => r.CreateBy == subjectID); + } + } + + return FromQueryWithShared(q); + } + + public List GetFolders(object[] folderIds, FilterType filterType = FilterType.None, bool subjectGroup = false, Guid? subjectID = null, string searchText = "", bool searchSubfolders = false, bool checkShare = true) + { + if (filterType == FilterType.FilesOnly || filterType == FilterType.ByExtension + || filterType == FilterType.DocumentsOnly || filterType == FilterType.ImagesOnly + || filterType == FilterType.PresentationsOnly || filterType == FilterType.SpreadsheetsOnly + || filterType == FilterType.ArchiveOnly || filterType == FilterType.MediaOnly) + return new List(); + + var folderIdsStrings = folderIds.Select(r => r.ToString()).ToList(); + var q = GetFolderQuery(r => folderIdsStrings.Any(q => q == r.Id.ToString())); + + if (searchSubfolders) + { + q = GetFolderQuery() + .Join(FilesDbContext.Tree, r => r.Id, a => a.FolderId, (folder, tree) => new { folder, tree }) + .Where(r => folderIdsStrings.Any(q => q == r.folder.ParentId.ToString())) + .Select(r => r.folder); + } + + if (!string.IsNullOrEmpty(searchText)) + { + if (FactoryIndexer.TrySelectIds(s => + searchSubfolders + ? s.MatchAll(searchText) + : s.MatchAll(searchText).In(r => r.Id, folderIds), + out var searchIds)) + { + q = q.Where(r => searchIds.Any(a => a == r.Id)); + } + else + { + q = q.Where(r => BuildSearch(r, searchText, SearhTypeEnum.Any)); + } + } + + + if (subjectID.HasValue && subjectID != Guid.Empty) + { + if (subjectGroup) + { + var users = UserManager.GetUsersByGroup(subjectID.Value).Select(u => u.ID).ToArray(); + q = q.Where(r => users.Any(a => a == r.CreateBy)); + } + else + { + q = q.Where(r => r.CreateBy == subjectID); + } + } + + return checkShare ? FromQueryWithShared(q) : FromQuery(q); + } + + public List GetParentFolders(object folderId) + { + var folderIdString = folderId.ToString(); + var q = GetFolderQuery() + .Join(FilesDbContext.Tree, r => r.Id, a => a.ParentId, (folder, tree) => new { folder, tree }) + .Where(r => r.tree.FolderId.ToString() == folderIdString) + .OrderByDescending(r => r.tree.Level) + .Select(r => r.folder); + + return FromQueryWithShared(q); + } + + public object SaveFolder(Folder folder) + { + if (folder == null) throw new ArgumentNullException("folder"); + + folder.Title = Global.ReplaceInvalidCharsAndTruncate(folder.Title); + + folder.ModifiedOn = TenantUtil.DateTimeNow(); + folder.ModifiedBy = AuthContext.CurrentAccount.ID; + + if (folder.CreateOn == default) folder.CreateOn = TenantUtil.DateTimeNow(); + if (folder.CreateBy == default) folder.CreateBy = AuthContext.CurrentAccount.ID; + + var isnew = false; + + using (var tx = FilesDbContext.Database.BeginTransaction()) + { + if (folder.ID != null && IsExist(folder.ID)) + { + var toUpdate = Query(FilesDbContext.Folders) + .Where(r => r.Id == (int)folder.ID) + .FirstOrDefault(); + + toUpdate.Title = folder.Title; + toUpdate.CreateBy = folder.CreateBy; + toUpdate.ModifiedOn = TenantUtil.DateTimeToUtc(folder.ModifiedOn); + toUpdate.ModifiedBy = folder.ModifiedBy; + + FilesDbContext.SaveChanges(); + } + else + { + isnew = true; + var newFolder = new DbFolder + { + Id = 0, + ParentId = (int)folder.ParentFolderID, + Title = folder.Title, + CreateOn = TenantUtil.DateTimeToUtc(folder.CreateOn), + CreateBy = folder.CreateBy, + ModifiedOn = TenantUtil.DateTimeToUtc(folder.ModifiedOn), + ModifiedBy = folder.ModifiedBy, + FolderType = folder.FolderType, + TenantId = TenantID + }; + + newFolder = FilesDbContext.Folders.Add(newFolder).Entity; + FilesDbContext.SaveChanges(); + folder.ID = newFolder.Id; + + //itself link + var newTree = new DbFolderTree + { + FolderId = (int)folder.ID, + ParentId = (int)folder.ID, + Level = 0 + }; + + FilesDbContext.Tree.Add(newTree); + FilesDbContext.SaveChanges(); + + //full path to root + var oldTree = FilesDbContext.Tree + .Where(r => r.FolderId == (int)folder.ParentFolderID) + .FirstOrDefault(); + + var treeToAdd = new DbFolderTree + { + FolderId = (int)folder.ID, + ParentId = oldTree.ParentId, + Level = oldTree.Level + 1 + }; + + FilesDbContext.Tree.Add(treeToAdd); + FilesDbContext.SaveChanges(); + } + + tx.Commit(); + } + + if (isnew) + { + RecalculateFoldersCount(folder.ID); + } + + FactoryIndexer.IndexAsync(FoldersWrapper.GetFolderWrapper(ServiceProvider, folder)); + return folder.ID; + } + + private bool IsExist(object folderId) + { + return Query(FilesDbContext.Folders) + .Where(r => r.Id == (int)folderId) + .Any(); + } + + public void DeleteFolder(object folderId) + { + if (folderId == null) throw new ArgumentNullException("folderId"); + + var id = int.Parse(Convert.ToString(folderId)); + + if (id == 0) return; + + using (var tx = FilesDbContext.Database.BeginTransaction()) + { + var subfolders = + FilesDbContext.Tree + .Where(r => r.ParentId == id) + .Select(r => r.FolderId) + .ToList(); + + if (!subfolders.Contains(id)) subfolders.Add(id); // chashed folder_tree + + var parent = Query(FilesDbContext.Folders) + .Where(r => r.Id == id) + .Select(r => r.ParentId) + .FirstOrDefault(); + + var folderToDelete = Query(FilesDbContext.Folders).Where(r => subfolders.Any(a => r.Id == a)); + FilesDbContext.Folders.RemoveRange(folderToDelete); + + var treeToDelete = FilesDbContext.Tree.Where(r => subfolders.Any(a => r.FolderId == a)); + FilesDbContext.Tree.RemoveRange(treeToDelete); + + var linkToDelete = Query(FilesDbContext.TagLink) + .Where(r => subfolders.Any(a => r.EntryId == a.ToString())) + .Where(r => r.EntryType == FileEntryType.Folder); + FilesDbContext.TagLink.RemoveRange(linkToDelete); + + var tagsToRemove = Query(FilesDbContext.Tag) + .Where(r => !Query(FilesDbContext.TagLink).Where(a => a.TagId == r.Id).Any()); + + FilesDbContext.Tag.RemoveRange(tagsToRemove); + + var securityToDelete = Query(FilesDbContext.Security) + .Where(r => subfolders.Any(a => r.EntryId == a.ToString())) + .Where(r => r.EntryType == FileEntryType.Folder); + + FilesDbContext.Security.RemoveRange(securityToDelete); + FilesDbContext.SaveChanges(); + + var bunchToDelete = Query(FilesDbContext.BunchObjects) + .Where(r => r.LeftNode == id.ToString()); + + FilesDbContext.RemoveRange(bunchToDelete); + FilesDbContext.SaveChanges(); + + tx.Commit(); + + RecalculateFoldersCount(parent); + } + + FactoryIndexer.DeleteAsync(new FoldersWrapper { Id = (int)folderId }); + } + + public object MoveFolder(object folderId, object toFolderId, CancellationToken? cancellationToken) + { + using (var tx = FilesDbContext.Database.BeginTransaction()) + { + var folder = GetFolder(folderId); + + if (folder.FolderType != FolderType.DEFAULT) + throw new ArgumentException("It is forbidden to move the System folder.", "folderId"); + + var recalcFolders = new List { toFolderId }; + var parent = FilesDbContext.Folders + .Where(r => r.Id == (int)folderId) + .Select(r => r.ParentId) + .FirstOrDefault(); + + if (parent != 0 && !recalcFolders.Contains(parent)) recalcFolders.Add(parent); + + var toUpdate = Query(FilesDbContext.Folders) + .Where(r => r.Id == (int)folderId) + .FirstOrDefault(); + + toUpdate.ParentId = (int)toFolderId; + toUpdate.ModifiedOn = DateTime.UtcNow; + toUpdate.ModifiedBy = AuthContext.CurrentAccount.ID; + + FilesDbContext.SaveChanges(); + + var subfolders = FilesDbContext.Tree + .Where(r => r.ParentId == (int)folderId) + .ToDictionary(r => r.FolderId, r => r.Level); + + var toDelete = FilesDbContext.Tree + .Where(r => subfolders.Keys.Any(a => a == r.FolderId) && !subfolders.Keys.Any(a => a == r.ParentId)); + + FilesDbContext.Tree.RemoveRange(toDelete); + FilesDbContext.SaveChanges(); + + var toInsert = FilesDbContext.Tree + .Where(r => r.FolderId == (int)toFolderId) + .ToList(); + + foreach (var subfolder in subfolders) + { + foreach (var f in toInsert) + { + var newTree = new DbFolderTree + { + FolderId = subfolder.Key, + ParentId = f.ParentId, + Level = f.Level + 1 + }; + FilesDbContext.AddOrUpdate(r => r.Tree, newTree); + } + } + + FilesDbContext.SaveChanges(); + tx.Commit(); + + recalcFolders.ForEach(RecalculateFoldersCount); + recalcFolders.ForEach(fid => GetRecalculateFilesCountUpdate(fid)); + } + return folderId; + } + + public Folder CopyFolder(object folderId, object toFolderId, CancellationToken? cancellationToken) + { + var folder = GetFolder(folderId); + + var toFolder = GetFolder(toFolderId); + + if (folder.FolderType == FolderType.BUNCH) + folder.FolderType = FolderType.DEFAULT; + + var copy = ServiceProvider.GetService(); + copy.ParentFolderID = toFolderId; + copy.RootFolderId = toFolder.RootFolderId; + copy.RootFolderCreator = toFolder.RootFolderCreator; + copy.RootFolderType = toFolder.RootFolderType; + copy.Title = folder.Title; + copy.FolderType = folder.FolderType; + + copy = GetFolder(SaveFolder(copy)); + + FactoryIndexer.IndexAsync(FoldersWrapper.GetFolderWrapper(ServiceProvider, copy)); + return copy; + } + + public IDictionary CanMoveOrCopy(object[] folderIds, object to) + { + var result = new Dictionary(); + + foreach (var folderId in folderIds) + { + var exists = FilesDbContext.Tree + .Where(r => r.ParentId == (int)folderId) + .Where(r => r.FolderId == (int)to) + .Any(); + + if (exists) + { + throw new InvalidOperationException(FilesCommonResource.ErrorMassage_FolderCopyError); + } + + var title = Query(FilesDbContext.Folders) + .Where(r => r.Id == (int)folderId) + .Select(r => r.Title.ToLower()) + .FirstOrDefault(); + + var conflict = Query(FilesDbContext.Folders) + .Where(r => r.Title.ToLower() == title) + .Where(r => r.ParentId == (int)to) + .Select(r => r.Id) + .FirstOrDefault(); + + if (conflict != 0) + { + FilesDbContext.Files + .Join(FilesDbContext.Files, f1 => f1.Title.ToLower(), f2 => f2.Title.ToLower(), (f1, f2) => new { f1, f2 }) + .Where(r => r.f1.TenantId == TenantID && r.f1.CurrentVersion && r.f1.FolderId == (int)folderId) + .Where(r => r.f2.TenantId == TenantID && r.f2.CurrentVersion && r.f2.FolderId == (int)conflict) + .Select(r => r.f1) + .ToList() + .ForEach(r => result[r.Id] = r.Title); + + var childs = Query(FilesDbContext.Folders) + .Where(r => r.ParentId == (int)folderId) + .Select(r => r.Id.ToString()); + + foreach (var pair in CanMoveOrCopy(childs.ToArray(), conflict)) + { + result.Add(pair.Key, pair.Value); + } + } + } + + return result; + } + + public object RenameFolder(Folder folder, string newTitle) + { + var toUpdate = Query(FilesDbContext.Folders) + .Where(r => r.Id == (int)folder.ID) + .FirstOrDefault(); + + toUpdate.Title = Global.ReplaceInvalidCharsAndTruncate(newTitle); + toUpdate.ModifiedOn = DateTime.UtcNow; + toUpdate.ModifiedBy = AuthContext.CurrentAccount.ID; + + FilesDbContext.SaveChanges(); + + return folder.ID; + } + + public int GetItemsCount(object folderId) + { + return GetFoldersCount(folderId) + + GetFilesCount(folderId); + } + + private int GetFoldersCount(object parentId) + { + var count = FilesDbContext.Tree + .Where(r => r.ParentId == (int)parentId) + .Where(r => r.Level >= 0) + .Count(); + + return count; + } + + private int GetFilesCount(object folderId) + { + var count = Query(FilesDbContext.Files) + .Distinct() + .Where(r => FilesDbContext.Tree.Where(r => r.ParentId == (int)folderId).Select(r => r.FolderId).Any(b => b == r.FolderId)) + .Count(); + + return count; + } + + public bool IsEmpty(object folderId) + { + return GetItemsCount(folderId) == 0; + } + + public bool UseTrashForRemove(Folder folder) + { + return folder.RootFolderType != FolderType.TRASH && folder.FolderType != FolderType.BUNCH; + } + + public bool UseRecursiveOperation(object folderId, object toRootFolderId) + { + return true; + } + + public bool CanCalculateSubitems(object entryId) + { + return true; + } + + public long GetMaxUploadSize(object folderId, bool chunkedUpload) + { + var tmp = long.MaxValue; + + if (CoreBaseSettings.Personal && SetupInfo.IsVisibleSettings("PersonalMaxSpace")) + tmp = CoreConfiguration.PersonalMaxSpace(SettingsManager) - GlobalSpace.GetUserUsedSpace(); + + return Math.Min(tmp, chunkedUpload ? SetupInfo.MaxChunkedUploadSize(TenantExtra, TenantStatisticProvider) : SetupInfo.MaxUploadSize(TenantExtra, TenantStatisticProvider)); + } + + private void RecalculateFoldersCount(object id) + { + var toUpdate = Query(FilesDbContext.Folders) + .Where(r => FilesDbContext.Tree.Where(a => a.FolderId == (int)id).Select(a => a.ParentId).Any(a => a == r.Id)) + .ToList(); + + foreach (var f in toUpdate) + { + var count = FilesDbContext.Tree.Where(r => r.ParentId == (int)id).Count() - 1; + f.FoldersCount = count; + } + + FilesDbContext.SaveChanges(); + } + + #region Only for TMFolderDao + + public void ReassignFolders(object[] folderIds, Guid newOwnerId) + { + var toUpdate = Query(FilesDbContext.Folders) + .Where(r => folderIds.Any(a => r.Id == (int)a)); + + foreach (var f in toUpdate) + { + f.CreateBy = newOwnerId; + } + + FilesDbContext.SaveChanges(); + } + + public IEnumerable Search(string text, bool bunch) + { + return Search(text).Where(f => bunch + ? f.RootFolderType == FolderType.BUNCH + : (f.RootFolderType == FolderType.USER || f.RootFolderType == FolderType.COMMON)).ToList(); + } + + private IEnumerable Search(string text) + { + if (string.IsNullOrEmpty(text)) return new List(); + + if (FactoryIndexer.TrySelectIds(s => s.MatchAll(text), out var ids)) + { + var q1 = GetFolderQuery(r => ids.Any(a => r.Id == a)); + return FromQueryWithShared(q1); + } + + var q = GetFolderQuery(r => BuildSearch(r, text, SearhTypeEnum.Any)); + return FromQueryWithShared(q); + } + + public virtual IEnumerable GetFolderIDs(string module, string bunch, IEnumerable data, bool createIfNotExists) + { + if (string.IsNullOrEmpty(module)) throw new ArgumentNullException("module"); + if (string.IsNullOrEmpty(bunch)) throw new ArgumentNullException("bunch"); + + var keys = data.Select(id => string.Format("{0}/{1}/{2}", module, bunch, id)).ToArray(); + + var folderIdsDictionary = Query(FilesDbContext.BunchObjects) + .Where(r => keys.Length > 1 ? keys.Any(a => a == r.RightNode) : r.RightNode == keys[0]) + .ToDictionary(r => r.RightNode, r => r.LeftNode); + + var folderIds = new List(); + + foreach (var key in keys) + { + string folderId = null; + if (createIfNotExists && !folderIdsDictionary.TryGetValue(key, out folderId)) + { + var folder = ServiceProvider.GetService(); + switch (bunch) + { + case my: + folder.FolderType = FolderType.USER; + folder.Title = my; + break; + case common: + folder.FolderType = FolderType.COMMON; + folder.Title = common; + break; + case trash: + folder.FolderType = FolderType.TRASH; + folder.Title = trash; + break; + case share: + folder.FolderType = FolderType.SHARE; + folder.Title = share; + break; + case projects: + folder.FolderType = FolderType.Projects; + folder.Title = projects; + break; + default: + folder.FolderType = FolderType.BUNCH; + folder.Title = (string)key; + break; + } + using var tx = FilesDbContext.Database.BeginTransaction();//NOTE: Maybe we shouldn't start transaction here at all + + folderId = (string)SaveFolder(folder); //Save using our db manager + + var newBunch = new DbFilesBunchObjects + { + LeftNode = folderId, + RightNode = key, + TenantId = TenantID + }; + + FilesDbContext.AddOrUpdate(r => r.BunchObjects, newBunch); + FilesDbContext.SaveChanges(); + + tx.Commit(); //Commit changes + } + folderIds.Add(folderId); + } + return folderIds; + } + + public virtual object GetFolderID(string module, string bunch, string data, bool createIfNotExists) + { + if (string.IsNullOrEmpty(module)) throw new ArgumentNullException("module"); + if (string.IsNullOrEmpty(bunch)) throw new ArgumentNullException("bunch"); + + var key = string.Format("{0}/{1}/{2}", module, bunch, data); + var folderId = Query(FilesDbContext.BunchObjects) + .Where(r => r.RightNode == key) + .Select(r => r.LeftNode) + .FirstOrDefault(); + + if (createIfNotExists && folderId == null) + { + var folder = ServiceProvider.GetService(); + folder.ParentFolderID = 0; + switch (bunch) + { + case my: + folder.FolderType = FolderType.USER; + folder.Title = my; + folder.CreateBy = new Guid(data); + break; + case common: + folder.FolderType = FolderType.COMMON; + folder.Title = common; + break; + case trash: + folder.FolderType = FolderType.TRASH; + folder.Title = trash; + folder.CreateBy = new Guid(data); + break; + case share: + folder.FolderType = FolderType.SHARE; + folder.Title = share; + break; + case projects: + folder.FolderType = FolderType.Projects; + folder.Title = projects; + break; + default: + folder.FolderType = FolderType.BUNCH; + folder.Title = key; + break; + } + using var tx = FilesDbContext.Database.BeginTransaction(); //NOTE: Maybe we shouldn't start transaction here at all + folderId = (string)SaveFolder(folder); //Save using our db manager + var toInsert = new DbFilesBunchObjects + { + LeftNode = folderId, + RightNode = key, + TenantId = TenantID + }; + + FilesDbContext.AddOrUpdate(r => r.BunchObjects, toInsert); + tx.Commit(); //Commit changes + } + + return Convert.ToInt32(folderId); + } + + + public object GetFolderIDProjects(bool createIfNotExists) + { + return GetFolderID(FileConstant.ModuleId, projects, null, createIfNotExists); + } + + public object GetFolderIDTrash(bool createIfNotExists, Guid? userId = null) + { + return GetFolderID(FileConstant.ModuleId, trash, (userId ?? AuthContext.CurrentAccount.ID).ToString(), createIfNotExists); + } + + public object GetFolderIDCommon(bool createIfNotExists) + { + return GetFolderID(FileConstant.ModuleId, common, null, createIfNotExists); + } + + public object GetFolderIDUser(bool createIfNotExists, Guid? userId = null) + { + return GetFolderID(FileConstant.ModuleId, my, (userId ?? AuthContext.CurrentAccount.ID).ToString(), createIfNotExists); + } + + public object GetFolderIDShare(bool createIfNotExists) + { + return GetFolderID(FileConstant.ModuleId, share, null, createIfNotExists); + } + + #endregion + + protected IQueryable GetFolderQuery(Expression> where = null) + { + var q = Query(FilesDbContext.Folders); + if (where != null) + { + q = q.Where(where); + } + return q; + } + + protected List FromQueryWithShared(IQueryable dbFiles) + { + return dbFiles + .Select(r => new DbFolderQuery + { + folder = r, + root = FilesDbContext.Folders + .Join(FilesDbContext.Tree, a => a.Id, b => b.ParentId, (folder, tree) => new { folder, tree }) + .Where(x => x.folder.TenantId == r.TenantId) + .Where(x => x.tree.FolderId == r.ParentId) + .OrderByDescending(r => r.tree.Level) + .Select(r => r.folder) + .Take(1) + .FirstOrDefault(), + shared = FilesDbContext.Security + .Where(r => r.EntryType == FileEntryType.Folder) + .Where(x => x.EntryId == r.Id.ToString()) + .Any() + }) + .ToList() + .Select(ToFolder) + .ToList(); + } + + protected List FromQuery(IQueryable dbFiles) + { + return dbFiles + .Select(r => new DbFolderQuery + { + folder = r, + root = FilesDbContext.Folders + .Join(FilesDbContext.Tree, a => a.Id, b => b.ParentId, (folder, tree) => new { folder, tree }) + .Where(x => x.folder.TenantId == r.TenantId) + .Where(x => x.tree.FolderId == r.ParentId) + .OrderByDescending(x => x.tree.Level) + .Select(x => x.folder) + .Take(1) + .FirstOrDefault(), + shared = true + }) + .ToList() + .Select(ToFolder) + .ToList(); + } + + public Folder ToFolder(DbFolderQuery r) + { + var result = ServiceProvider.GetService(); + result.ID = r.folder.Id; + result.ParentFolderID = r.folder.ParentId; + result.Title = r.folder.Title; + result.CreateOn = TenantUtil.DateTimeFromUtc(r.folder.CreateOn); + result.CreateBy = r.folder.CreateBy; + result.ModifiedOn = TenantUtil.DateTimeFromUtc(r.folder.ModifiedOn); + result.ModifiedBy = r.folder.ModifiedBy; + result.FolderType = r.folder.FolderType; + result.TotalSubFolders = r.folder.FoldersCount; + result.TotalFiles = r.folder.FilesCount; + result.RootFolderType = r.root?.FolderType ?? default; + result.RootFolderCreator = r.root?.CreateBy ?? default; + result.RootFolderId = r.root?.Id ?? default; + result.Shared = r.shared; + + switch (result.FolderType) + { + case FolderType.COMMON: + result.Title = FilesUCResource.CorporateFiles; + break; + case FolderType.USER: + result.Title = FilesUCResource.MyFiles; + break; + case FolderType.SHARE: + result.Title = FilesUCResource.SharedForMe; + break; + case FolderType.TRASH: + result.Title = FilesUCResource.Trash; + break; + case FolderType.Projects: + result.Title = FilesUCResource.ProjectFiles; + break; + case FolderType.BUNCH: + try + { + result.Title = GetProjectTitle(result.ID); + } + catch (Exception e) + { + //Global.Logger.Error(e); + } + break; + } + + if (result.FolderType != FolderType.DEFAULT && 0.Equals(result.ParentFolderID)) result.RootFolderType = result.FolderType; + if (result.FolderType != FolderType.DEFAULT && result.RootFolderCreator == default) result.RootFolderCreator = result.CreateBy; + if (result.FolderType != FolderType.DEFAULT && 0.Equals(result.RootFolderId)) result.RootFolderId = result.ID; + + return result; + } + + public string GetBunchObjectID(object folderID) + { + return Query(FilesDbContext.BunchObjects) + .Where(r => r.LeftNode == (folderID ?? string.Empty).ToString()) + .Select(r => r.RightNode) + .FirstOrDefault(); + } + + public Dictionary GetBunchObjectIDs(List folderIDs) + { + return Query(FilesDbContext.BunchObjects) + .Where(r => folderIDs.Any(a => a.ToString() == r.LeftNode)) + .ToDictionary(r => r.LeftNode, r => r.RightNode); + } + + private string GetProjectTitle(object folderID) + { + return ""; + //if (!ApiServer.Available) + //{ + // return string.Empty; + //} + + //var cacheKey = "documents/folders/" + folderID.ToString(); + + //var projectTitle = Convert.ToString(cache.Get(cacheKey)); + + //if (!string.IsNullOrEmpty(projectTitle)) return projectTitle; + + //var bunchObjectID = GetBunchObjectID(folderID); + + //if (string.IsNullOrEmpty(bunchObjectID)) + // throw new Exception("Bunch Object id is null for " + folderID); + + //if (!bunchObjectID.StartsWith("projects/project/")) + // return string.Empty; + + //var bunchObjectIDParts = bunchObjectID.Split('/'); + + //if (bunchObjectIDParts.Length < 3) + // throw new Exception("Bunch object id is not supported format"); + + //var projectID = Convert.ToInt32(bunchObjectIDParts[bunchObjectIDParts.Length - 1]); + + //if (HttpContext.Current == null) + // return string.Empty; + + //var apiServer = new ApiServer(); + + //var apiUrl = string.Format("{0}project/{1}.json?fields=id,title", SetupInfo.WebApiBaseUrl, projectID); + + //var responseApi = JObject.Parse(Encoding.UTF8.GetString(Convert.FromBase64String(apiServer.GetApiResponse(apiUrl, "GET"))))["response"]; + + //if (responseApi != null && responseApi.HasValues) + //{ + // projectTitle = Global.ReplaceInvalidCharsAndTruncate(responseApi["title"].Value()); + //} + //else + //{ + // return string.Empty; + //} + //if (!string.IsNullOrEmpty(projectTitle)) + //{ + // cache.Insert(cacheKey, projectTitle, TimeSpan.FromMinutes(15)); + //} + //return projectTitle; + } + } + + public class DbFolderQuery + { + public DbFolder folder { get; set; } + public DbFolder root { get; set; } + public bool shared { get; set; } + } + + public static class FolderDaoExtention + { + public static DIHelper AddFolderDaoService(this DIHelper services) + { + services.TryAddScoped(); + services.TryAddTransient(); + return services + .AddFactoryIndexerService() + .AddTenantManagerService() + .AddUserManagerService() + .AddFilesDbContextService() + .AddTenantUtilService() + .AddSetupInfo() + .AddTenantExtraService() + .AddTenantStatisticsProviderService() + .AddCoreBaseSettingsService() + .AddCoreConfigurationService() + .AddSettingsManagerService() + .AddAuthContextService() + .AddGlobalSpaceService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/Dao/TeamlabDao/SecurityDao.cs b/products/ASC.Files/Server/Core/Dao/TeamlabDao/SecurityDao.cs new file mode 100644 index 0000000000..df391d3f72 --- /dev/null +++ b/products/ASC.Files/Server/Core/Dao/TeamlabDao/SecurityDao.cs @@ -0,0 +1,388 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; + +using ASC.Common; +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.Core.Common.Settings; +using ASC.Core.Tenants; +using ASC.Files.Core.EF; +using ASC.Files.Core.Security; +using ASC.Web.Studio.Core; +using ASC.Web.Studio.UserControls.Statistics; +using ASC.Web.Studio.Utility; + +namespace ASC.Files.Core.Data +{ + internal class SecurityDao : AbstractDao, ISecurityDao + { + public SecurityDao(UserManager userManager, + DbContextManager dbContextManager, + TenantManager tenantManager, + TenantUtil tenantUtil, + SetupInfo setupInfo, + TenantExtra tenantExtra, + TenantStatisticsProvider tenantStatisticProvider, + CoreBaseSettings coreBaseSettings, + CoreConfiguration coreConfiguration, + SettingsManager settingsManager, + AuthContext authContext, + IServiceProvider serviceProvider) + : base(dbContextManager, + userManager, + tenantManager, + tenantUtil, + setupInfo, + tenantExtra, + tenantStatisticProvider, + coreBaseSettings, + coreConfiguration, + settingsManager, + authContext, + serviceProvider) + { + } + + public void DeleteShareRecords(IEnumerable records) + { + using var tx = FilesDbContext.Database.BeginTransaction(); + + foreach (var record in records) + { + var query = FilesDbContext.Security + .Where(r => r.TenantId == record.Tenant) + .Where(r => r.EntryId == MappingID(record.EntryId).ToString()) + .Where(r => r.EntryType == record.EntryType) + .Where(r => r.Subject == record.Subject); + + FilesDbContext.RemoveRange(query); + } + + tx.Commit(); + } + + public bool IsShared(object entryId, FileEntryType type) + { + return Query(FilesDbContext.Security) + .Where(r => r.EntryId == MappingID(entryId).ToString()) + .Where(r => r.EntryType == type) + .Any(); + } + + public void SetShare(FileShareRecord r) + { + if (r.Share == FileShare.None) + { + var entryId = (MappingID(r.EntryId) ?? "").ToString(); + if (string.IsNullOrEmpty(entryId)) return; + + using (var tx = FilesDbContext.Database.BeginTransaction()) + { + var files = new List(); + + if (r.EntryType == FileEntryType.Folder) + { + var folders = new List(); + if (int.TryParse(entryId, out var intEntryId)) + { + var foldersInt = FilesDbContext.Tree + .Where(r => r.ParentId.ToString() == entryId) + .Select(r => r.FolderId) + .ToList(); + + folders.AddRange(foldersInt.Select(folderInt => folderInt.ToString())); + files.AddRange(Query(FilesDbContext.Files).Where(r => foldersInt.Any(a => a == r.FolderId)).Select(r => r.Id.ToString())); + } + else + { + folders.Add(entryId); + } + + var toDelete = FilesDbContext.Security + .Where(a => a.TenantId == r.Tenant) + .Where(a => folders.Any(b => b == a.EntryId)) + .Where(a => a.EntryType == FileEntryType.Folder) + .Where(a => a.Subject == r.Subject); + + FilesDbContext.Security.RemoveRange(toDelete); + FilesDbContext.SaveChanges(); + + } + else + { + files.Add(entryId); + } + + if (0 < files.Count) + { + var toDelete = FilesDbContext.Security + .Where(a => a.TenantId == r.Tenant) + .Where(a => files.Any(b => b == a.EntryId)) + .Where(a => a.EntryType == FileEntryType.File) + .Where(a => a.Subject == r.Subject); + + FilesDbContext.Security.RemoveRange(toDelete); + FilesDbContext.SaveChanges(); + } + + tx.Commit(); + } + } + else + { + var toInsert = new DbFilesSecurity + { + TenantId = r.Tenant, + EntryId = MappingID(r.EntryId, true).ToString(), + EntryType = r.EntryType, + Subject = r.Subject, + Owner = r.Owner, + Security = r.Share, + TimeStamp = DateTime.UtcNow + }; + + FilesDbContext.AddOrUpdate(r => r.Security, toInsert); + FilesDbContext.SaveChanges(); + } + } + + public IEnumerable GetShares(IEnumerable subjects) + { + var q = GetQuery(r => subjects.Any(a => r.Subject == a)); + return FromQuery(q); + } + + public IEnumerable GetPureShareRecords(IEnumerable entries) + { + if (entries == null) return new List(); + + var files = new List(); + var folders = new List(); + + foreach (var entry in entries) + { + SelectFilesAndFoldersForShare(entry, files, folders, null); + } + + return GetPureShareRecordsDb(files, folders); + } + + public IEnumerable GetPureShareRecords(FileEntry entry) + { + if (entry == null) return new List(); + + var files = new List(); + var folders = new List(); + + SelectFilesAndFoldersForShare(entry, files, folders, null); + + return GetPureShareRecordsDb(files, folders); + } + + private IEnumerable GetPureShareRecordsDb(List files, List folders) + { + var result = new List(); + + var q = GetQuery(r => folders.Any(a => a == r.EntryId) && r.EntryType == FileEntryType.Folder); + + if (files.Any()) + { + q = q.Union(GetQuery(r => files.Any(q => q == r.EntryId) && r.EntryType == FileEntryType.File)); + } + + result.AddRange(FromQuery(q)); + + return result; + } + + /// + /// Get file share records with hierarchy. + /// + /// + /// + public IEnumerable GetShares(IEnumerable entries) + { + if (entries == null) return new List(); + + var files = new List(); + var foldersInt = new List(); + + foreach (var entry in entries) + { + SelectFilesAndFoldersForShare(entry, files, null, foldersInt); + } + + return SaveFilesAndFoldersForShare(files, foldersInt); + } + + /// + /// Get file share records with hierarchy. + /// + /// + /// + public IEnumerable GetShares(FileEntry entry) + { + if (entry == null) return new List(); + + var files = new List(); + var foldersInt = new List(); + + SelectFilesAndFoldersForShare(entry, files, null, foldersInt); + return SaveFilesAndFoldersForShare(files, foldersInt); + } + + private void SelectFilesAndFoldersForShare(FileEntry entry, ICollection files, ICollection folders, ICollection foldersInt) + { + object folderId; + if (entry.FileEntryType == FileEntryType.File) + { + var fileId = MappingID(entry.ID); + folderId = ((File)entry).FolderID; + if (!files.Contains(fileId.ToString())) files.Add(fileId.ToString()); + } + else + { + folderId = entry.ID; + } + + if (foldersInt != null && int.TryParse(folderId.ToString(), out var folderIdInt) && !foldersInt.Contains(folderIdInt)) foldersInt.Add(folderIdInt); + + if (folders != null) folders.Add(MappingID(folderId).ToString()); + } + + private IEnumerable SaveFilesAndFoldersForShare(List files, List folders) + { + var q = Query(FilesDbContext.Security) + .Join(FilesDbContext.Tree, r => r.EntryId, a => a.ParentId.ToString(), (security, tree) => new SecurityTreeRecord { DbFilesSecurity = security, DbFolderTree = tree }) + .Where(r => folders.Any(f => f == r.DbFolderTree.FolderId)) + .Where(r => r.DbFilesSecurity.EntryType == FileEntryType.Folder) + .ToList(); + + if (0 < files.Count) + { + var q1 = GetQuery(r => files.Any(f => f == r.EntryId) && r.EntryType == FileEntryType.File) + .Select(r => new SecurityTreeRecord { DbFilesSecurity = r }) + .ToList(); + q = q.Union(q1).ToList(); + } + + return q.Select(ToFileShareRecord) + .OrderBy(r => r.Level) + .ThenByDescending(r => r.Share, new FileShareRecord.ShareComparer()) + .ToList(); + } + + public void RemoveSubject(Guid subject) + { + using var tr = FilesDbContext.Database.BeginTransaction(); + + var toDelete1 = FilesDbContext.Security.Where(r => r.Subject == subject); + var toDelete2 = FilesDbContext.Security.Where(r => r.Owner == subject); + + FilesDbContext.RemoveRange(toDelete1); + FilesDbContext.SaveChanges(); + + FilesDbContext.RemoveRange(toDelete2); + FilesDbContext.SaveChanges(); + + tr.Commit(); + } + + private IQueryable GetQuery(Expression> where = null) + { + var q = Query(FilesDbContext.Security); + if (q != null) + { + q = q.Where(where); + } + return q; + } + + protected List FromQuery(IQueryable filesSecurities) + { + return filesSecurities + .ToList() + .Select(ToFileShareRecord) + .ToList(); + } + + protected List FromQuery(IQueryable filesSecurities) + { + return filesSecurities + .ToList() + .Select(ToFileShareRecord) + .ToList(); + } + + private FileShareRecord ToFileShareRecord(DbFilesSecurity r) => new FileShareRecord + { + Tenant = r.TenantId, + EntryId = MappingID(r.EntryId), + EntryType = r.EntryType, + Subject = r.Subject, + Owner = r.Owner, + Share = r.Security + }; + private FileShareRecord ToFileShareRecord(SecurityTreeRecord r) + { + var result = ToFileShareRecord(r.DbFilesSecurity); + result.Level = r.DbFolderTree?.Level ?? -1; + return result; + } + } + + internal class SecurityTreeRecord + { + public DbFilesSecurity DbFilesSecurity { get; set; } + public DbFolderTree DbFolderTree { get; set; } + } + + public static class SecurityDaoExtention + { + public static DIHelper AddSecurityDaoService(this DIHelper services) + { + services.TryAddScoped(); + + return services + .AddUserManagerService() + .AddFilesDbContextService() + .AddTenantManagerService() + .AddTenantUtilService() + .AddSetupInfo() + .AddTenantExtraService() + .AddTenantStatisticsProviderService() + .AddCoreBaseSettingsService() + .AddCoreConfigurationService() + .AddSettingsManagerService() + .AddAuthContextService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/Dao/TeamlabDao/TagDao.cs b/products/ASC.Files/Server/Core/Dao/TeamlabDao/TagDao.cs new file mode 100644 index 0000000000..dbcc17510a --- /dev/null +++ b/products/ASC.Files/Server/Core/Dao/TeamlabDao/TagDao.cs @@ -0,0 +1,652 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; + +using ASC.Common; +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.Core.Common.Settings; +using ASC.Core.Tenants; +using ASC.Files.Core.EF; +using ASC.Web.Studio.Core; +using ASC.Web.Studio.UserControls.Statistics; +using ASC.Web.Studio.Utility; + +namespace ASC.Files.Core.Data +{ + internal class TagDao : AbstractDao, ITagDao + { + private static readonly object syncRoot = new object(); + + public TagDao( + UserManager userManager, + DbContextManager dbContextManager, + TenantManager tenantManager, + TenantUtil tenantUtil, + SetupInfo setupInfo, + TenantExtra tenantExtra, + TenantStatisticsProvider tenantStatisticProvider, + CoreBaseSettings coreBaseSettings, + CoreConfiguration coreConfiguration, + SettingsManager settingsManager, + AuthContext authContext, + IServiceProvider serviceProvider) + : base(dbContextManager, + userManager, + tenantManager, + tenantUtil, + setupInfo, + tenantExtra, + tenantStatisticProvider, + coreBaseSettings, + coreConfiguration, + settingsManager, + authContext, + serviceProvider) + { + } + + public IEnumerable GetTags(TagType tagType, IEnumerable fileEntries) + { + var filesId = fileEntries.Where(e => e.FileEntryType == FileEntryType.File).Select(e => MappingID(e.ID).ToString()).ToList(); + var foldersId = fileEntries.Where(e => e.FileEntryType == FileEntryType.Folder).Select(e => MappingID(e.ID).ToString()).ToList(); + + var q = Query(FilesDbContext.Tag) + .Join(FilesDbContext.TagLink, r => r.Id, l => l.TagId, (tag, link) => new TagLinkData { Tag = tag, Link = link }) + .Where(r => r.Link.TenantId == r.Tag.TenantId) + .Where(r => r.Tag.Flag == tagType) + .Where(r => r.Link.EntryType == FileEntryType.File && filesId.Any(f => r.Link.EntryId == f) + || r.Link.EntryType == FileEntryType.Folder && foldersId.Any(f => r.Link.EntryId == f)); + + return FromQuery(q); + } + + public IEnumerable GetTags(object entryID, FileEntryType entryType, TagType tagType) + { + var q = Query(FilesDbContext.Tag) + .Join(FilesDbContext.TagLink, r => r.Id, l => l.TagId, (tag, link) => new TagLinkData { Tag = tag, Link = link }) + .Where(r => r.Link.TenantId == r.Tag.TenantId) + .Where(r => r.Link.EntryType == entryType) + .Where(r => r.Link.EntryId == MappingID(entryID).ToString()) + .Where(r => r.Tag.Flag == tagType); + + return FromQuery(q); + } + + public IEnumerable GetTags(string[] names, TagType tagType) + { + if (names == null) throw new ArgumentNullException("names"); + + var q = Query(FilesDbContext.Tag) + .Join(FilesDbContext.TagLink, r => r.Id, l => l.TagId, (tag, link) => new TagLinkData { Tag = tag, Link = link }) + .Where(r => r.Link.TenantId == r.Tag.TenantId) + .Where(r => r.Tag.Owner == Guid.Empty) + .Where(r => names.Any(n => r.Tag.Name == n)) + .Where(r => r.Tag.Flag == tagType); + + return FromQuery(q); + } + + public IEnumerable GetTags(string name, TagType tagType) + { + if (string.IsNullOrEmpty(name)) throw new ArgumentNullException("name"); + + return GetTags(new[] { name }, tagType); + } + + public IEnumerable GetTags(Guid owner, TagType tagType) + { + var q = + Query(FilesDbContext.Tag) + .Join(FilesDbContext.TagLink, r => r.Id, l => l.TagId, (tag, link) => new TagLinkData { Tag = tag, Link = link }) + .Where(r => r.Link.TenantId == r.Tag.TenantId) + .Where(r => r.Tag.Flag == tagType); + + if (owner != Guid.Empty) + { + q = q.Where(r => r.Tag.Owner == owner); + } + + return FromQuery(q); + } + + public IEnumerable SaveTags(IEnumerable tags) + { + var result = new List(); + + if (tags == null) return result; + + tags = tags.Where(x => x != null && !x.EntryId.Equals(null) && !x.EntryId.Equals(0)).ToArray(); + + if (!tags.Any()) return result; + + lock (syncRoot) + { + using var tx = FilesDbContext.Database.BeginTransaction(); + DeleteTagsBeforeSave(); + + var createOn = TenantUtil.DateTimeToUtc(TenantUtil.DateTimeNow()); + var cacheTagId = new Dictionary(); + + result.AddRange(tags.Select(t => SaveTag(t, cacheTagId, createOn))); + + tx.Commit(); + } + + return result; + } + + public IEnumerable SaveTags(Tag tag) + { + var result = new List(); + + if (tag == null) return result; + + if (tag.EntryId.Equals(null) || tag.EntryId.Equals(0)) return result; + + lock (syncRoot) + { + using var tx = FilesDbContext.Database.BeginTransaction(); + DeleteTagsBeforeSave(); + + var createOn = TenantUtil.DateTimeToUtc(TenantUtil.DateTimeNow()); + var cacheTagId = new Dictionary(); + + result.Add(SaveTag(tag, cacheTagId, createOn)); + + tx.Commit(); + } + + return result; + } + + private void DeleteTagsBeforeSave() + { + var mustBeDeleted = + Query(FilesDbContext.Tag) + .Join(FilesDbContext.TagLink, r => r.Id, l => l.TagId, (tag, link) => new TagLinkData { Tag = tag, Link = link }) + .Where(r => r.Link.TenantId == r.Tag.TenantId) + .Where(r => r.Tag.Flag == TagType.New && r.Link.CreateOn <= TenantUtil.DateTimeNow().AddMonths(-1)); + + foreach (var row in mustBeDeleted) + { + var linksToRemove = Query(FilesDbContext.TagLink) + .Where(r => r.TagId == row.Link.TagId) + .Where(r => r.EntryId == row.Link.EntryId) + .Where(r => r.EntryType == row.Link.EntryType); + FilesDbContext.TagLink.RemoveRange(linksToRemove); + } + + FilesDbContext.SaveChanges(); + + var tagsToRemove = Query(FilesDbContext.Tag) + .Where(r => !Query(FilesDbContext.TagLink).Where(a => a.TagId == r.Id).Any()); + + FilesDbContext.Tag.RemoveRange(tagsToRemove); + FilesDbContext.SaveChanges(); + } + + private Tag SaveTag(Tag t, Dictionary cacheTagId, DateTime createOn) + { + var cacheTagIdKey = string.Join("/", new[] { TenantID.ToString(), t.Owner.ToString(), t.TagName, ((int)t.TagType).ToString(CultureInfo.InvariantCulture) }); + + if (!cacheTagId.TryGetValue(cacheTagIdKey, out var id)) + { + id = FilesDbContext.Tag + .Where(r => r.Owner == t.Owner) + .Where(r => r.Name == t.TagName) + .Where(r => r.Flag == t.TagType) + .Select(r => r.Id) + .FirstOrDefault(); + + if (id == 0) + { + var toAdd = new DbFilesTag + { + Id = 0, + Name = t.TagName, + Owner = t.Owner, + Flag = t.TagType, + TenantId = TenantID + }; + + toAdd = FilesDbContext.Tag.Add(toAdd).Entity; + id = toAdd.Id; + } + + cacheTagId.Add(cacheTagIdKey, id); + } + + t.Id = id; + + var linkToInsert = new DbFilesTagLink + { + TenantId = TenantID, + TagId = id, + EntryId = MappingID(t.EntryId, true).ToString(), + EntryType = t.EntryType, + CreateBy = AuthContext.CurrentAccount.ID, + CreateOn = createOn, + TagCount = t.Count + }; + + FilesDbContext.AddOrUpdate(r => r.TagLink, linkToInsert); + FilesDbContext.SaveChanges(); + + return t; + } + + public void UpdateNewTags(IEnumerable tags) + { + if (tags == null || !tags.Any()) return; + + lock (syncRoot) + { + using (var tx = FilesDbContext.Database.BeginTransaction()) + { + var createOn = TenantUtil.DateTimeToUtc(TenantUtil.DateTimeNow()); + + foreach (var tag in tags) + { + UpdateNewTagsInDb(tag, createOn); + } + tx.Commit(); + } + } + } + + public void UpdateNewTags(Tag tag) + { + if (tag == null) return; + + lock (syncRoot) + { + var createOn = TenantUtil.DateTimeToUtc(TenantUtil.DateTimeNow()); + + UpdateNewTagsInDb(tag, createOn); + } + } + + private void UpdateNewTagsInDb(Tag tag, DateTime createOn) + { + if (tag == null) return; + + var forUpdate = Query(FilesDbContext.TagLink) + .Where(r => r.TagId == tag.Id) + .Where(r => r.EntryType == tag.EntryType) + .Where(r => r.EntryId == MappingID(tag.EntryId).ToString()); + + foreach (var f in forUpdate) + { + f.CreateBy = AuthContext.CurrentAccount.ID; + f.CreateOn = createOn; + f.TagCount = tag.Count; + } + + FilesDbContext.SaveChanges(); + } + + public void RemoveTags(IEnumerable tags) + { + if (tags == null || !tags.Any()) return; + + lock (syncRoot) + { + using (var tx = FilesDbContext.Database.BeginTransaction()) + { + foreach (var t in tags) + { + RemoveTagInDb(t); + } + tx.Commit(); + } + } + } + + public void RemoveTags(Tag tag) + { + if (tag == null) return; + + lock (syncRoot) + { + using (var tx = FilesDbContext.Database.BeginTransaction()) + { + RemoveTagInDb(tag); + + tx.Commit(); + } + } + } + + private void RemoveTagInDb(Tag tag) + { + if (tag == null) return; + + var id = Query(FilesDbContext.Tag) + .Where(r => r.Name == tag.TagName) + .Where(r => r.Owner == tag.Owner) + .Where(r => r.Flag == tag.TagType) + .Select(r => r.Id) + .FirstOrDefault(); + + if (id != 0) + { + var toDelete = Query(FilesDbContext.TagLink) + .Where(r => r.TagId == id) + .Where(r => r.EntryId == MappingID(tag.EntryId).ToString()) + .Where(r => r.EntryType == tag.EntryType); + + FilesDbContext.TagLink.RemoveRange(toDelete); + FilesDbContext.SaveChanges(); + + var count = Query(FilesDbContext.TagLink).Where(r => r.TagId == id).Count(); + if (count == 0) + { + var tagToDelete = Query(FilesDbContext.Tag).Where(r => r.Id == id); + FilesDbContext.Tag.RemoveRange(tagToDelete); + FilesDbContext.SaveChanges(); + } + } + } + + public IEnumerable GetNewTags(Guid subject, FileEntry fileEntry) + { + return GetNewTags(subject, new List(1) { fileEntry }); + } + + public IEnumerable GetNewTags(Guid subject, IEnumerable fileEntries) + { + List result; + + var tags = fileEntries.Select(r => new DbFilesTagLink + { + TenantId = TenantID, + EntryId = MappingID(r.ID).ToString(), + EntryType = (r.FileEntryType == FileEntryType.File) ? FileEntryType.File : FileEntryType.Folder + }) + .ToList(); + + var entryIds = tags.Select(r => r.EntryId).ToList(); + var entryTypes = tags.Select(r => (int)r.EntryType).ToList(); + + var sqlQuery = Query(FilesDbContext.Tag) + .Join(FilesDbContext.TagLink, r => r.Id, l => l.TagId, (tag, link) => new TagLinkData { Tag = tag, Link = link }) + .Where(r => r.Link.TenantId == r.Tag.TenantId) + .Where(r => r.Tag.Flag == TagType.New) + .Where(x => x.Link.EntryId != null) + //.Where(r => tags.Any(t => t.TenantId == r.Link.TenantId && t.EntryId == r.Link.EntryId && t.EntryType == (int)r.Link.EntryType)); ; + .Where(r => entryIds.Any(t => t == r.Link.EntryId) && entryTypes.Any(t => t == (int)r.Link.EntryType)); + + if (subject != Guid.Empty) + { + sqlQuery = sqlQuery.Where(r => r.Tag.Owner == subject); + } + + result = FromQuery(sqlQuery); + + return result; + } + + public IEnumerable GetNewTags(Guid subject, Folder parentFolder, bool deepSearch) + { + if (parentFolder == null || parentFolder.ID == null) + throw new ArgumentException("folderId"); + + var result = new List(); + + var monitorFolderIds = new[] { parentFolder.ID }.AsEnumerable(); + + var getBaseSqlQuery = new Func>(() => + { + var fnResult = Query(FilesDbContext.Tag) + .Join(FilesDbContext.TagLink, r => r.Id, l => l.TagId, (tag, link) => new TagLinkData { Tag = tag, Link = link }) + .Where(r => r.Link.TenantId == r.Tag.TenantId) + .Where(r => r.Tag.Flag == TagType.New) + .Distinct(); + + if (subject != Guid.Empty) + { + fnResult = fnResult.Where(r => r.Tag.Owner == subject); + } + + return fnResult; + }); + + var tempTags = Enumerable.Empty(); + + if (parentFolder.FolderType == FolderType.SHARE) + { + var shareQuery = + new Func>(() => getBaseSqlQuery().Where( + r => FilesDbContext.Security + .Where(a => a.TenantId == r.Link.TenantId) + .Where(a => a.EntryId == r.Link.EntryId) + .Where(a => a.EntryType == r.Link.EntryType) + .Any())); + + var tmpShareFileTags = + shareQuery() + .Join(FilesDbContext.Files, r => r.Link.EntryId, f => f.Id.ToString(), (tagLink, file) => new { tagLink, file }) + .Where(r => r.file.TenantId == r.tagLink.Link.TenantId) + .Where(r => r.file.CreateBy != subject) + .Where(r => r.tagLink.Link.EntryType == FileEntryType.File) + .Select(r => new + { + r.tagLink, + root = FilesDbContext.Folders + .Join(FilesDbContext.Tree, a => a.Id, b => b.ParentId, (folder, tree) => new { folder, tree }) + .Where(x => x.folder.TenantId == r.file.TenantId) + .Where(x => x.tree.FolderId == r.file.FolderId) + .OrderByDescending(r => r.tree.Level) + .Select(r => r.folder) + .FirstOrDefault() + }) + .Where(r => r.root.FolderType == FolderType.USER) + .Select(r => r.tagLink); + + tempTags = tempTags.Concat(FromQuery(tmpShareFileTags)); + + + var tmpShareFolderTags = + shareQuery() + .Join(FilesDbContext.Folders, r => r.Link.EntryId, f => f.Id.ToString(), (tagLink, folder) => new { tagLink, folder }) + .Where(r => r.folder.TenantId == r.tagLink.Link.TenantId) + .Where(r => r.folder.CreateBy != subject) + .Where(r => r.tagLink.Link.EntryType == FileEntryType.Folder) + .Select(r => new + { + r.tagLink, + root = FilesDbContext.Folders + .Join(FilesDbContext.Tree, a => a.Id, b => b.ParentId, (folder, tree) => new { folder, tree }) + .Where(x => x.folder.TenantId == r.folder.TenantId) + .Where(x => x.tree.FolderId == r.folder.ParentId) + .OrderByDescending(r => r.tree.Level) + .Select(r => r.folder) + .FirstOrDefault() + }) + .Where(r => r.root.FolderType == FolderType.USER) + .Select(r => r.tagLink); + + tempTags = tempTags.Concat(FromQuery(tmpShareFolderTags)); + + var tmpShareSboxTags = + shareQuery() + .Join(FilesDbContext.ThirdpartyIdMapping, r => r.Link.EntryId, r => r.HashId, (tagLink, mapping) => new { tagLink, mapping }) + .Where(r => r.mapping.TenantId == r.tagLink.Link.TenantId) + .Join(FilesDbContext.ThirdpartyAccount, r => r.mapping.TenantId, r => r.TenantId, (tagLinkMapping, account) => new { tagLinkMapping.tagLink, tagLinkMapping.mapping, account }) + .Where(r => r.account.UserId != subject) + .Where(r => r.account.FolderType == FolderType.USER) + .Where(r => + r.mapping.Id.StartsWith($"'sbox-'{r.account.Id}") || + r.mapping.Id.StartsWith($"'box-'{r.account.Id}") || + r.mapping.Id.StartsWith($"'dropbox-'{r.account.Id}") || + r.mapping.Id.StartsWith($"'spoint-'{r.account.Id}") || + r.mapping.Id.StartsWith($"'drive-'{r.account.Id}") || + r.mapping.Id.StartsWith($"'onedrive-'{r.account.Id}") + ) + .Select(r => r.tagLink); + + tempTags = tempTags.Concat(FromQuery(tmpShareSboxTags)); + } + else if (parentFolder.FolderType == FolderType.Projects) + { + var q = getBaseSqlQuery() + .Join(FilesDbContext.BunchObjects, r => r.Link.TenantId, r => r.TenantId, (tagLink, bunch) => new { tagLink, bunch }) + .Where(r => r.bunch.LeftNode == r.tagLink.Link.EntryId) + .Where(r => r.tagLink.Link.EntryType == FileEntryType.Folder) + .Where(r => r.bunch.RightNode.StartsWith("projects/project/")) + .Select(r => r.tagLink); + tempTags = tempTags.Concat(FromQuery(q)); + } + + if (tempTags.Any()) + { + if (!deepSearch) return tempTags; + + monitorFolderIds = monitorFolderIds.Concat(tempTags.Where(x => x.EntryType == FileEntryType.Folder).Select(x => x.EntryId)); + result.AddRange(tempTags); + } + + var monitorFolderIdsInt = monitorFolderIds.Select(r => Convert.ToInt32(r)).ToList(); + var subFoldersSqlQuery = + FilesDbContext.Tree + .Where(r => monitorFolderIdsInt.Any(a => r.ParentId == a)); + + if (!deepSearch) + { + subFoldersSqlQuery = subFoldersSqlQuery.Where(r => r.Level == 1); + } + + monitorFolderIds = monitorFolderIds.Concat(subFoldersSqlQuery.Select(r => r.FolderId).ToList().ConvertAll(r => (object)r)); + + var monitorFolderIdsStrings = monitorFolderIds.Select(r => r.ToString()).ToList(); + + var newTagsForFolders = getBaseSqlQuery() + .Where(r => monitorFolderIdsStrings.Any(a => r.Link.EntryId == a)) + .Where(r => r.Link.EntryType == FileEntryType.Folder); + + result.AddRange(FromQuery(newTagsForFolders)); + + var where = (deepSearch ? monitorFolderIds.ToArray() : new[] { parentFolder.ID }) + .Select(r => r.ToString()) + .ToList(); + + var newTagsForFiles = + getBaseSqlQuery() + .Join(FilesDbContext.Files, r => r.Link.EntryId, r => r.Id.ToString(), (tagLink, file) => new { tagLink, file }) + .Where(r => r.file.TenantId == r.tagLink.Link.TenantId) + .Where(r => where.Any(a => r.file.FolderId.ToString() == a)) + .Where(r => r.tagLink.Link.EntryType == FileEntryType.File) + .Select(r => r.tagLink); + + result.AddRange(FromQuery(newTagsForFiles)); + + if (parentFolder.FolderType == FolderType.USER || parentFolder.FolderType == FolderType.COMMON) + { + var folderType = parentFolder.FolderType; + + var querySelect = FilesDbContext.ThirdpartyAccount + .Where(r => r.TenantId == TenantID) + .Where(r => r.FolderType == folderType); + + if (folderType == FolderType.USER) + { + querySelect = querySelect.Where(r => r.UserId == subject); + } + + var folderIds = querySelect.Select(r => r.Id).ToList(); + var thirdpartyFolderIds = folderIds.ConvertAll(r => "sbox-" + r) + .Concat(folderIds.ConvertAll(r => $"box-{r}")) + .Concat(folderIds.ConvertAll(r => $"dropbox-{r}")) + .Concat(folderIds.ConvertAll(r => $"spoint-{r}")) + .Concat(folderIds.ConvertAll(r => $"drive-{r}")) + .Concat(folderIds.ConvertAll(r => $"onedrive-{r}")); + + var newTagsForSBox = getBaseSqlQuery() + .Join(FilesDbContext.ThirdpartyIdMapping, r => r.Link.EntryId, r => r.HashId, (tagLink, mapping) => new { tagLink, mapping }) + .Where(r => r.mapping.TenantId == r.tagLink.Link.TenantId) + .Where(r => thirdpartyFolderIds.Any(a => r.mapping.Id == a)) + .Where(r => r.tagLink.Tag.Owner == subject) + .Where(r => r.tagLink.Link.EntryType == FileEntryType.Folder) + .Select(r => r.tagLink); + + result.AddRange(FromQuery(newTagsForSBox)); + } + + return result; + } + + protected List FromQuery(IQueryable dbFilesTags) + { + return dbFilesTags + .ToList() + .Select(ToTag) + .ToList(); + } + + private Tag ToTag(TagLinkData r) + { + var result = new Tag(r.Tag.Name, r.Tag.Flag, r.Tag.Owner, null, r.Link.TagCount) + { + EntryId = MappingID(r.Link.EntryId), + EntryType = r.Link.EntryType, + Id = r.Tag.Id, + }; + + return result; + } + } + + public class TagLinkData + { + public DbFilesTag Tag { get; set; } + + public DbFilesTagLink Link { get; set; } + } + public static class TagDaoExtention + { + public static DIHelper AddTagDaoService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddUserManagerService() + .AddFilesDbContextService() + .AddTenantManagerService() + .AddTenantUtilService() + .AddSetupInfo() + .AddTenantExtraService() + .AddTenantStatisticsProviderService() + .AddCoreBaseSettingsService() + .AddCoreConfigurationService() + .AddSettingsManagerService() + .AddAuthContextService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/EF/DbEncryptedData.cs b/products/ASC.Files/Server/Core/EF/DbEncryptedData.cs new file mode 100644 index 0000000000..c3ed00f233 --- /dev/null +++ b/products/ASC.Files/Server/Core/EF/DbEncryptedData.cs @@ -0,0 +1,40 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +using ASC.Core.Common.EF; + +using Microsoft.EntityFrameworkCore; + +namespace ASC.Files.Core.EF +{ + [Table("encrypted_data")] + public class DbEncryptedData : BaseEntity, IDbFile + { + [Column("public_key")] + public string PublicKey { get; set; } + + [Column("file_hash")] + public string FileHash { get; set; } + + public string Data { get; set; } + + [Column("create_on")] + public DateTime CreateOn { get; set; } + + [Column("tenant_id")] + public int TenantId { get; set; } + + public override object[] GetKeys() => new object[] { PublicKey, FileHash }; + } + + public static class DbEncryptedDataExtension + { + public static ModelBuilder AddDbEncryptedData(this ModelBuilder modelBuilder) + { + modelBuilder.Entity() + .HasKey(c => new { c.PublicKey, c.FileHash }); + + return modelBuilder; + } + } +} diff --git a/products/ASC.Files/Server/Core/EF/DbFile.cs b/products/ASC.Files/Server/Core/EF/DbFile.cs new file mode 100644 index 0000000000..8169178086 --- /dev/null +++ b/products/ASC.Files/Server/Core/EF/DbFile.cs @@ -0,0 +1,75 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +using ASC.Core.Common.EF; + +using Microsoft.EntityFrameworkCore; + +namespace ASC.Files.Core.EF +{ + [Table("files_file")] + public class DbFile : BaseEntity, IDbFile, IDbSearch + { + public int Id { get; set; } + public int Version { get; set; } + + [Column("version_group")] + public int VersionGroup { get; set; } + + [Column("current_version")] + public bool CurrentVersion { get; set; } + + [Column("folder_id")] + public int FolderId { get; set; } + + public string Title { get; set; } + + [Column("content_length")] + public long ContentLength { get; set; } + + [Column("file_status")] + public int FileStatus { get; set; } + + [Column("category")] + public int Category { get; set; } + + [Column("create_by")] + public Guid CreateBy { get; set; } + + [Column("create_on")] + public DateTime CreateOn { get; set; } + + [Column("modified_by")] + public Guid ModifiedBy { get; set; } + + [Column("modified_on")] + public DateTime ModifiedOn { get; set; } + + [Column("tenant_id")] + public int TenantId { get; set; } + + [Column("converted_type")] + public string ConvertedType { get; set; } + + public string Comment { get; set; } + public string Changes { get; set; } + public bool Encrypted { get; set; } + public ForcesaveType Forcesave { get; set; } + + public override object[] GetKeys() + { + return new object[] { TenantId, Id, Version }; + } + } + + public static class DbFileExtension + { + public static ModelBuilder AddDbFiles(this ModelBuilder modelBuilder) + { + modelBuilder.Entity() + .HasKey(c => new { c.TenantId, c.Id, c.Version }); + + return modelBuilder; + } + } +} diff --git a/products/ASC.Files/Server/Core/EF/DbFilesBunchObjects.cs b/products/ASC.Files/Server/Core/EF/DbFilesBunchObjects.cs new file mode 100644 index 0000000000..7805749706 --- /dev/null +++ b/products/ASC.Files/Server/Core/EF/DbFilesBunchObjects.cs @@ -0,0 +1,34 @@ +using System.ComponentModel.DataAnnotations.Schema; + +using ASC.Core.Common.EF; + +using Microsoft.EntityFrameworkCore; + +namespace ASC.Files.Core.EF +{ + [Table("files_bunch_objects")] + public class DbFilesBunchObjects : BaseEntity, IDbFile + { + [Column("tenant_id")] + public int TenantId { get; set; } + + [Column("right_node")] + public string RightNode { get; set; } + + [Column("left_node")] + public string LeftNode { get; set; } + + public override object[] GetKeys() => new object[] { TenantId, RightNode }; + } + + public static class DbFilesBunchObjectsExtension + { + public static ModelBuilder AddDbFilesBunchObjects(this ModelBuilder modelBuilder) + { + modelBuilder.Entity() + .HasKey(c => new { c.TenantId, c.RightNode }); + + return modelBuilder; + } + } +} diff --git a/products/ASC.Files/Server/Core/EF/DbFilesSecurity.cs b/products/ASC.Files/Server/Core/EF/DbFilesSecurity.cs new file mode 100644 index 0000000000..9caf5ed386 --- /dev/null +++ b/products/ASC.Files/Server/Core/EF/DbFilesSecurity.cs @@ -0,0 +1,44 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +using ASC.Core.Common.EF; +using ASC.Files.Core.Security; + +using Microsoft.EntityFrameworkCore; + +namespace ASC.Files.Core.EF +{ + [Table("files_security")] + public class DbFilesSecurity : BaseEntity, IDbFile + { + [Column("tenant_id")] + public int TenantId { get; set; } + + [Column("entry_id")] + public string EntryId { get; set; } + + [Column("entry_type")] + public FileEntryType EntryType { get; set; } + + public Guid Subject { get; set; } + public Guid Owner { get; set; } + public FileShare Security { get; set; } + public DateTime TimeStamp { get; set; } + + public override object[] GetKeys() + { + return new object[] { TenantId, EntryType, EntryId, Owner }; + } + } + + public static class DbFilesSecurityExtension + { + public static ModelBuilder AddDbFilesSecurity(this ModelBuilder modelBuilder) + { + modelBuilder.Entity() + .HasKey(c => new { c.TenantId, c.EntryType, c.EntryId, c.Owner }); + + return modelBuilder; + } + } +} diff --git a/products/ASC.Files/Server/Core/EF/DbFilesTag.cs b/products/ASC.Files/Server/Core/EF/DbFilesTag.cs new file mode 100644 index 0000000000..9490163de4 --- /dev/null +++ b/products/ASC.Files/Server/Core/EF/DbFilesTag.cs @@ -0,0 +1,20 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ASC.Files.Core.EF +{ + [Table("files_tag")] + public class DbFilesTag : IDbFile + { + [Column("tenant_id")] + public int TenantId { get; set; } + + public int Id { get; set; } + + public string Name { get; set; } + + public Guid Owner { get; set; } + + public TagType Flag { get; set; } + } +} diff --git a/products/ASC.Files/Server/Core/EF/DbFilesTagLink.cs b/products/ASC.Files/Server/Core/EF/DbFilesTagLink.cs new file mode 100644 index 0000000000..4415bcf15d --- /dev/null +++ b/products/ASC.Files/Server/Core/EF/DbFilesTagLink.cs @@ -0,0 +1,50 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +using ASC.Core.Common.EF; + +using Microsoft.EntityFrameworkCore; + +namespace ASC.Files.Core.EF +{ + [Table("files_tag_link")] + public class DbFilesTagLink : BaseEntity, IDbFile + { + [Column("tenant_id")] + public int TenantId { get; set; } + + [Column("tag_id")] + public int TagId { get; set; } + + [Column("entry_type")] + public FileEntryType EntryType { get; set; } + + [Column("entry_id")] + public string EntryId { get; set; } + + [Column("create_by")] + public Guid CreateBy { get; set; } + + [Column("create_on")] + public DateTime CreateOn { get; set; } + + [Column("tag_count")] + public int TagCount { get; set; } + + public override object[] GetKeys() + { + return new object[] { TenantId, TagId, EntryId, EntryType }; + } + } + + public static class DbFilesTagLinkExtension + { + public static ModelBuilder AddDbFilesTagLink(this ModelBuilder modelBuilder) + { + modelBuilder.Entity() + .HasKey(c => new { c.TenantId, c.TagId, c.EntryId, c.EntryType }); + + return modelBuilder; + } + } +} diff --git a/products/ASC.Files/Server/Core/EF/DbFilesThirdpartyAccount.cs b/products/ASC.Files/Server/Core/EF/DbFilesThirdpartyAccount.cs new file mode 100644 index 0000000000..301cabaf5c --- /dev/null +++ b/products/ASC.Files/Server/Core/EF/DbFilesThirdpartyAccount.cs @@ -0,0 +1,37 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ASC.Files.Core.EF +{ + [Table("files_thirdparty_account")] + public class DbFilesThirdpartyAccount : IDbFile + { + public int Id { get; set; } + + public string Provider { get; set; } + + [Column("customer_title")] + public string CustomerTitle { get; set; } + + [Column("user_name")] + public string UserName { get; set; } + + public string Password { get; set; } + + public string Token { get; set; } + + [Column("user_id")] + public Guid UserId { get; set; } + + [Column("folder_type")] + public FolderType FolderType { get; set; } + + [Column("create_on")] + public DateTime CreateOn { get; set; } + + public string Url { get; set; } + + [Column("tenant_id")] + public int TenantId { get; set; } + } +} diff --git a/products/ASC.Files/Server/Core/EF/DbFilesThirdpartyApp.cs b/products/ASC.Files/Server/Core/EF/DbFilesThirdpartyApp.cs new file mode 100644 index 0000000000..e2f6eb69f5 --- /dev/null +++ b/products/ASC.Files/Server/Core/EF/DbFilesThirdpartyApp.cs @@ -0,0 +1,39 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +using ASC.Core.Common.EF; + +using Microsoft.EntityFrameworkCore; + +namespace ASC.Files.Core.EF +{ + [Table("files_thirdparty_app")] + public class DbFilesThirdpartyApp : BaseEntity, IDbFile + { + [Column("user_id")] + public Guid UserId { get; set; } + + public string App { get; set; } + + public string Token { get; set; } + + [Column("tenant_id")] + public int TenantId { get; set; } + + [Column("modified_on")] + public DateTime ModifiedOn { get; set; } + + public override object[] GetKeys() => new object[] { UserId, App }; + } + + public static class DbFilesThirdpartyAppExtension + { + public static ModelBuilder AddDbFilesThirdpartyApp(this ModelBuilder modelBuilder) + { + modelBuilder.Entity() + .HasKey(c => new { c.UserId, c.App }); + + return modelBuilder; + } + } +} diff --git a/products/ASC.Files/Server/Core/EF/DbFilesThirdpartyIdMapping.cs b/products/ASC.Files/Server/Core/EF/DbFilesThirdpartyIdMapping.cs new file mode 100644 index 0000000000..22d2cea11d --- /dev/null +++ b/products/ASC.Files/Server/Core/EF/DbFilesThirdpartyIdMapping.cs @@ -0,0 +1,32 @@ +using System.ComponentModel.DataAnnotations.Schema; + +using ASC.Core.Common.EF; + +using Microsoft.EntityFrameworkCore; + +namespace ASC.Files.Core.EF +{ + [Table("files_thirdparty_id_mapping")] + public class DbFilesThirdpartyIdMapping : BaseEntity, IDbFile + { + [Column("tenant_id")] + public int TenantId { get; set; } + + [Column("hash_id")] + public string HashId { get; set; } + public string Id { get; set; } + + public override object[] GetKeys() => new object[] { Id }; + } + + public static class DbDbFilesThirdpartyIdMapping + { + public static ModelBuilder AddDbFilesThirdpartyIdMapping(this ModelBuilder modelBuilder) + { + modelBuilder.Entity() + .HasKey(c => new { c.HashId }); + + return modelBuilder; + } + } +} diff --git a/products/ASC.Files/Server/Core/EF/DbFolder.cs b/products/ASC.Files/Server/Core/EF/DbFolder.cs new file mode 100644 index 0000000000..dd5563d64d --- /dev/null +++ b/products/ASC.Files/Server/Core/EF/DbFolder.cs @@ -0,0 +1,36 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ASC.Files.Core.EF +{ + [Table("files_folder")] + public class DbFolder : IDbFile, IDbSearch + { + public int Id { get; set; } + + [Column("parent_id")] + public int ParentId { get; set; } + + public string Title { get; set; } + + [Column("folder_type")] + public FolderType FolderType { get; set; } + + [Column("create_by")] + public Guid CreateBy { get; set; } + + [Column("create_on")] + public DateTime CreateOn { get; set; } + + [Column("modified_by")] + public Guid ModifiedBy { get; set; } + + [Column("modified_on")] + public DateTime ModifiedOn { get; set; } + + [Column("tenant_id")] + public int TenantId { get; set; } + public int FoldersCount { get; set; } + public int FilesCount { get; set; } + } +} diff --git a/products/ASC.Files/Server/Core/EF/DbFolderTree.cs b/products/ASC.Files/Server/Core/EF/DbFolderTree.cs new file mode 100644 index 0000000000..b1c4b43f4b --- /dev/null +++ b/products/ASC.Files/Server/Core/EF/DbFolderTree.cs @@ -0,0 +1,38 @@ +using System.ComponentModel.DataAnnotations.Schema; + +using ASC.Core.Common.EF; + +using Microsoft.EntityFrameworkCore; + +namespace ASC.Files.Core.EF +{ + + [Table("files_folder_tree")] + public class DbFolderTree : BaseEntity + { + [Column("folder_id")] + public int FolderId { get; set; } + + [Column("parent_id")] + public int ParentId { get; set; } + + [Column("level")] + public int Level { get; set; } + + public override object[] GetKeys() + { + return new object[] { ParentId, FolderId }; + } + } + + public static class DbFolderTreeExtension + { + public static ModelBuilder AddDbFolderTree(this ModelBuilder modelBuilder) + { + modelBuilder.Entity() + .HasKey(c => new { c.ParentId, c.FolderId }); + + return modelBuilder; + } + } +} diff --git a/products/ASC.Files/Server/Core/EF/FilesDbContext.cs b/products/ASC.Files/Server/Core/EF/FilesDbContext.cs new file mode 100644 index 0000000000..14dfb10f6c --- /dev/null +++ b/products/ASC.Files/Server/Core/EF/FilesDbContext.cs @@ -0,0 +1,43 @@ +using ASC.Common; +using ASC.Core.Common.EF; + +using Microsoft.EntityFrameworkCore; + +namespace ASC.Files.Core.EF +{ + public class FilesDbContext : BaseDbContext + { + public DbSet Files { get; set; } + public DbSet Folders { get; set; } + public DbSet Tree { get; set; } + public DbSet BunchObjects { get; set; } + public DbSet Security { get; set; } + public DbSet ThirdpartyIdMapping { get; set; } + public DbSet ThirdpartyAccount { get; set; } + public DbSet TagLink { get; set; } + public DbSet Tag { get; set; } + public DbSet ThirdpartyApp { get; set; } + public DbSet EncryptedData { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder + .AddDbFiles() + .AddDbFolderTree() + .AddDbFilesBunchObjects() + .AddDbFilesSecurity() + .AddDbFilesThirdpartyIdMapping() + .AddDbFilesTagLink() + .AddDbFilesThirdpartyApp() + .AddDbEncryptedData(); + } + } + + public static class FilesDbExtension + { + public static DIHelper AddFilesDbContextService(this DIHelper services) + { + return services.AddDbContextManagerService(); + } + } +} diff --git a/products/ASC.Files/Server/Core/EF/IDbFile.cs b/products/ASC.Files/Server/Core/EF/IDbFile.cs new file mode 100644 index 0000000000..3f06ee49d9 --- /dev/null +++ b/products/ASC.Files/Server/Core/EF/IDbFile.cs @@ -0,0 +1,12 @@ +namespace ASC.Files.Core.EF +{ + public interface IDbFile + { + public int TenantId { get; set; } + } + + public interface IDbSearch + { + public string Title { get; set; } + } +} diff --git a/products/ASC.Files/Server/Core/Entries/AuthData.cs b/products/ASC.Files/Server/Core/Entries/AuthData.cs new file mode 100644 index 0000000000..e3e335c2ae --- /dev/null +++ b/products/ASC.Files/Server/Core/Entries/AuthData.cs @@ -0,0 +1,61 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.Diagnostics; +using System.Runtime.Serialization; + +namespace ASC.Files.Core +{ + [DataContract(Name = "AuthData", Namespace = "")] + [DebuggerDisplay("{Login} {Password} {Token} {Url}")] + public class AuthData + { + [DataMember(Name = "login")] + public string Login { get; set; } + + [DataMember(Name = "password")] + public string Password { get; set; } + + [DataMember(Name = "token")] + public string Token { get; set; } + + [DataMember(Name = "url")] + public string Url { get; set; } + + public AuthData(string url = null, string login = null, string password = null, string token = null) + { + Url = url ?? ""; + Login = login ?? ""; + Password = password ?? ""; + Token = token ?? ""; + } + + public bool IsEmpty() + { + return string.IsNullOrEmpty((Url ?? "") + (Login ?? "") + (Password ?? "") + (Token ?? "")); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/Entries/ChunkedUploadSession.cs b/products/ASC.Files/Server/Core/Entries/ChunkedUploadSession.cs new file mode 100644 index 0000000000..3b5832e31e --- /dev/null +++ b/products/ASC.Files/Server/Core/Entries/ChunkedUploadSession.cs @@ -0,0 +1,112 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +using ASC.Common; +using ASC.Common.Logging; +using ASC.Core.ChunkedUploader; +using ASC.Web.Files.Utils; + +using Microsoft.Extensions.Options; + +namespace ASC.Files.Core +{ + [DebuggerDisplay("{Id} into {FolderId}")] + [Serializable] + public class ChunkedUploadSession : CommonChunkedUploadSession + { + public string FolderId { get; set; } + + public File File { get; set; } + + public bool Encrypted { get; set; } + + public ChunkedUploadSession(File file, long bytesTotal) : base(bytesTotal) + { + File = file; + } + + public override object Clone() + { + var clone = (ChunkedUploadSession)MemberwiseClone(); + clone.File = (File)File.Clone(); + return clone; + } + } + + public class ChunkedUploadSessionHelper + { + public EntryManager EntryManager { get; } + public ILog Logger { get; } + + public ChunkedUploadSessionHelper(IOptionsMonitor options, EntryManager entryManager) + { + EntryManager = entryManager; + Logger = options.CurrentValue; + } + + + public object ToResponseObject(ChunkedUploadSession session, bool appendBreadCrumbs = false) + { + var pathFolder = appendBreadCrumbs + ? EntryManager.GetBreadCrumbs(session.FolderId).Select(f => + { + //todo: check how? + if (f == null) + { + Logger.ErrorFormat("GetBreadCrumbs {0} with null", session.FolderId); + return string.Empty; + } + return f.ID; + }) + : new List { session.FolderId }; + + return new + { + id = session.Id, + path = pathFolder, + created = session.Created, + expired = session.Expired, + location = session.Location, + bytes_uploaded = session.BytesUploaded, + bytes_total = session.BytesTotal + }; + } + } + + public static class ChunkedUploadSessionHelperExtention + { + public static DIHelper AddChunkedUploadSessionHelperService(this DIHelper services) + { + services.TryAddScoped(); + return services.AddEntryManagerService(); + } + } +} diff --git a/products/ASC.Files/Server/Core/Entries/EditHistory.cs b/products/ASC.Files/Server/Core/Entries/EditHistory.cs new file mode 100644 index 0000000000..772b222630 --- /dev/null +++ b/products/ASC.Files/Server/Core/Entries/EditHistory.cs @@ -0,0 +1,257 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.Serialization; + +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Tenants; +using ASC.Core.Users; +using ASC.Files.Resources; +using ASC.Web.Core.Users; + +using Microsoft.Extensions.Options; + +using Newtonsoft.Json.Linq; + +namespace ASC.Files.Core +{ + [DataContract(Name = "editHistory", Namespace = "")] + [DebuggerDisplay("{ID} v{Version}")] + public class EditHistory + { + public EditHistory( + IOptionsMonitor options, + TenantUtil tenantUtil, + AuthContext authContext, + UserManager userManager, + DisplayUserSettingsHelper displayUserSettingsHelper) + { + Logger = options.CurrentValue; + TenantUtil = tenantUtil; + AuthContext = authContext; + UserManager = userManager; + DisplayUserSettingsHelper = displayUserSettingsHelper; + } + + public int ID; + [DataMember(Name = "key")] public string Key; + [DataMember(Name = "version")] public int Version; + [DataMember(Name = "versionGroup")] public int VersionGroup; + + [DataMember(Name = "user")] public EditHistoryAuthor ModifiedBy; + + [DataMember(Name = "changeshistory", EmitDefaultValue = false)] public string ChangesString; + + [DataMember(Name = "changes", EmitDefaultValue = false)] + public List Changes + { + get + { + var changes = new List(); + if (string.IsNullOrEmpty(ChangesString)) return changes; + + //new scheme + Exception newSchemeException = null; + try + { + var jObject = JObject.Parse(ChangesString); + ServerVersion = jObject.Value("serverVersion"); + + var jChanges = jObject.Value("changes"); + + changes = jChanges.Children() + .Select(jChange => + { + var jUser = jChange.Value("user"); + return new EditHistoryChanges(TenantUtil) + { + Date = jChange.Value("created"), + Author = new EditHistoryAuthor(AuthContext, UserManager, DisplayUserSettingsHelper) + { + Id = new Guid(jUser.Value("id") ?? Guid.Empty.ToString()), + Name = jUser.Value("name"), + }, + }; + }) + .ToList(); + return changes; + } + catch (Exception ex) + { + newSchemeException = ex; + } + + //old scheme + //todo: delete + try + { + var jChanges = JArray.Parse(ChangesString); + + changes = jChanges.Children() + .Select(jChange => + new EditHistoryChanges(TenantUtil) + { + Date = jChange.Value("date"), + Author = new EditHistoryAuthor(AuthContext, UserManager, DisplayUserSettingsHelper) + { + Id = new Guid(jChange.Value("userid") ?? Guid.Empty.ToString()), + Name = jChange.Value("username") + } + }) + .ToList(); + } + catch (Exception ex) + { + Logger.Error("DeSerialize new scheme exception", newSchemeException); + Logger.Error("DeSerialize old scheme exception", ex); + } + + return changes; + } + set { throw new NotImplementedException(); } + } + + public DateTime ModifiedOn; + + [DataMember(Name = "created")] + public string ModifiedOnString + { + get { return ModifiedOn.Equals(default) ? null : ModifiedOn.ToString("g"); } + set { throw new NotImplementedException(); } + } + + public ILog Logger { get; } + public TenantUtil TenantUtil { get; } + public AuthContext AuthContext { get; } + public UserManager UserManager { get; } + public DisplayUserSettingsHelper DisplayUserSettingsHelper { get; } + + [DataMember(Name = "serverVersion", EmitDefaultValue = false)] public string ServerVersion; + } + + [DataContract(Name = "user", Namespace = "")] + [DebuggerDisplay("{Id} {Name}")] + public class EditHistoryAuthor + { + public EditHistoryAuthor( + AuthContext authContext, + UserManager userManager, + DisplayUserSettingsHelper displayUserSettingsHelper) + { + AuthContext = authContext; + UserManager = userManager; + DisplayUserSettingsHelper = displayUserSettingsHelper; + } + + [DataMember(Name = "id")] public Guid Id; + + private string _name; + + [DataMember(Name = "name")] + public string Name + { + get + { + UserInfo user; + return + Id.Equals(AuthContext.CurrentAccount.ID) + ? FilesCommonResource.Author_Me + : Id.Equals(Guid.Empty) + || Id.Equals(ASC.Core.Configuration.Constants.Guest.ID) + || (user = UserManager.GetUsers(Id)).Equals(Constants.LostUser) + ? string.IsNullOrEmpty(_name) + ? FilesCommonResource.Guest + : _name + : user.DisplayUserName(false, DisplayUserSettingsHelper); + } + set { _name = value; } + } + + public AuthContext AuthContext { get; } + public UserManager UserManager { get; } + public DisplayUserSettingsHelper DisplayUserSettingsHelper { get; } + } + + [DataContract(Name = "change", Namespace = "")] + [DebuggerDisplay("{Author.Name}")] + public class EditHistoryChanges + { + public EditHistoryChanges(TenantUtil tenantUtil) + { + TenantUtil = tenantUtil; + } + + [DataMember(Name = "user")] public EditHistoryAuthor Author; + + private DateTime _date; + + [DataMember(Name = "created")] + public string Date + { + get { return _date.Equals(default) ? null : _date.ToString("g"); } + set + { + if (DateTime.TryParse(value, out _date)) + { + _date = TenantUtil.DateTimeFromUtc(_date); + } + } + } + + public TenantUtil TenantUtil { get; } + } + + [DataContract(Name = "data")] + [DebuggerDisplay("{Version}")] + public class EditHistoryData + { + [DataMember(Name = "changesUrl", EmitDefaultValue = false)] public string ChangesUrl; + + [DataMember(Name = "key")] public string Key; + + [DataMember(Name = "previous", EmitDefaultValue = false)] public EditHistoryUrl Previous; + + [DataMember(Name = "token", EmitDefaultValue = false)] public string Token; + + [DataMember(Name = "url")] public string Url; + + [DataMember(Name = "version")] public int Version; + } + + [DataContract(Name = "url")] + [DebuggerDisplay("{Key} - {Url}")] + public class EditHistoryUrl + { + [DataMember(Name = "key")] public string Key; + + [DataMember(Name = "url")] public string Url; + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/Entries/EncryptedData.cs b/products/ASC.Files/Server/Core/Entries/EncryptedData.cs new file mode 100644 index 0000000000..56794d10b9 --- /dev/null +++ b/products/ASC.Files/Server/Core/Entries/EncryptedData.cs @@ -0,0 +1,43 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.Runtime.Serialization; + +namespace ASC.Web.Files.Core.Entries +{ + [DataContract(Name = "encrypted_data", Namespace = "")] + public class EncryptedData + { + [DataMember(Name = "publicKey")] + public string PublicKey; + + [DataMember(Name = "fileHash")] + public string FileHash; + + [DataMember(Name = "data")] + public string Data; + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/Entries/EncryptionAddress.cs b/products/ASC.Files/Server/Core/Entries/EncryptionAddress.cs new file mode 100644 index 0000000000..bf91e98ad4 --- /dev/null +++ b/products/ASC.Files/Server/Core/Entries/EncryptionAddress.cs @@ -0,0 +1,79 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; + +using ASC.Common; +using ASC.Files.Core; +using ASC.Files.Core.Security; +using ASC.Web.Files.Services.WCFService; +using ASC.Web.Files.Utils; +using ASC.Web.Studio.Core; + +namespace ASC.Web.Files.Core.Entries +{ + [DataContract(Name = "account", Namespace = "")] + public class EncryptionAddress + { + [DataMember(Name = "address")] + public string Address; + + [DataMember(Name = "publicKey")] + public string PublicKey; + } + + public class EncryptionAddressHelper + { + public FileSharing FileSharing { get; } + public EncryptionLoginProvider EncryptionLoginProvider { get; } + + public EncryptionAddressHelper(FileSharing fileSharing, EncryptionLoginProvider encryptionLoginProvider) + { + FileSharing = fileSharing; + EncryptionLoginProvider = encryptionLoginProvider; + } + + public IEnumerable GetAddresses(string fileId) + { + var fileShares = FileSharing.GetSharedInfo(new ItemList { string.Format("file_{0}", fileId) }).ToList(); + fileShares = fileShares.Where(share => !share.SubjectGroup && !share.SubjectId.Equals(FileConstant.ShareLinkId) && share.Share == FileShare.ReadWrite).ToList(); + var accountsString = fileShares.Select(share => EncryptionLoginProvider.GetAddress(share.SubjectId)).Where(address => !string.IsNullOrEmpty(address)); + return accountsString; + } + } + public static class EncryptionAddressHelperExtension + { + public static DIHelper AddEncryptionAddressHelperService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddEncryptionLoginProviderService() + .AddFileSharingService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/Entries/File.cs b/products/ASC.Files/Server/Core/Entries/File.cs new file mode 100644 index 0000000000..7d82694d14 --- /dev/null +++ b/products/ASC.Files/Server/Core/Entries/File.cs @@ -0,0 +1,230 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Diagnostics; +using System.Runtime.Serialization; +using System.Text; + +using ASC.Web.Core.Files; +using ASC.Web.Files.Classes; +using ASC.Web.Files.Services.WCFService; +using ASC.Web.Files.Utils; +using ASC.Web.Studio.Core; + +namespace ASC.Files.Core +{ + [Flags] + [DataContract(Namespace = "")] + public enum FileStatus + { + [EnumMember] None = 0x0, + + [EnumMember] IsEditing = 0x1, + + [EnumMember] IsNew = 0x2, + + [EnumMember] IsConverting = 0x4, + + [EnumMember] IsOriginal = 0x8, + + [EnumMember] IsEditingAlone = 0x10 + } + + [Serializable] + [DataContract(Name = "file", Namespace = "")] + [DebuggerDisplay("{Title} ({ID} v{Version})")] + public class File : FileEntry + { + private FileStatus _status; + + public File(Global global, + FilesLinkUtility filesLinkUtility, + FileUtility fileUtility, + FileConverter fileConverter) + : base(global) + { + Version = 1; + VersionGroup = 1; + FileEntryType = FileEntryType.File; + FilesLinkUtility = filesLinkUtility; + FileUtility = fileUtility; + FileConverter = fileConverter; + } + + public object FolderID { get; set; } + + [DataMember(Name = "version")] + public int Version { get; set; } + + [DataMember(Name = "version_group")] + public int VersionGroup { get; set; } + + [DataMember(EmitDefaultValue = false, Name = "comment")] + public string Comment { get; set; } + + public string PureTitle + { + get { return base.Title; } + set { base.Title = value; } + } + + [DataMember(Name = "title", IsRequired = true)] + public override string Title + { + get + { + return string.IsNullOrEmpty(ConvertedType) + ? base.Title + : FileUtility.ReplaceFileExtension(base.Title, FileUtility.GetInternalExtension(base.Title)); + } + set { base.Title = value; } + } + + [DataMember(EmitDefaultValue = true, Name = "content_length", IsRequired = true)] + public long ContentLength { get; set; } + + [DataMember(EmitDefaultValue = false, Name = "content_length_string", IsRequired = true)] + public string ContentLengthString + { + get { return FileSizeComment.FilesSizeToString(ContentLength); } + set { } + } + + public FilterType FilterType + { + get + { + switch (FileUtility.GetFileTypeByFileName(Title)) + { + case FileType.Image: + return FilterType.ImagesOnly; + case FileType.Document: + return FilterType.DocumentsOnly; + case FileType.Presentation: + return FilterType.PresentationsOnly; + case FileType.Spreadsheet: + return FilterType.SpreadsheetsOnly; + case FileType.Archive: + return FilterType.ArchiveOnly; + case FileType.Audio: + case FileType.Video: + return FilterType.MediaOnly; + } + + return FilterType.None; + } + } + + [DataMember(EmitDefaultValue = false, Name = "file_status")] + public FileStatus FileStatus + { + get + { + if (FileTracker.IsEditing(ID)) + { + _status |= FileStatus.IsEditing; + } + + if (FileTracker.IsEditingAlone(ID)) + { + _status |= FileStatus.IsEditingAlone; + } + + if (FileConverter.IsConverting(this)) + { + _status |= FileStatus.IsConverting; + } + + return _status; + } + set { _status = value; } + } + + [DataMember(EmitDefaultValue = false, Name = "locked")] + public bool Locked { get; set; } + + [DataMember(EmitDefaultValue = false, Name = "locked_by")] + public string LockedBy { get; set; } + + public override bool IsNew + { + get { return (_status & FileStatus.IsNew) == FileStatus.IsNew; } + set + { + if (value) + _status |= FileStatus.IsNew; + else + _status ^= FileStatus.IsNew; + } + } + + [DataMember(EmitDefaultValue = false, Name = "encrypted")] + public bool Encrypted { get; set; } + + public ForcesaveType Forcesave { get; set; } + + public string DownloadUrl + { + get { return FilesLinkUtility.GetFileDownloadUrl(ID); } + } + + public string ConvertedType { get; set; } + + public string ConvertedExtension + { + get + { + if (string.IsNullOrEmpty(ConvertedType)) return FileUtility.GetFileExtension(Title); + + var curFileType = FileUtility.GetFileTypeByFileName(Title); + switch (curFileType) + { + case FileType.Image: + return ConvertedType.Trim('.') == "zip" ? ".pptt" : ConvertedType; + case FileType.Spreadsheet: + return ConvertedType.Trim('.') != "xlsx" ? ".xlst" : ConvertedType; + case FileType.Document: + return ConvertedType.Trim('.') == "zip" ? ".doct" : ConvertedType; + } + return ConvertedType; + } + } + + public object NativeAccessor { get; set; } + public FilesLinkUtility FilesLinkUtility { get; } + public FileUtility FileUtility { get; } + public FileConverter FileConverter { get; } + + public static string Serialize(File file) + { + using (var ms = new FileEntrySerializer().ToXml(file)) + { + return Encoding.UTF8.GetString(ms.ToArray()); + } + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/Entries/FileEntry.cs b/products/ASC.Files/Server/Core/Entries/FileEntry.cs new file mode 100644 index 0000000000..91b2ce4f5d --- /dev/null +++ b/products/ASC.Files/Server/Core/Entries/FileEntry.cs @@ -0,0 +1,170 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Runtime.Serialization; + +using ASC.Files.Core.Security; +using ASC.Web.Files.Classes; + +namespace ASC.Files.Core +{ + [DataContract(Name = "entry", Namespace = "")] + [KnownType(typeof(Folder))] + [KnownType(typeof(File))] + [Serializable] + public abstract class FileEntry : ICloneable + { + public FileEntry(Global global) + { + Global = global; + } + + [DataMember(Name = "id")] + public object ID { get; set; } + + [DataMember(Name = "title", IsRequired = true)] + public virtual string Title { get; set; } + + [DataMember(Name = "create_by_id")] + public Guid CreateBy { get; set; } + + [DataMember(Name = "create_by")] + public string CreateByString + { + get { return !CreateBy.Equals(Guid.Empty) ? Global.GetUserName(CreateBy) : _createByString; } + set { _createByString = value; } + } + + [DataMember(Name = "create_on")] + public string CreateOnString + { + get { return CreateOn.Equals(default) ? null : CreateOn.ToString("g"); } + set { throw new NotImplementedException(); } + } + + [DataMember(Name = "modified_on")] + public string ModifiedOnString + { + get { return ModifiedOn.Equals(default) ? null : ModifiedOn.ToString("g"); } + set { throw new NotImplementedException(); } + } + + [DataMember(Name = "modified_by_id")] + public Guid ModifiedBy { get; set; } + + [DataMember(Name = "modified_by")] + public string ModifiedByString + { + get { return !ModifiedBy.Equals(Guid.Empty) ? Global.GetUserName(ModifiedBy) : _modifiedByString; } + set { _modifiedByString = value; } + } + + [DataMember(Name = "error", EmitDefaultValue = false)] + public string Error { get; set; } + + [DataMember(Name = "access")] + public FileShare Access { get; set; } + + [DataMember(Name = "shared")] + public bool Shared { get; set; } + + [DataMember(Name = "provider_id", EmitDefaultValue = false)] + public int ProviderId { get; set; } + + [DataMember(Name = "provider_key", EmitDefaultValue = false)] + public string ProviderKey { get; set; } + + [DataMember(Name = "folder_id")] + public object FolderIdDisplay + { + get + { + if (_folderIdDisplay != null) return _folderIdDisplay; + + var folder = this as Folder; + if (folder != null) return folder.ParentFolderID; + + var file = this as File; + if (file != null) return file.FolderID; + + return null; + } + set { _folderIdDisplay = value; } + } + + public bool ProviderEntry + { + get { return !string.IsNullOrEmpty(ProviderKey); } + } + + public DateTime CreateOn { get; set; } + + public DateTime ModifiedOn { get; set; } + + public FolderType RootFolderType { get; set; } + + public Guid RootFolderCreator { get; set; } + + public object RootFolderId { get; set; } + + public abstract bool IsNew { get; set; } + + public FileEntryType FileEntryType; + + public string UniqID + { + get { return string.Format("{0}_{1}", GetType().Name.ToLower(), ID); } + } + + public Global Global { get; } + + private string _modifiedByString; + private string _createByString; + private object _folderIdDisplay; + + public override bool Equals(object obj) + { + var f = obj as FileEntry; + return f != null && Equals(f.ID, ID); + } + + public override int GetHashCode() + { + return ID.GetHashCode(); + } + + public override string ToString() + { + return Title; + } + + public object Clone() + { + return MemberwiseClone(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/Entries/FileEntryTypeEnum.cs b/products/ASC.Files/Server/Core/Entries/FileEntryTypeEnum.cs new file mode 100644 index 0000000000..646a07a988 --- /dev/null +++ b/products/ASC.Files/Server/Core/Entries/FileEntryTypeEnum.cs @@ -0,0 +1,37 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; + +namespace ASC.Files.Core +{ + [Flags] + public enum FileEntryType + { + Folder = 1, + File = 2, + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/Entries/Folder.cs b/products/ASC.Files/Server/Core/Entries/Folder.cs new file mode 100644 index 0000000000..f5d8d7bc93 --- /dev/null +++ b/products/ASC.Files/Server/Core/Entries/Folder.cs @@ -0,0 +1,89 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Diagnostics; +using System.Runtime.Serialization; + +using ASC.Web.Files.Classes; + +namespace ASC.Files.Core +{ + [DataContract(Namespace = "")] + public enum FolderType + { + [EnumMember] DEFAULT = 0, + + [EnumMember] COMMON = 1, + + [EnumMember] BUNCH = 2, + + [EnumMember] TRASH = 3, + + [EnumMember] USER = 5, + + [EnumMember] SHARE = 6, + + [EnumMember] Projects = 8 + } + + [DataContract(Name = "folder", Namespace = "")] + [DebuggerDisplay("{Title} ({ID})")] + public class Folder : FileEntry + { + public FolderType FolderType { get; set; } + + public object ParentFolderID { get; set; } + + [DataMember(Name = "total_files")] + public int TotalFiles { get; set; } + + [DataMember(Name = "total_sub_folder")] + public int TotalSubFolders { get; set; } + + [DataMember(Name = "shareable", EmitDefaultValue = false)] + public bool Shareable { get; set; } + + [DataMember(Name = "isnew")] + public int NewForMe { get; set; } + + [DataMember(Name = "folder_url", EmitDefaultValue = false)] + public string FolderUrl { get; set; } + + public override bool IsNew + { + get { return Convert.ToBoolean(NewForMe); } + set { NewForMe = Convert.ToInt32(value); } + } + + public Folder(Global global) + : base(global) + { + Title = string.Empty; + FileEntryType = FileEntryType.Folder; + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/Entries/ForcesaveType.cs b/products/ASC.Files/Server/Core/Entries/ForcesaveType.cs new file mode 100644 index 0000000000..a9c984c1ea --- /dev/null +++ b/products/ASC.Files/Server/Core/Entries/ForcesaveType.cs @@ -0,0 +1,16 @@ +using System.Runtime.Serialization; + +namespace ASC.Files.Core +{ + [DataContract(Namespace = "")] + public enum ForcesaveType + { + [EnumMember] None = 0, + + [EnumMember] Command = 1, + + [EnumMember] User = 2, + + [EnumMember] Timer = 3 + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/Entries/OrderBy.cs b/products/ASC.Files/Server/Core/Entries/OrderBy.cs new file mode 100644 index 0000000000..d3121c8fe3 --- /dev/null +++ b/products/ASC.Files/Server/Core/Entries/OrderBy.cs @@ -0,0 +1,67 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.Runtime.Serialization; +using System.Diagnostics; + +namespace ASC.Files.Core +{ + [DataContract(Name = "sorted_by_type", Namespace = "")] + public enum SortedByType + { + [EnumMember] DateAndTime, + + [EnumMember] AZ, + + [EnumMember] Size, + + [EnumMember] Author, + + [EnumMember] Type, + + [EnumMember] New, + + [EnumMember] DateAndTimeCreation + + } + + [DataContract(Name = "orderBy", IsReference = true, Namespace = "")] + [DebuggerDisplay("{SortedBy} {IsAsc}")] + public class OrderBy + { + [DataMember(Name = "is_asc")] + public bool IsAsc { get; set; } + + [DataMember(Name = "property")] + public SortedByType SortedBy { get; set; } + + public OrderBy(SortedByType sortedByType, bool isAsc) + { + SortedBy = sortedByType; + IsAsc = isAsc; + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/Entries/Tag.cs b/products/ASC.Files/Server/Core/Entries/Tag.cs new file mode 100644 index 0000000000..37d2db8a4a --- /dev/null +++ b/products/ASC.Files/Server/Core/Entries/Tag.cs @@ -0,0 +1,102 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; + +namespace ASC.Files.Core +{ + [Flags] + public enum TagType + { + New = 1, + //Favorite = 2, + System = 4, + Locked = 8, + } + + [Serializable] + public class Tag + { + public string TagName { get; set; } + + public TagType TagType { get; set; } + + public Guid Owner { get; set; } + + public object EntryId { get; set; } + + public FileEntryType EntryType { get; set; } + + public int Id { get; set; } + + public int Count { get; set; } + + + public Tag() + { + } + + public Tag(string name, TagType type, Guid owner) + : this(name, type, owner, null, 0) + { + } + + public Tag(string name, TagType type, Guid owner, FileEntry entry, int count) + { + TagName = name; + TagType = type; + Owner = owner; + Count = count; + if (entry != null) + { + EntryId = entry.ID; + EntryType = entry.FileEntryType; + } + } + + + public static Tag New(Guid owner, FileEntry entry) + { + return New(owner, entry, 1); + } + + public static Tag New(Guid owner, FileEntry entry, int count) + { + return new Tag("new", TagType.New, owner, entry, count); + } + + public override bool Equals(object obj) + { + var f = obj as Tag; + return f != null && f.Id == Id && f.EntryType == EntryType && Equals(f.EntryId, EntryId); + } + + public override int GetHashCode() + { + return (Id + EntryType + EntryId.ToString()).GetHashCode(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/FileStorageService.cs b/products/ASC.Files/Server/Core/FileStorageService.cs new file mode 100644 index 0000000000..b42f992c1d --- /dev/null +++ b/products/ASC.Files/Server/Core/FileStorageService.cs @@ -0,0 +1,2069 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Security; +using System.Web; + +using ASC.Api.Core; +using ASC.Common; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Common; +using ASC.Core.Users; +using ASC.Data.Storage; +using ASC.ElasticSearch; +using ASC.FederatedLogin.LoginProviders; +using ASC.Files.Core; +using ASC.Files.Core.Data; +using ASC.Files.Core.Security; +using ASC.Files.Resources; +using ASC.MessagingSystem; +using ASC.Web.Core.Files; +using ASC.Web.Core.Users; +using ASC.Web.Core.Utility; +using ASC.Web.Files.Classes; +using ASC.Web.Files.Core.Search; +using ASC.Web.Files.Helpers; +using ASC.Web.Files.Services.DocumentService; +using ASC.Web.Files.Services.NotifyService; +using ASC.Web.Files.Services.WCFService.FileOperations; +using ASC.Web.Files.ThirdPartyApp; +using ASC.Web.Files.Utils; + +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +using File = ASC.Files.Core.File; +using FileShare = ASC.Files.Core.Security.FileShare; +using UrlShortener = ASC.Web.Core.Utility.UrlShortener; + +namespace ASC.Web.Files.Services.WCFService +{ + public class FileStorageService : IFileStorageService + { + private static readonly FileEntrySerializer serializer = new FileEntrySerializer(); + public Global Global { get; } + public GlobalStore GlobalStore { get; } + public GlobalFolderHelper GlobalFolderHelper { get; } + public FilesSettingsHelper FilesSettingsHelper { get; } + public AuthContext AuthContext { get; } + public UserManager UserManager { get; } + public FactoryIndexer FoldersIndexer { get; } + public FactoryIndexer FilesIndexer { get; } + public FileUtility FileUtility { get; } + public FilesLinkUtility FilesLinkUtility { get; } + public BaseCommonLinkUtility BaseCommonLinkUtility { get; } + public CoreBaseSettings CoreBaseSettings { get; } + public CustomNamingPeople CustomNamingPeople { get; } + public DisplayUserSettingsHelper DisplayUserSettingsHelper { get; } + public IHttpContextAccessor HttpContextAccessor { get; } + public DocuSignLoginProvider DocuSignLoginProvider { get; } + public PathProvider PathProvider { get; } + public FileSecurity FileSecurity { get; } + public SocketManager SocketManager { get; } + public IDaoFactory DaoFactory { get; } + public FileMarker FileMarker { get; } + public EntryManager EntryManager { get; } + public FilesMessageService FilesMessageService { get; } + public DocumentServiceTrackerHelper DocumentServiceTrackerHelper { get; } + public DocuSignToken DocuSignToken { get; } + public DocuSignHelper DocuSignHelper { get; } + public FileShareLink FileShareLink { get; } + public FileConverter FileConverter { get; } + public DocumentServiceHelper DocumentServiceHelper { get; } + public ThirdpartyConfiguration ThirdpartyConfiguration { get; } + public DocumentServiceConnector DocumentServiceConnector { get; } + public FileSharing FileSharing { get; } + public NotifyClient NotifyClient { get; } + public FileOperationsManagerHelper FileOperationsManagerHelper { get; } + public UrlShortener UrlShortener { get; } + public IServiceProvider ServiceProvider { get; } + public FileSharingAceHelper FileSharingAceHelper { get; } + public ApiContext ApiContext { get; } + public ILog Logger { get; set; } + + public FileStorageService( + Global global, + GlobalStore globalStore, + GlobalFolderHelper globalFolderHelper, + FilesSettingsHelper filesSettingsHelper, + AuthContext authContext, + UserManager userManager, + FactoryIndexer foldersIndexer, + FactoryIndexer filesIndexer, + FileUtility fileUtility, + FilesLinkUtility filesLinkUtility, + BaseCommonLinkUtility baseCommonLinkUtility, + CoreBaseSettings coreBaseSettings, + CustomNamingPeople customNamingPeople, + DisplayUserSettingsHelper displayUserSettingsHelper, + IHttpContextAccessor httpContextAccessor, + IOptionsMonitor optionMonitor, + DocuSignLoginProvider docuSignLoginProvider, + PathProvider pathProvider, + FileSecurity fileSecurity, + SocketManager socketManager, + IDaoFactory daoFactory, + FileMarker fileMarker, + EntryManager entryManager, + FilesMessageService filesMessageService, + DocumentServiceTrackerHelper documentServiceTrackerHelper, + DocuSignToken docuSignToken, + DocuSignHelper docuSignHelper, + FileShareLink fileShareLink, + FileConverter fileConverter, + DocumentServiceHelper documentServiceHelper, + ThirdpartyConfiguration thirdpartyConfiguration, + DocumentServiceConnector documentServiceConnector, + FileSharing fileSharing, + NotifyClient notifyClient, + FileOperationsManagerHelper fileOperationsManagerHelper, + UrlShortener urlShortener, + IServiceProvider serviceProvider, + FileSharingAceHelper fileSharingAceHelper, + ApiContext apiContext) + { + Global = global; + GlobalStore = globalStore; + GlobalFolderHelper = globalFolderHelper; + FilesSettingsHelper = filesSettingsHelper; + AuthContext = authContext; + UserManager = userManager; + FoldersIndexer = foldersIndexer; + FilesIndexer = filesIndexer; + FileUtility = fileUtility; + FilesLinkUtility = filesLinkUtility; + BaseCommonLinkUtility = baseCommonLinkUtility; + CoreBaseSettings = coreBaseSettings; + CustomNamingPeople = customNamingPeople; + DisplayUserSettingsHelper = displayUserSettingsHelper; + HttpContextAccessor = httpContextAccessor; + DocuSignLoginProvider = docuSignLoginProvider; + PathProvider = pathProvider; + FileSecurity = fileSecurity; + SocketManager = socketManager; + DaoFactory = daoFactory; + FileMarker = fileMarker; + EntryManager = entryManager; + FilesMessageService = filesMessageService; + DocumentServiceTrackerHelper = documentServiceTrackerHelper; + DocuSignToken = docuSignToken; + DocuSignHelper = docuSignHelper; + FileShareLink = fileShareLink; + FileConverter = fileConverter; + DocumentServiceHelper = documentServiceHelper; + ThirdpartyConfiguration = thirdpartyConfiguration; + DocumentServiceConnector = documentServiceConnector; + FileSharing = fileSharing; + NotifyClient = notifyClient; + FileOperationsManagerHelper = fileOperationsManagerHelper; + UrlShortener = urlShortener; + ServiceProvider = serviceProvider; + FileSharingAceHelper = fileSharingAceHelper; + ApiContext = apiContext; + Logger = optionMonitor.Get("ASC.Files"); + } + + public Folder GetFolder(string folderId) + { + var folderDao = GetFolderDao(); + var folder = folderDao.GetFolder(folderId); + + ErrorIf(folder == null, FilesCommonResource.ErrorMassage_FolderNotFound); + ErrorIf(!FileSecurity.CanRead(folder), FilesCommonResource.ErrorMassage_SecurityException_ReadFolder); + + return folder; + } + + public ItemList GetFolders(string parentId) + { + var folderDao = GetFolderDao(); + + try + { + var folders = EntryManager.GetEntries(folderDao.GetFolder(parentId), 0, 0, FilterType.FoldersOnly, false, Guid.Empty, string.Empty, false, false, new OrderBy(SortedByType.AZ, true), out var total); + return new ItemList(folders.OfType()); + } + catch (Exception e) + { + throw GenerateException(e); + } + } + + public ItemList GetPath(string folderId) + { + var folderDao = GetFolderDao(); + var folder = folderDao.GetFolder(folderId); + + ErrorIf(folder == null, FilesCommonResource.ErrorMassage_FolderNotFound); + ErrorIf(!FileSecurity.CanRead(folder), FilesCommonResource.ErrorMassage_SecurityException_ViewFolder); + + return new ItemList(EntryManager.GetBreadCrumbs(folderId, folderDao).Select(f => f.ID)); + } + + public DataWrapper GetFolderItems(string parentId, int from, int count, FilterType filter, bool subjectGroup, string ssubject, string searchText, bool searchInContent, bool withSubfolders, OrderBy orderBy) + { + var subjectId = string.IsNullOrEmpty(ssubject) ? Guid.Empty : new Guid(ssubject); + + var folderDao = GetFolderDao(); + var fileDao = GetFileDao(); + + Folder parent = null; + try + { + parent = folderDao.GetFolder(parentId); + if (parent != null && !string.IsNullOrEmpty(parent.Error)) throw new Exception(parent.Error); + } + catch (Exception e) + { + if (parent != null && parent.ProviderEntry) + { + throw GenerateException(new Exception(FilesCommonResource.ErrorMassage_SharpBoxException, e)); + } + throw GenerateException(e); + } + + ErrorIf(parent == null, FilesCommonResource.ErrorMassage_FolderNotFound); + ErrorIf(!FileSecurity.CanRead(parent), FilesCommonResource.ErrorMassage_SecurityException_ViewFolder); + ErrorIf(parent.RootFolderType == FolderType.TRASH && !Equals(parent.ID, GlobalFolderHelper.FolderTrash), FilesCommonResource.ErrorMassage_ViewTrashItem); + + if (orderBy != null) + { + FilesSettingsHelper.DefaultOrder = orderBy; + } + else + { + orderBy = FilesSettingsHelper.DefaultOrder; + } + if (Equals(parent.ID, GlobalFolderHelper.FolderShare) && orderBy.SortedBy == SortedByType.DateAndTime) + orderBy.SortedBy = SortedByType.New; + + int total; + IEnumerable entries; + try + { + entries = EntryManager.GetEntries(parent, from, count, filter, subjectGroup, subjectId, searchText, searchInContent, withSubfolders, orderBy, out total); + } + catch (Exception e) + { + if (parent.ProviderEntry) + { + throw GenerateException(new Exception(FilesCommonResource.ErrorMassage_SharpBoxException, e)); + } + throw GenerateException(e); + } + + var breadCrumbs = EntryManager.GetBreadCrumbs(parentId, folderDao); + + var prevVisible = breadCrumbs.ElementAtOrDefault(breadCrumbs.Count() - 2); + if (prevVisible != null) + { + parent.ParentFolderID = prevVisible.ID; + } + + parent.Shareable = FileSharing.CanSetAccess(parent) || parent.FolderType == FolderType.SHARE; + + entries = entries.Where(x => x.FileEntryType == FileEntryType.Folder || !FileConverter.IsConverting((File)x)); + + var result = new DataWrapper + { + Total = total, + Entries = new ItemList(entries.ToList()), + FolderPathParts = new ItemList(breadCrumbs.Select(f => f.ID)), + FolderInfo = parent, + RootFoldersIdMarkedAsNew = FileMarker.GetRootFoldersIdMarkedAsNew() + }; + + return result; + } + + public object GetFolderItemsXml(string parentId, int from, int count, FilterType filter, bool subjectGroup, string subjectID, string search, bool searchInContent, bool withSubfolders, OrderBy orderBy) + { + var folderItems = GetFolderItems(parentId, from, count, filter, subjectGroup, subjectID, search, searchInContent, withSubfolders, orderBy); + var response = new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StreamContent(serializer.ToXml(folderItems)) + }; + response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/xml"); + return response; + } + + public ItemList GetItems(ItemList items, FilterType filter, bool subjectGroup, string subjectID, string search) + { + ParseArrayItems(items, out var foldersId, out var filesId); + + var subjectId = string.IsNullOrEmpty(subjectID) ? Guid.Empty : new Guid(subjectID); + + var entries = Enumerable.Empty(); + + var folderDao = GetFolderDao(); + var fileDao = GetFileDao(); + var folders = folderDao.GetFolders(foldersId.ToArray()).Cast(); + folders = FileSecurity.FilterRead(folders); + entries = entries.Concat(folders); + + var files = fileDao.GetFiles(filesId.ToArray()).Cast(); + files = FileSecurity.FilterRead(files); + entries = entries.Concat(files); + + entries = EntryManager.FilterEntries(entries, filter, subjectGroup, subjectId, search, true); + + foreach (var fileEntry in entries) + { + if (fileEntry.RootFolderType == FolderType.USER + && !Equals(fileEntry.RootFolderCreator, AuthContext.CurrentAccount.ID) + && !FileSecurity.CanRead(folderDao.GetFolder(fileEntry.FolderIdDisplay))) + fileEntry.FolderIdDisplay = GlobalFolderHelper.FolderShare; + } + + EntryManager.SetFileStatus(entries.OfType().Where(r => r.ID != null).ToList()); + + return new ItemList(entries); + } + + public Folder CreateNewFolder(string parentId, string title) + { + if (string.IsNullOrEmpty(title) || string.IsNullOrEmpty(parentId)) throw new ArgumentException(); + + var folderDao = GetFolderDao(); + var parent = folderDao.GetFolder(parentId); + ErrorIf(parent == null, FilesCommonResource.ErrorMassage_FolderNotFound); + ErrorIf(!FileSecurity.CanCreate(parent), FilesCommonResource.ErrorMassage_SecurityException_Create); + + try + { + var newFolder = ServiceProvider.GetService(); + newFolder.Title = title; + newFolder.ParentFolderID = parent.ID; + + var folderId = folderDao.SaveFolder(newFolder); + var folder = folderDao.GetFolder(folderId); + FilesMessageService.Send(folder, GetHttpHeaders(), MessageAction.FolderCreated, folder.Title); + + return folder; + } + catch (Exception e) + { + throw GenerateException(e); + } + } + + public Folder FolderRename(string folderId, string title) + { + var tagDao = GetTagDao(); + var folderDao = GetFolderDao(); + var folder = folderDao.GetFolder(folderId); + ErrorIf(folder == null, FilesCommonResource.ErrorMassage_FolderNotFound); + ErrorIf(!FileSecurity.CanEdit(folder), FilesCommonResource.ErrorMassage_SecurityException_RenameFolder); + if (!FileSecurity.CanDelete(folder) && UserManager.GetUsers(AuthContext.CurrentAccount.ID).IsVisitor(UserManager)) throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException_RenameFolder); + ErrorIf(folder.RootFolderType == FolderType.TRASH, FilesCommonResource.ErrorMassage_ViewTrashItem); + + var folderAccess = folder.Access; + + if (string.Compare(folder.Title, title, false) != 0) + { + var newFolderID = folderDao.RenameFolder(folder, title); + folder = folderDao.GetFolder(newFolderID); + folder.Access = folderAccess; + + FilesMessageService.Send(folder, GetHttpHeaders(), MessageAction.FolderRenamed, folder.Title); + + if (!folder.ProviderEntry) + { + FoldersIndexer.IndexAsync(FoldersWrapper.GetFolderWrapper(ServiceProvider, folder)); + } + } + + var tag = tagDao.GetNewTags(AuthContext.CurrentAccount.ID, folder).FirstOrDefault(); + if (tag != null) + { + folder.NewForMe = tag.Count; + } + + if (folder.RootFolderType == FolderType.USER + && !Equals(folder.RootFolderCreator, AuthContext.CurrentAccount.ID) + && !FileSecurity.CanRead(folderDao.GetFolder(folder.ParentFolderID))) + folder.FolderIdDisplay = GlobalFolderHelper.FolderShare; + + return folder; + } + + public File GetFile(string fileId, int version) + { + var fileDao = GetFileDao(); + fileDao.InvalidateCache(fileId); + + var file = version > 0 + ? fileDao.GetFile(fileId, version) + : fileDao.GetFile(fileId); + ErrorIf(file == null, FilesCommonResource.ErrorMassage_FileNotFound); + ErrorIf(!FileSecurity.CanRead(file), FilesCommonResource.ErrorMassage_SecurityException_ReadFile); + + EntryManager.SetFileStatus(file); + + if (file.RootFolderType == FolderType.USER + && !Equals(file.RootFolderCreator, AuthContext.CurrentAccount.ID)) + { + var folderDao = GetFolderDao(); + if (!FileSecurity.CanRead(folderDao.GetFolder(file.FolderID))) + file.FolderIdDisplay = GlobalFolderHelper.FolderShare; + } + + return file; + } + + public ItemList GetSiblingsFile(string fileId, string parentId, FilterType filter, bool subjectGroup, string subjectID, string search, bool searchInContent, bool withSubfolders, OrderBy orderBy) + { + var subjectId = string.IsNullOrEmpty(subjectID) ? Guid.Empty : new Guid(subjectID); + + var fileDao = GetFileDao(); + var folderDao = GetFolderDao(); + + var file = fileDao.GetFile(fileId); + ErrorIf(file == null, FilesCommonResource.ErrorMassage_FileNotFound); + ErrorIf(!FileSecurity.CanRead(file), FilesCommonResource.ErrorMassage_SecurityException_ReadFile); + + var parent = folderDao.GetFolder(string.IsNullOrEmpty(parentId) ? file.FolderID : parentId); + ErrorIf(parent == null, FilesCommonResource.ErrorMassage_FolderNotFound); + ErrorIf(parent.RootFolderType == FolderType.TRASH, FilesCommonResource.ErrorMassage_ViewTrashItem); + + if (filter == FilterType.FoldersOnly) + { + return new ItemList(); + } + if (filter == FilterType.None) + { + filter = FilterType.FilesOnly; + } + + if (orderBy == null) + { + orderBy = FilesSettingsHelper.DefaultOrder; + } + if (Equals(parent.ID, GlobalFolderHelper.FolderShare) && orderBy.SortedBy == SortedByType.DateAndTime) + { + orderBy.SortedBy = SortedByType.New; + } + + var entries = Enumerable.Empty(); + + if (!FileSecurity.CanRead(parent)) + { + file.FolderID = GlobalFolderHelper.FolderShare; + entries = entries.Concat(new[] { file }); + } + else + { + try + { + entries = EntryManager.GetEntries(parent, 0, 0, filter, subjectGroup, subjectId, search, searchInContent, withSubfolders, orderBy, out var total); + } + catch (Exception e) + { + if (parent.ProviderEntry) + { + throw GenerateException(new Exception(FilesCommonResource.ErrorMassage_SharpBoxException, e)); + } + throw GenerateException(e); + } + } + + var previewedType = new[] { FileType.Image, FileType.Audio, FileType.Video }; + + var result = + FileSecurity.FilterRead(entries) + .OfType() + .Where(f => previewedType.Contains(FileUtility.GetFileTypeByFileName(f.Title))); + + return new ItemList(result); + } + + public File CreateNewFile(FileModel fileWrapper) + { + if (string.IsNullOrEmpty(fileWrapper.Title) || string.IsNullOrEmpty(fileWrapper.ParentId)) throw new ArgumentException(); + + var fileDao = GetFileDao(); + var folderDao = GetFolderDao(); + + var folder = folderDao.GetFolder(fileWrapper.ParentId); + ErrorIf(folder == null, FilesCommonResource.ErrorMassage_FolderNotFound); + ErrorIf(folder.RootFolderType == FolderType.TRASH, FilesCommonResource.ErrorMassage_CreateNewFolderInTrash); + ErrorIf(!FileSecurity.CanCreate(folder), FilesCommonResource.ErrorMassage_SecurityException_Create); + + var file = ServiceProvider.GetService(); + file.FolderID = folder.ID; + file.Comment = FilesCommonResource.CommentCreate; + + var fileExt = FileUtility.GetInternalExtension(fileWrapper.Title); + if (!FileUtility.InternalExtension.Values.Contains(fileExt)) + { + fileExt = FileUtility.InternalExtension[FileType.Document]; + file.Title = fileWrapper.Title + fileExt; + } + else + { + file.Title = FileUtility.ReplaceFileExtension(fileWrapper.Title, fileExt); + } + + var culture = UserManager.GetUsers(AuthContext.CurrentAccount.ID).GetCulture(); + var storeTemplate = GetStoreTemplate(); + + var path = FileConstant.NewDocPath + culture + "/"; + if (!storeTemplate.IsDirectory(path)) + { + path = FileConstant.NewDocPath + "default/"; + } + + path += "new" + fileExt; + + try + { + using var stream = storeTemplate.GetReadStream("", path); + file.ContentLength = stream.CanSeek ? stream.Length : storeTemplate.GetFileSize(path); + file = fileDao.SaveFile(file, stream); + } + catch (Exception e) + { + throw GenerateException(e); + } + FilesMessageService.Send(file, GetHttpHeaders(), MessageAction.FileCreated, file.Title); + + FileMarker.MarkAsNew(file); + + return file; + } + + public KeyValuePair TrackEditFile(string fileId, Guid tabId, string docKeyForTrack, string doc = null, bool isFinish = false) + { + try + { + var id = FileShareLink.Parse(doc); + if (string.IsNullOrEmpty(id)) + { + if (!AuthContext.IsAuthenticated) throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException); + if (!string.IsNullOrEmpty(doc)) throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException); + id = fileId; + } + + if (docKeyForTrack != DocumentServiceHelper.GetDocKey(id, -1, DateTime.MinValue)) throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException); + + if (isFinish) + { + FileTracker.Remove(id, tabId); + SocketManager.FilesChangeEditors(id, true); + } + else + { + EntryManager.TrackEditing(id, tabId, AuthContext.CurrentAccount.ID, doc); + } + + return new KeyValuePair(true, string.Empty); + } + catch (Exception ex) + { + return new KeyValuePair(false, ex.Message); + } + } + + public ItemDictionary CheckEditing(ItemList filesId) + { + ErrorIf(!AuthContext.IsAuthenticated, FilesCommonResource.ErrorMassage_SecurityException); + var result = new ItemDictionary(); + + var fileDao = GetFileDao(); + var ids = filesId.Where(FileTracker.IsEditing).Select(id => id).ToArray(); + + foreach (var file in fileDao.GetFiles(ids)) + { + if (file == null || !FileSecurity.CanEdit(file) && !FileSecurity.CanReview(file)) continue; + + var usersId = FileTracker.GetEditingBy(file.ID); + var value = string.Join(", ", usersId.Select(userId => Global.GetUserName(userId, true)).ToArray()); + result[file.ID.ToString()] = value; + } + + return result; + } + + public File SaveEditing(string fileId, string fileExtension, string fileuri, Stream stream, string doc = null, bool forcesave = false) + { + try + { + if (!forcesave && FileTracker.IsEditingAlone(fileId)) + { + FileTracker.Remove(fileId); + } + + var file = EntryManager.SaveEditing(fileId, fileExtension, fileuri, stream, doc, forcesave: forcesave ? ForcesaveType.User : ForcesaveType.None); + + if (file != null) + FilesMessageService.Send(file, GetHttpHeaders(), MessageAction.FileUpdated, file.Title); + + SocketManager.FilesChangeEditors(fileId, !forcesave); + return file; + } + catch (Exception e) + { + throw GenerateException(e); + } + } + + public File UpdateFileStream(string fileId, Stream stream, bool encrypted) + { + try + { + if (FileTracker.IsEditing(fileId)) + { + FileTracker.Remove(fileId); + } + + var file = EntryManager.SaveEditing(fileId, null, null, stream, null, encrypted ? FilesCommonResource.CommentEncrypted : null, encrypted: encrypted); + + if (file != null) + FilesMessageService.Send(file, GetHttpHeaders(), MessageAction.FileUpdated, file.Title); + + SocketManager.FilesChangeEditors(fileId, true); + return file; + } + catch (Exception e) + { + throw GenerateException(e); + } + } + + public string StartEdit(string fileId, bool editingAlone = false, string doc = null) + { + try + { + IThirdPartyApp app; + if (editingAlone) + { + ErrorIf(FileTracker.IsEditing(fileId), FilesCommonResource.ErrorMassage_SecurityException_EditFileTwice); + + app = ThirdPartySelector.GetAppByFileId(fileId); + if (app == null) + { + EntryManager.TrackEditing(fileId, Guid.Empty, AuthContext.CurrentAccount.ID, doc, true); + } + + //without StartTrack, track via old scheme + return DocumentServiceHelper.GetDocKey(fileId, -1, DateTime.MinValue); + } + + DocumentService.Configuration configuration; + + app = ThirdPartySelector.GetAppByFileId(fileId); + if (app == null) + { + DocumentServiceHelper.GetParams(fileId, -1, doc, true, true, false, out configuration); + } + else + { + var file = app.GetFile(fileId, out var editable); + DocumentServiceHelper.GetParams(file, true, editable ? FileShare.ReadWrite : FileShare.Read, false, editable, editable, editable, false, out configuration); + } + + ErrorIf(!configuration.EditorConfig.ModeWrite + || !(configuration.Document.Permissions.Edit + || configuration.Document.Permissions.Review + || configuration.Document.Permissions.FillForms + || configuration.Document.Permissions.Comment), + !string.IsNullOrEmpty(configuration.ErrorMessage) ? configuration.ErrorMessage : FilesCommonResource.ErrorMassage_SecurityException_EditFile); + var key = configuration.Document.Key; + + if (!DocumentServiceTrackerHelper.StartTrack(fileId, key)) + { + throw new Exception(FilesCommonResource.ErrorMassage_StartEditing); + } + + return key; + } + catch (Exception e) + { + FileTracker.Remove(fileId); + throw GenerateException(e); + } + } + + public File FileRename(string fileId, string title) + { + try + { + var renamed = EntryManager.FileRename(fileId, title, out var file); + if (renamed) + { + FilesMessageService.Send(file, GetHttpHeaders(), MessageAction.FileRenamed, file.Title); + + if (!file.ProviderEntry) + { + FilesIndexer.UpdateAsync(FilesWrapper.GetFilesWrapper(ServiceProvider, file), true, r => r.Title); + } + } + + if (file.RootFolderType == FolderType.USER + && !Equals(file.RootFolderCreator, AuthContext.CurrentAccount.ID)) + { + var folderDao = GetFolderDao(); + if (!FileSecurity.CanRead(folderDao.GetFolder(file.FolderID))) + file.FolderIdDisplay = GlobalFolderHelper.FolderShare; + } + + return file; + } + catch (Exception ex) + { + throw GenerateException(ex); + } + } + + public ItemList GetFileHistory(string fileId) + { + var fileDao = GetFileDao(); + var file = fileDao.GetFile(fileId); + ErrorIf(!FileSecurity.CanRead(file), FilesCommonResource.ErrorMassage_SecurityException_ReadFile); + + return new ItemList(fileDao.GetFileHistory(fileId)); + } + + public KeyValuePair> UpdateToVersion(string fileId, int version) + { + var file = EntryManager.UpdateToVersionFile(fileId, version); + FilesMessageService.Send(file, GetHttpHeaders(), MessageAction.FileRestoreVersion, file.Title, version.ToString(CultureInfo.InvariantCulture)); + + if (file.RootFolderType == FolderType.USER + && !Equals(file.RootFolderCreator, AuthContext.CurrentAccount.ID)) + { + var folderDao = GetFolderDao(); + if (!FileSecurity.CanRead(folderDao.GetFolder(file.FolderID))) + { + file.FolderIdDisplay = GlobalFolderHelper.FolderShare; + } + } + + return new KeyValuePair>(file, GetFileHistory(fileId)); + } + + public string UpdateComment(string fileId, int version, string comment) + { + var fileDao = GetFileDao(); + var file = fileDao.GetFile(fileId, version); + ErrorIf(file == null, FilesCommonResource.ErrorMassage_FileNotFound); + ErrorIf(!FileSecurity.CanEdit(file) || UserManager.GetUsers(AuthContext.CurrentAccount.ID).IsVisitor(UserManager), FilesCommonResource.ErrorMassage_SecurityException_EditFile); + ErrorIf(EntryManager.FileLockedForMe(file.ID), FilesCommonResource.ErrorMassage_LockedFile); + ErrorIf(file.RootFolderType == FolderType.TRASH, FilesCommonResource.ErrorMassage_ViewTrashItem); + + comment = fileDao.UpdateComment(fileId, version, comment); + + FilesMessageService.Send(file, GetHttpHeaders(), MessageAction.FileUpdatedRevisionComment, file.Title, version.ToString(CultureInfo.InvariantCulture)); + + return comment; + } + + public KeyValuePair> CompleteVersion(string fileId, int version, bool continueVersion) + { + var file = EntryManager.CompleteVersionFile(fileId, version, continueVersion); + + FilesMessageService.Send(file, GetHttpHeaders(), + continueVersion ? MessageAction.FileDeletedVersion : MessageAction.FileCreatedVersion, + file.Title, version == 0 ? (file.Version - 1).ToString(CultureInfo.InvariantCulture) : version.ToString(CultureInfo.InvariantCulture)); + + if (file.RootFolderType == FolderType.USER + && !Equals(file.RootFolderCreator, AuthContext.CurrentAccount.ID)) + { + var folderDao = GetFolderDao(); + if (!FileSecurity.CanRead(folderDao.GetFolder(file.FolderID))) + file.FolderIdDisplay = GlobalFolderHelper.FolderShare; + } + + return new KeyValuePair>(file, GetFileHistory(fileId)); + } + + public File LockFile(string fileId, bool lockfile) + { + var tagDao = GetTagDao(); + var fileDao = GetFileDao(); + var file = fileDao.GetFile(fileId); + + ErrorIf(file == null, FilesCommonResource.ErrorMassage_FileNotFound); + ErrorIf(!FileSecurity.CanEdit(file) || lockfile && UserManager.GetUsers(AuthContext.CurrentAccount.ID).IsVisitor(UserManager), FilesCommonResource.ErrorMassage_SecurityException_EditFile); + ErrorIf(file.RootFolderType == FolderType.TRASH, FilesCommonResource.ErrorMassage_ViewTrashItem); + + var tagLocked = tagDao.GetTags(file.ID, FileEntryType.File, TagType.Locked).FirstOrDefault(); + + ErrorIf(tagLocked != null + && tagLocked.Owner != AuthContext.CurrentAccount.ID + && !Global.IsAdministrator + && (file.RootFolderType != FolderType.USER || file.RootFolderCreator != AuthContext.CurrentAccount.ID), FilesCommonResource.ErrorMassage_LockedFile); + + if (lockfile) + { + if (tagLocked == null) + { + tagLocked = new Tag("locked", TagType.Locked, AuthContext.CurrentAccount.ID, file, 0); + + tagDao.SaveTags(tagLocked); + } + + var usersDrop = FileTracker.GetEditingBy(file.ID).Where(uid => uid != AuthContext.CurrentAccount.ID).Select(u => u.ToString()).ToArray(); + if (usersDrop.Any()) + { + var fileStable = file.Forcesave == ForcesaveType.None ? file : fileDao.GetFileStable(file.ID, file.Version); + var docKey = DocumentServiceHelper.GetDocKey(fileStable); + DocumentServiceHelper.DropUser(docKey, usersDrop, file.ID); + } + + FilesMessageService.Send(file, GetHttpHeaders(), MessageAction.FileLocked, file.Title); + } + else + { + if (tagLocked != null) + { + tagDao.RemoveTags(tagLocked); + + FilesMessageService.Send(file, GetHttpHeaders(), MessageAction.FileUnlocked, file.Title); + } + + if (!file.ProviderEntry) + { + file = EntryManager.CompleteVersionFile(file.ID, 0, false); + UpdateComment(file.ID.ToString(), file.Version, FilesCommonResource.UnlockComment); + } + } + + EntryManager.SetFileStatus(file); + + if (file.RootFolderType == FolderType.USER + && !Equals(file.RootFolderCreator, AuthContext.CurrentAccount.ID)) + { + var folderDao = GetFolderDao(); + if (!FileSecurity.CanRead(folderDao.GetFolder(file.FolderID))) + file.FolderIdDisplay = GlobalFolderHelper.FolderShare; + } + + return file; + } + + public ItemList GetEditHistory(string fileId, string doc = null) + { + var fileDao = GetFileDao(); + var readLink = FileShareLink.Check(doc, true, fileDao, out var file); + if (file == null) + file = fileDao.GetFile(fileId); + + ErrorIf(file == null, FilesCommonResource.ErrorMassage_FileNotFound); + ErrorIf(!readLink && !FileSecurity.CanRead(file), FilesCommonResource.ErrorMassage_SecurityException_ReadFile); + ErrorIf(file.ProviderEntry, FilesCommonResource.ErrorMassage_BadRequest); + + return new ItemList(fileDao.GetEditHistory(DocumentServiceHelper, file.ID)); + } + + public EditHistoryData GetEditDiffUrl(string fileId, int version = 0, string doc = null) + { + var fileDao = GetFileDao(); + var readLink = FileShareLink.Check(doc, true, fileDao, out var file); + + if (file != null) + { + fileId = file.ID.ToString(); + } + + if (file == null + || version > 0 && file.Version != version) + { + file = version > 0 + ? fileDao.GetFile(fileId, version) + : fileDao.GetFile(fileId); + } + + ErrorIf(file == null, FilesCommonResource.ErrorMassage_FileNotFound); + ErrorIf(!readLink && !FileSecurity.CanRead(file), FilesCommonResource.ErrorMassage_SecurityException_ReadFile); + ErrorIf(file.ProviderEntry, FilesCommonResource.ErrorMassage_BadRequest); + + var result = new EditHistoryData + { + Key = DocumentServiceHelper.GetDocKey(file), + Url = DocumentServiceConnector.ReplaceCommunityAdress(PathProvider.GetFileStreamUrl(file, doc)), + Version = version, + }; + + if (fileDao.ContainChanges(file.ID, file.Version)) + { + string previouseKey; + string sourceFileUrl; + if (file.Version > 1) + { + var previousFileStable = fileDao.GetFileStable(file.ID, file.Version - 1); + ErrorIf(previousFileStable == null, FilesCommonResource.ErrorMassage_FileNotFound); + + sourceFileUrl = PathProvider.GetFileStreamUrl(previousFileStable, doc); + + previouseKey = DocumentServiceHelper.GetDocKey(previousFileStable); + } + else + { + var culture = UserManager.GetUsers(AuthContext.CurrentAccount.ID).GetCulture(); + var storeTemplate = GetStoreTemplate(); + + var path = FileConstant.NewDocPath + culture + "/"; + if (!storeTemplate.IsDirectory(path)) + { + path = FileConstant.NewDocPath + "default/"; + } + + var fileExt = FileUtility.GetFileExtension(file.Title); + + path += "new" + fileExt; + + sourceFileUrl = storeTemplate.GetUri("", path).ToString(); + sourceFileUrl = BaseCommonLinkUtility.GetFullAbsolutePath(sourceFileUrl); + + previouseKey = DocumentServiceConnector.GenerateRevisionId(Guid.NewGuid().ToString()); + } + + result.Previous = new EditHistoryUrl + { + Key = previouseKey, + Url = DocumentServiceConnector.ReplaceCommunityAdress(sourceFileUrl), + }; + result.ChangesUrl = PathProvider.GetFileChangesUrl(file, doc); + } + + result.Token = DocumentServiceHelper.GetSignature(result); + + return result; + } + + public ItemList RestoreVersion(string fileId, int version, string url = null, string doc = null) + { + IFileDao fileDao; + File file; + if (string.IsNullOrEmpty(url)) + { + file = EntryManager.UpdateToVersionFile(fileId, version, doc); + } + else + { + string modifiedOnString; + fileDao = GetFileDao(); + var fromFile = fileDao.GetFile(fileId, version); + modifiedOnString = fromFile.ModifiedOnString; + file = EntryManager.SaveEditing(fileId, null, url, null, doc, string.Format(FilesCommonResource.CommentRevertChanges, modifiedOnString)); + } + + FilesMessageService.Send(file, GetHttpHeaders(), MessageAction.FileRestoreVersion, file.Title, version.ToString(CultureInfo.InvariantCulture)); + + fileDao = GetFileDao(); + return new ItemList(fileDao.GetEditHistory(DocumentServiceHelper, file.ID)); + } + + public Web.Core.Files.DocumentService.FileLink GetPresignedUri(string fileId) + { + var file = GetFile(fileId, -1); + var result = new Web.Core.Files.DocumentService.FileLink + { + FileType = FileUtility.GetFileExtension(file.Title), + Url = DocumentServiceConnector.ReplaceCommunityAdress(PathProvider.GetFileStreamUrl(file)) + }; + + result.Token = DocumentServiceHelper.GetSignature(result); + + return result; + } + + public object GetNewItems(string folderId) + { + try + { + Folder folder; + var folderDao = GetFolderDao(); + folder = folderDao.GetFolder(folderId); + + var result = FileMarker.MarkedItems(folder); + + result = new List(EntryManager.SortEntries(result, new OrderBy(SortedByType.DateAndTime, false))); + + if (!result.ToList().Any()) + { + MarkAsRead(new ItemList { "folder_" + folderId }); + } + + var response = new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StreamContent(serializer.ToXml(new ItemList(result))) + }; + response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/xml"); + return response; + } + catch (Exception e) + { + throw GenerateException(e); + } + } + + public ItemList MarkAsRead(ItemList items) + { + if (items.Count == 0) return GetTasksStatuses(); + + ParseArrayItems(items, out var foldersId, out var filesId); + + return FileOperationsManagerHelper.MarkAsRead(foldersId, filesId); + } + + public ItemList GetThirdParty() + { + var providerDao = GetProviderDao(); + if (providerDao == null) return new ItemList(); + + var providersInfo = providerDao.GetProvidersInfo(); + + var resultList = providersInfo + .Select(r => + new ThirdPartyParams + { + CustomerTitle = r.CustomerTitle, + Corporate = r.RootFolderType == FolderType.COMMON, + ProviderId = r.ID.ToString(), + ProviderKey = r.ProviderKey + } + ); + return new ItemList(resultList.ToList()); + } + + public ItemList GetThirdPartyFolder(int folderType = 0) + { + var providerDao = GetProviderDao(); + if (providerDao == null) return new ItemList(); + + var providersInfo = providerDao.GetProvidersInfo((FolderType)folderType); + + var folders = providersInfo.Select(providerInfo => + { + var folder = EntryManager.GetFakeThirdpartyFolder(providerInfo); + folder.NewForMe = folder.RootFolderType == FolderType.COMMON ? 1 : 0; + return folder; + }); + + return new ItemList(folders); + } + + public Folder SaveThirdParty(ThirdPartyParams thirdPartyParams) + { + var folderDao = GetFolderDao(); + var providerDao = GetProviderDao(); + + if (providerDao == null) return null; + + ErrorIf(thirdPartyParams == null, FilesCommonResource.ErrorMassage_BadRequest); + var parentFolder = folderDao.GetFolder(thirdPartyParams.Corporate && !CoreBaseSettings.Personal ? GlobalFolderHelper.FolderCommon : GlobalFolderHelper.FolderMy); + ErrorIf(!FileSecurity.CanCreate(parentFolder), FilesCommonResource.ErrorMassage_SecurityException_Create); + ErrorIf(!Global.IsAdministrator && !FilesSettingsHelper.EnableThirdParty, FilesCommonResource.ErrorMassage_SecurityException_Create); + + var lostFolderType = FolderType.USER; + var folderType = thirdPartyParams.Corporate ? FolderType.COMMON : FolderType.USER; + + int curProviderId; + + MessageAction messageAction; + if (string.IsNullOrEmpty(thirdPartyParams.ProviderId)) + { + ErrorIf(!ThirdpartyConfiguration.SupportInclusion + || + (!Global.IsAdministrator + && !CoreBaseSettings.Personal + && !FilesSettingsHelper.EnableThirdParty) + , FilesCommonResource.ErrorMassage_SecurityException_Create); + + thirdPartyParams.CustomerTitle = Global.ReplaceInvalidCharsAndTruncate(thirdPartyParams.CustomerTitle); + ErrorIf(string.IsNullOrEmpty(thirdPartyParams.CustomerTitle), FilesCommonResource.ErrorMassage_InvalidTitle); + + try + { + curProviderId = providerDao.SaveProviderInfo(thirdPartyParams.ProviderKey, thirdPartyParams.CustomerTitle, thirdPartyParams.AuthData, folderType); + messageAction = MessageAction.ThirdPartyCreated; + } + catch (UnauthorizedAccessException e) + { + throw GenerateException(e, true); + } + catch (Exception e) + { + throw GenerateException(e); + } + } + else + { + curProviderId = Convert.ToInt32(thirdPartyParams.ProviderId); + + var lostProvider = providerDao.GetProviderInfo(curProviderId); + ErrorIf(lostProvider.Owner != AuthContext.CurrentAccount.ID, FilesCommonResource.ErrorMassage_SecurityException); + + lostFolderType = lostProvider.RootFolderType; + if (lostProvider.RootFolderType == FolderType.COMMON && !thirdPartyParams.Corporate) + { + var lostFolder = folderDao.GetFolder(lostProvider.RootFolderId); + FileMarker.RemoveMarkAsNewForAll(lostFolder); + } + + curProviderId = providerDao.UpdateProviderInfo(curProviderId, thirdPartyParams.CustomerTitle, thirdPartyParams.AuthData, folderType); + messageAction = MessageAction.ThirdPartyUpdated; + } + + var provider = providerDao.GetProviderInfo(curProviderId); + provider.InvalidateStorage(); + + var folder = folderDao.GetFolder(provider.RootFolderId); + ErrorIf(!FileSecurity.CanRead(folder), FilesCommonResource.ErrorMassage_SecurityException_ViewFolder); + + FilesMessageService.Send(parentFolder, GetHttpHeaders(), messageAction, folder.ID.ToString(), provider.ProviderKey); + + if (thirdPartyParams.Corporate && lostFolderType != FolderType.COMMON) + { + FileMarker.MarkAsNew(folder); + } + + return folder; + } + + public object DeleteThirdParty(string providerId) + { + var providerDao = GetProviderDao(); + if (providerDao == null) return null; + + var curProviderId = Convert.ToInt32(providerId); + var providerInfo = providerDao.GetProviderInfo(curProviderId); + + var folder = EntryManager.GetFakeThirdpartyFolder(providerInfo); + ErrorIf(!FileSecurity.CanDelete(folder), FilesCommonResource.ErrorMassage_SecurityException_DeleteFolder); + + if (providerInfo.RootFolderType == FolderType.COMMON) + { + FileMarker.RemoveMarkAsNewForAll(folder); + } + + providerDao.RemoveProviderInfo(folder.ProviderId); + FilesMessageService.Send(folder, GetHttpHeaders(), MessageAction.ThirdPartyDeleted, folder.ID.ToString(), providerInfo.ProviderKey); + + return folder.ID; + } + + public bool ChangeAccessToThirdparty(bool enable) + { + ErrorIf(!Global.IsAdministrator, FilesCommonResource.ErrorMassage_SecurityException); + + FilesSettingsHelper.EnableThirdParty = enable; + FilesMessageService.Send(GetHttpHeaders(), MessageAction.DocumentsThirdPartySettingsUpdated); + + return FilesSettingsHelper.EnableThirdParty; + } + + public bool SaveDocuSign(string code) + { + ErrorIf(!AuthContext.IsAuthenticated + || UserManager.GetUsers(AuthContext.CurrentAccount.ID).IsVisitor(UserManager) + || !Global.IsAdministrator && !FilesSettingsHelper.EnableThirdParty + || !ThirdpartyConfiguration.SupportDocuSignInclusion, FilesCommonResource.ErrorMassage_SecurityException_Create); + + var token = DocuSignLoginProvider.Instance.GetAccessToken(code); + DocuSignHelper.ValidateToken(token); + DocuSignToken.SaveToken(token); + return true; + } + + public object DeleteDocuSign() + { + DocuSignToken.DeleteToken(); + return null; + } + + public string SendDocuSign(string fileId, DocuSignData docuSignData) + { + try + { + ErrorIf(UserManager.GetUsers(AuthContext.CurrentAccount.ID).IsVisitor(UserManager) + || !FilesSettingsHelper.EnableThirdParty || !ThirdpartyConfiguration.SupportDocuSignInclusion, FilesCommonResource.ErrorMassage_SecurityException_Create); + + return DocuSignHelper.SendDocuSign(fileId, docuSignData, GetHttpHeaders()); + } + catch (Exception e) + { + throw GenerateException(e); + } + } + + public ItemList GetTasksStatuses() + { + ErrorIf(!AuthContext.IsAuthenticated, FilesCommonResource.ErrorMassage_SecurityException); + + return FileOperationsManagerHelper.GetOperationResults(); + } + + public ItemList TerminateTasks() + { + ErrorIf(!AuthContext.IsAuthenticated, FilesCommonResource.ErrorMassage_SecurityException); + + return FileOperationsManagerHelper.CancelOperations(); + } + + public ItemList BulkDownload(Dictionary items) + { + ParseArrayItems(items, out var folders, out var files); + ErrorIf(folders.Count == 0 && files.Count == 0, FilesCommonResource.ErrorMassage_BadRequest); + + return FileOperationsManagerHelper.Download(folders, files, GetHttpHeaders()); + } + + public ItemDictionary MoveOrCopyFilesCheck(ItemList items, string destFolderId) + { + if (items.Count == 0) return new ItemDictionary(); + + ParseArrayItems(items, out var foldersId, out var filesId); + + return new ItemDictionary(MoveOrCopyFilesCheck(filesId, foldersId, destFolderId)); + } + + private Dictionary MoveOrCopyFilesCheck(IEnumerable filesId, IEnumerable foldersId, object destFolderId) + { + var result = new Dictionary(); + var folderDao = GetFolderDao(); + var fileDao = GetFileDao(); + + var toFolder = folderDao.GetFolder(destFolderId); + ErrorIf(toFolder == null, FilesCommonResource.ErrorMassage_FolderNotFound); + ErrorIf(!FileSecurity.CanCreate(toFolder), FilesCommonResource.ErrorMassage_SecurityException_Create); + + foreach (var id in filesId) + { + var file = fileDao.GetFile(id); + if (file != null && fileDao.IsExist(file.Title, toFolder.ID)) + { + result.Add(id.ToString(), file.Title); + } + } + + var folders = folderDao.GetFolders(foldersId.ToArray()); + var foldersProject = folders.Where(folder => folder.FolderType == FolderType.BUNCH).ToList(); + if (foldersProject.Any()) + { + var toSubfolders = folderDao.GetFolders(toFolder.ID); + + foreach (var folderProject in foldersProject) + { + var toSub = toSubfolders.FirstOrDefault(to => Equals(to.Title, folderProject.Title)); + if (toSub == null) continue; + + var filesPr = fileDao.GetFiles(folderProject.ID); + var foldersPr = folderDao.GetFolders(folderProject.ID).Select(d => d.ID); + + var recurseItems = MoveOrCopyFilesCheck(filesPr, foldersPr, toSub.ID); + foreach (var recurseItem in recurseItems) + { + result.Add(recurseItem.Key, recurseItem.Value); + } + } + } + try + { + foreach (var pair in folderDao.CanMoveOrCopy(foldersId.ToArray(), toFolder.ID)) + { + result.Add(pair.Key.ToString(), pair.Value); + } + } + catch (Exception e) + { + throw GenerateException(e); + } + return result; + } + + public ItemList MoveOrCopyItems(ItemList items, string destFolderId, FileConflictResolveType resolve, bool ic, bool deleteAfter = false) + { + ItemList result; + if (items.Count != 0) + { + ParseArrayItems(items, out var foldersId, out var filesId); + + result = FileOperationsManagerHelper.MoveOrCopy(foldersId, filesId, destFolderId, ic, resolve, !deleteAfter, GetHttpHeaders()); + } + else + { + result = FileOperationsManagerHelper.GetOperationResults(); + } + return result; + } + + public ItemList DeleteItems(string action, ItemList items, bool ignoreException = false, bool deleteAfter = false, bool immediately = false) + { + ParseArrayItems(items, out var foldersId, out var filesId); + + return FileOperationsManagerHelper.Delete(foldersId, filesId, ignoreException, !deleteAfter, immediately, GetHttpHeaders()); + } + + public ItemList EmptyTrash() + { + var folderDao = GetFolderDao(); + var fileDao = GetFileDao(); + var trashId = folderDao.GetFolderIDTrash(true); + var foldersId = folderDao.GetFolders(trashId).Select(f => f.ID).ToList(); + var filesId = fileDao.GetFiles(trashId).ToList(); + + return FileOperationsManagerHelper.Delete(foldersId, filesId, false, true, false, GetHttpHeaders()); + } + + public ItemList CheckConversion(ItemList> filesInfoJSON) + { + if (filesInfoJSON == null || filesInfoJSON.Count == 0) return new ItemList(); + + var fileDao = GetFileDao(); + var files = new List>(); + foreach (var fileInfo in filesInfoJSON) + { + object fileId; + + var fileIdAsString = fileInfo[0]; + + if (int.TryParse(fileIdAsString, out var fileIdAsInt)) + fileId = fileIdAsInt; + else + fileId = fileIdAsString; + + var file = int.TryParse(fileInfo[1], out var version) && version > 0 + ? fileDao.GetFile(fileId, version) + : fileDao.GetFile(fileId); + + if (file == null) + { + var newFile = ServiceProvider.GetService(); + newFile.ID = fileId; + newFile.Version = version; + + files.Add(new KeyValuePair(newFile, true)); + continue; + } + + ErrorIf(!FileSecurity.CanRead(file), FilesCommonResource.ErrorMassage_SecurityException_ReadFile); + + var startConvert = Convert.ToBoolean(fileInfo[2]); + if (startConvert && FileConverter.MustConvert(file)) + { + try + { + FileConverter.ExecAsync(file, false, fileInfo.Count > 3 ? fileInfo[3] : null); + } + catch (Exception e) + { + throw GenerateException(e); + } + } + + files.Add(new KeyValuePair(file, false)); + } + + var results = FileConverter.GetStatus(files).ToList(); + + return new ItemList(results); + } + + public void ReassignStorage(Guid userFromId, Guid userToId) + { + //check current user have access + ErrorIf(!Global.IsAdministrator, FilesCommonResource.ErrorMassage_SecurityException); + + //check exist userFrom + var userFrom = UserManager.GetUsers(userFromId); + ErrorIf(Equals(userFrom, Constants.LostUser), FilesCommonResource.ErrorMassage_UserNotFound); + + //check exist userTo + var userTo = UserManager.GetUsers(userToId); + ErrorIf(Equals(userTo, Constants.LostUser), FilesCommonResource.ErrorMassage_UserNotFound); + ErrorIf(userTo.IsVisitor(UserManager), FilesCommonResource.ErrorMassage_SecurityException); + + var providerDao = GetProviderDao(); + if (providerDao != null) + { + var providersInfo = providerDao.GetProvidersInfo(userFrom.ID); + var commonProvidersInfo = providersInfo.Where(provider => provider.RootFolderType == FolderType.COMMON).ToList(); + + //move common thirdparty storage userFrom + foreach (var commonProviderInfo in commonProvidersInfo) + { + Logger.InfoFormat("Reassign provider {0} from {1} to {2}", commonProviderInfo.ID, userFrom.ID, userTo.ID); + providerDao.UpdateProviderInfo(commonProviderInfo.ID, null, null, FolderType.DEFAULT, userTo.ID); + } + } + + var folderDao = GetFolderDao(); + var fileDao = GetFileDao(); + + if (!userFrom.IsVisitor(UserManager)) + { + var folderIdFromMy = folderDao.GetFolderIDUser(false, userFrom.ID); + + if (!Equals(folderIdFromMy, 0)) + { + //create folder with name userFrom in folder userTo + var folderIdToMy = folderDao.GetFolderIDUser(true, userTo.ID); + var newFolder = ServiceProvider.GetService(); + newFolder.Title = string.Format(CustomNamingPeople.Substitute("TitleDeletedUserFolder"), userFrom.DisplayUserName(false, DisplayUserSettingsHelper)); + newFolder.ParentFolderID = folderIdToMy; + + var newFolderTo = folderDao.SaveFolder(newFolder); + + //move items from userFrom to userTo + EntryManager.MoveSharedItems(folderIdFromMy, newFolderTo, folderDao, fileDao); + + EntryManager.ReassignItems(newFolderTo, userFrom.ID, userTo.ID, folderDao, fileDao); + } + } + + EntryManager.ReassignItems(GlobalFolderHelper.FolderCommon, userFrom.ID, userTo.ID, folderDao, fileDao); + } + + public void DeleteStorage(Guid userId) + { + //check current user have access + ErrorIf(!Global.IsAdministrator, FilesCommonResource.ErrorMassage_SecurityException); + + //delete docuSign + DocuSignToken.DeleteToken(userId); + + var providerDao = GetProviderDao(); + if (providerDao != null) + { + var providersInfo = providerDao.GetProvidersInfo(userId); + + //delete thirdparty storage + foreach (var myProviderInfo in providersInfo) + { + Logger.InfoFormat("Delete provider {0} for {1}", myProviderInfo.ID, userId); + providerDao.RemoveProviderInfo(myProviderInfo.ID); + } + } + + var folderDao = GetFolderDao(); + var fileDao = GetFileDao(); + + //delete all markAsNew + var rootFoldersId = new List + { + GlobalFolderHelper.FolderShare, + GlobalFolderHelper.FolderCommon, + GlobalFolderHelper.FolderProjects, + }; + + var folderIdFromMy = folderDao.GetFolderIDUser(false, userId); + if (!Equals(folderIdFromMy, 0)) + { + rootFoldersId.Add(folderIdFromMy); + } + + var rootFolders = folderDao.GetFolders(rootFoldersId.ToArray()); + foreach (var rootFolder in rootFolders) + { + FileMarker.RemoveMarkAsNew(rootFolder, userId); + } + + //delete all from My + if (!Equals(folderIdFromMy, 0)) + { + EntryManager.DeleteSubitems(folderIdFromMy, folderDao, fileDao); + + //delete My userFrom folder + folderDao.DeleteFolder(folderIdFromMy); + GlobalFolderHelper.FolderMy = userId; + } + + //delete all from Trash + var folderIdFromTrash = folderDao.GetFolderIDTrash(false, userId); + if (!Equals(folderIdFromTrash, 0)) + { + EntryManager.DeleteSubitems(folderIdFromTrash, folderDao, fileDao); + folderDao.DeleteFolder(folderIdFromTrash); + GlobalFolderHelper.FolderTrash = userId; + } + + EntryManager.ReassignItems(GlobalFolderHelper.FolderCommon, userId, AuthContext.CurrentAccount.ID, folderDao, fileDao); + } + + public ItemList GetSharedInfo(ItemList objectIds) + { + return FileSharing.GetSharedInfo(objectIds); + } + + public ItemList GetSharedInfoShort(string objectId) + { + return FileSharing.GetSharedInfoShort(objectId); + } + + public ItemList SetAceObject(AceCollection aceCollection, bool notify) + { + var fileDao = GetFileDao(); + var folderDao = GetFolderDao(); + var result = new ItemList(); + foreach (var objectId in aceCollection.Entries) + { + Debug.Assert(objectId != null, "objectId != null"); + var entryType = objectId.StartsWith("file_") ? FileEntryType.File : FileEntryType.Folder; + var entryId = objectId.Substring((entryType == FileEntryType.File ? "file_" : "folder_").Length); + var entry = entryType == FileEntryType.File + ? (FileEntry)fileDao.GetFile(entryId) + : (FileEntry)folderDao.GetFolder(entryId); + + try + { + var changed = FileSharingAceHelper.SetAceObject(aceCollection.Aces, entry, notify, aceCollection.Message); + if (changed) + { + FilesMessageService.Send(entry, GetHttpHeaders(), + entryType == FileEntryType.Folder ? MessageAction.FolderUpdatedAccess : MessageAction.FileUpdatedAccess, + entry.Title); + } + } + catch (Exception e) + { + throw GenerateException(e); + } + + var securityDao = GetSecurityDao(); + if (securityDao.IsShared(entry.ID, entryType)) + { + result.Add(objectId); + } + } + return result; + } + + public void RemoveAce(ItemList items) + { + ErrorIf(!AuthContext.IsAuthenticated, FilesCommonResource.ErrorMassage_SecurityException); + ParseArrayItems(items, out var foldersId, out var filesId); + + var entries = new List(); + + var fileDao = GetFileDao(); + var folderDao = GetFolderDao(); + entries.AddRange(filesId.Select(fileId => fileDao.GetFile(fileId))); + entries.AddRange(foldersId.Select(folderDao.GetFolder)); + + FileSharingAceHelper.RemoveAce(entries); + } + + public string GetShortenLink(string fileId) + { + File file; + var fileDao = GetFileDao(); + file = fileDao.GetFile(fileId); + ErrorIf(!FileSharing.CanSetAccess(file), FilesCommonResource.ErrorMassage_SecurityException); + var shareLink = FileShareLink.GetLink(file); + + try + { + return UrlShortener.Instance.GetShortenLink(shareLink); + } + catch (Exception e) + { + throw GenerateException(e); + } + } + + public bool SetAceLink(string fileId, FileShare share) + { + FileEntry file; + var fileDao = GetFileDao(); + file = fileDao.GetFile(fileId); + var aces = new List + { + new AceWrapper + { + Share = share, + SubjectId = FileConstant.ShareLinkId, + SubjectGroup = true, + } + }; + + try + { + + var changed = FileSharingAceHelper.SetAceObject(aces, file, false, null); + if (changed) + { + FilesMessageService.Send(file, GetHttpHeaders(), MessageAction.FileUpdatedAccess, file.Title); + } + } + catch (Exception e) + { + throw GenerateException(e); + } + + var securityDao = GetSecurityDao(); + return securityDao.IsShared(file.ID, FileEntryType.File); + } + + public ItemList SharedUsers(string fileId) + { + if (!AuthContext.IsAuthenticated || CoreBaseSettings.Personal) + return null; + + FileEntry file; + var fileDao = GetFileDao(); + file = fileDao.GetFile(fileId); + + ErrorIf(file == null, FilesCommonResource.ErrorMassage_FileNotFound); + + var usersIdWithAccess = new List(); + if (FileSharing.CanSetAccess(file)) + { + var access = FileSharing.GetSharedInfo(file); + usersIdWithAccess = access.Where(aceWrapper => !aceWrapper.SubjectGroup && aceWrapper.Share != FileShare.Restrict) + .Select(aceWrapper => aceWrapper.SubjectId) + .ToList(); + } + else + { + usersIdWithAccess.Add(file.CreateBy); + } + + var users = UserManager.GetUsersByGroup(Constants.GroupEveryone.ID) + .Where(user => !user.ID.Equals(AuthContext.CurrentAccount.ID) + && !user.ID.Equals(Constants.LostUser.ID)) + .Select(user => new MentionWrapper(user, DisplayUserSettingsHelper) { HasAccess = usersIdWithAccess.Contains(user.ID) }) + .ToList(); + + users = users + .OrderBy(user => !user.HasAccess) + .ThenBy(user => user.User, UserInfoComparer.Default) + .ToList(); + + return new ItemList(users); + } + + public ItemList SendEditorNotify(string fileId, MentionMessageWrapper mentionMessage) + { + ErrorIf(!AuthContext.IsAuthenticated, FilesCommonResource.ErrorMassage_SecurityException); + + File file; + var fileDao = GetFileDao(); + file = fileDao.GetFile(fileId); + + ErrorIf(file == null, FilesCommonResource.ErrorMassage_FileNotFound); + + var fileSecurity = FileSecurity; + ErrorIf(!fileSecurity.CanRead(file), FilesCommonResource.ErrorMassage_SecurityException_ReadFile); + ErrorIf(mentionMessage == null || mentionMessage.Emails == null, FilesCommonResource.ErrorMassage_BadRequest); + + var changed = false; + bool? canShare = null; + if (file.Encrypted) canShare = false; + + var recipients = new List(); + foreach (var email in mentionMessage.Emails) + { + if (!canShare.HasValue) + { + canShare = FileSharing.CanSetAccess(file); + } + + var recipient = UserManager.GetUserByEmail(email); + if (recipient == null || recipient.ID == Constants.LostUser.ID) + { + changed = canShare.Value; + continue; + } + + if (!fileSecurity.CanRead(file, recipient.ID)) + { + if (!canShare.Value) + { + continue; + } + + try + { + var aces = new List + { + new AceWrapper + { + Share = FileShare.Read, + SubjectId = recipient.ID, + SubjectGroup = false, + } + }; + + changed |= FileSharingAceHelper.SetAceObject(aces, file, false, null); + + recipients.Add(recipient.ID); + } + catch (Exception e) + { + throw GenerateException(e); + } + } + else + { + recipients.Add(recipient.ID); + } + } + + if (changed) + { + FilesMessageService.Send(file, GetHttpHeaders(), MessageAction.FileUpdatedAccess, file.Title); + } + + var fileLink = FilesLinkUtility.GetFileWebEditorUrl(file.ID); + if (mentionMessage.ActionLink != null) + { + fileLink += "&" + FilesLinkUtility.Anchor + "=" + HttpUtility.UrlEncode( + DocumentService.Configuration.EditorConfiguration.ActionLinkConfig.Serialize(mentionMessage.ActionLink)); + } + + var message = (mentionMessage.Message ?? "").Trim(); + const int maxMessageLength = 200; + if (message.Length > maxMessageLength) + { + message = message.Substring(0, maxMessageLength) + "..."; + } + + NotifyClient.SendEditorMentions(file, fileLink, recipients, message); + + return changed ? GetSharedInfoShort("file_" + fileId) : null; + } + + public ItemList GetMailAccounts() + { + return null; + //var apiServer = new ASC.Api.ApiServer(); + //var apiUrl = string.Format("{0}mail/accounts.json", SetupInfo.WebApiBaseUrl); + + //var accounts = new List(); + + //var responseBody = apiServer.GetApiResponse(apiUrl, "GET"); + //if (responseBody != null) + //{ + // var responseApi = JObject.Parse(Encoding.UTF8.GetString(Convert.FromBase64String(responseBody))); + + // var responseData = responseApi["response"]; + // if (responseData is JArray) + // { + // accounts.AddRange( + // from account in responseData.Children() + // orderby account["isDefault"].Value() descending + // where account["enabled"].Value() && !account["isGroup"].Value() + // select account["email"].Value() + // ); + // } + //} + //ErrorIf(!accounts.Any(), FilesCommonResource.ErrorMassage_MailAccountNotFound); + + //return new ItemList(accounts); + } + + public ItemList ChangeOwner(ItemList items, Guid userId) + { + var userInfo = UserManager.GetUsers(userId); + ErrorIf(Equals(userInfo, Constants.LostUser) || userInfo.IsVisitor(UserManager), FilesCommonResource.ErrorMassage_ChangeOwner); + + ParseArrayItems(items, out var foldersId, out var filesId); + + var entries = new List(); + + var folderDao = GetFolderDao(); + var folders = folderDao.GetFolders(foldersId.ToArray()); + + foreach (var folder in folders) + { + ErrorIf(!FileSecurity.CanEdit(folder), FilesCommonResource.ErrorMassage_SecurityException); + ErrorIf(folder.RootFolderType != FolderType.COMMON, FilesCommonResource.ErrorMassage_SecurityException); + if (folder.ProviderEntry) continue; + + var newFolder = folder; + if (folder.CreateBy != userInfo.ID) + { + var folderAccess = folder.Access; + + newFolder.CreateBy = userInfo.ID; + var newFolderID = folderDao.SaveFolder((Folder)newFolder); + + newFolder = folderDao.GetFolder(newFolderID); + newFolder.Access = folderAccess; + + FilesMessageService.Send(newFolder, GetHttpHeaders(), MessageAction.FileChangeOwner, new[] { newFolder.Title, userInfo.DisplayUserName(false, DisplayUserSettingsHelper) }); + } + entries.Add(newFolder); + } + + var fileDao = GetFileDao(); + var files = fileDao.GetFiles(filesId.ToArray()); + + foreach (var file in files) + { + ErrorIf(!FileSecurity.CanEdit(file), FilesCommonResource.ErrorMassage_SecurityException); + ErrorIf(EntryManager.FileLockedForMe(file.ID), FilesCommonResource.ErrorMassage_LockedFile); + ErrorIf(FileTracker.IsEditing(file.ID), FilesCommonResource.ErrorMassage_UpdateEditingFile); + ErrorIf(file.RootFolderType != FolderType.COMMON, FilesCommonResource.ErrorMassage_SecurityException); + if (file.ProviderEntry) continue; + + var newFile = file; + if (file.CreateBy != userInfo.ID) + { + newFile = ServiceProvider.GetService(); + newFile.ID = file.ID; + newFile.Version = file.Version + 1; + newFile.VersionGroup = file.VersionGroup + 1; + newFile.Title = file.Title; + newFile.FileStatus = file.FileStatus; + newFile.FolderID = file.FolderID; + newFile.CreateBy = userInfo.ID; + newFile.CreateOn = file.CreateOn; + newFile.ConvertedType = file.ConvertedType; + newFile.Comment = FilesCommonResource.CommentChangeOwner; + newFile.Encrypted = file.Encrypted; + + using (var stream = fileDao.GetFileStream(file)) + { + newFile.ContentLength = stream.CanSeek ? stream.Length : file.ContentLength; + newFile = fileDao.SaveFile(newFile, stream); + } + + FileMarker.MarkAsNew(newFile); + + EntryManager.SetFileStatus(newFile); + + FilesMessageService.Send(newFile, GetHttpHeaders(), MessageAction.FileChangeOwner, new[] { newFile.Title, userInfo.DisplayUserName(false, DisplayUserSettingsHelper) }); + } + entries.Add(newFile); + } + + return new ItemList(entries); + } + + public bool StoreOriginal(bool set) + { + FilesSettingsHelper.StoreOriginalFiles = set; + FilesMessageService.Send(GetHttpHeaders(), MessageAction.DocumentsUploadingFormatsSettingsUpdated); + + return FilesSettingsHelper.StoreOriginalFiles; + } + + public bool HideConfirmConvert(bool isForSave) + { + if (isForSave) + { + FilesSettingsHelper.HideConfirmConvertSave = true; + } + else + { + FilesSettingsHelper.HideConfirmConvertOpen = true; + } + + return true; + } + + public bool UpdateIfExist(bool set) + { + FilesSettingsHelper.UpdateIfExist = set; + FilesMessageService.Send(GetHttpHeaders(), MessageAction.DocumentsOverwritingSettingsUpdated); + + return FilesSettingsHelper.UpdateIfExist; + } + + public bool Forcesave(bool set) + { + FilesSettingsHelper.Forcesave = set; + FilesMessageService.Send(GetHttpHeaders(), MessageAction.DocumentsForcesave); + + return FilesSettingsHelper.Forcesave; + } + + public bool StoreForcesave(bool set) + { + ErrorIf(!Global.IsAdministrator, FilesCommonResource.ErrorMassage_SecurityException); + + FilesSettingsHelper.StoreForcesave = set; + FilesMessageService.Send(GetHttpHeaders(), MessageAction.DocumentsStoreForcesave); + + return FilesSettingsHelper.StoreForcesave; + } + + public bool ChangeDeleteConfrim(bool set) + { + FilesSettingsHelper.ConfirmDelete = set; + + return FilesSettingsHelper.ConfirmDelete; + } + + public string GetHelpCenter() + { + return ""; //TODO: Studio.UserControls.Common.HelpCenter.HelpCenter.RenderControlToString(); + } + + private IFolderDao GetFolderDao() + { + return DaoFactory.FolderDao; + } + + private IFileDao GetFileDao() + { + return DaoFactory.FileDao; + } + + private ITagDao GetTagDao() + { + return DaoFactory.TagDao; + } + + private IDataStore GetStoreTemplate() + { + return GlobalStore.GetStoreTemplate(); + } + + private IProviderDao GetProviderDao() + { + return DaoFactory.ProviderDao; + } + + private ISecurityDao GetSecurityDao() + { + return DaoFactory.SecurityDao; + } + + private static void ParseArrayItems(IEnumerable data, out List foldersId, out List filesId) + { + //TODO:!!!!Fix + foldersId = new List(); + filesId = new List(); + foreach (var id in data) + { + if (id.StartsWith("file_")) filesId.Add(id.Substring("file_".Length)); + if (id.StartsWith("folder_")) foldersId.Add(id.Substring("folder_".Length)); + } + } + + private static void ParseArrayItems(Dictionary items, out Dictionary folders, out Dictionary files) + { + //TODO:!!!!Fix + folders = new Dictionary(); + files = new Dictionary(); + foreach (var item in (items ?? new Dictionary())) + { + if (item.Key.StartsWith("file_")) files.Add(item.Key.Substring("file_".Length), item.Value); + if (item.Key.StartsWith("folder_")) folders.Add(item.Key.Substring("folder_".Length), item.Value); + } + } + + private static void ErrorIf(bool condition, string errorMessage) + { + if (condition) throw new InvalidOperationException(errorMessage); + } + + private Exception GenerateException(Exception error, bool warning = false) + { + if (warning) + { + Logger.Info(error); + } + else + { + Logger.Error(error); + } + return new InvalidOperationException(error.Message, error); + } + + private Dictionary GetHttpHeaders() + { + if (HttpContextAccessor?.HttpContext != null && HttpContextAccessor?.HttpContext.Request != null && HttpContextAccessor?.HttpContext.Request.Headers != null) + { + var headers = new Dictionary(); + foreach (var k in HttpContextAccessor?.HttpContext.Request.Headers) + { + headers[k.Key] = string.Join(", ", k.Value); + } + return headers; + } + return null; + } + } + + public static class FileStorageServiceExtention + { + public static DIHelper AddFileStorageService(this DIHelper services) + { + services.TryAddScoped(); + services.TryAddScoped(); + return services + .AddGlobalService() + .AddGlobalStoreService() + .AddGlobalFolderHelperService() + .AddAuthContextService() + .AddUserManagerService() + .AddFoldersWrapperService() + .AddFilesWrapperService() + .AddFilesLinkUtilityService() + .AddBaseCommonLinkUtilityService() + .AddCoreBaseSettingsService() + .AddCustomNamingPeopleService() + .AddDisplayUserSettingsService() + .AddPathProviderService() + .AddDaoFactoryService() + .AddFileMarkerService() + .AddFilesSettingsHelperService() + .AddFileUtilityService() + .AddFileSecurityService() + .AddFilesMessageService() + .AddFileShareLinkService() + .AddDocumentServiceConnectorService() + .AddDocuSignLoginProviderService() + .AddEntryManagerService() + .AddDocumentServiceHelperService() + .AddThirdpartyConfigurationService() + .AddUrlShortener() + .AddDocuSignHelperService() + .AddDocuSignTokenService() + .AddFileConverterService() + .AddNotifyClientService() + .AddFileSharingService() + .AddDocumentServiceTrackerHelperService() + .AddSocketManagerService() + .AddFileOperationsManagerHelperService() + .AddFileSharingAceHelperService(); + ; + } + } + + public class FileModel + { + public string ParentId { get; set; } + public string Title { get; set; } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/FilesIntegration.cs b/products/ASC.Files/Server/Core/FilesIntegration.cs new file mode 100644 index 0000000000..8be9cca643 --- /dev/null +++ b/products/ASC.Files/Server/Core/FilesIntegration.cs @@ -0,0 +1,171 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Common; +using ASC.Data.Storage; +using ASC.Files.Core; +using ASC.Files.Core.Data; +using ASC.Files.Core.Security; +using ASC.Web.Files.Classes; + +namespace ASC.Web.Files.Api +{ + public class FilesIntegration + { + private static readonly IDictionary providers = new Dictionary(); + + public IDaoFactory DaoFactory { get; } + public IFileSecurity FileSecurity { get; } + public GlobalStore GlobalStore { get; } + + public FilesIntegration(IDaoFactory daoFactory, IFileSecurity fileSecurity, GlobalStore globalStore) + { + DaoFactory = daoFactory; + FileSecurity = fileSecurity; + GlobalStore = globalStore; + } + + public object RegisterBunch(string module, string bunch, string data) + { + var folderDao = GetFolderDao(); + return folderDao.GetFolderID(module, bunch, data, true); + } + + public IEnumerable RegisterBunchFolders(string module, string bunch, IEnumerable data) + { + if (data == null) + throw new ArgumentNullException("data"); + + data = data.ToList(); + if (!data.Any()) + return new List(); + + var folderDao = GetFolderDao(); + return folderDao.GetFolderIDs(module, bunch, data, true); + } + + public bool IsRegisteredFileSecurityProvider(string module, string bunch) + { + lock (providers) + { + return providers.ContainsKey(module + bunch); + } + + } + + public void RegisterFileSecurityProvider(string module, string bunch, IFileSecurityProvider securityProvider) + { + lock (providers) + { + providers[module + bunch] = securityProvider; + } + } + + public IFileDao GetFileDao() + { + return DaoFactory.FileDao; + } + + public IFolderDao GetFolderDao() + { + return DaoFactory.FolderDao; + } + + public ITagDao TagDao() + { + return DaoFactory.TagDao; + } + + public IDataStore GetStore() + { + return GlobalStore.GetStore(); + } + + + internal static IFileSecurity GetFileSecurity(string path) + { + if (string.IsNullOrEmpty(path)) return null; + + var parts = path.Split('/'); + if (parts.Length < 3) return null; + + IFileSecurityProvider provider; + lock (providers) + { + providers.TryGetValue(parts[0] + parts[1], out provider); + } + return provider?.GetFileSecurity(parts[2]); + } + + internal static Dictionary GetFileSecurity(Dictionary paths) + { + var result = new Dictionary(); + var gropped = paths.GroupBy(r => + { + var parts = r.Value.Split('/'); + if (parts.Length < 3) return ""; + + return parts[0] + parts[1]; + }, v => + { + var parts = v.Value.Split('/'); + if (parts.Length < 3) return new KeyValuePair(v.Key, ""); + + return new KeyValuePair(v.Key, parts[2]); + }); + + foreach (var grouping in gropped) + { + IFileSecurityProvider provider; + lock (providers) + { + providers.TryGetValue(grouping.Key, out provider); + } + if (provider == null) continue; + + var data = provider.GetFileSecurity(grouping.ToDictionary(r => r.Key, r => r.Value)); + data.ToList().ForEach(x => result.Add(x.Key, x.Value)); + } + + return result; + } + } + public static class FilesIntegrationExtension + { + public static DIHelper AddFilesIntegrationService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddDaoFactoryService() + .AddFileSecurityService() + .AddGlobalStoreService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/FilterTypeEnum.cs b/products/ASC.Files/Server/Core/FilterTypeEnum.cs new file mode 100644 index 0000000000..8a6ae32d45 --- /dev/null +++ b/products/ASC.Files/Server/Core/FilterTypeEnum.cs @@ -0,0 +1,58 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.Runtime.Serialization; + +namespace ASC.Files.Core +{ + [DataContract] + public enum FilterType + { + [EnumMember] None = 0, + + [EnumMember] FilesOnly = 1, + + [EnumMember] FoldersOnly = 2, + + [EnumMember] DocumentsOnly = 3, + + [EnumMember] PresentationsOnly = 4, + + [EnumMember] SpreadsheetsOnly = 5, + + [EnumMember] ImagesOnly = 7, + + [EnumMember] ByUser = 8, + + [EnumMember] ByDepartment = 9, + + [EnumMember] ArchiveOnly = 10, + + [EnumMember] ByExtension = 11, + + [EnumMember] MediaOnly = 12, + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/ResponseStream.cs b/products/ASC.Files/Server/Core/ResponseStream.cs new file mode 100644 index 0000000000..d06e0eb780 --- /dev/null +++ b/products/ASC.Files/Server/Core/ResponseStream.cs @@ -0,0 +1,120 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.IO; +using System.Net; + +namespace ASC.Web.Files.Core +{ + public class ResponseStream : Stream + { + private readonly WebResponse _webResponse; + private readonly Stream _stream; + private readonly long _length; + + public ResponseStream(Stream stream, long length) + { + _stream = stream; + _length = length; + } + + public ResponseStream(WebResponse response) + { + _stream = response.GetResponseStream(); + _length = response.ContentLength; + _webResponse = response; + } + + public override bool CanRead + { + get { return _stream.CanRead; } + } + + public override bool CanSeek + { + get { return _stream.CanSeek; } + } + + public override bool CanWrite + { + get { return _stream.CanWrite; } + } + + public override void Flush() + { + _stream.Flush(); + } + + public override long Length + { + get { return _length; } + } + + public override long Position + { + get { return _stream.Position; } + set { _stream.Position = value; } + } + + public override int Read(byte[] buffer, int offset, int count) + { + return _stream.Read(buffer, offset, count); + } + + public override long Seek(long offset, SeekOrigin origin) + { + return _stream.Seek(offset, origin); + } + + public override void SetLength(long value) + { + _stream.SetLength(value); + } + + public override void Write(byte[] buffer, int offset, int count) + { + _stream.Write(buffer, offset, count); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _stream.Dispose(); + if (_webResponse != null) + _webResponse.Dispose(); + } + base.Dispose(disposing); + } + + public override void Close() + { + _stream.Close(); + if (_webResponse != null) + _webResponse.Close(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/Search/FilesWrapper.cs b/products/ASC.Files/Server/Core/Search/FilesWrapper.cs new file mode 100644 index 0000000000..901320bd06 --- /dev/null +++ b/products/ASC.Files/Server/Core/Search/FilesWrapper.cs @@ -0,0 +1,176 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using ASC.Common; +using ASC.Core; +using ASC.ElasticSearch; +using ASC.ElasticSearch.Core; +using ASC.Files.Core; +using ASC.Files.Core.Data; +using ASC.Files.Resources; +using ASC.Web.Core.Files; + +using Microsoft.Extensions.DependencyInjection; + +using File = ASC.Files.Core.File; + +namespace ASC.Web.Files.Core.Search +{ + public sealed class FilesWrapper : WrapperWithDoc + { + [Column("title", 1)] + public string Title { get; set; } + + [ColumnLastModified("modified_on")] + public override DateTime LastModifiedOn { get; set; } + + [ColumnMeta("version", 2)] + public int Version { get; set; } + + [ColumnCondition("current_version", 3, true)] + public bool Current { get; set; } + + [ColumnMeta("encrypted", 4)] + public bool Encrypted { get; set; } + + [ColumnMeta("content_length", 5)] + public long ContentLength { get; set; } + + [ColumnMeta("create_by", 6)] + public Guid CreateBy { get; set; } + + [ColumnMeta("create_on", 7)] + public DateTime CreateOn { get; set; } + + [ColumnMeta("category", 8)] + public int Category { get; set; } + + + [Join(JoinTypeEnum.Sub, "folder_id:folder_id")] + public List Folders { get; set; } + + protected override string Table { get { return "files_file"; } } + + + public FilesWrapper() + { + + } + + public FilesWrapper(IServiceProvider serviceProvider, TenantManager tenantManager, FileUtility fileUtility, IDaoFactory daoFactory) + { + ServiceProvider = serviceProvider; + TenantManager = tenantManager; + FileUtility = fileUtility; + DaoFactory = daoFactory; + } + + public static FilesWrapper GetFilesWrapper(IServiceProvider serviceProvider, File d, List parentFolders = null) + { + var wrapper = serviceProvider.GetService(); + var tenantManager = serviceProvider.GetService(); + + wrapper.Id = (int)d.ID; + wrapper.Title = d.Title; + wrapper.Version = d.Version; + wrapper.Encrypted = d.Encrypted; + wrapper.ContentLength = d.ContentLength; + wrapper.LastModifiedOn = d.ModifiedOn; + wrapper.TenantId = tenantManager.GetCurrentTenant().TenantId; + + if (parentFolders != null) + { + wrapper.Folders = parentFolders.Select(r => new FilesFoldersWrapper { FolderId = r.ToString() }).ToList(); + } + return wrapper; + } + + protected override Stream GetDocumentStream() + { + TenantManager.SetCurrentTenant(TenantId); + + if (Encrypted) return null; + if (!FileUtility.CanIndex(Title)) return null; + + var fileDao = DaoFactory.FileDao; + var file = ServiceProvider.GetService(); + file.ID = Id; + file.Title = Title; + file.Version = Version; + file.ContentLength = ContentLength; + + if (!fileDao.IsExistOnStorage(file)) return null; + if (file.ContentLength > MaxContentLength) return null; + + return fileDao.GetFileStream(file); + } + + public override string SettingsTitle + { + get { return FilesCommonResource.IndexTitle; } + } + + private IServiceProvider ServiceProvider { get; } + private TenantManager TenantManager { get; } + private FileUtility FileUtility { get; } + private IDaoFactory DaoFactory { get; } + } + + public sealed class FilesFoldersWrapper : Wrapper + { + [Column("parent_id", 1)] + public string FolderId { get; set; } + + [ColumnId("")] + public override int Id { get; set; } + + [ColumnTenantId("")] + public override int TenantId { get; set; } + + [ColumnLastModified("")] + public override DateTime LastModifiedOn { get; set; } + + protected override string Table { get { return "files_folder_tree"; } } + } + + public static class FilesWrapperExtention + { + public static DIHelper AddFilesWrapperService(this DIHelper services) + { + services.TryAddTransient(); + return services + .AddTenantManagerService() + .AddFileUtilityService() + .AddDaoFactoryService() + .AddFactoryIndexerService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/Search/FoldersWrapper.cs b/products/ASC.Files/Server/Core/Search/FoldersWrapper.cs new file mode 100644 index 0000000000..0ee067115c --- /dev/null +++ b/products/ASC.Files/Server/Core/Search/FoldersWrapper.cs @@ -0,0 +1,69 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; + +using ASC.Common; +using ASC.Core; +using ASC.ElasticSearch; +using ASC.Files.Core; + +using Microsoft.Extensions.DependencyInjection; + +namespace ASC.Web.Files.Core.Search +{ + public sealed class FoldersWrapper : Wrapper + { + [Column("title", 1)] + public string Title { get; set; } + + [ColumnLastModified("modified_on")] + public override DateTime LastModifiedOn { get; set; } + + protected override string Table { get { return "files_folder"; } } + + public static FoldersWrapper GetFolderWrapper(IServiceProvider serviceProvider, Folder d) + { + var tenantManager = serviceProvider.GetService(); + + return new FoldersWrapper + { + Id = (int)d.ID, + Title = d.Title, + TenantId = tenantManager.GetCurrentTenant().TenantId + }; + } + } + public static class FoldersWrapperExtention + { + public static DIHelper AddFoldersWrapperService(this DIHelper services) + { + services.TryAddTransient(); + return services + .AddFactoryIndexerService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/Security/FileSecurity.cs b/products/ASC.Files/Server/Core/Security/FileSecurity.cs new file mode 100644 index 0000000000..0ee74b380f --- /dev/null +++ b/products/ASC.Files/Server/Core/Security/FileSecurity.cs @@ -0,0 +1,775 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Common; +using ASC.Core; +using ASC.Core.Users; +using ASC.Files.Core.Data; +using ASC.Web.Core; +using ASC.Web.Files.Api; +using ASC.Web.Files.Classes; +using ASC.Web.Files.Configuration; + +namespace ASC.Files.Core.Security +{ + public class FileSecurityCommon + { + public UserManager UserManager { get; } + public WebItemSecurity WebItemSecurity { get; } + + public FileSecurityCommon(UserManager userManager, WebItemSecurity webItemSecurity) + { + UserManager = userManager; + WebItemSecurity = webItemSecurity; + } + + public bool IsAdministrator(Guid userId) + { + return UserManager.IsUserInGroup(userId, Constants.GroupAdmin.ID) || + WebItemSecurity.IsProductAdministrator(ProductEntryPoint.ID, userId); + } + } + + public class FileSecurity : IFileSecurity + { + private readonly IDaoFactory daoFactory; + + public FileShare DefaultMyShare + { + get { return FileShare.Restrict; } + } + + public FileShare DefaultProjectsShare + { + get { return FileShare.ReadWrite; } + } + + public FileShare DefaultCommonShare + { + get { return FileShare.Read; } + } + + public UserManager UserManager { get; } + public TenantManager TenantManager { get; } + public AuthContext AuthContext { get; } + public AuthManager AuthManager { get; } + public GlobalFolder GlobalFolder { get; } + public FileSecurityCommon FileSecurityCommon { get; } + + public FileSecurity( + IDaoFactory daoFactory, + UserManager userManager, + TenantManager tenantManager, + AuthContext authContext, + AuthManager authManager, + GlobalFolder globalFolder, + FileSecurityCommon fileSecurityCommon) + { + this.daoFactory = daoFactory; + UserManager = userManager; + TenantManager = tenantManager; + AuthContext = authContext; + AuthManager = authManager; + GlobalFolder = globalFolder; + FileSecurityCommon = fileSecurityCommon; + } + + public List> CanRead(IEnumerable entry, Guid userId) + { + return Can(entry, userId, FilesSecurityActions.Read); + } + + public bool CanRead(FileEntry entry, Guid userId) + { + return Can(entry, userId, FilesSecurityActions.Read); + } + + public bool CanComment(FileEntry entry, Guid userId) + { + return Can(entry, userId, FilesSecurityActions.Comment); + } + + public bool CanFillForms(FileEntry entry, Guid userId) + { + return Can(entry, userId, FilesSecurityActions.FillForms); + } + + public bool CanReview(FileEntry entry, Guid userId) + { + return Can(entry, userId, FilesSecurityActions.Review); + } + + public bool CanCreate(FileEntry entry, Guid userId) + { + return Can(entry, userId, FilesSecurityActions.Create); + } + + public bool CanEdit(FileEntry entry, Guid userId) + { + return Can(entry, userId, FilesSecurityActions.Edit); + } + + public bool CanDelete(FileEntry entry, Guid userId) + { + return Can(entry, userId, FilesSecurityActions.Delete); + } + + public bool CanRead(FileEntry entry) + { + return CanRead(entry, AuthContext.CurrentAccount.ID); + } + + public bool CanComment(FileEntry entry) + { + return CanComment(entry, AuthContext.CurrentAccount.ID); + } + + public bool CanFillForms(FileEntry entry) + { + return CanFillForms(entry, AuthContext.CurrentAccount.ID); + } + + public bool CanReview(FileEntry entry) + { + return CanReview(entry, AuthContext.CurrentAccount.ID); + } + + public bool CanCreate(FileEntry entry) + { + return CanCreate(entry, AuthContext.CurrentAccount.ID); + } + + public bool CanEdit(FileEntry entry) + { + return CanEdit(entry, AuthContext.CurrentAccount.ID); + } + + public bool CanDelete(FileEntry entry) + { + return CanDelete(entry, AuthContext.CurrentAccount.ID); + } + + public IEnumerable WhoCanRead(FileEntry entry) + { + return WhoCan(entry, FilesSecurityActions.Read); + } + + private IEnumerable WhoCan(FileEntry entry, FilesSecurityActions action) + { + var shares = GetShares(entry); + + FileShareRecord defaultShareRecord; + + switch (entry.RootFolderType) + { + case FolderType.COMMON: + defaultShareRecord = new FileShareRecord + { + Level = int.MaxValue, + EntryId = entry.ID, + EntryType = entry.FileEntryType, + Share = DefaultCommonShare, + Subject = Constants.GroupEveryone.ID, + Tenant = TenantManager.GetCurrentTenant().TenantId, + Owner = AuthContext.CurrentAccount.ID + }; + + if (!shares.Any()) + { + if ((defaultShareRecord.Share == FileShare.Read && action == FilesSecurityActions.Read) || + (defaultShareRecord.Share == FileShare.ReadWrite)) + return UserManager.GetUsersByGroup(defaultShareRecord.Subject) + .Where(x => x.Status == EmployeeStatus.Active).Select(y => y.ID).Distinct(); + + return Enumerable.Empty(); + } + + break; + + case FolderType.USER: + defaultShareRecord = new FileShareRecord + { + Level = int.MaxValue, + EntryId = entry.ID, + EntryType = entry.FileEntryType, + Share = DefaultMyShare, + Subject = entry.RootFolderCreator, + Tenant = TenantManager.GetCurrentTenant().TenantId, + Owner = entry.RootFolderCreator + }; + + if (!shares.Any()) + return new List + { + entry.RootFolderCreator + }; + + break; + + case FolderType.BUNCH: + if (action == FilesSecurityActions.Read) + { + var folderDao = daoFactory.FolderDao; + var root = folderDao.GetFolder(entry.RootFolderId); + if (root != null) + { + var path = folderDao.GetBunchObjectID(root.ID); + + var adapter = FilesIntegration.GetFileSecurity(path); + + if (adapter != null) + { + return adapter.WhoCanRead(entry); + } + } + } + + // TODO: For Projects and other + defaultShareRecord = null; + break; + + default: + defaultShareRecord = null; + break; + } + + if (defaultShareRecord != null) + shares = shares.Concat(new[] { defaultShareRecord }); + + return shares.SelectMany(x => + { + var groupInfo = UserManager.GetGroupInfo(x.Subject); + + if (groupInfo.ID != Constants.LostGroupInfo.ID) + return + UserManager.GetUsersByGroup(groupInfo.ID) + .Where(p => p.Status == EmployeeStatus.Active) + .Select(y => y.ID); + + return new[] { x.Subject }; + }) + .Distinct() + .Where(x => Can(entry, x, action)) + .ToList(); + } + + public IEnumerable FilterRead(IEnumerable entries) + { + return Filter(entries, FilesSecurityActions.Read, AuthContext.CurrentAccount.ID); + } + + public IEnumerable FilterRead(IEnumerable entries) + { + return Filter(entries.Cast(), FilesSecurityActions.Read, AuthContext.CurrentAccount.ID).Cast(); + } + + public IEnumerable FilterRead(IEnumerable entries) + { + return Filter(entries.Cast(), FilesSecurityActions.Read, AuthContext.CurrentAccount.ID).Cast(); + } + + public IEnumerable FilterEdit(IEnumerable entries) + { + return Filter(entries.Cast(), FilesSecurityActions.Edit, AuthContext.CurrentAccount.ID).Cast(); + } + + public IEnumerable FilterEdit(IEnumerable entries) + { + return Filter(entries.Cast(), FilesSecurityActions.Edit, AuthContext.CurrentAccount.ID).Cast(); + } + + private bool Can(FileEntry entry, Guid userId, FilesSecurityActions action) + { + return Filter(new[] { entry }, action, userId).Any(); + } + + private List> Can(IEnumerable entry, Guid userId, FilesSecurityActions action) + { + var filtres = Filter(entry, action, userId); + return entry.Select(r => new Tuple(r, filtres.Any(a => a.ID.Equals(r.ID)))).ToList(); + } + + private IEnumerable Filter(IEnumerable entries, FilesSecurityActions action, Guid userId) + { + if (entries == null || !entries.Any()) return Enumerable.Empty(); + + var user = UserManager.GetUsers(userId); + var isOutsider = user.IsOutsider(UserManager); + + if (isOutsider && action != FilesSecurityActions.Read) return Enumerable.Empty(); + + entries = entries.Where(f => f != null).ToList(); + var result = new List(entries.Count()); + + // save entries order + var order = entries.Select((f, i) => new { Id = f.UniqID, Pos = i }).ToDictionary(e => e.Id, e => e.Pos); + + // common or my files + Func filter = + f => f.RootFolderType == FolderType.COMMON || + f.RootFolderType == FolderType.USER || + f.RootFolderType == FolderType.SHARE || + f.RootFolderType == FolderType.Projects; + + var isVisitor = user.IsVisitor(UserManager); + + if (entries.Any(filter)) + { + var subjects = GetUserSubjects(userId); + List shares = null; + foreach (var e in entries.Where(filter)) + { + if (!AuthManager.GetAccountByID(TenantManager.GetCurrentTenant().TenantId, userId).IsAuthenticated && userId != FileConstant.ShareLinkId) + { + continue; + } + + if (isOutsider && (e.RootFolderType == FolderType.USER + || e.RootFolderType == FolderType.SHARE + || e.RootFolderType == FolderType.TRASH)) + { + continue; + } + + if (action != FilesSecurityActions.Read && e.FileEntryType == FileEntryType.Folder && ((Folder)e).FolderType == FolderType.Projects) + { + // Root Projects folder read-only + continue; + } + + if (action != FilesSecurityActions.Read && e.FileEntryType == FileEntryType.Folder && ((Folder)e).FolderType == FolderType.SHARE) + { + // Root Share folder read-only + continue; + } + + if (isVisitor && e.ProviderEntry) + { + continue; + } + + if (e.RootFolderType == FolderType.USER && e.RootFolderCreator == userId && !isVisitor) + { + // user has all right in his folder + result.Add(e); + continue; + } + + if (DefaultCommonShare == FileShare.Read && action == FilesSecurityActions.Read && e.FileEntryType == FileEntryType.Folder && + ((Folder)e).FolderType == FolderType.COMMON) + { + // all can read Common folder + result.Add(e); + continue; + } + + if (action == FilesSecurityActions.Read && e.FileEntryType == FileEntryType.Folder && + ((Folder)e).FolderType == FolderType.SHARE) + { + // all can read Share folder + result.Add(e); + continue; + } + + if (e.RootFolderType == FolderType.COMMON && FileSecurityCommon.IsAdministrator(userId)) + { + // administrator in Common has all right + result.Add(e); + continue; + } + + if (shares == null) + { + shares = GetShares(entries).Join(subjects, r => r.Subject, s => s, (r, s) => r).ToList(); + // shares ordered by level + } + + FileShareRecord ace; + if (e.FileEntryType == FileEntryType.File) + { + ace = shares + .OrderBy(r => r, new SubjectComparer(subjects)) + .ThenByDescending(r => r.Share, new FileShareRecord.ShareComparer()) + .FirstOrDefault(r => Equals(r.EntryId, e.ID) && r.EntryType == FileEntryType.File); + if (ace == null) + { + // share on parent folders + ace = shares.Where(r => Equals(r.EntryId, ((File)e).FolderID) && r.EntryType == FileEntryType.Folder) + .OrderBy(r => r, new SubjectComparer(subjects)) + .ThenBy(r => r.Level) + .ThenByDescending(r => r.Share, new FileShareRecord.ShareComparer()) + .FirstOrDefault(); + } + } + else + { + ace = shares.Where(r => Equals(r.EntryId, e.ID) && r.EntryType == FileEntryType.Folder) + .OrderBy(r => r, new SubjectComparer(subjects)) + .ThenBy(r => r.Level) + .ThenByDescending(r => r.Share, new FileShareRecord.ShareComparer()) + .FirstOrDefault(); + } + var defaultShare = e.RootFolderType == FolderType.USER ? DefaultMyShare : DefaultCommonShare; + e.Access = ace != null ? ace.Share : defaultShare; + + if (action == FilesSecurityActions.Read && e.Access != FileShare.Restrict) result.Add(e); + else if (action == FilesSecurityActions.Comment && (e.Access == FileShare.Comment || e.Access == FileShare.Review || e.Access == FileShare.ReadWrite)) result.Add(e); + else if (action == FilesSecurityActions.FillForms && (e.Access == FileShare.FillForms || e.Access == FileShare.Review || e.Access == FileShare.ReadWrite)) result.Add(e); + else if (action == FilesSecurityActions.Review && (e.Access == FileShare.Review || e.Access == FileShare.ReadWrite)) result.Add(e); + else if (action == FilesSecurityActions.Edit && e.Access == FileShare.ReadWrite) result.Add(e); + else if (action == FilesSecurityActions.Create && e.Access == FileShare.ReadWrite) result.Add(e); + else if (e.Access != FileShare.Restrict && e.CreateBy == userId && (e.FileEntryType == FileEntryType.File || ((Folder)e).FolderType != FolderType.COMMON)) result.Add(e); + + if (e.CreateBy == userId) e.Access = FileShare.None; //HACK: for client + } + } + + // files in bunch + filter = f => f.RootFolderType == FolderType.BUNCH; + if (entries.Any(filter)) + { + var folderDao = daoFactory.FolderDao; + var filteredEntries = entries.Where(filter).ToList(); + var roots = filteredEntries + .Select(r => r.RootFolderId) + .ToArray(); + + var rootsFolders = folderDao.GetFolders(roots); + var bunches = folderDao.GetBunchObjectIDs(rootsFolders.Select(r => r.ID).ToList()); + var findedAdapters = FilesIntegration.GetFileSecurity(bunches); + + foreach (var e in filteredEntries) + { + var adapter = findedAdapters[e.RootFolderId.ToString()]; + + if (adapter == null) continue; + + if (adapter.CanRead(e, userId) && + adapter.CanCreate(e, userId) && + adapter.CanEdit(e, userId) && + adapter.CanDelete(e, userId)) + { + e.Access = FileShare.None; + result.Add(e); + } + else if (action == FilesSecurityActions.Comment && adapter.CanComment(e, userId)) + { + e.Access = FileShare.Comment; + result.Add(e); + } + else if (action == FilesSecurityActions.FillForms && adapter.CanFillForms(e, userId)) + { + e.Access = FileShare.FillForms; + result.Add(e); + } + else if (action == FilesSecurityActions.Review && adapter.CanReview(e, userId)) + { + e.Access = FileShare.Review; + result.Add(e); + } + else if (action == FilesSecurityActions.Create && adapter.CanCreate(e, userId)) + { + e.Access = FileShare.ReadWrite; + result.Add(e); + } + else if (action == FilesSecurityActions.Delete && adapter.CanDelete(e, userId)) + { + e.Access = FileShare.ReadWrite; + result.Add(e); + } + else if (action == FilesSecurityActions.Read && adapter.CanRead(e, userId)) + { + if (adapter.CanCreate(e, userId) || + adapter.CanDelete(e, userId) || + adapter.CanEdit(e, userId)) + e.Access = FileShare.ReadWrite; + else + e.Access = FileShare.Read; + + result.Add(e); + } + else if (action == FilesSecurityActions.Edit && adapter.CanEdit(e, userId)) + { + e.Access = FileShare.ReadWrite; + + result.Add(e); + } + } + } + + // files in trash + filter = f => f.RootFolderType == FolderType.TRASH; + if ((action == FilesSecurityActions.Read || action == FilesSecurityActions.Delete) && entries.Any(filter)) + { + var folderDao = daoFactory.FolderDao; + var mytrashId = folderDao.GetFolderIDTrash(false, userId); + if (!Equals(mytrashId, 0)) + { + result.AddRange(entries.Where(filter).Where(e => Equals(e.RootFolderId, mytrashId))); + } + } + + if (FileSecurityCommon.IsAdministrator(userId)) + { + // administrator can work with crashed entries (crash in files_folder_tree) + filter = f => f.RootFolderType == FolderType.DEFAULT; + result.AddRange(entries.Where(filter)); + } + + // restore entries order + result.Sort((x, y) => order[x.UniqID].CompareTo(order[y.UniqID])); + return result; + } + + public void Share(object entryId, FileEntryType entryType, Guid @for, FileShare share) + { + var securityDao = daoFactory.SecurityDao; + var r = new FileShareRecord + { + Tenant = TenantManager.GetCurrentTenant().TenantId, + EntryId = entryId, + EntryType = entryType, + Subject = @for, + Owner = AuthContext.CurrentAccount.ID, + Share = share, + }; + securityDao.SetShare(r); + } + + public IEnumerable GetShares(IEnumerable entries) + { + return daoFactory.SecurityDao.GetShares(entries); + } + + public IEnumerable GetShares(FileEntry entry) + { + return daoFactory.SecurityDao.GetShares(entry); + } + + public List GetSharesForMe(FilterType filterType, bool subjectGroup, Guid subjectID, string searchText = "", bool searchInContent = false, bool withSubfolders = false) + { + var folderDao = daoFactory.FolderDao; + var fileDao = daoFactory.FileDao; + var securityDao = daoFactory.SecurityDao; + var subjects = GetUserSubjects(AuthContext.CurrentAccount.ID); + + var records = securityDao.GetShares(subjects); + + var fileIds = new Dictionary(); + var folderIds = new Dictionary(); + + var recordGroup = records.GroupBy(r => new { r.EntryId, r.EntryType }, (key, group) => new + { + firstRecord = group.OrderBy(r => r, new SubjectComparer(subjects)) + .ThenByDescending(r => r.Share, new FileShareRecord.ShareComparer()) + .First() + }); + + foreach (var r in recordGroup.Where(r => r.firstRecord.Share != FileShare.Restrict)) + { + if (r.firstRecord.EntryType == FileEntryType.Folder) + { + if (!folderIds.ContainsKey(r.firstRecord.EntryId)) + folderIds.Add(r.firstRecord.EntryId, r.firstRecord.Share); + } + else + { + if (!fileIds.ContainsKey(r.firstRecord.EntryId)) + fileIds.Add(r.firstRecord.EntryId, r.firstRecord.Share); + } + } + + var entries = new List(); + + if (filterType != FilterType.FoldersOnly) + { + var files = fileDao.GetFilesForShare(fileIds.Keys.ToArray(), filterType, subjectGroup, subjectID, searchText, searchInContent); + + files.ForEach(x => + { + if (fileIds.ContainsKey(x.ID)) + { + x.Access = fileIds[x.ID]; + x.FolderIdDisplay = GlobalFolder.GetFolderShare(folderDao); + } + }); + + entries.AddRange(files); + } + + if (filterType == FilterType.None || filterType == FilterType.FoldersOnly) + { + var folders = folderDao.GetFolders(folderIds.Keys.ToArray(), filterType, subjectGroup, subjectID, searchText, withSubfolders, false); + + if (withSubfolders) + { + folders = FilterRead(folders).ToList(); + } + folders.ForEach(x => + { + if (folderIds.ContainsKey(x.ID)) + { + x.Access = folderIds[x.ID]; + x.FolderIdDisplay = GlobalFolder.GetFolderShare(folderDao); + } + }); + + entries.AddRange(folders.Cast()); + } + + if (filterType != FilterType.FoldersOnly && withSubfolders) + { + var filesInSharedFolders = fileDao.GetFiles(folderIds.Keys.ToArray(), filterType, subjectGroup, subjectID, searchText, searchInContent); + filesInSharedFolders = FilterRead(filesInSharedFolders).ToList(); + entries.AddRange(filesInSharedFolders); + entries = entries.Distinct().ToList(); + } + + entries = entries.Where(f => + f.RootFolderType == FolderType.USER // show users files + && f.RootFolderCreator != AuthContext.CurrentAccount.ID // don't show my files + ).ToList(); + + if (UserManager.GetUsers(AuthContext.CurrentAccount.ID).IsVisitor(UserManager)) + { + entries = entries.Where(r => !r.ProviderEntry).ToList(); + } + + var failedEntries = entries.Where(x => !string.IsNullOrEmpty(x.Error)); + var failedRecords = new List(); + + foreach (var failedEntry in failedEntries) + { + var entryType = failedEntry.FileEntryType; + + var failedRecord = records.First(x => x.EntryId.Equals(failedEntry.ID) && x.EntryType == entryType); + + failedRecord.Share = FileShare.None; + + failedRecords.Add(failedRecord); + } + + if (failedRecords.Any()) + { + securityDao.DeleteShareRecords(failedRecords); + } + + return entries.Where(x => string.IsNullOrEmpty(x.Error)).ToList(); + } + + public void RemoveSubject(Guid subject) + { + daoFactory.SecurityDao.RemoveSubject(subject); + } + + public List GetUserSubjects(Guid userId) + { + // priority order + // User, Departments, admin, everyone + + var result = new List { userId }; + if (userId == FileConstant.ShareLinkId) + return result; + + result.AddRange(UserManager.GetUserGroups(userId).Select(g => g.ID)); + if (FileSecurityCommon.IsAdministrator(userId)) result.Add(Constants.GroupAdmin.ID); + result.Add(Constants.GroupEveryone.ID); + + return result; + } + + private class SubjectComparer : IComparer + { + private readonly List _subjects; + + public SubjectComparer(List subjects) + { + _subjects = subjects; + } + + public int Compare(FileShareRecord x, FileShareRecord y) + { + if (x.Subject == y.Subject) + { + return 0; + } + + var index1 = _subjects.IndexOf(x.Subject); + var index2 = _subjects.IndexOf(y.Subject); + if (index1 == 0 || index2 == 0 // UserId + || Constants.BuildinGroups.Any(g => g.ID == x.Subject) || Constants.BuildinGroups.Any(g => g.ID == y.Subject)) // System Groups + { + return index1.CompareTo(index2); + } + + // Departments are equal. + return 0; + } + } + + private enum FilesSecurityActions + { + Read, + Comment, + FillForms, + Review, + Create, + Edit, + Delete, + } + } + + public static class FileSecurityExtention + { + public static DIHelper AddFileSecurityCommonService(this DIHelper services) + { + services.TryAddScoped(); + + return services + .AddUserManagerService() + .AddWebItemSecurity(); + } + + public static DIHelper AddFileSecurityService(this DIHelper services) + { + services.TryAddScoped(); + services.TryAddScoped(); + + return services + .AddDaoFactoryService() + .AddUserManagerService() + .AddTenantManagerService() + .AddAuthContextService() + .AddAuthManager() + .AddGlobalFolderService() + .AddFileSecurityCommonService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/Security/FileShare.cs b/products/ASC.Files/Server/Core/Security/FileShare.cs new file mode 100644 index 0000000000..11200075a3 --- /dev/null +++ b/products/ASC.Files/Server/Core/Security/FileShare.cs @@ -0,0 +1,58 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.Runtime.Serialization; + +namespace ASC.Files.Core.Security +{ + [DataContract(Name = "fileShare", Namespace = "")] + public enum FileShare + { + [EnumMember(Value = "0")] + None, + + [EnumMember(Value = "1")] + ReadWrite, + + [EnumMember(Value = "2")] + Read, + + [EnumMember(Value = "3")] + Restrict, + + [EnumMember(Value = "4")] + Varies, + + [EnumMember(Value = "5")] + Review, + + [EnumMember(Value = "6")] + Comment, + + [EnumMember(Value = "7")] + FillForms, + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/Security/FileShareRecord.cs b/products/ASC.Files/Server/Core/Security/FileShareRecord.cs new file mode 100644 index 0000000000..9256983d23 --- /dev/null +++ b/products/ASC.Files/Server/Core/Security/FileShareRecord.cs @@ -0,0 +1,78 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; + +namespace ASC.Files.Core.Security +{ + public class FileShareRecord + { + public int Tenant { get; set; } + + public object EntryId { get; set; } + + public FileEntryType EntryType { get; set; } + + public Guid Subject { get; set; } + + public Guid Owner { get; set; } + + public FileShare Share { get; set; } + + public int Level { get; set; } + + + public class ShareComparer : IComparer + { + private static readonly int[] ShareOrder = new[] + { + (int)FileShare.None, + (int)FileShare.ReadWrite, + (int)FileShare.Review, + (int)FileShare.FillForms, + (int)FileShare.Comment, + (int)FileShare.Read, + (int)FileShare.Restrict, + (int)FileShare.Varies + }; + + public int Compare(FileShare x, FileShare y) + { + return Array.IndexOf(ShareOrder, (int)x).CompareTo(Array.IndexOf(ShareOrder, (int)y)); + } + } + } + + public class SmallShareRecord + { + public Guid ShareTo { get; set; } + public Guid ShareParentTo { get; set; } + public Guid ShareBy { get; set; } + public DateTime ShareOn { get; set; } + public FileShare Share { get; set; } + } +} diff --git a/products/ASC.Files/Server/Core/Security/IFileSecurity.cs b/products/ASC.Files/Server/Core/Security/IFileSecurity.cs new file mode 100644 index 0000000000..9b35da0afd --- /dev/null +++ b/products/ASC.Files/Server/Core/Security/IFileSecurity.cs @@ -0,0 +1,50 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; + +namespace ASC.Files.Core.Security +{ + public interface IFileSecurity + { + bool CanRead(FileEntry entry, Guid userId); + + bool CanComment(FileEntry entry, Guid userId); + + bool CanReview(FileEntry entry, Guid userId); + + bool CanFillForms(FileEntry entry, Guid userId); + + bool CanCreate(FileEntry entry, Guid userId); + + bool CanEdit(FileEntry entry, Guid userId); + + bool CanDelete(FileEntry entry, Guid userId); + + IEnumerable WhoCanRead(FileEntry entry); + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/Security/IFileSecurityProvider.cs b/products/ASC.Files/Server/Core/Security/IFileSecurityProvider.cs new file mode 100644 index 0000000000..99b470390d --- /dev/null +++ b/products/ASC.Files/Server/Core/Security/IFileSecurityProvider.cs @@ -0,0 +1,36 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.Collections.Generic; + +namespace ASC.Files.Core.Security +{ + public interface IFileSecurityProvider + { + IFileSecurity GetFileSecurity(string data); + Dictionary GetFileSecurity(Dictionary data); + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Core/Security/ISecurityDao.cs b/products/ASC.Files/Server/Core/Security/ISecurityDao.cs new file mode 100644 index 0000000000..59cb798a49 --- /dev/null +++ b/products/ASC.Files/Server/Core/Security/ISecurityDao.cs @@ -0,0 +1,52 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; + +namespace ASC.Files.Core.Security +{ + public interface ISecurityDao + { + void SetShare(FileShareRecord r); + + IEnumerable GetShares(IEnumerable subjects); + + IEnumerable GetShares(IEnumerable entry); + + IEnumerable GetShares(FileEntry entry); + + void RemoveSubject(Guid subject); + + IEnumerable GetPureShareRecords(IEnumerable entries); + + IEnumerable GetPureShareRecords(FileEntry entry); + + void DeleteShareRecords(IEnumerable records); + + bool IsShared(object entryId, FileEntryType type); + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/DocStore/newdocuments/az-Latn-AZ/new.docx b/products/ASC.Files/Server/DocStore/newdocuments/az-Latn-AZ/new.docx new file mode 100644 index 0000000000..b50903382a Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/az-Latn-AZ/new.docx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/az-Latn-AZ/new.pptx b/products/ASC.Files/Server/DocStore/newdocuments/az-Latn-AZ/new.pptx new file mode 100644 index 0000000000..6ab0fd7987 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/az-Latn-AZ/new.pptx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/az-Latn-AZ/new.xlsx b/products/ASC.Files/Server/DocStore/newdocuments/az-Latn-AZ/new.xlsx new file mode 100644 index 0000000000..3b301e70ac Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/az-Latn-AZ/new.xlsx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/cs-CZ/new.docx b/products/ASC.Files/Server/DocStore/newdocuments/cs-CZ/new.docx new file mode 100644 index 0000000000..cd19d53fc1 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/cs-CZ/new.docx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/cs-CZ/new.pptx b/products/ASC.Files/Server/DocStore/newdocuments/cs-CZ/new.pptx new file mode 100644 index 0000000000..58b2f2708c Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/cs-CZ/new.pptx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/cs-CZ/new.xlsx b/products/ASC.Files/Server/DocStore/newdocuments/cs-CZ/new.xlsx new file mode 100644 index 0000000000..3b301e70ac Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/cs-CZ/new.xlsx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/de-DE/new.docx b/products/ASC.Files/Server/DocStore/newdocuments/de-DE/new.docx new file mode 100644 index 0000000000..c5c6daaba2 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/de-DE/new.docx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/de-DE/new.pptx b/products/ASC.Files/Server/DocStore/newdocuments/de-DE/new.pptx new file mode 100644 index 0000000000..44206d1efd Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/de-DE/new.pptx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/de-DE/new.xlsx b/products/ASC.Files/Server/DocStore/newdocuments/de-DE/new.xlsx new file mode 100644 index 0000000000..3b301e70ac Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/de-DE/new.xlsx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/default/new.docx b/products/ASC.Files/Server/DocStore/newdocuments/default/new.docx new file mode 100644 index 0000000000..99c1e6ed54 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/default/new.docx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/default/new.pptx b/products/ASC.Files/Server/DocStore/newdocuments/default/new.pptx new file mode 100644 index 0000000000..f4e4bdf079 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/default/new.pptx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/default/new.xlsx b/products/ASC.Files/Server/DocStore/newdocuments/default/new.xlsx new file mode 100644 index 0000000000..4bc868dbcc Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/default/new.xlsx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/el-GR/new.docx b/products/ASC.Files/Server/DocStore/newdocuments/el-GR/new.docx new file mode 100644 index 0000000000..ba5d6f1dcd Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/el-GR/new.docx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/el-GR/new.pptx b/products/ASC.Files/Server/DocStore/newdocuments/el-GR/new.pptx new file mode 100644 index 0000000000..99e52bb33b Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/el-GR/new.pptx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/el-GR/new.xlsx b/products/ASC.Files/Server/DocStore/newdocuments/el-GR/new.xlsx new file mode 100644 index 0000000000..3b301e70ac Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/el-GR/new.xlsx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/en-GB/new.docx b/products/ASC.Files/Server/DocStore/newdocuments/en-GB/new.docx new file mode 100644 index 0000000000..cabe1e7804 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/en-GB/new.docx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/en-GB/new.pptx b/products/ASC.Files/Server/DocStore/newdocuments/en-GB/new.pptx new file mode 100644 index 0000000000..daaa845141 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/en-GB/new.pptx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/en-GB/new.xlsx b/products/ASC.Files/Server/DocStore/newdocuments/en-GB/new.xlsx new file mode 100644 index 0000000000..3b301e70ac Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/en-GB/new.xlsx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/es-ES/new.docx b/products/ASC.Files/Server/DocStore/newdocuments/es-ES/new.docx new file mode 100644 index 0000000000..40a6fb60f2 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/es-ES/new.docx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/es-ES/new.pptx b/products/ASC.Files/Server/DocStore/newdocuments/es-ES/new.pptx new file mode 100644 index 0000000000..7aa064e335 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/es-ES/new.pptx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/es-ES/new.xlsx b/products/ASC.Files/Server/DocStore/newdocuments/es-ES/new.xlsx new file mode 100644 index 0000000000..3b301e70ac Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/es-ES/new.xlsx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/fr-FR/new.docx b/products/ASC.Files/Server/DocStore/newdocuments/fr-FR/new.docx new file mode 100644 index 0000000000..865113e781 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/fr-FR/new.docx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/fr-FR/new.pptx b/products/ASC.Files/Server/DocStore/newdocuments/fr-FR/new.pptx new file mode 100644 index 0000000000..c60475d989 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/fr-FR/new.pptx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/fr-FR/new.xlsx b/products/ASC.Files/Server/DocStore/newdocuments/fr-FR/new.xlsx new file mode 100644 index 0000000000..3b301e70ac Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/fr-FR/new.xlsx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/it-IT/new.docx b/products/ASC.Files/Server/DocStore/newdocuments/it-IT/new.docx new file mode 100644 index 0000000000..c40e4458fd Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/it-IT/new.docx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/it-IT/new.pptx b/products/ASC.Files/Server/DocStore/newdocuments/it-IT/new.pptx new file mode 100644 index 0000000000..739b1e8514 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/it-IT/new.pptx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/it-IT/new.xlsx b/products/ASC.Files/Server/DocStore/newdocuments/it-IT/new.xlsx new file mode 100644 index 0000000000..3b301e70ac Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/it-IT/new.xlsx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/ko-KR/new.docx b/products/ASC.Files/Server/DocStore/newdocuments/ko-KR/new.docx new file mode 100644 index 0000000000..bfbe091628 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/ko-KR/new.docx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/ko-KR/new.pptx b/products/ASC.Files/Server/DocStore/newdocuments/ko-KR/new.pptx new file mode 100644 index 0000000000..d4ce762803 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/ko-KR/new.pptx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/ko-KR/new.xlsx b/products/ASC.Files/Server/DocStore/newdocuments/ko-KR/new.xlsx new file mode 100644 index 0000000000..3b301e70ac Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/ko-KR/new.xlsx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/lv-LV/new.docx b/products/ASC.Files/Server/DocStore/newdocuments/lv-LV/new.docx new file mode 100644 index 0000000000..0d27b6aa91 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/lv-LV/new.docx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/lv-LV/new.pptx b/products/ASC.Files/Server/DocStore/newdocuments/lv-LV/new.pptx new file mode 100644 index 0000000000..3b9e7ddedd Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/lv-LV/new.pptx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/lv-LV/new.xlsx b/products/ASC.Files/Server/DocStore/newdocuments/lv-LV/new.xlsx new file mode 100644 index 0000000000..3b301e70ac Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/lv-LV/new.xlsx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/nl-NL/new.docx b/products/ASC.Files/Server/DocStore/newdocuments/nl-NL/new.docx new file mode 100644 index 0000000000..dc35bd39db Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/nl-NL/new.docx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/nl-NL/new.pptx b/products/ASC.Files/Server/DocStore/newdocuments/nl-NL/new.pptx new file mode 100644 index 0000000000..d85f561293 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/nl-NL/new.pptx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/nl-NL/new.xlsx b/products/ASC.Files/Server/DocStore/newdocuments/nl-NL/new.xlsx new file mode 100644 index 0000000000..a2c38912ed Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/nl-NL/new.xlsx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/pl-PL/new.docx b/products/ASC.Files/Server/DocStore/newdocuments/pl-PL/new.docx new file mode 100644 index 0000000000..9964336fcf Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/pl-PL/new.docx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/pl-PL/new.pptx b/products/ASC.Files/Server/DocStore/newdocuments/pl-PL/new.pptx new file mode 100644 index 0000000000..1f543d4856 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/pl-PL/new.pptx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/pl-PL/new.xlsx b/products/ASC.Files/Server/DocStore/newdocuments/pl-PL/new.xlsx new file mode 100644 index 0000000000..3b301e70ac Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/pl-PL/new.xlsx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/pt-BR/new.docx b/products/ASC.Files/Server/DocStore/newdocuments/pt-BR/new.docx new file mode 100644 index 0000000000..70554fdedc Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/pt-BR/new.docx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/pt-BR/new.pptx b/products/ASC.Files/Server/DocStore/newdocuments/pt-BR/new.pptx new file mode 100644 index 0000000000..73a85ae0ab Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/pt-BR/new.pptx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/pt-BR/new.xlsx b/products/ASC.Files/Server/DocStore/newdocuments/pt-BR/new.xlsx new file mode 100644 index 0000000000..d4c26dc98b Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/pt-BR/new.xlsx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/pt-PT/new.docx b/products/ASC.Files/Server/DocStore/newdocuments/pt-PT/new.docx new file mode 100644 index 0000000000..ebe556f904 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/pt-PT/new.docx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/pt-PT/new.pptx b/products/ASC.Files/Server/DocStore/newdocuments/pt-PT/new.pptx new file mode 100644 index 0000000000..7395b3b0f9 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/pt-PT/new.pptx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/pt-PT/new.xlsx b/products/ASC.Files/Server/DocStore/newdocuments/pt-PT/new.xlsx new file mode 100644 index 0000000000..3b301e70ac Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/pt-PT/new.xlsx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/ru-RU/new.docx b/products/ASC.Files/Server/DocStore/newdocuments/ru-RU/new.docx new file mode 100644 index 0000000000..ab7bd1ca7e Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/ru-RU/new.docx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/ru-RU/new.pptx b/products/ASC.Files/Server/DocStore/newdocuments/ru-RU/new.pptx new file mode 100644 index 0000000000..868a23c695 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/ru-RU/new.pptx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/ru-RU/new.xlsx b/products/ASC.Files/Server/DocStore/newdocuments/ru-RU/new.xlsx new file mode 100644 index 0000000000..3b301e70ac Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/ru-RU/new.xlsx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/sk-SK/new.docx b/products/ASC.Files/Server/DocStore/newdocuments/sk-SK/new.docx new file mode 100644 index 0000000000..08cf84faac Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/sk-SK/new.docx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/sk-SK/new.pptx b/products/ASC.Files/Server/DocStore/newdocuments/sk-SK/new.pptx new file mode 100644 index 0000000000..865f47e400 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/sk-SK/new.pptx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/sk-SK/new.xlsx b/products/ASC.Files/Server/DocStore/newdocuments/sk-SK/new.xlsx new file mode 100644 index 0000000000..4bc868dbcc Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/sk-SK/new.xlsx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/sv-SE/new.docx b/products/ASC.Files/Server/DocStore/newdocuments/sv-SE/new.docx new file mode 100644 index 0000000000..b341ecce5f Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/sv-SE/new.docx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/sv-SE/new.pptx b/products/ASC.Files/Server/DocStore/newdocuments/sv-SE/new.pptx new file mode 100644 index 0000000000..55b1a817a4 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/sv-SE/new.pptx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/sv-SE/new.xlsx b/products/ASC.Files/Server/DocStore/newdocuments/sv-SE/new.xlsx new file mode 100644 index 0000000000..ce7c1b4309 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/sv-SE/new.xlsx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/uk-UA/new.docx b/products/ASC.Files/Server/DocStore/newdocuments/uk-UA/new.docx new file mode 100644 index 0000000000..edb620d983 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/uk-UA/new.docx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/uk-UA/new.pptx b/products/ASC.Files/Server/DocStore/newdocuments/uk-UA/new.pptx new file mode 100644 index 0000000000..7b62d75ddc Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/uk-UA/new.pptx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/uk-UA/new.xlsx b/products/ASC.Files/Server/DocStore/newdocuments/uk-UA/new.xlsx new file mode 100644 index 0000000000..3b301e70ac Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/uk-UA/new.xlsx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/vi-VN/new.docx b/products/ASC.Files/Server/DocStore/newdocuments/vi-VN/new.docx new file mode 100644 index 0000000000..0c63d8d89e Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/vi-VN/new.docx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/vi-VN/new.pptx b/products/ASC.Files/Server/DocStore/newdocuments/vi-VN/new.pptx new file mode 100644 index 0000000000..09eb42cc5d Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/vi-VN/new.pptx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/vi-VN/new.xlsx b/products/ASC.Files/Server/DocStore/newdocuments/vi-VN/new.xlsx new file mode 100644 index 0000000000..3b301e70ac Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/vi-VN/new.xlsx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/zh-CN/new.docx b/products/ASC.Files/Server/DocStore/newdocuments/zh-CN/new.docx new file mode 100644 index 0000000000..f74a7296f8 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/zh-CN/new.docx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/zh-CN/new.pptx b/products/ASC.Files/Server/DocStore/newdocuments/zh-CN/new.pptx new file mode 100644 index 0000000000..4ab6f7e36f Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/zh-CN/new.pptx differ diff --git a/products/ASC.Files/Server/DocStore/newdocuments/zh-CN/new.xlsx b/products/ASC.Files/Server/DocStore/newdocuments/zh-CN/new.xlsx new file mode 100644 index 0000000000..3b301e70ac Binary files /dev/null and b/products/ASC.Files/Server/DocStore/newdocuments/zh-CN/new.xlsx differ diff --git a/products/ASC.Files/Server/DocStore/startdocuments/de-DE/my/ONLYOFFICE-Musterdokument.docx b/products/ASC.Files/Server/DocStore/startdocuments/de-DE/my/ONLYOFFICE-Musterdokument.docx new file mode 100644 index 0000000000..c4e1389f66 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/startdocuments/de-DE/my/ONLYOFFICE-Musterdokument.docx differ diff --git a/products/ASC.Files/Server/DocStore/startdocuments/de-DE/my/ONLYOFFICE-Musterpraesentation.pptx b/products/ASC.Files/Server/DocStore/startdocuments/de-DE/my/ONLYOFFICE-Musterpraesentation.pptx new file mode 100644 index 0000000000..2dbc522cbb Binary files /dev/null and b/products/ASC.Files/Server/DocStore/startdocuments/de-DE/my/ONLYOFFICE-Musterpraesentation.pptx differ diff --git a/products/ASC.Files/Server/DocStore/startdocuments/de-DE/my/ONLYOFFICE-Mustertabelle.xlsx b/products/ASC.Files/Server/DocStore/startdocuments/de-DE/my/ONLYOFFICE-Mustertabelle.xlsx new file mode 100644 index 0000000000..5519f20628 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/startdocuments/de-DE/my/ONLYOFFICE-Mustertabelle.xlsx differ diff --git a/products/ASC.Files/Server/DocStore/startdocuments/default/my/ONLYOFFICE Sample Audio.mp3 b/products/ASC.Files/Server/DocStore/startdocuments/default/my/ONLYOFFICE Sample Audio.mp3 new file mode 100644 index 0000000000..d31bdeec87 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/startdocuments/default/my/ONLYOFFICE Sample Audio.mp3 differ diff --git a/products/ASC.Files/Server/DocStore/startdocuments/default/my/ONLYOFFICE Sample Document.docx b/products/ASC.Files/Server/DocStore/startdocuments/default/my/ONLYOFFICE Sample Document.docx new file mode 100644 index 0000000000..6ee90b8d98 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/startdocuments/default/my/ONLYOFFICE Sample Document.docx differ diff --git a/products/ASC.Files/Server/DocStore/startdocuments/default/my/ONLYOFFICE Sample Presentation.pptx b/products/ASC.Files/Server/DocStore/startdocuments/default/my/ONLYOFFICE Sample Presentation.pptx new file mode 100644 index 0000000000..3f752109fa Binary files /dev/null and b/products/ASC.Files/Server/DocStore/startdocuments/default/my/ONLYOFFICE Sample Presentation.pptx differ diff --git a/products/ASC.Files/Server/DocStore/startdocuments/default/my/ONLYOFFICE Sample Spreadsheets.xlsx b/products/ASC.Files/Server/DocStore/startdocuments/default/my/ONLYOFFICE Sample Spreadsheets.xlsx new file mode 100644 index 0000000000..124a39df6e Binary files /dev/null and b/products/ASC.Files/Server/DocStore/startdocuments/default/my/ONLYOFFICE Sample Spreadsheets.xlsx differ diff --git a/products/ASC.Files/Server/DocStore/startdocuments/default/my/ONLYOFFICE Sample Video.mp4 b/products/ASC.Files/Server/DocStore/startdocuments/default/my/ONLYOFFICE Sample Video.mp4 new file mode 100644 index 0000000000..34a2bdd6f4 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/startdocuments/default/my/ONLYOFFICE Sample Video.mp4 differ diff --git a/products/ASC.Files/Server/DocStore/startdocuments/es-ES/my/ONLYOFFICE Sample Document.docx b/products/ASC.Files/Server/DocStore/startdocuments/es-ES/my/ONLYOFFICE Sample Document.docx new file mode 100644 index 0000000000..cc7d25513d Binary files /dev/null and b/products/ASC.Files/Server/DocStore/startdocuments/es-ES/my/ONLYOFFICE Sample Document.docx differ diff --git a/products/ASC.Files/Server/DocStore/startdocuments/es-ES/my/ONLYOFFICE Sample Presentation.pptx b/products/ASC.Files/Server/DocStore/startdocuments/es-ES/my/ONLYOFFICE Sample Presentation.pptx new file mode 100644 index 0000000000..567ebcaad9 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/startdocuments/es-ES/my/ONLYOFFICE Sample Presentation.pptx differ diff --git a/products/ASC.Files/Server/DocStore/startdocuments/es-ES/my/ONLYOFFICE Sample Spreadsheets.xlsx b/products/ASC.Files/Server/DocStore/startdocuments/es-ES/my/ONLYOFFICE Sample Spreadsheets.xlsx new file mode 100644 index 0000000000..7c117fadfe Binary files /dev/null and b/products/ASC.Files/Server/DocStore/startdocuments/es-ES/my/ONLYOFFICE Sample Spreadsheets.xlsx differ diff --git a/products/ASC.Files/Server/DocStore/startdocuments/fr-FR/my/ONLYOFFICE Sample Document.docx b/products/ASC.Files/Server/DocStore/startdocuments/fr-FR/my/ONLYOFFICE Sample Document.docx new file mode 100644 index 0000000000..29afb51c33 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/startdocuments/fr-FR/my/ONLYOFFICE Sample Document.docx differ diff --git a/products/ASC.Files/Server/DocStore/startdocuments/fr-FR/my/ONLYOFFICE Sample Presentation.pptx b/products/ASC.Files/Server/DocStore/startdocuments/fr-FR/my/ONLYOFFICE Sample Presentation.pptx new file mode 100644 index 0000000000..85d2517f36 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/startdocuments/fr-FR/my/ONLYOFFICE Sample Presentation.pptx differ diff --git a/products/ASC.Files/Server/DocStore/startdocuments/fr-FR/my/ONLYOFFICE Sample Spreadsheets.xlsx b/products/ASC.Files/Server/DocStore/startdocuments/fr-FR/my/ONLYOFFICE Sample Spreadsheets.xlsx new file mode 100644 index 0000000000..3084c3ea68 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/startdocuments/fr-FR/my/ONLYOFFICE Sample Spreadsheets.xlsx differ diff --git a/products/ASC.Files/Server/DocStore/startdocuments/it-IT/my/ONLYOFFICE Sample Document.docx b/products/ASC.Files/Server/DocStore/startdocuments/it-IT/my/ONLYOFFICE Sample Document.docx new file mode 100644 index 0000000000..095ae16fd8 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/startdocuments/it-IT/my/ONLYOFFICE Sample Document.docx differ diff --git a/products/ASC.Files/Server/DocStore/startdocuments/it-IT/my/ONLYOFFICE Sample Presentation.pptx b/products/ASC.Files/Server/DocStore/startdocuments/it-IT/my/ONLYOFFICE Sample Presentation.pptx new file mode 100644 index 0000000000..9c9b3a4770 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/startdocuments/it-IT/my/ONLYOFFICE Sample Presentation.pptx differ diff --git a/products/ASC.Files/Server/DocStore/startdocuments/it-IT/my/ONLYOFFICE Sample Spreadsheets.xlsx b/products/ASC.Files/Server/DocStore/startdocuments/it-IT/my/ONLYOFFICE Sample Spreadsheets.xlsx new file mode 100644 index 0000000000..769d22346c Binary files /dev/null and b/products/ASC.Files/Server/DocStore/startdocuments/it-IT/my/ONLYOFFICE Sample Spreadsheets.xlsx differ diff --git a/products/ASC.Files/Server/DocStore/startdocuments/ru-RU/my/ONLYOFFICE Sample Document.docx b/products/ASC.Files/Server/DocStore/startdocuments/ru-RU/my/ONLYOFFICE Sample Document.docx new file mode 100644 index 0000000000..2765f96df4 Binary files /dev/null and b/products/ASC.Files/Server/DocStore/startdocuments/ru-RU/my/ONLYOFFICE Sample Document.docx differ diff --git a/products/ASC.Files/Server/DocStore/startdocuments/ru-RU/my/ONLYOFFICE Sample Presentation.pptx b/products/ASC.Files/Server/DocStore/startdocuments/ru-RU/my/ONLYOFFICE Sample Presentation.pptx new file mode 100644 index 0000000000..85b6748d2f Binary files /dev/null and b/products/ASC.Files/Server/DocStore/startdocuments/ru-RU/my/ONLYOFFICE Sample Presentation.pptx differ diff --git a/products/ASC.Files/Server/DocStore/startdocuments/ru-RU/my/ONLYOFFICE Sample Spreadsheets.xlsx b/products/ASC.Files/Server/DocStore/startdocuments/ru-RU/my/ONLYOFFICE Sample Spreadsheets.xlsx new file mode 100644 index 0000000000..7a30ed6dbc Binary files /dev/null and b/products/ASC.Files/Server/DocStore/startdocuments/ru-RU/my/ONLYOFFICE Sample Spreadsheets.xlsx differ diff --git a/products/ASC.Files/Server/DocStore/test.docbuilder b/products/ASC.Files/Server/DocStore/test.docbuilder new file mode 100644 index 0000000000..8a4ef13176 --- /dev/null +++ b/products/ASC.Files/Server/DocStore/test.docbuilder @@ -0,0 +1 @@ +builder.CloseFile(); \ No newline at end of file diff --git a/products/ASC.Files/Server/Helpers/Constants.cs b/products/ASC.Files/Server/Helpers/Constants.cs new file mode 100644 index 0000000000..ab4bcde329 --- /dev/null +++ b/products/ASC.Files/Server/Helpers/Constants.cs @@ -0,0 +1,48 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; + +namespace ASC.Files.Core +{ + public static class FileConstant + { + public static readonly string ModuleId = "files"; + + public static readonly string StorageModule = "files"; + public static readonly string StorageDomainTmp = "files_temp"; + public static readonly string StorageTemplate = "files_template"; + + public static readonly string DatabaseId = "files"; + + public static readonly Guid ShareLinkId = new Guid("{D77BD6AF-828B-41f5-84ED-7FFE2565B13A}"); + + public const string StartDocPath = "startdocuments/"; + public const string NewDocPath = "newdocuments/"; + + public const string DownloadTitle = "download"; + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Helpers/DocuSignHelper.cs b/products/ASC.Files/Server/Helpers/DocuSignHelper.cs new file mode 100644 index 0000000000..17dbceb48d --- /dev/null +++ b/products/ASC.Files/Server/Helpers/DocuSignHelper.cs @@ -0,0 +1,509 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.Serialization; +using System.Security; + +using ASC.Common; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Common; +using ASC.Core.Users; +using ASC.FederatedLogin; +using ASC.FederatedLogin.Helpers; +using ASC.FederatedLogin.LoginProviders; +using ASC.Files.Core; +using ASC.Files.Core.Data; +using ASC.Files.Core.Security; +using ASC.Files.Resources; +using ASC.MessagingSystem; +using ASC.Web.Core.Files; +using ASC.Web.Core.Users; +using ASC.Web.Files.Classes; +using ASC.Web.Files.HttpHandlers; +using ASC.Web.Files.Services.WCFService; +using ASC.Web.Files.ThirdPartyApp; +using ASC.Web.Files.Utils; +using ASC.Web.Studio.Core; + +using DocuSign.eSign.Api; +using DocuSign.eSign.Client; +using DocuSign.eSign.Model; + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +using Newtonsoft.Json; + +using File = ASC.Files.Core.File; +using Folder = ASC.Files.Core.Folder; + +namespace ASC.Web.Files.Helpers +{ + public class DocuSignToken + { + public ILog Log { get; set; } + + public const string AppAttr = "docusign"; + + public TokenHelper TokenHelper { get; } + public AuthContext AuthContext { get; } + public DocuSignLoginProvider DocuSignLoginProvider { get; } + + public DocuSignToken( + TokenHelper tokenHelper, + IOptionsMonitor options, + AuthContext authContext, + DocuSignLoginProvider docuSignLoginProvider) + { + TokenHelper = tokenHelper; + AuthContext = authContext; + DocuSignLoginProvider = docuSignLoginProvider; + Log = options.CurrentValue; + } + + public OAuth20Token GetToken() + { + return TokenHelper.GetToken(AppAttr); + } + + public void DeleteToken(Guid? userId = null) + { + TokenHelper.DeleteToken(AppAttr, userId); + } + + public void SaveToken(OAuth20Token token) + { + if (token == null) throw new ArgumentNullException("token"); + + TokenHelper.SaveToken(new Token(token, AppAttr)); + } + + internal string GetRefreshedToken(OAuth20Token token) + { + if (token.IsExpired) + { + try + { + Log.Info("DocuSign refresh token for user " + AuthContext.CurrentAccount.ID); + + var refreshed = DocuSignLoginProvider.Instance.RefreshToken(token.RefreshToken); + + if (refreshed != null) + { + token.AccessToken = refreshed.AccessToken; + token.RefreshToken = refreshed.RefreshToken; + token.ExpiresIn = refreshed.ExpiresIn; + token.Timestamp = DateTime.UtcNow; + + SaveToken(token); + } + } + catch (Exception ex) + { + Log.Error("DocuSign refresh token for user " + AuthContext.CurrentAccount.ID, ex); + } + } + return token.AccessToken; + } + } + + public class DocuSignHelper + { + public ILog Log { get; set; } + + public const string UserField = "userId"; + + public static readonly List SupportedFormats = new List + { + ".as", ".asl", ".doc", ".docm", ".docx", ".dot", ".dotm", ".dotx", ".htm", ".html", ".msg", ".pdf", ".pdx", ".rtf", ".txt", ".wpd", ".wps", ".wpt", ".xps", + ".emz", ".svg", ".svgz", ".vdx", ".vss", ".vst", + ".bmp", ".cdr", ".dcx", ".gif", ".ico", ".jpg", ".jpeg", ".pct", ".pic", ".png", ".rgb", ".sam", ".tga", ".tif", ".tiff", ".wpg", + ".dps", ".dpt", ".pot", ".potx", ".pps", ".ppt", ".pptm", ".pptx", + ".csv", ".et", ".ett", ".xls", ".xlsm", ".xlsx", ".xlt" + }; + + public static long MaxFileSize = 25L * 1024L * 1024L; + + public static int MaxEmailLength = 10000; + + public DocuSignToken DocuSignToken { get; } + public DocuSignLoginProvider DocuSignLoginProvider { get; } + public FileSecurity FileSecurity { get; } + public IDaoFactory DaoFactory { get; } + public BaseCommonLinkUtility BaseCommonLinkUtility { get; } + public UserManager UserManager { get; } + public AuthContext AuthContext { get; } + public DisplayUserSettingsHelper DisplayUserSettingsHelper { get; } + public FileMarker FileMarker { get; } + public GlobalFolderHelper GlobalFolderHelper { get; } + public FilesMessageService FilesMessageService { get; } + public FilesLinkUtility FilesLinkUtility { get; } + public IServiceProvider ServiceProvider { get; } + + public DocuSignHelper( + DocuSignToken docuSignToken, + DocuSignLoginProvider docuSignLoginProvider, + FileSecurity fileSecurity, + IDaoFactory daoFactory, + IOptionsMonitor options, + BaseCommonLinkUtility baseCommonLinkUtility, + UserManager userManager, + AuthContext authContext, + DisplayUserSettingsHelper displayUserSettingsHelper, + FileMarker fileMarker, + GlobalFolderHelper globalFolderHelper, + FilesMessageService filesMessageService, + FilesLinkUtility filesLinkUtility, + IServiceProvider serviceProvider) + { + DocuSignToken = docuSignToken; + DocuSignLoginProvider = docuSignLoginProvider; + FileSecurity = fileSecurity; + DaoFactory = daoFactory; + BaseCommonLinkUtility = baseCommonLinkUtility; + UserManager = userManager; + AuthContext = authContext; + DisplayUserSettingsHelper = displayUserSettingsHelper; + FileMarker = fileMarker; + GlobalFolderHelper = globalFolderHelper; + FilesMessageService = filesMessageService; + FilesLinkUtility = filesLinkUtility; + ServiceProvider = serviceProvider; + Log = options.CurrentValue; + } + + public bool ValidateToken(OAuth20Token token) + { + GetDocuSignAccount(token); + return true; + } + + public string SendDocuSign(object fileId, DocuSignData docuSignData, Dictionary requestHeaders) + { + if (docuSignData == null) throw new ArgumentNullException("docuSignData"); + var token = DocuSignToken.GetToken(); + var account = GetDocuSignAccount(token); + + var configuration = GetConfiguration(account, token); + var document = CreateDocument(fileId, docuSignData.Name, docuSignData.FolderId, out var sourceFile); + + var url = CreateEnvelope(account.AccountId, document, docuSignData, configuration); + + FilesMessageService.Send(sourceFile, requestHeaders, MessageAction.DocumentSendToSign, "DocuSign", sourceFile.Title); + + return url; + } + + private DocuSignAccount GetDocuSignAccount(OAuth20Token token) + { + if (token == null) throw new ArgumentNullException("token"); + + var userInfoString = RequestHelper.PerformRequest(DocuSignLoginProvider.Instance.DocuSignHost + "/oauth/userinfo", + headers: new Dictionary { { "Authorization", "Bearer " + DocuSignToken.GetRefreshedToken(token) } }); + + Log.Debug("DocuSing userInfo: " + userInfoString); + + var userInfo = (DocuSignUserInfo)JsonConvert.DeserializeObject(userInfoString, typeof(DocuSignUserInfo)); + + if (userInfo.Accounts == null || userInfo.Accounts.Count == 0) throw new Exception("Account is null"); + + var account = userInfo.Accounts[0]; + return account; + } + + private DocuSign.eSign.Client.Configuration GetConfiguration(DocuSignAccount account, OAuth20Token token) + { + if (account == null) throw new ArgumentNullException("account"); + if (token == null) throw new ArgumentNullException("token"); + + var apiClient = new ApiClient(account.BaseUri + "/restapi"); + + var configuration = new DocuSign.eSign.Client.Configuration { ApiClient = apiClient }; + configuration.AddDefaultHeader("Authorization", "Bearer " + DocuSignToken.GetRefreshedToken(token)); + + return configuration; + } + + private Document CreateDocument(object fileId, string documentName, string folderId, out File file) + { + var fileDao = DaoFactory.FileDao; + file = fileDao.GetFile(fileId); + if (file == null) throw new Exception(FilesCommonResource.ErrorMassage_FileNotFound); + if (!FileSecurity.CanRead(file)) throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException_ReadFile); + if (!SupportedFormats.Contains(FileUtility.GetFileExtension(file.Title))) throw new ArgumentException(FilesCommonResource.ErrorMassage_NotSupportedFormat); + if (file.ContentLength > MaxFileSize) throw new Exception(FileSizeComment.GetFileSizeExceptionString(MaxFileSize)); + + byte[] fileBytes; + using (var stream = fileDao.GetFileStream(file)) + { + var buffer = new byte[16 * 1024]; + using var ms = new MemoryStream(); + int read; + while ((read = stream.Read(buffer, 0, buffer.Length)) > 0) + { + ms.Write(buffer, 0, read); + } + fileBytes = ms.ToArray(); + } + + if (string.IsNullOrEmpty(documentName)) + { + documentName = file.Title; + } + + var document = new Document + { + DocumentBase64 = Convert.ToBase64String(fileBytes), + DocumentFields = new List + { + new NameValue {Name = FilesLinkUtility.FolderId, Value = folderId}, + new NameValue {Name = FilesLinkUtility.FileTitle, Value = file.Title}, + }, + DocumentId = "1", //file.ID.ToString(), + FileExtension = FileUtility.GetFileExtension(file.Title), + Name = documentName, + }; + + return document; + } + + private string CreateEnvelope(string accountId, Document document, DocuSignData docuSignData, DocuSign.eSign.Client.Configuration configuration) + { + var eventNotification = new EventNotification + { + EnvelopeEvents = new List + { + //new EnvelopeEvent {EnvelopeEventStatusCode = DocuSignStatus.Sent.ToString()}, + //new EnvelopeEvent {EnvelopeEventStatusCode = DocuSignStatus.Delivered.ToString()}, + new EnvelopeEvent {EnvelopeEventStatusCode = DocuSignStatus.Completed.ToString()}, + new EnvelopeEvent {EnvelopeEventStatusCode = DocuSignStatus.Declined.ToString()}, + new EnvelopeEvent {EnvelopeEventStatusCode = DocuSignStatus.Voided.ToString()}, + }, + IncludeDocumentFields = "true", + //RecipientEvents = new List + // { + // new RecipientEvent {RecipientEventStatusCode = "Sent"}, + // new RecipientEvent {RecipientEventStatusCode = "Delivered"}, + // new RecipientEvent {RecipientEventStatusCode = "Completed"}, + // new RecipientEvent {RecipientEventStatusCode = "Declined"}, + // new RecipientEvent {RecipientEventStatusCode = "AuthenticationFailed"}, + // new RecipientEvent {RecipientEventStatusCode = "AutoResponded"}, + // }, + Url = BaseCommonLinkUtility.GetFullAbsolutePath(DocuSignHandler.Path(FilesLinkUtility) + "?" + FilesLinkUtility.Action + "=webhook"), + }; + + Log.Debug("DocuSign hook url: " + eventNotification.Url); + + var signers = new List(); + docuSignData.Users.ForEach(uid => + { + try + { + var user = UserManager.GetUsers(uid); + signers.Add(new Signer + { + Email = user.Email, + Name = user.DisplayUserName(false, DisplayUserSettingsHelper), + RecipientId = user.ID.ToString(), + }); + } + catch (Exception ex) + { + Log.Error("Signer is undefined", ex); + } + }); + + var envelopeDefinition = new EnvelopeDefinition + { + CustomFields = new CustomFields + { + TextCustomFields = new List + { + new TextCustomField {Name = UserField, Value = AuthContext.CurrentAccount.ID.ToString()}, + } + }, + Documents = new List { document }, + EmailBlurb = docuSignData.Message, + EmailSubject = docuSignData.Name, + EventNotification = eventNotification, + Recipients = new Recipients + { + Signers = signers, + }, + Status = "created", + }; + + var envelopesApi = new EnvelopesApi(configuration); + var envelopeSummary = envelopesApi.CreateEnvelope(accountId, envelopeDefinition); + + Log.Debug("DocuSign createdEnvelope: " + envelopeSummary.EnvelopeId); + + var envelopeId = envelopeSummary.EnvelopeId; + var url = envelopesApi.CreateSenderView(accountId, envelopeId, new ReturnUrlRequest + { + ReturnUrl = BaseCommonLinkUtility.GetFullAbsolutePath(DocuSignHandler.Path(FilesLinkUtility) + "?" + FilesLinkUtility.Action + "=redirect") + }); + Log.Debug("DocuSign senderView: " + url.Url); + + return url.Url; + } + + public File SaveDocument(string envelopeId, string documentId, string documentName, object folderId) + { + if (string.IsNullOrEmpty(envelopeId)) throw new ArgumentNullException("envelopeId"); + if (string.IsNullOrEmpty(documentId)) throw new ArgumentNullException("documentId"); + + var token = DocuSignToken.GetToken(); + var account = GetDocuSignAccount(token); + var configuration = GetConfiguration(account, token); + + var fileDao = DaoFactory.FileDao; + var folderDao = DaoFactory.FolderDao; + if (string.IsNullOrEmpty(documentName)) + { + documentName = "new.pdf"; + } + + Folder folder; + if (folderId == null + || (folder = folderDao.GetFolder(folderId)) == null + || folder.RootFolderType == FolderType.TRASH + || !FileSecurity.CanCreate(folder)) + { + if (GlobalFolderHelper.FolderMy != null) + { + folderId = GlobalFolderHelper.FolderMy; + } + else + { + throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException_Create); + } + } + + var file = ServiceProvider.GetService(); + file.FolderID = folderId; + file.Comment = FilesCommonResource.CommentCreateByDocuSign; + file.Title = FileUtility.ReplaceFileExtension(documentName, ".pdf"); + + var envelopesApi = new EnvelopesApi(configuration); + Log.Info("DocuSign webhook get stream: " + documentId); + using (var stream = envelopesApi.GetDocument(account.AccountId, envelopeId, documentId)) + { + file.ContentLength = stream.Length; + file = fileDao.SaveFile(file, stream); + } + + FilesMessageService.Send(file, MessageInitiator.ThirdPartyProvider, MessageAction.DocumentSignComplete, "DocuSign", file.Title); + + FileMarker.MarkAsNew(file); + + return file; + } + + + [DataContract] + [DebuggerDisplay("{AccountId} {BaseUri}")] + private class DocuSignAccount + { + [DataMember(Name = "account_id", EmitDefaultValue = false)] + public string AccountId { get; set; } + + [DataMember(Name = "base_uri", EmitDefaultValue = false)] + public string BaseUri { get; set; } + } + + [DataContract] + private class DocuSignUserInfo + { + [DataMember(Name = "accounts", EmitDefaultValue = false)] + public List Accounts { get; set; } + } + } + + [DataContract(Name = "docusign_data", Namespace = "")] + [DebuggerDisplay("{Name}")] + public class DocuSignData + { + [DataMember(Name = "folderId")] + public string FolderId { get; set; } + + [DataMember(Name = "message")] + public string Message { get; set; } + + [DataMember(Name = "name", IsRequired = true, EmitDefaultValue = false)] + public string Name { get; set; } + + [DataMember(Name = "users")] + public ItemList Users { get; set; } + } + + public enum DocuSignStatus + { + Draft, + Sent, + Delivered, + Completed, + Declined, + Voided, + } + + public static class DocuSignHelperExtension + { + public static DIHelper AddDocuSignTokenService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddAuthContextService() + .AddDocuSignLoginProviderService() + .AddTokenHelperService(); + } + + public static DIHelper AddDocuSignHelperService(this DIHelper services) + { + services.TryAddScoped(); + + return services + .AddDocuSignLoginProviderService() + .AddFileSecurityService() + .AddDaoFactoryService() + .AddBaseCommonLinkUtilityService() + .AddUserManagerService() + .AddAuthContextService() + .AddDisplayUserSettingsService() + .AddFileMarkerService() + .AddGlobalFolderHelperService() + .AddFilesMessageService() + .AddDocuSignTokenService() + .AddFilesLinkUtilityService() + ; + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Helpers/EasyBibHelper.cs b/products/ASC.Files/Server/Helpers/EasyBibHelper.cs new file mode 100644 index 0000000000..773a30a003 --- /dev/null +++ b/products/ASC.Files/Server/Helpers/EasyBibHelper.cs @@ -0,0 +1,174 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Common.Configuration; +using ASC.FederatedLogin.Helpers; + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Options; + +using Newtonsoft.Json.Linq; + +namespace ASC.Web.Files.Helpers +{ + public class EasyBibHelper : Consumer + { + public ILog Log { get; set; } + + static string searchBookUrl = "https://worldcat.citation-api.com/query?search=", + searchJournalUrl = "https://crossref.citation-api.com/query?search=", + searchWebSiteUrl = "https://web.citation-api.com/query?search=", + easyBibStyles = "http://easybib-csl.herokuapp.com/1.0/styles"; + + public enum EasyBibSource + { + book = 0, + journal = 1, + website = 2 + } + + public string AppKey + { + get { return this["easyBibappkey"]; } + } + + public EasyBibHelper() + { + + } + + public EasyBibHelper( + IOptionsMonitor option, + TenantManager tenantManager, + CoreBaseSettings coreBaseSettings, + CoreSettings coreSettings, + ConsumerFactory consumerFactory, + IConfiguration configuration, + ICacheNotify cache, + string name, + int order, + Dictionary props, + Dictionary additional = null) + : base(tenantManager, coreBaseSettings, coreSettings, consumerFactory, configuration, cache, name, order, props, additional) + { + Log = option.CurrentValue; + } + + public static string GetEasyBibCitationsList(int source, string data) + { + var uri = ""; + switch (source) + { + case 0: + uri = searchBookUrl; + break; + case 1: + uri = searchJournalUrl; + break; + case 2: + uri = searchWebSiteUrl; + break; + default: + break; + } + uri += data; + + const string method = "GET"; + var headers = new Dictionary() { }; + try + { + return RequestHelper.PerformRequest(uri, "", method, "", headers); + } + catch (Exception) + { + return "error"; + } + + } + + public static string GetEasyBibStyles() + { + + const string method = "GET"; + var headers = new Dictionary() { }; + try + { + return RequestHelper.PerformRequest(easyBibStyles, "", method, "", headers); + } + catch (Exception) + { + return "error"; + } + } + public object GetEasyBibCitation(string data) + { + try + { + var easyBibappkey = ConsumerFactory.Get().AppKey; + + var jsonBlogInfo = JObject.Parse(data); + jsonBlogInfo.Add("key", easyBibappkey); + var citationData = jsonBlogInfo.ToString(); + + var uri = "https://api.citation-api.com/2.0/rest/cite"; + const string contentType = "application/json"; + const string method = "POST"; + var body = citationData; + var headers = new Dictionary() { }; + + return RequestHelper.PerformRequest(uri, contentType, method, body, headers); + + } + catch (Exception) + { + return null; + throw; + } + + } + } + + public static class EasyBibHelperExtension + { + public static DIHelper AddEasyBibHelperService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddConsumerFactoryService() + .AddKafkaService() + .AddTenantManagerService() + .AddCoreBaseSettingsService() + .AddCoreSettingsService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Helpers/FilesMessageService.cs b/products/ASC.Files/Server/Helpers/FilesMessageService.cs new file mode 100644 index 0000000000..6b156a5882 --- /dev/null +++ b/products/ASC.Files/Server/Helpers/FilesMessageService.cs @@ -0,0 +1,133 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.Collections.Generic; + +using ASC.Common; +using ASC.Common.Logging; +using ASC.Files.Core; +using ASC.MessagingSystem; + +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Options; + +namespace ASC.Web.Files.Helpers +{ + public class FilesMessageService + { + private readonly ILog log; + + public MessageTarget MessageTarget { get; } + public MessageService MessageService { get; } + public IHttpContextAccessor HttpContextAccessor { get; } + + public FilesMessageService( + IOptionsMonitor options, + MessageTarget messageTarget, + MessageService messageService) + { + log = options.Get("ASC.Messaging"); + MessageTarget = messageTarget; + MessageService = messageService; + } + + public FilesMessageService( + IOptionsMonitor options, + MessageTarget messageTarget, + MessageService messageService, + IHttpContextAccessor httpContextAccessor) + : this(options, messageTarget, messageService) + { + HttpContextAccessor = httpContextAccessor; + } + + public void Send(Dictionary headers, MessageAction action) + { + SendHeadersMessage(headers, action, null); + } + + public void Send(FileEntry entry, Dictionary headers, MessageAction action, params string[] description) + { + // do not log actions in users folder + if (entry == null || entry.RootFolderType == FolderType.USER) return; + + SendHeadersMessage(headers, action, MessageTarget.Create(entry.ID), description); + } + + public void Send(FileEntry entry1, FileEntry entry2, Dictionary headers, MessageAction action, params string[] description) + { + // do not log actions in users folder + if (entry1 == null || entry2 == null || entry1.RootFolderType == FolderType.USER || entry2.RootFolderType == FolderType.USER) return; + + SendHeadersMessage(headers, action, MessageTarget.Create(new[] { entry1.ID, entry2.ID }), description); + } + + private void SendHeadersMessage(Dictionary headers, MessageAction action, MessageTarget target, params string[] description) + { + if (headers == null) + { + log.Debug(string.Format("Empty Request Headers for \"{0}\" type of event", action)); + return; + } + + MessageService.Send(headers, action, target, description); + } + + + public void Send(FileEntry entry, MessageAction action, params string[] description) + { + // do not log actions in users folder + if (entry == null || entry.RootFolderType == FolderType.USER) return; + + if (HttpContextAccessor == null) + { + log.Debug(string.Format("Empty Http Request for \"{0}\" type of event", action)); + return; + } + + MessageService.Send(action, MessageTarget.Create(entry.ID), description); + } + + + public void Send(FileEntry entry, MessageInitiator initiator, MessageAction action, params string[] description) + { + if (entry == null || entry.RootFolderType == FolderType.USER) return; + + MessageService.Send(initiator, action, MessageTarget.Create(entry.ID), description); + } + } + + public static class FilesMessageServiceExtension + { + public static DIHelper AddFilesMessageService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddMessageTargetService() + .AddMessageServiceService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Helpers/Global.cs b/products/ASC.Files/Server/Helpers/Global.cs new file mode 100644 index 0000000000..86b740ff7f --- /dev/null +++ b/products/ASC.Files/Server/Helpers/Global.cs @@ -0,0 +1,599 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Common.Settings; +using ASC.Core.Users; +using ASC.Data.Storage; +using ASC.Files.Core; +using ASC.Files.Core.Data; +using ASC.Files.Core.Security; +using ASC.Files.Resources; +using ASC.Web.Core; +using ASC.Web.Core.Users; +using ASC.Web.Core.WhiteLabel; +using ASC.Web.Files.Utils; + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +using Constants = ASC.Core.Configuration.Constants; +using File = ASC.Files.Core.File; + +namespace ASC.Web.Files.Classes +{ + public class GlobalNotify + { + public ICacheNotify Notify { get; set; } + public ILog Logger { get; set; } + + public GlobalNotify(ICacheNotify notify, IOptionsMonitor options, CoreBaseSettings coreBaseSettings) + { + Notify = notify; + Logger = options.Get("ASC.Files"); + if (coreBaseSettings.Standalone) + { + ClearCache(); + } + } + + private void ClearCache() + { + try + { + Notify.Subscribe((item) => + { + try + { + GlobalFolder.ProjectsRootFolderCache.Clear(); + GlobalFolder.UserRootFolderCache.Clear(); + GlobalFolder.CommonFolderCache.Clear(); + GlobalFolder.ShareFolderCache.Clear(); + GlobalFolder.TrashFolderCache.Clear(); + } + catch (Exception e) + { + Logger.Fatal("ClearCache action", e); + } + }, CacheNotifyAction.Any); + } + catch (Exception e) + { + Logger.Fatal("ClearCache subscribe", e); + } + } + } + + public class Global + { + public IConfiguration Configuration { get; } + public AuthContext AuthContext { get; } + public UserManager UserManager { get; } + public CoreSettings CoreSettings { get; } + public DisplayUserSettingsHelper DisplayUserSettingsHelper { get; } + public CustomNamingPeople CustomNamingPeople { get; } + public FileSecurityCommon FileSecurityCommon { get; } + + public Global( + IConfiguration configuration, + AuthContext authContext, + UserManager userManager, + CoreSettings coreSettings, + DisplayUserSettingsHelper displayUserSettingsHelper, + CustomNamingPeople customNamingPeople, + FileSecurityCommon fileSecurityCommon) + { + Configuration = configuration; + AuthContext = authContext; + UserManager = userManager; + CoreSettings = coreSettings; + DisplayUserSettingsHelper = displayUserSettingsHelper; + CustomNamingPeople = customNamingPeople; + FileSecurityCommon = fileSecurityCommon; + } + + #region Property + + public const int MaxTitle = 170; + + public static readonly Regex InvalidTitleChars = new Regex("[\t*\\+:\"<>?|\\\\/\\p{Cs}]"); + + public bool EnableUploadFilter + { + get { return bool.TrueString.Equals(Configuration["files:upload-filter"] ?? "false", StringComparison.InvariantCultureIgnoreCase); } + } + + public TimeSpan StreamUrlExpire + { + get + { + int.TryParse(Configuration["files:stream-url-minute"], out var validateTimespan); + if (validateTimespan <= 0) validateTimespan = 16; + return TimeSpan.FromMinutes(validateTimespan); + } + } + + public bool IsAdministrator + { + get { return FileSecurityCommon.IsAdministrator(AuthContext.CurrentAccount.ID); } + } + + public string GetDocDbKey() + { + const string dbKey = "UniqueDocument"; + var resultKey = CoreSettings.GetSetting(dbKey); + + if (!string.IsNullOrEmpty(resultKey)) return resultKey; + + resultKey = Guid.NewGuid().ToString(); + CoreSettings.SaveSetting(dbKey, resultKey); + + return resultKey; + } + + #endregion + + public static string ReplaceInvalidCharsAndTruncate(string title) + { + if (string.IsNullOrEmpty(title)) return title; + title = title.Trim(); + if (MaxTitle < title.Length) + { + var pos = title.LastIndexOf('.'); + if (MaxTitle - 20 < pos) + { + title = title.Substring(0, MaxTitle - (title.Length - pos)) + title.Substring(pos); + } + else + { + title = title.Substring(0, MaxTitle); + } + } + return InvalidTitleChars.Replace(title, "_"); + } + + public string GetUserName(Guid userId, bool alive = false) + { + if (userId.Equals(AuthContext.CurrentAccount.ID)) return FilesCommonResource.Author_Me; + if (userId.Equals(Constants.Guest.ID)) return FilesCommonResource.Guest; + + var userInfo = UserManager.GetUsers(userId); + if (userInfo.Equals(ASC.Core.Users.Constants.LostUser)) return alive ? FilesCommonResource.Guest : CustomNamingPeople.Substitute("ProfileRemoved"); + + return userInfo.DisplayUserName(false, DisplayUserSettingsHelper); + } + } + + public class GlobalStore + { + public StorageFactory StorageFactory { get; } + public TenantManager TenantManager { get; } + + public GlobalStore(StorageFactory storageFactory, TenantManager tenantManager) + { + StorageFactory = storageFactory; + TenantManager = tenantManager; + } + + public IDataStore GetStore(bool currentTenant = true) + { + return StorageFactory.GetStorage(currentTenant ? TenantManager.GetCurrentTenant().TenantId.ToString() : string.Empty, FileConstant.StorageModule); + } + + public IDataStore GetStoreTemplate() + { + return StorageFactory.GetStorage(string.Empty, FileConstant.StorageTemplate); + } + } + + public class GlobalSpace + { + public FilesUserSpaceUsage FilesUserSpaceUsage { get; } + public AuthContext AuthContext { get; } + + public GlobalSpace(FilesUserSpaceUsage filesUserSpaceUsage, AuthContext authContext) + { + FilesUserSpaceUsage = filesUserSpaceUsage; + AuthContext = authContext; + } + + public long GetUserUsedSpace() + { + return GetUserUsedSpace(AuthContext.CurrentAccount.ID); + } + + public long GetUserUsedSpace(Guid userId) + { + return FilesUserSpaceUsage.GetUserSpaceUsage(userId); + } + } + + public class GlobalFolder + { + public CoreBaseSettings CoreBaseSettings { get; } + public WebItemManager WebItemManager { get; } + public WebItemSecurity WebItemSecurity { get; } + public AuthContext AuthContext { get; } + public TenantManager TenantManager { get; } + public UserManager UserManager { get; } + public SettingsManager SettingsManager { get; } + public GlobalStore GlobalStore { get; } + public IServiceProvider ServiceProvider { get; } + public ILog Logger { get; } + + public GlobalFolder( + CoreBaseSettings coreBaseSettings, + WebItemManager webItemManager, + WebItemSecurity webItemSecurity, + AuthContext authContext, + TenantManager tenantManager, + UserManager userManager, + SettingsManager settingsManager, + GlobalStore globalStore, + IOptionsMonitor options, + IServiceProvider serviceProvider + ) + { + CoreBaseSettings = coreBaseSettings; + WebItemManager = webItemManager; + WebItemSecurity = webItemSecurity; + AuthContext = authContext; + TenantManager = tenantManager; + UserManager = userManager; + SettingsManager = settingsManager; + GlobalStore = globalStore; + ServiceProvider = serviceProvider; + Logger = options.Get("ASC.Files"); + } + + internal static readonly IDictionary ProjectsRootFolderCache = + new ConcurrentDictionary(); /*Use SYNCHRONIZED for cross thread blocks*/ + + public object GetFolderProjects(IDaoFactory daoFactory) + { + if (CoreBaseSettings.Personal) return null; + + if (WebItemManager[WebItemManager.ProjectsProductID].IsDisabled(WebItemSecurity, AuthContext)) return null; + + var folderDao = daoFactory.FolderDao; + if (!ProjectsRootFolderCache.TryGetValue(TenantManager.GetCurrentTenant().TenantId, out var result)) + { + result = folderDao.GetFolderIDProjects(true); + + ProjectsRootFolderCache[TenantManager.GetCurrentTenant().TenantId] = result; + } + + return result; + } + + internal static readonly IDictionary UserRootFolderCache = + new ConcurrentDictionary(); /*Use SYNCHRONIZED for cross thread blocks*/ + + public object GetFolderMy(FileMarker fileMarker, IDaoFactory daoFactory) + { + if (!AuthContext.IsAuthenticated) return 0; + if (UserManager.GetUsers(AuthContext.CurrentAccount.ID).IsVisitor(UserManager)) return 0; + + var cacheKey = string.Format("my/{0}/{1}", TenantManager.GetCurrentTenant().TenantId, AuthContext.CurrentAccount.ID); + + if (!UserRootFolderCache.TryGetValue(cacheKey, out var myFolderId)) + { + myFolderId = GetFolderIdAndProccessFirstVisit(fileMarker, daoFactory, true); + if (!Equals(myFolderId, 0)) + UserRootFolderCache[cacheKey] = myFolderId; + } + return myFolderId; + } + + protected internal void SetFolderMy(object value) + { + var cacheKey = string.Format("my/{0}/{1}", TenantManager.GetCurrentTenant().TenantId, value); + UserRootFolderCache.Remove(cacheKey); + } + + internal static readonly IDictionary CommonFolderCache = + new ConcurrentDictionary(); /*Use SYNCHRONIZED for cross thread blocks*/ + + public object GetFolderCommon(FileMarker fileMarker, IDaoFactory daoFactory) + { + if (CoreBaseSettings.Personal) return null; + + if (!CommonFolderCache.TryGetValue(TenantManager.GetCurrentTenant().TenantId, out var commonFolderId)) + { + commonFolderId = GetFolderIdAndProccessFirstVisit(fileMarker, daoFactory, false); + if (!Equals(commonFolderId, 0)) + CommonFolderCache[TenantManager.GetCurrentTenant().TenantId] = commonFolderId; + } + return commonFolderId; + } + + internal static readonly IDictionary ShareFolderCache = + new ConcurrentDictionary(); /*Use SYNCHRONIZED for cross thread blocks*/ + + public object GetFolderShare(IFolderDao folderDao) + { + if (CoreBaseSettings.Personal) return null; + if (IsOutsider) return null; + + if (!ShareFolderCache.TryGetValue(TenantManager.GetCurrentTenant().TenantId, out var sharedFolderId)) + { + sharedFolderId = folderDao.GetFolderIDShare(true); + + if (!sharedFolderId.Equals(0)) + ShareFolderCache[TenantManager.GetCurrentTenant().TenantId] = sharedFolderId; + } + + return sharedFolderId; + } + + internal static readonly IDictionary TrashFolderCache = + new ConcurrentDictionary(); /*Use SYNCHRONIZED for cross thread blocks*/ + + public object GetFolderTrash(IFolderDao folderDao) + { + if (IsOutsider) return null; + + var cacheKey = string.Format("trash/{0}/{1}", TenantManager.GetCurrentTenant().TenantId, AuthContext.CurrentAccount.ID); + + if (!TrashFolderCache.TryGetValue(cacheKey, out var trashFolderId)) + { + trashFolderId = AuthContext.IsAuthenticated ? folderDao.GetFolderIDTrash(true) : 0; + TrashFolderCache[cacheKey] = trashFolderId; + } + return trashFolderId; + } + + protected internal void SetFolderTrash(object value) + { + var cacheKey = string.Format("trash/{0}/{1}", TenantManager.GetCurrentTenant().TenantId, value); + TrashFolderCache.Remove(cacheKey); + } + + private object GetFolderIdAndProccessFirstVisit(FileMarker fileMarker, IDaoFactory daoFactory, bool my) + { + var folderDao = daoFactory.FolderDao; + var fileDao = daoFactory.FileDao; + + var id = my ? folderDao.GetFolderIDUser(false) : folderDao.GetFolderIDCommon(false); + + if (Equals(id, 0)) //TODO: think about 'null' + { + id = my ? folderDao.GetFolderIDUser(true) : folderDao.GetFolderIDCommon(true); + + //Copy start document + if (AdditionalWhiteLabelSettings.Instance(SettingsManager).StartDocsEnabled) + { + try + { + var storeTemplate = GlobalStore.GetStoreTemplate(); + + var culture = my ? UserManager.GetUsers(AuthContext.CurrentAccount.ID).GetCulture() : TenantManager.GetCurrentTenant().GetCulture(); + var path = FileConstant.StartDocPath + culture + "/"; + + if (!storeTemplate.IsDirectory(path)) + path = FileConstant.StartDocPath + "default/"; + path += my ? "my/" : "corporate/"; + + SaveStartDocument(fileMarker, folderDao, fileDao, id, path, storeTemplate); + } + catch (Exception ex) + { + Logger.Error(ex); + } + } + } + + return id; + } + + private void SaveStartDocument(FileMarker fileMarker, IFolderDao folderDao, IFileDao fileDao, object folderId, string path, IDataStore storeTemplate) + { + foreach (var file in storeTemplate.ListFilesRelative("", path, "*", false)) + { + SaveFile(fileMarker, fileDao, folderId, path + file, storeTemplate); + } + + foreach (var folderName in storeTemplate.ListDirectoriesRelative(path, false)) + { + var folder = ServiceProvider.GetService(); + folder.Title = folderName; + folder.ParentFolderID = folderId; + + var subFolderId = folderDao.SaveFolder(folder); + + SaveStartDocument(fileMarker, folderDao, fileDao, subFolderId, path + folderName + "/", storeTemplate); + } + } + + private void SaveFile(FileMarker fileMarker, IFileDao fileDao, object folder, string filePath, IDataStore storeTemp) + { + using var stream = storeTemp.GetReadStream("", filePath); + var fileName = Path.GetFileName(filePath); + var file = ServiceProvider.GetService(); + + file.Title = fileName; + file.ContentLength = stream.CanSeek ? stream.Length : storeTemp.GetFileSize("", filePath); + file.FolderID = folder; + file.Comment = FilesCommonResource.CommentCreate; + + stream.Position = 0; + try + { + file = fileDao.SaveFile(file, stream); + + fileMarker.MarkAsNew(file); + } + catch (Exception ex) + { + Logger.Error(ex); + } + } + public bool IsOutsider + { + get { return UserManager.GetUsers(AuthContext.CurrentAccount.ID).IsOutsider(UserManager); } + } + } + + public class GlobalFolderHelper + { + public FileMarker FileMarker { get; } + public IDaoFactory DaoFactory { get; } + public GlobalFolder GlobalFolder { get; } + + public GlobalFolderHelper(FileMarker fileMarker, IDaoFactory daoFactory, GlobalFolder globalFolder) + { + FileMarker = fileMarker; + DaoFactory = daoFactory; + GlobalFolder = globalFolder; + } + + public object FolderProjects + { + get + { + return GlobalFolder.GetFolderProjects(DaoFactory); + } + } + public object FolderCommon + { + get + { + return GlobalFolder.GetFolderCommon(FileMarker, DaoFactory); + } + } + + public object FolderMy + { + get + { + return GlobalFolder.GetFolderMy(FileMarker, DaoFactory); + } + set + { + GlobalFolder.SetFolderMy(value); + } + } + + public object FolderShare + { + get + { + return GlobalFolder.GetFolderShare(DaoFactory.FolderDao); + } + } + + public object FolderTrash + { + get + { + return GlobalFolder.GetFolderTrash(DaoFactory.FolderDao); + } + set + { + GlobalFolder.SetFolderTrash(value); + } + } + } + + public static class GlobalExtention + { + public static DIHelper AddGlobalNotifyService(this DIHelper services) + { + services.TryAddSingleton(); + + return services + .AddKafkaService() + .AddCoreBaseSettingsService(); + } + + public static DIHelper AddGlobalService(this DIHelper services) + { + services.TryAddScoped(); + + return services + .AddAuthContextService() + .AddUserManagerService() + .AddCoreSettingsService() + .AddTenantManagerService() + .AddDisplayUserSettingsService() + .AddCustomNamingPeopleService() + .AddFileSecurityCommonService(); + } + + public static DIHelper AddGlobalStoreService(this DIHelper services) + { + services.TryAddScoped(); + + return services + .AddStorageFactoryService() + .AddTenantManagerService(); + } + + public static DIHelper AddGlobalSpaceService(this DIHelper services) + { + services.TryAddScoped(); + + return services + .AddFilesUserSpaceUsageService() + .AddAuthContextService(); + } + public static DIHelper AddGlobalFolderService(this DIHelper services) + { + services.TryAddScoped(); + + return services + .AddCoreBaseSettingsService() + .AddWebItemManager() + .AddWebItemSecurity() + .AddAuthContextService() + .AddTenantManagerService() + .AddUserManagerService() + .AddSettingsManagerService() + .AddGlobalStoreService(); + } + + public static DIHelper AddGlobalFolderHelperService(this DIHelper services) + { + services.TryAddScoped(); + + return services + .AddGlobalFolderService() + .AddDaoFactoryService() + .AddFileMarkerService() + ; + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Helpers/PathProvider.cs b/products/ASC.Files/Server/Helpers/PathProvider.cs new file mode 100644 index 0000000000..b3332959cf --- /dev/null +++ b/products/ASC.Files/Server/Helpers/PathProvider.cs @@ -0,0 +1,236 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Web; + +using ASC.Common; +using ASC.Common.Web; +using ASC.Core.Common; +using ASC.Files.Core; +using ASC.Files.Core.Data; +using ASC.Files.Resources; +using ASC.Security.Cryptography; +using ASC.Web.Core.Files; +using ASC.Web.Core.Utility.Skins; +using ASC.Web.Studio.Utility; + +using File = ASC.Files.Core.File; + +namespace ASC.Web.Files.Classes +{ + public class PathProvider + { + public static readonly string ProjectVirtualPath = "~/Products/Projects/TMDocs.aspx"; + + public static readonly string TemplatePath = "/Products/Files/Templates/"; + + public static readonly string StartURL = FilesLinkUtility.FilesBaseVirtualPath; + + public readonly string GetFileServicePath; + + public WebImageSupplier WebImageSupplier { get; } + public IDaoFactory DaoFactory { get; } + public CommonLinkUtility CommonLinkUtility { get; } + public FilesLinkUtility FilesLinkUtility { get; } + public EmailValidationKeyProvider EmailValidationKeyProvider { get; } + public GlobalStore GlobalStore { get; } + public BaseCommonLinkUtility BaseCommonLinkUtility { get; } + + public PathProvider( + WebImageSupplier webImageSupplier, + IDaoFactory daoFactory, + CommonLinkUtility commonLinkUtility, + FilesLinkUtility filesLinkUtility, + EmailValidationKeyProvider emailValidationKeyProvider, + GlobalStore globalStore, + BaseCommonLinkUtility baseCommonLinkUtility) + { + WebImageSupplier = webImageSupplier; + DaoFactory = daoFactory; + CommonLinkUtility = commonLinkUtility; + FilesLinkUtility = filesLinkUtility; + EmailValidationKeyProvider = emailValidationKeyProvider; + GlobalStore = globalStore; + BaseCommonLinkUtility = baseCommonLinkUtility; + GetFileServicePath = BaseCommonLinkUtility.ToAbsolute("~/Products/Files/Services/WCFService/service.svc/"); + } + + public string GetImagePath(string imgFileName) + { + return WebImageSupplier.GetAbsoluteWebPath(imgFileName, Configuration.ProductEntryPoint.ID); + } + + public string GetFileStaticRelativePath(string fileName) + { + var ext = FileUtility.GetFileExtension(fileName); + switch (ext) + { + case ".js": //Attention: Only for ResourceBundleControl + return VirtualPathUtility.ToAbsolute("~/Products/Files/js/" + fileName); + case ".ascx": + return BaseCommonLinkUtility.ToAbsolute("~/Products/Files/Controls/" + fileName); + case ".css": //Attention: Only for ResourceBundleControl + return VirtualPathUtility.ToAbsolute("~/Products/Files/App_Themes/default/" + fileName); + } + + return fileName; + } + + public string GetFileControlPath(string fileName) + { + return BaseCommonLinkUtility.ToAbsolute("~/Products/Files/Controls/" + fileName); + } + + public string GetFolderUrl(Folder folder, int projectID = 0) + { + if (folder == null) throw new ArgumentNullException("folder", FilesCommonResource.ErrorMassage_FolderNotFound); + + var folderDao = DaoFactory.FolderDao; + + switch (folder.RootFolderType) + { + case FolderType.BUNCH: + if (projectID == 0) + { + var path = folderDao.GetBunchObjectID(folder.RootFolderId); + + var projectIDFromDao = path.Split('/').Last(); + + if (string.IsNullOrEmpty(projectIDFromDao)) return string.Empty; + + projectID = Convert.ToInt32(projectIDFromDao); + } + return CommonLinkUtility.GetFullAbsolutePath(string.Format("{0}?prjid={1}#{2}", ProjectVirtualPath, projectID, folder.ID)); + default: + return CommonLinkUtility.GetFullAbsolutePath(FilesLinkUtility.FilesBaseAbsolutePath + "#" + HttpUtility.UrlPathEncode(folder.ID.ToString())); + } + } + + public string GetFolderUrl(object folderId) + { + var folder = DaoFactory.FolderDao.GetFolder(folderId); + + return GetFolderUrl(folder); + } + + public string GetFileStreamUrl(File file, string doc = null, bool lastVersion = false) + { + if (file == null) throw new ArgumentNullException("file", FilesCommonResource.ErrorMassage_FileNotFound); + + //NOTE: Always build path to handler! + var uriBuilder = new UriBuilder(CommonLinkUtility.GetFullAbsolutePath(FilesLinkUtility.FileHandlerPath)); + var query = uriBuilder.Query; + query += FilesLinkUtility.Action + "=stream&"; + query += FilesLinkUtility.FileId + "=" + HttpUtility.UrlEncode(file.ID.ToString()) + "&"; + var version = 0; + if (!lastVersion) + { + version = file.Version; + query += FilesLinkUtility.Version + "=" + file.Version + "&"; + } + query += FilesLinkUtility.AuthKey + "=" + EmailValidationKeyProvider.GetEmailKey(file.ID.ToString() + version); + if (!string.IsNullOrEmpty(doc)) + { + query += "&" + FilesLinkUtility.DocShareKey + "=" + HttpUtility.UrlEncode(doc); + } + + return uriBuilder.Uri + "?" + query; + } + + public string GetFileChangesUrl(File file, string doc = null) + { + if (file == null) throw new ArgumentNullException("file", FilesCommonResource.ErrorMassage_FileNotFound); + + var uriBuilder = new UriBuilder(CommonLinkUtility.GetFullAbsolutePath(FilesLinkUtility.FileHandlerPath)); + var query = uriBuilder.Query; + query += $"{FilesLinkUtility.Action}=diff&"; + query += $"{FilesLinkUtility.FileId}={HttpUtility.UrlEncode(file.ID.ToString())}&"; + query += $"{FilesLinkUtility.Version}={file.Version}&"; + query += $"{FilesLinkUtility.AuthKey}={EmailValidationKeyProvider.GetEmailKey(file.ID + file.Version.ToString(CultureInfo.InvariantCulture))}"; + if (!string.IsNullOrEmpty(doc)) + { + query += $"&{FilesLinkUtility.DocShareKey}={HttpUtility.UrlEncode(doc)}"; + } + + return $"{uriBuilder.Uri}?{query}"; + } + + public string GetTempUrl(Stream stream, string ext) + { + if (stream == null) throw new ArgumentNullException("stream"); + + var store = GlobalStore.GetStore(); + var fileName = string.Format("{0}{1}", Guid.NewGuid(), ext); + var path = Path.Combine("temp_stream", fileName); + + store.Save( + FileConstant.StorageDomainTmp, + path, + stream, + MimeMapping.GetMimeMapping(ext), + "attachment; filename=\"" + fileName + "\""); + + var uriBuilder = new UriBuilder(CommonLinkUtility.GetFullAbsolutePath(FilesLinkUtility.FileHandlerPath)); + var query = uriBuilder.Query; + query += $"{FilesLinkUtility.Action}=tmp&"; + query += $"{FilesLinkUtility.FileTitle}={HttpUtility.UrlEncode(fileName)}&"; + query += $"{FilesLinkUtility.AuthKey}={EmailValidationKeyProvider.GetEmailKey(fileName)}"; + + return $"{uriBuilder.Uri}?{query}"; + } + + public string GetEmptyFileUrl(string extension) + { + var uriBuilder = new UriBuilder(CommonLinkUtility.GetFullAbsolutePath(FilesLinkUtility.FileHandlerPath)); + var query = uriBuilder.Query; + query += $"{FilesLinkUtility.Action}=empty&"; + query += $"{FilesLinkUtility.FileTitle}={HttpUtility.UrlEncode(extension)}"; + + return $"{uriBuilder.Uri}?{query}"; + } + } + + public static class PathProviderExtention + { + public static DIHelper AddPathProviderService(this DIHelper services) + { + services.TryAddScoped(); + + return services + .AddWebImageSupplierService() + .AddCommonLinkUtilityService() + .AddEmailValidationKeyProviderService() + .AddGlobalStoreService() + .AddBaseCommonLinkUtilityService() + .AddFilesLinkUtilityService() + .AddDaoFactoryService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Helpers/ThirdpartyConfiguration.cs b/products/ASC.Files/Server/Helpers/ThirdpartyConfiguration.cs new file mode 100644 index 0000000000..988caa1f15 --- /dev/null +++ b/products/ASC.Files/Server/Helpers/ThirdpartyConfiguration.cs @@ -0,0 +1,174 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Common; +using ASC.FederatedLogin.LoginProviders; +using ASC.Files.Core; +using ASC.Files.Core.Data; + +using Microsoft.Extensions.Configuration; + +namespace ASC.Web.Files.Helpers +{ + public class ThirdpartyConfiguration + { + public IConfiguration Configuration { get; } + public IDaoFactory DaoFactory { get; } + public BoxLoginProvider BoxLoginProvider { get; } + public DropboxLoginProvider DropboxLoginProvider { get; } + public OneDriveLoginProvider OneDriveLoginProvider { get; } + public DocuSignLoginProvider DocuSignLoginProvider { get; } + public GoogleLoginProvider GoogleLoginProvider { get; } + + public ThirdpartyConfiguration( + IConfiguration configuration, + IDaoFactory daoFactory, + BoxLoginProvider boxLoginProvider, + DropboxLoginProvider dropboxLoginProvider, + OneDriveLoginProvider oneDriveLoginProvider, + DocuSignLoginProvider docuSignLoginProvider, + GoogleLoginProvider googleLoginProvider) + { + Configuration = configuration; + DaoFactory = daoFactory; + BoxLoginProvider = boxLoginProvider; + DropboxLoginProvider = dropboxLoginProvider; + OneDriveLoginProvider = oneDriveLoginProvider; + DocuSignLoginProvider = docuSignLoginProvider; + GoogleLoginProvider = googleLoginProvider; + } + + public IEnumerable ThirdPartyProviders + { + get { return (Configuration["files:thirdparty:enable"] ?? "").Split(new char[] { '|', ',' }, StringSplitOptions.RemoveEmptyEntries); } + } + + public bool SupportInclusion + { + get + { + var providerDao = DaoFactory.ProviderDao; + if (providerDao == null) return false; + + return SupportBoxInclusion || SupportDropboxInclusion || SupportDocuSignInclusion || SupportGoogleDriveInclusion || SupportOneDriveInclusion || SupportSharePointInclusion || SupportWebDavInclusion || SupportNextcloudInclusion || SupportOwncloudInclusion || SupportYandexInclusion; + } + } + + public bool SupportBoxInclusion + { + get + { + return ThirdPartyProviders.Contains("box") && BoxLoginProvider.Instance.IsEnabled; + } + } + + public bool SupportDropboxInclusion + { + get + { + return ThirdPartyProviders.Contains("dropboxv2") && DropboxLoginProvider.Instance.IsEnabled; + } + } + + public bool SupportOneDriveInclusion + { + get + { + return ThirdPartyProviders.Contains("onedrive") && OneDriveLoginProvider.Instance.IsEnabled; + } + } + + public bool SupportSharePointInclusion + { + get { return ThirdPartyProviders.Contains("sharepoint"); } + } + + public bool SupportWebDavInclusion + { + get { return ThirdPartyProviders.Contains("webdav"); } + } + + public bool SupportNextcloudInclusion + { + get { return ThirdPartyProviders.Contains("nextcloud"); } + } + + public bool SupportOwncloudInclusion + { + get { return ThirdPartyProviders.Contains("owncloud"); } + } + + public bool SupportYandexInclusion + { + get { return ThirdPartyProviders.Contains("yandex"); } + } + + public string DropboxAppKey + { + get { return DropboxLoginProvider.Instance["dropboxappkey"]; } + } + + public string DropboxAppSecret + { + get { return DropboxLoginProvider.Instance["dropboxappsecret"]; } + } + + public bool SupportDocuSignInclusion + { + get + { + return ThirdPartyProviders.Contains("docusign") && DocuSignLoginProvider.Instance.IsEnabled; + } + } + + public bool SupportGoogleDriveInclusion + { + get + { + return ThirdPartyProviders.Contains("google") && GoogleLoginProvider.Instance.IsEnabled; + } + } + } + public static class ThirdpartyConfigurationExtension + { + public static DIHelper AddThirdpartyConfigurationService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddDaoFactoryService() + .AddDocuSignLoginProviderService() + .AddBoxLoginProviderService() + .AddDropboxLoginProviderService() + .AddOneDriveLoginProviderService() + .AddGoogleLoginProviderService() + ; + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Helpers/WordpressHelper.cs b/products/ASC.Files/Server/Helpers/WordpressHelper.cs new file mode 100644 index 0000000000..337682d44b --- /dev/null +++ b/products/ASC.Files/Server/Helpers/WordpressHelper.cs @@ -0,0 +1,129 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; + +using ASC.Common; +using ASC.Common.Logging; +using ASC.FederatedLogin; +using ASC.FederatedLogin.LoginProviders; +using ASC.Web.Files.ThirdPartyApp; + +using Microsoft.Extensions.Options; + +namespace ASC.Web.Files.Helpers +{ + public class WordpressToken + { + public ILog Log { get; set; } + public TokenHelper TokenHelper { get; } + + public const string AppAttr = "wordpress"; + + public WordpressToken(IOptionsMonitor optionsMonitor, TokenHelper tokenHelper) + { + Log = optionsMonitor.CurrentValue; + TokenHelper = tokenHelper; + } + + public OAuth20Token GetToken() + { + return TokenHelper.GetToken(AppAttr); + } + + public void SaveToken(OAuth20Token token) + { + if (token == null) throw new ArgumentNullException("token"); + TokenHelper.SaveToken(new Token(token, AppAttr)); + } + + public void DeleteToken(OAuth20Token token) + { + if (token == null) throw new ArgumentNullException("token"); + TokenHelper.DeleteToken(AppAttr); + + } + } + public class WordpressHelper + { + public ILog Log { get; set; } + public enum WordpressStatus + { + draft = 0, + publish = 1 + } + + public WordpressHelper(IOptionsMonitor optionsMonitor) + { + Log = optionsMonitor.CurrentValue; + } + + public string GetWordpressMeInfo(string token) + { + try + { + return WordpressLoginProvider.GetWordpressMeInfo(token); + } + catch (Exception ex) + { + Log.Error("Get Wordpress info about me ", ex); + return ""; + } + + } + + public bool CreateWordpressPost(string title, string content, int status, string blogId, OAuth20Token token) + { + try + { + var wpStatus = ((WordpressStatus)status).ToString(); + WordpressLoginProvider.CreateWordpressPost(title, content, wpStatus, blogId, token); + return true; + } + catch (Exception ex) + { + Log.Error("Create Wordpress post ", ex); + return false; + } + } + } + + public static class WordpressHelperExtention + { + public static DIHelper AddWordpressHelperService(this DIHelper services) + { + services.TryAddSingleton(); + return services; + } + + public static DIHelper AddWordpressTokenService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddTokenHelperService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/HttpHandlers/ChunkedUploaderHandler.cs b/products/ASC.Files/Server/HttpHandlers/ChunkedUploaderHandler.cs new file mode 100644 index 0000000000..3913b782e4 --- /dev/null +++ b/products/ASC.Files/Server/HttpHandlers/ChunkedUploaderHandler.cs @@ -0,0 +1,387 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Net; +using System.Threading; +using System.Threading.Tasks; + +using ASC.Common; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Tenants; +using ASC.Files.Core; +using ASC.Files.Resources; +using ASC.MessagingSystem; +using ASC.Security.Cryptography; +using ASC.Web.Core.Files; +using ASC.Web.Files.Helpers; +using ASC.Web.Files.Utils; +using ASC.Web.Studio.Core; + +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Options; + +using Newtonsoft.Json; + +using File = ASC.Files.Core.File; + +namespace ASC.Web.Files.HttpHandlers +{ + public class ChunkedUploaderHandler //: AbstractHttpAsyncHandler + { + public RequestDelegate Next { get; } + public TenantManager TenantManager { get; } + public FileUploader FileUploader { get; } + public FilesMessageService FilesMessageService { get; } + public AuthManager AuthManager { get; } + public SecurityContext SecurityContext { get; } + public SetupInfo SetupInfo { get; } + public EntryManager EntryManager { get; } + public InstanceCrypto InstanceCrypto { get; } + public ChunkedUploadSessionHolder ChunkedUploadSessionHolder { get; } + public ChunkedUploadSessionHelper ChunkedUploadSessionHelper { get; } + public ILog Logger { get; } + + public ChunkedUploaderHandler( + RequestDelegate next, + IOptionsMonitor optionsMonitor, + TenantManager tenantManager, + FileUploader fileUploader, + FilesMessageService filesMessageService, + AuthManager authManager, + SecurityContext securityContext, + SetupInfo setupInfo, + EntryManager entryManager, + InstanceCrypto instanceCrypto, + ChunkedUploadSessionHolder chunkedUploadSessionHolder, + ChunkedUploadSessionHelper chunkedUploadSessionHelper) + { + Next = next; + TenantManager = tenantManager; + FileUploader = fileUploader; + FilesMessageService = filesMessageService; + AuthManager = authManager; + SecurityContext = securityContext; + SetupInfo = setupInfo; + EntryManager = entryManager; + InstanceCrypto = instanceCrypto; + ChunkedUploadSessionHolder = chunkedUploadSessionHolder; + ChunkedUploadSessionHelper = chunkedUploadSessionHelper; + Logger = optionsMonitor.CurrentValue; + } + + public async Task Invoke(HttpContext context) + { + try + { + var request = new ChunkedRequestHelper(context.Request); + + if (!TryAuthorize(request)) + { + WriteError(context, "Can't authorize given initiate session request or session with specified upload id already expired"); + return; + } + + if (TenantManager.GetCurrentTenant().Status != TenantStatus.Active) + { + WriteError(context, "Can't perform upload for deleted or transfering portals"); + return; + } + + switch (request.Type(InstanceCrypto)) + { + case ChunkedRequestType.Abort: + FileUploader.AbortUpload(request.UploadId); + WriteSuccess(context, null); + return; + + case ChunkedRequestType.Initiate: + var createdSession = FileUploader.InitiateUpload(request.FolderId, request.FileId, request.FileName, request.FileSize, request.Encrypted); + WriteSuccess(context, ChunkedUploadSessionHelper.ToResponseObject(createdSession, true)); + return; + + case ChunkedRequestType.Upload: + var resumedSession = FileUploader.UploadChunk(request.UploadId, request.ChunkStream, request.ChunkSize); + + if (resumedSession.BytesUploaded == resumedSession.BytesTotal) + { + WriteSuccess(context, ToResponseObject(resumedSession.File), (int)HttpStatusCode.Created); + FilesMessageService.Send(resumedSession.File, MessageAction.FileUploaded, resumedSession.File.Title); + } + else + { + WriteSuccess(context, ChunkedUploadSessionHelper.ToResponseObject(resumedSession)); + } + return; + + default: + WriteError(context, "Unknown request type."); + return; + } + } + catch (FileNotFoundException error) + { + Logger.Error(error); + WriteError(context, FilesCommonResource.ErrorMassage_FileNotFound); + } + catch (Exception error) + { + Logger.Error(error); + WriteError(context, error.Message); + } + + await Next.Invoke(context); + } + + private bool TryAuthorize(ChunkedRequestHelper request) + { + if (request.Type(InstanceCrypto) == ChunkedRequestType.Initiate) + { + TenantManager.SetCurrentTenant(request.TenantId); + SecurityContext.AuthenticateMe(AuthManager.GetAccountByID(TenantManager.GetCurrentTenant().TenantId, request.AuthKey(InstanceCrypto))); + var cultureInfo = request.CultureInfo(SetupInfo); + if (cultureInfo != null) + Thread.CurrentThread.CurrentUICulture = cultureInfo; + return true; + } + + if (!string.IsNullOrEmpty(request.UploadId)) + { + var uploadSession = ChunkedUploadSessionHolder.GetSession(request.UploadId); + if (uploadSession != null) + { + TenantManager.SetCurrentTenant(uploadSession.TenantId); + SecurityContext.AuthenticateMe(AuthManager.GetAccountByID(TenantManager.GetCurrentTenant().TenantId, uploadSession.UserId)); + var culture = SetupInfo.EnabledCulturesPersonal.Find(c => string.Equals(c.Name, uploadSession.CultureName, StringComparison.InvariantCultureIgnoreCase)); + if (culture != null) + Thread.CurrentThread.CurrentUICulture = culture; + return true; + } + } + + return false; + } + + private static void WriteError(HttpContext context, string message) + { + WriteResponse(context, false, null, message, (int)HttpStatusCode.OK); + } + + private static void WriteSuccess(HttpContext context, object data, int statusCode = (int)HttpStatusCode.OK) + { + WriteResponse(context, true, data, string.Empty, statusCode); + } + + private static void WriteResponse(HttpContext context, bool success, object data, string message, int statusCode) + { + context.Response.StatusCode = statusCode; + context.Response.WriteAsync(JsonConvert.SerializeObject(new { success, data, message })).Wait(); + context.Response.ContentType = "application/json"; + } + + private static object ToResponseObject(File file) + { + return new + { + id = file.ID, + folderId = file.FolderID, + version = file.Version, + title = file.Title, + provider_key = file.ProviderKey, + uploaded = true + }; + } + + private enum ChunkedRequestType + { + None, + Initiate, + Abort, + Upload + } + + [DebuggerDisplay("{Type} ({UploadId})")] + private class ChunkedRequestHelper + { + private readonly HttpRequest _request; + private IFormFile _file; + private int? _tenantId; + private long? _fileContentLength; + private Guid? _authKey; + private CultureInfo _cultureInfo; + + public ChunkedRequestType Type(InstanceCrypto instanceCrypto) + { + if (_request.Query["initiate"] == "true" && IsAuthDataSet(instanceCrypto) && IsFileDataSet()) + return ChunkedRequestType.Initiate; + + if (_request.Query["abort"] == "true" && !string.IsNullOrEmpty(UploadId)) + return ChunkedRequestType.Abort; + + return !string.IsNullOrEmpty(UploadId) + ? ChunkedRequestType.Upload + : ChunkedRequestType.None; + } + + public string UploadId + { + get { return _request.Query["uid"]; } + } + + public int TenantId + { + get + { + if (!_tenantId.HasValue) + { + if (int.TryParse(_request.Query["tid"], out var v)) + _tenantId = v; + else + _tenantId = -1; + } + return _tenantId.Value; + } + } + + public Guid AuthKey(InstanceCrypto instanceCrypto) + { + if (!_authKey.HasValue) + { + _authKey = !string.IsNullOrEmpty(_request.Query["userid"]) + ? new Guid(instanceCrypto.Decrypt(_request.Query["userid"])) + : Guid.Empty; + } + return _authKey.Value; + } + + public string FolderId + { + get { return _request.Query[FilesLinkUtility.FolderId]; } + } + + public string FileId + { + get { return _request.Query[FilesLinkUtility.FileId]; } + } + + public string FileName + { + get { return _request.Query[FilesLinkUtility.FileTitle]; } + } + + public long FileSize + { + get + { + if (!_fileContentLength.HasValue) + { + long.TryParse(_request.Query["fileSize"], out var v); + _fileContentLength = v; + } + return _fileContentLength.Value; + } + } + + public long ChunkSize + { + get { return File.Length; } + } + + public Stream ChunkStream + { + get { return File.OpenReadStream(); } + } + + public CultureInfo CultureInfo(SetupInfo setupInfo) + { + if (_cultureInfo != null) + return _cultureInfo; + + var culture = _request.Query["culture"]; + if (string.IsNullOrEmpty(culture)) culture = "en-US"; + + return _cultureInfo = setupInfo.EnabledCulturesPersonal.Find(c => string.Equals(c.Name, culture, StringComparison.InvariantCultureIgnoreCase)); + } + + public bool Encrypted + { + get { return _request.Query["encrypted"] == "true"; } + } + + private IFormFile File + { + get + { + if (_file != null) + return _file; + + if (_request.Form.Files.Count > 0) + return _file = _request.Form.Files[0]; + + throw new Exception("HttpRequest.Files is empty"); + } + } + + public ChunkedRequestHelper(HttpRequest request) + { + _request = request ?? throw new ArgumentNullException("request"); + } + + private bool IsAuthDataSet(InstanceCrypto instanceCrypto) + { + return TenantId > -1 && AuthKey(instanceCrypto) != Guid.Empty; + } + + private bool IsFileDataSet() + { + return !string.IsNullOrEmpty(FileName) && !string.IsNullOrEmpty(FolderId); + } + } + } + + public static class ChunkedUploaderHandlerExtention + { + public static DIHelper AddChunkedUploaderHandlerService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddTenantManagerService() + .AddFileUploaderService() + .AddFilesMessageService() + .AddAuthManager() + .AddSecurityContextService() + .AddSetupInfo() + .AddEntryManagerService() + .AddInstanceCryptoService() + .AddChunkedUploadSessionHolderService() + .AddChunkedUploadSessionHelperService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/HttpHandlers/FileHandler.ashx.cs b/products/ASC.Files/Server/HttpHandlers/FileHandler.ashx.cs new file mode 100644 index 0000000000..2c06b23be8 --- /dev/null +++ b/products/ASC.Files/Server/HttpHandlers/FileHandler.ashx.cs @@ -0,0 +1,1185 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using System.Web; + +using ASC.Common.Logging; +using ASC.Common.Web; +using ASC.Core; +using ASC.Files.Core; +using ASC.Files.Core.Data; +using ASC.Files.Core.Security; +using ASC.Files.Resources; +using ASC.MessagingSystem; +using ASC.Security.Cryptography; +using ASC.Web.Core; +using ASC.Web.Core.Files; +using ASC.Web.Files.Classes; +using ASC.Web.Files.Core; +using ASC.Web.Files.Helpers; +using ASC.Web.Files.Services.DocumentService; +using ASC.Web.Files.Services.FFmpegService; +using ASC.Web.Files.Utils; +using ASC.Web.Studio.Core; +using ASC.Web.Studio.UserControls.Statistics; +using ASC.Web.Studio.Utility; + +using JWT; + +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +using Newtonsoft.Json.Linq; + +using File = ASC.Files.Core.File; +using FileShare = ASC.Files.Core.Security.FileShare; +using MimeMapping = ASC.Common.Web.MimeMapping; +using SecurityContext = ASC.Core.SecurityContext; + +namespace ASC.Web.Files +{ + public class FileHandler //: AbstractHttpAsyncHandler + { + public string FileHandlerPath + { + get { return FilesLinkUtility.FileHandlerPath; } + } + + public RequestDelegate Next { get; } + public FilesLinkUtility FilesLinkUtility { get; } + public TenantExtra TenantExtra { get; } + public AuthContext AuthContext { get; } + public SecurityContext SecurityContext { get; } + public GlobalStore GlobalStore { get; } + public IDaoFactory DaoFactory { get; } + public FileSecurity FileSecurity { get; } + public FileMarker FileMarker { get; } + public SetupInfo SetupInfo { get; } + public FileUtility FileUtility { get; } + public Global Global { get; } + public EmailValidationKeyProvider EmailValidationKeyProvider { get; } + public CoreBaseSettings CoreBaseSettings { get; } + public GlobalFolderHelper GlobalFolderHelper { get; } + public PathProvider PathProvider { get; } + public DocumentServiceTrackerHelper DocumentServiceTrackerHelper { get; } + public FilesMessageService FilesMessageService { get; } + public FileShareLink FileShareLink { get; } + public FileConverter FileConverter { get; } + public FFmpegService FFmpegService { get; } + public IServiceProvider ServiceProvider { get; } + public UserManager UserManager { get; } + public ILog Logger { get; } + public CookiesManager CookiesManager { get; } + public TenantStatisticsProvider TenantStatisticsProvider { get; } + + public FileHandler( + RequestDelegate next, + FilesLinkUtility filesLinkUtility, + TenantExtra tenantExtra, + CookiesManager cookiesManager, + AuthContext authContext, + SecurityContext securityContext, + GlobalStore globalStore, + IOptionsMonitor optionsMonitor, + IDaoFactory daoFactory, + FileSecurity fileSecurity, + FileMarker fileMarker, + SetupInfo setupInfo, + FileUtility fileUtility, + Global global, + EmailValidationKeyProvider emailValidationKeyProvider, + CoreBaseSettings coreBaseSettings, + GlobalFolderHelper globalFolderHelper, + PathProvider pathProvider, + UserManager userManager, + DocumentServiceTrackerHelper documentServiceTrackerHelper, + FilesMessageService filesMessageService, + FileShareLink fileShareLink, + FileConverter fileConverter, + FFmpegService fFmpegService, + IServiceProvider serviceProvider) + { + Next = next; + FilesLinkUtility = filesLinkUtility; + TenantExtra = tenantExtra; + AuthContext = authContext; + SecurityContext = securityContext; + GlobalStore = globalStore; + DaoFactory = daoFactory; + FileSecurity = fileSecurity; + FileMarker = fileMarker; + SetupInfo = setupInfo; + FileUtility = fileUtility; + Global = global; + EmailValidationKeyProvider = emailValidationKeyProvider; + CoreBaseSettings = coreBaseSettings; + GlobalFolderHelper = globalFolderHelper; + PathProvider = pathProvider; + DocumentServiceTrackerHelper = documentServiceTrackerHelper; + FilesMessageService = filesMessageService; + FileShareLink = fileShareLink; + FileConverter = fileConverter; + FFmpegService = fFmpegService; + ServiceProvider = serviceProvider; + UserManager = userManager; + Logger = optionsMonitor.CurrentValue; + CookiesManager = cookiesManager; + } + + public async Task Invoke(HttpContext context) + { + if (TenantExtra.IsNotPaid()) + { + context.Response.StatusCode = (int)HttpStatusCode.PaymentRequired; + //context.Response.StatusDescription = "Payment Required."; + return; + } + + try + { + switch ((context.Request.Query[FilesLinkUtility.Action].FirstOrDefault() ?? "").ToLower()) + { + case "view": + case "download": + DownloadFile(context); + break; + case "bulk": + BulkDownloadFile(context); + break; + case "stream": + StreamFile(context); + break; + case "empty": + EmptyFile(context); + break; + case "tmp": + TempFile(context); + break; + case "create": + CreateFile(context); + break; + case "redirect": + Redirect(context); + break; + case "diff": + DifferenceFile(context); + break; + case "track": + TrackFile(context); + break; + default: + throw new HttpException((int)HttpStatusCode.BadRequest, FilesCommonResource.ErrorMassage_BadRequest); + } + + } + catch (InvalidOperationException e) + { + throw new HttpException((int)HttpStatusCode.InternalServerError, FilesCommonResource.ErrorMassage_BadRequest, e); + } + + await Next.Invoke(context); + } + + private void BulkDownloadFile(HttpContext context) + { + if (!SecurityContext.AuthenticateMe(CookiesManager.GetCookies(CookiesType.AuthKey))) + { + context.Response.StatusCode = (int)HttpStatusCode.Forbidden; + return; + } + + var store = GlobalStore.GetStore(); + var path = string.Format(@"{0}\{1}.zip", AuthContext.CurrentAccount.ID, FileConstant.DownloadTitle); + if (!store.IsFile(FileConstant.StorageDomainTmp, path)) + { + Logger.ErrorFormat("BulkDownload file error. File is not exist on storage. UserId: {0}.", AuthContext.CurrentAccount.ID); + context.Response.StatusCode = (int)HttpStatusCode.NotFound; + return; + } + + if (store.IsSupportedPreSignedUri) + { + var url = store.GetPreSignedUri(FileConstant.StorageDomainTmp, path, TimeSpan.FromHours(1), null).ToString(); + context.Response.Redirect(url); + return; + } + + context.Response.Clear(); + + try + { + var flushed = false; + using (var readStream = store.GetReadStream(FileConstant.StorageDomainTmp, path)) + { + long offset = 0; + var length = readStream.Length; + if (readStream.CanSeek) + { + length = ProcessRangeHeader(context, readStream.Length, ref offset); + readStream.Seek(offset, SeekOrigin.Begin); + } + + SendStreamByChunks(context, length, FileConstant.DownloadTitle + ".zip", readStream, ref flushed); + } + + context.Response.Body.Flush(); + //context.Response.SuppressContent = true; + //context.ApplicationInstance.CompleteRequest(); + } + catch (Exception e) + { + Logger.ErrorFormat("BulkDownloadFile failed for user {0} with error: ", SecurityContext.CurrentAccount.ID, e.Message); + throw new HttpException((int)HttpStatusCode.BadRequest, e.Message); + } + } + + private void DownloadFile(HttpContext context) + { + var flushed = false; + try + { + var id = context.Request.Query[FilesLinkUtility.FileId]; + var doc = context.Request.Query[FilesLinkUtility.DocShareKey].FirstOrDefault() ?? ""; + + var fileDao = DaoFactory.FileDao; + var readLink = FileShareLink.Check(doc, true, fileDao, out var file); + if (!readLink && file == null) + { + fileDao.InvalidateCache(id); + + file = int.TryParse(context.Request.Query[FilesLinkUtility.Version], out var version) && version > 0 + ? fileDao.GetFile(id, version) + : fileDao.GetFile(id); + } + + if (file == null) + { + context.Response.StatusCode = (int)HttpStatusCode.NotFound; + + return; + } + + if (!readLink && !FileSecurity.CanRead(file)) + { + context.Response.StatusCode = (int)HttpStatusCode.Forbidden; + return; + } + + if (!string.IsNullOrEmpty(file.Error)) throw new Exception(file.Error); + + if (!fileDao.IsExistOnStorage(file)) + { + Logger.ErrorFormat("Download file error. File is not exist on storage. File id: {0}.", file.ID); + context.Response.StatusCode = (int)HttpStatusCode.NotFound; + + return; + } + + FileMarker.RemoveMarkAsNew(file); + + context.Response.Clear(); + context.Response.Headers.Clear(); + //TODO + //context.Response.Headers.Charset = "utf-8"; + + FilesMessageService.Send(file, MessageAction.FileDownloaded, file.Title); + + if (string.Equals(context.Request.Headers["If-None-Match"], GetEtag(file))) + { + //Its cached. Reply 304 + context.Response.StatusCode = (int)HttpStatusCode.NotModified; + //context.Response.Cache.SetETag(GetEtag(file)); + } + else + { + //context.Response.CacheControl = "public"; + //context.Response.Cache.SetETag(GetEtag(file)); + //context.Response.Cache.SetCacheability(HttpCacheability.Public); + + Stream fileStream = null; + try + { + var title = file.Title; + + if (file.ContentLength <= SetupInfo.AvailableFileSize) + { + var ext = FileUtility.GetFileExtension(file.Title); + + var outType = (context.Request.Query[FilesLinkUtility.OutType].FirstOrDefault() ?? "").Trim(); + if (!string.IsNullOrEmpty(outType) + && FileUtility.ExtsConvertible.Keys.Contains(ext) + && FileUtility.ExtsConvertible[ext].Contains(outType)) + { + ext = outType; + } + + long offset = 0; + long length; + if (!file.ProviderEntry + && string.Equals(context.Request.Query["convpreview"], "true", StringComparison.InvariantCultureIgnoreCase) + && FFmpegService.IsConvertable(ext)) + { + const string mp4Name = "content.mp4"; + var mp4Path = FileDao.GetUniqFilePath(file, mp4Name); + var store = GlobalStore.GetStore(); + if (!store.IsFile(mp4Path)) + { + fileStream = fileDao.GetFileStream(file); + + Logger.InfoFormat("Converting {0} (fileId: {1}) to mp4", file.Title, file.ID); + var stream = FFmpegService.Convert(fileStream, ext); + store.Save(string.Empty, mp4Path, stream, mp4Name); + } + + var fullLength = store.GetFileSize(string.Empty, mp4Path); + + length = ProcessRangeHeader(context, fullLength, ref offset); + fileStream = store.GetReadStream(string.Empty, mp4Path, (int)offset); + + title = FileUtility.ReplaceFileExtension(title, ".mp4"); + } + else + { + if (!FileConverter.EnableConvert(file, ext)) + { + if (!readLink && fileDao.IsSupportedPreSignedUri(file)) + { + context.Response.Redirect(fileDao.GetPreSignedUri(file, TimeSpan.FromHours(1)).ToString(), true); + + return; + } + + fileStream = fileDao.GetFileStream(file); // getStream to fix file.ContentLength + + if (fileStream.CanSeek) + { + var fullLength = file.ContentLength; + length = ProcessRangeHeader(context, fullLength, ref offset); + fileStream.Seek(offset, SeekOrigin.Begin); + } + else + { + length = file.ContentLength; + } + } + else + { + title = FileUtility.ReplaceFileExtension(title, ext); + fileStream = FileConverter.Exec(file, ext); + + length = fileStream.Length; + } + } + + SendStreamByChunks(context, length, title, fileStream, ref flushed); + } + else + { + if (!readLink && fileDao.IsSupportedPreSignedUri(file)) + { + context.Response.Redirect(fileDao.GetPreSignedUri(file, TimeSpan.FromHours(1)).ToString(), true); + + return; + } + + fileStream = fileDao.GetFileStream(file); // getStream to fix file.ContentLength + + long offset = 0; + var length = file.ContentLength; + if (fileStream.CanSeek) + { + length = ProcessRangeHeader(context, file.ContentLength, ref offset); + fileStream.Seek(offset, SeekOrigin.Begin); + } + + SendStreamByChunks(context, length, title, fileStream, ref flushed); + } + } + catch (ThreadAbortException tae) + { + Logger.Error("DownloadFile", tae); + } + catch (HttpException e) + { + Logger.Error("DownloadFile", e); + throw new HttpException((int)HttpStatusCode.BadRequest, e.Message); + } + finally + { + if (fileStream != null) + { + fileStream.Close(); + fileStream.Dispose(); + } + } + + try + { + context.Response.Body.Flush(); + //context.Response.SuppressContent = true; + //context.ApplicationInstance.CompleteRequest(); + flushed = true; + } + catch (HttpException ex) + { + Logger.Error("DownloadFile", ex); + } + } + } + catch (ThreadAbortException tae) + { + Logger.Error("DownloadFile", tae); + } + catch (Exception ex) + { + // Get stack trace for the exception with source file information + var st = new StackTrace(ex, true); + // Get the top stack frame + var frame = st.GetFrame(0); + // Get the line number from the stack frame + var line = frame.GetFileLineNumber(); + + Logger.ErrorFormat("Url: {0} {1} IsClientConnected:{2}, line number:{3} frame:{4}", context.Request.Url(), ex, !context.RequestAborted.IsCancellationRequested, line, frame); + if (!flushed && !context.RequestAborted.IsCancellationRequested) + { + context.Response.StatusCode = 400; + context.Response.WriteAsync(HttpUtility.HtmlEncode(ex.Message)).Wait(); + } + } + } + + private long ProcessRangeHeader(HttpContext context, long fullLength, ref long offset) + { + if (context == null) throw new ArgumentNullException(); + if (context.Request.Headers["Range"].FirstOrDefault() == null) return fullLength; + + long endOffset = -1; + + var range = context.Request.Headers["Range"].FirstOrDefault().Split(new[] { '=', '-' }); + offset = Convert.ToInt64(range[1]); + if (range.Count() > 2 && !string.IsNullOrEmpty(range[2])) + { + endOffset = Convert.ToInt64(range[2]); + } + if (endOffset < 0 || endOffset >= fullLength) + { + endOffset = fullLength - 1; + } + + var length = endOffset - offset + 1; + + if (length <= 0) throw new HttpException(HttpStatusCode.RequestedRangeNotSatisfiable); + + Logger.InfoFormat("Starting file download (chunk {0}-{1})", offset, endOffset); + if (length < fullLength) + { + context.Response.StatusCode = (int)HttpStatusCode.PartialContent; + } + context.Response.Headers.Add("Accept-Ranges", "bytes"); + context.Response.Headers.Add("Content-Range", string.Format(" bytes {0}-{1}/{2}", offset, endOffset, fullLength)); + + return length; + } + + private void SendStreamByChunks(HttpContext context, long toRead, string title, Stream fileStream, ref bool flushed) + { + //context.Response.Buffer = false; + context.Response.Headers.Add("Connection", "Keep-Alive"); + context.Response.Headers.Add("Content-Length", toRead.ToString(CultureInfo.InvariantCulture)); + context.Response.Headers.Add("Content-Disposition", ContentDispositionUtil.GetHeaderValue(title)); + context.Response.ContentType = MimeMapping.GetMimeMapping(title); + + const int bufferSize = 32 * 1024; // 32KB + var buffer = new byte[bufferSize]; + while (toRead > 0) + { + var length = fileStream.Read(buffer, 0, bufferSize); + + if (!context.RequestAborted.IsCancellationRequested) + { + context.Response.Body.Write(buffer, 0, length); + context.Response.Body.Flush(); + flushed = true; + toRead -= length; + } + else + { + toRead = -1; + Logger.Warn(string.Format("IsClientConnected is false. Why? Download file {0} Connection is lost. ", title)); + } + } + } + + private void StreamFile(HttpContext context) + { + try + { + var fileDao = DaoFactory.FileDao; + var id = context.Request.Query[FilesLinkUtility.FileId]; + if (!int.TryParse(context.Request.Query[FilesLinkUtility.Version].FirstOrDefault() ?? "", out var version)) + { + version = 0; + } + var doc = context.Request.Query[FilesLinkUtility.DocShareKey]; + + fileDao.InvalidateCache(id); + + var linkRight = FileShareLink.Check(doc, fileDao, out var file); + if (linkRight == FileShare.Restrict && !SecurityContext.IsAuthenticated) + { + var auth = context.Request.Query[FilesLinkUtility.AuthKey]; + var validateResult = EmailValidationKeyProvider.ValidateEmailKey(id + version, auth.FirstOrDefault() ?? "", Global.StreamUrlExpire); + if (validateResult != EmailValidationKeyProvider.ValidationResult.Ok) + { + var exc = new HttpException((int)HttpStatusCode.Forbidden, FilesCommonResource.ErrorMassage_SecurityException); + + Logger.Error($"{FilesLinkUtility.AuthKey} {validateResult}: {context.Request.Url()}", exc); + + context.Response.StatusCode = (int)HttpStatusCode.Forbidden; + context.Response.WriteAsync(FilesCommonResource.ErrorMassage_SecurityException).Wait(); + return; + } + + if (!string.IsNullOrEmpty(FileUtility.SignatureSecret)) + { + try + { + var header = context.Request.Headers[FileUtility.SignatureHeader].FirstOrDefault(); + if (string.IsNullOrEmpty(header) || !header.StartsWith("Bearer ")) + { + throw new Exception("Invalid header " + header); + } + header = header.Substring("Bearer ".Length); + + JsonWebToken.JsonSerializer = new DocumentService.JwtSerializer(); + + var stringPayload = JsonWebToken.Decode(header, FileUtility.SignatureSecret); + + Logger.Debug("DocService StreamFile payload: " + stringPayload); + //var data = JObject.Parse(stringPayload); + //if (data == null) + //{ + // throw new ArgumentException("DocService StreamFile header is incorrect"); + //} + + //var signedStringUrl = data["url"] ?? (data["payload"] != null ? data["payload"]["url"] : null); + //if (signedStringUrl == null) + //{ + // throw new ArgumentException("DocService StreamFile header url is incorrect"); + //} + //var signedUrl = new Uri(signedStringUrl.ToString()); + + //var signedQuery = signedUrl.Query; + //if (!context.Request.Url.Query.Equals(signedQuery)) + //{ + // throw new SecurityException(string.Format("DocService StreamFile header id not equals: {0} and {1}", context.Request.Url.Query, signedQuery)); + //} + } + catch (Exception ex) + { + Logger.Error("Download stream header " + context.Request.Url(), ex); + context.Response.StatusCode = (int)HttpStatusCode.Forbidden; + context.Response.WriteAsync(FilesCommonResource.ErrorMassage_SecurityException).Wait(); + return; + } + } + } + + if (file == null + || version > 0 && file.Version != version) + { + file = version > 0 + ? fileDao.GetFile(id, version) + : fileDao.GetFile(id); + } + + if (file == null) + { + context.Response.StatusCode = (int)HttpStatusCode.NotFound; + return; + } + + if (linkRight == FileShare.Restrict && SecurityContext.IsAuthenticated && !FileSecurity.CanRead(file)) + { + context.Response.StatusCode = (int)HttpStatusCode.Forbidden; + return; + } + + if (!string.IsNullOrEmpty(file.Error)) + { + context.Response.WriteAsync(file.Error).Wait(); + context.Response.StatusCode = (int)HttpStatusCode.BadRequest; + return; + } + + context.Response.Headers.Add("Content-Disposition", ContentDispositionUtil.GetHeaderValue(file.Title)); + context.Response.ContentType = MimeMapping.GetMimeMapping(file.Title); + + using (var stream = fileDao.GetFileStream(file)) + { + context.Response.Headers.Add("Content-Length", + stream.CanSeek + ? stream.Length.ToString(CultureInfo.InvariantCulture) + : file.ContentLength.ToString(CultureInfo.InvariantCulture)); + stream.StreamCopyTo(context.Response.Body); + } + } + catch (Exception ex) + { + Logger.Error("Error for: " + context.Request.Url(), ex); + context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; + context.Response.WriteAsync(ex.Message).Wait(); + return; + } + + try + { + context.Response.Body.Flush(); + //context.Response.SuppressContent = true; + //context.ApplicationInstance.CompleteRequest(); + } + catch (HttpException he) + { + Logger.ErrorFormat("StreamFile", he); + } + } + + private void EmptyFile(HttpContext context) + { + try + { + var fileName = context.Request.Query[FilesLinkUtility.FileTitle]; + if (!string.IsNullOrEmpty(FileUtility.SignatureSecret)) + { + try + { + var header = context.Request.Headers[FileUtility.SignatureHeader].FirstOrDefault(); + if (string.IsNullOrEmpty(header) || !header.StartsWith("Bearer ")) + { + throw new Exception("Invalid header " + header); + } + header = header.Substring("Bearer ".Length); + + JsonWebToken.JsonSerializer = new DocumentService.JwtSerializer(); + + var stringPayload = JsonWebToken.Decode(header, FileUtility.SignatureSecret); + + Logger.Debug("DocService EmptyFile payload: " + stringPayload); + //var data = JObject.Parse(stringPayload); + //if (data == null) + //{ + // throw new ArgumentException("DocService EmptyFile header is incorrect"); + //} + + //var signedStringUrl = data["url"] ?? (data["payload"] != null ? data["payload"]["url"] : null); + //if (signedStringUrl == null) + //{ + // throw new ArgumentException("DocService EmptyFile header url is incorrect"); + //} + //var signedUrl = new Uri(signedStringUrl.ToString()); + + //var signedQuery = signedUrl.Query; + //if (!context.Request.Url.Query.Equals(signedQuery)) + //{ + // throw new SecurityException(string.Format("DocService EmptyFile header id not equals: {0} and {1}", context.Request.Url.Query, signedQuery)); + //} + } + catch (Exception ex) + { + Logger.Error("Download stream header " + context.Request.Url(), ex); + context.Response.StatusCode = (int)HttpStatusCode.Forbidden; + context.Response.WriteAsync(FilesCommonResource.ErrorMassage_SecurityException).Wait(); + return; + } + } + + var toExtension = FileUtility.GetFileExtension(fileName); + var fileExtension = FileUtility.GetInternalExtension(toExtension); + fileName = "new" + fileExtension; + var path = FileConstant.NewDocPath + + (CoreBaseSettings.CustomMode ? "ru-RU/" : "default/") + + fileName; + + var storeTemplate = GlobalStore.GetStoreTemplate(); + if (!storeTemplate.IsFile("", path)) + { + context.Response.StatusCode = (int)HttpStatusCode.NotFound; + context.Response.WriteAsync(FilesCommonResource.ErrorMassage_FileNotFound).Wait(); + return; + } + + context.Response.Headers.Add("Content-Disposition", ContentDispositionUtil.GetHeaderValue(fileName)); + context.Response.ContentType = MimeMapping.GetMimeMapping(fileName); + + using var stream = storeTemplate.GetReadStream("", path); + context.Response.Headers.Add("Content-Length", + stream.CanSeek + ? stream.Length.ToString(CultureInfo.InvariantCulture) + : storeTemplate.GetFileSize("", path).ToString(CultureInfo.InvariantCulture)); + stream.StreamCopyTo(context.Response.Body); + } + catch (Exception ex) + { + Logger.Error("Error for: " + context.Request.Url(), ex); + context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; + context.Response.WriteAsync(ex.Message).Wait(); + return; + } + + try + { + context.Response.Body.Flush(); + //context.Response.SuppressContent = true; + //context.ApplicationInstance.CompleteRequest(); + } + catch (HttpException he) + { + Logger.ErrorFormat("EmptyFile", he); + } + } + + private void TempFile(HttpContext context) + { + var fileName = context.Request.Query[FilesLinkUtility.FileTitle]; + var auth = context.Request.Query[FilesLinkUtility.AuthKey].FirstOrDefault(); + + var validateResult = EmailValidationKeyProvider.ValidateEmailKey(fileName, auth ?? "", Global.StreamUrlExpire); + if (validateResult != EmailValidationKeyProvider.ValidationResult.Ok) + { + var exc = new HttpException((int)HttpStatusCode.Forbidden, FilesCommonResource.ErrorMassage_SecurityException); + + Logger.Error($"{FilesLinkUtility.AuthKey} {validateResult}: {context.Request.Url()}", exc); + + context.Response.StatusCode = (int)HttpStatusCode.Forbidden; + context.Response.WriteAsync(FilesCommonResource.ErrorMassage_SecurityException); + return; + } + + context.Response.Clear(); + context.Response.ContentType = MimeMapping.GetMimeMapping(fileName); + context.Response.Headers.Add("Content-Disposition", ContentDispositionUtil.GetHeaderValue(fileName)); + + var store = GlobalStore.GetStore(); + + var path = Path.Combine("temp_stream", fileName); + + if (!store.IsFile(FileConstant.StorageDomainTmp, path)) + { + context.Response.StatusCode = (int)HttpStatusCode.NotFound; + context.Response.WriteAsync(FilesCommonResource.ErrorMassage_FileNotFound).Wait(); + return; + } + + using (var readStream = store.GetReadStream(FileConstant.StorageDomainTmp, path)) + { + context.Response.Headers.Add("Content-Length", readStream.Length.ToString(CultureInfo.InvariantCulture)); + readStream.StreamCopyTo(context.Response.Body); + } + + store.Delete(FileConstant.StorageDomainTmp, path); + + try + { + context.Response.Body.Flush(); + //context.Response.SuppressContent = true; + //context.ApplicationInstance.CompleteRequest(); + } + catch (HttpException he) + { + Logger.ErrorFormat("TempFile", he); + } + } + + private void DifferenceFile(HttpContext context) + { + try + { + var fileDao = DaoFactory.FileDao; + var id = context.Request.Query[FilesLinkUtility.FileId]; + int.TryParse(context.Request.Query[FilesLinkUtility.Version].FirstOrDefault() ?? "", out var version); + var doc = context.Request.Query[FilesLinkUtility.DocShareKey]; + + var linkRight = FileShareLink.Check(doc, fileDao, out var file); + if (linkRight == FileShare.Restrict && !SecurityContext.IsAuthenticated) + { + var auth = context.Request.Query[FilesLinkUtility.AuthKey].FirstOrDefault(); + var validateResult = EmailValidationKeyProvider.ValidateEmailKey(id + version, auth ?? "", Global.StreamUrlExpire); + if (validateResult != EmailValidationKeyProvider.ValidationResult.Ok) + { + var exc = new HttpException((int)HttpStatusCode.Forbidden, FilesCommonResource.ErrorMassage_SecurityException); + + Logger.Error($"{FilesLinkUtility.AuthKey} {validateResult}: {context.Request.Url()}", exc); + + context.Response.StatusCode = (int)HttpStatusCode.Forbidden; + context.Response.WriteAsync(FilesCommonResource.ErrorMassage_SecurityException).Wait(); + return; + } + } + + fileDao.InvalidateCache(id); + + if (file == null + || version > 0 && file.Version != version) + { + file = version > 0 + ? fileDao.GetFile(id, version) + : fileDao.GetFile(id); + } + + if (file == null) + { + context.Response.StatusCode = (int)HttpStatusCode.NotFound; + return; + } + + if (linkRight == FileShare.Restrict && SecurityContext.IsAuthenticated && !FileSecurity.CanRead(file)) + { + context.Response.StatusCode = (int)HttpStatusCode.Forbidden; + return; + } + + if (!string.IsNullOrEmpty(file.Error)) + { + context.Response.WriteAsync(file.Error).Wait(); + context.Response.StatusCode = (int)HttpStatusCode.BadRequest; + return; + } + + context.Response.Headers.Add("Content-Disposition", ContentDispositionUtil.GetHeaderValue(".zip")); + context.Response.ContentType = MimeMapping.GetMimeMapping(".zip"); + + using (var stream = fileDao.GetDifferenceStream(file)) + { + context.Response.Headers.Add("Content-Length", stream.Length.ToString(CultureInfo.InvariantCulture)); + stream.StreamCopyTo(context.Response.Body); + } + } + catch (Exception ex) + { + context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; + context.Response.WriteAsync(ex.Message).Wait(); + Logger.Error("Error for: " + context.Request.Url(), ex); + return; + } + + try + { + context.Response.Body.Flush(); + //context.Response.SuppressContent = true; + //context.ApplicationInstance.CompleteRequest(); + } + catch (HttpException he) + { + Logger.ErrorFormat("DifferenceFile", he); + } + } + + private static string GetEtag(File file) + { + return file.ID + ":" + file.Version + ":" + file.Title.GetHashCode() + ":" + file.ContentLength; + } + + private void CreateFile(HttpContext context) + { + var responseMessage = context.Request.Query["response"] == "message"; + var folderId = context.Request.Query[FilesLinkUtility.FolderId].FirstOrDefault(); + if (string.IsNullOrEmpty(folderId)) + folderId = GlobalFolderHelper.FolderMy.ToString(); + Folder folder; + + var folderDao = DaoFactory.FolderDao; + folder = folderDao.GetFolder(folderId); + + if (folder == null) throw new HttpException((int)HttpStatusCode.NotFound, FilesCommonResource.ErrorMassage_FolderNotFound); + if (!FileSecurity.CanCreate(folder)) throw new HttpException((int)HttpStatusCode.Forbidden, FilesCommonResource.ErrorMassage_SecurityException_Create); + + File file; + var fileUri = context.Request.Query[FilesLinkUtility.FileUri]; + var fileTitle = context.Request.Query[FilesLinkUtility.FileTitle]; + try + { + if (!string.IsNullOrEmpty(fileUri)) + { + file = CreateFileFromUri(folder, fileUri, fileTitle); + } + else + { + var docType = context.Request.Query["doctype"]; + file = CreateFileFromTemplate(folder, fileTitle, docType); + } + } + catch (Exception ex) + { + Logger.Error(ex); + if (responseMessage) + { + context.Response.WriteAsync("error: " + ex.Message).Wait(); + return; + } + context.Response.Redirect(PathProvider.StartURL + "#error/" + HttpUtility.UrlEncode(ex.Message), true); + return; + } + + FileMarker.MarkAsNew(file); + + if (responseMessage) + { + context.Response.WriteAsync("ok: " + string.Format(FilesCommonResource.MessageFileCreated, folder.Title)).Wait(); + return; + } + + context.Response.Redirect( + (context.Request.Query["openfolder"].FirstOrDefault() ?? "").Equals("true") + ? PathProvider.GetFolderUrl(file.FolderID) + : (FilesLinkUtility.GetFileWebEditorUrl(file.ID) + "#message/" + HttpUtility.UrlEncode(string.Format(FilesCommonResource.MessageFileCreated, folder.Title)))); + } + + private File CreateFileFromTemplate(Folder folder, string fileTitle, string docType) + { + var storeTemplate = GlobalStore.GetStoreTemplate(); + + var lang = UserManager.GetUsers(SecurityContext.CurrentAccount.ID).GetCulture(); + + var fileExt = FileUtility.InternalExtension[FileType.Document]; + if (!string.IsNullOrEmpty(docType)) + { + var tmpFileType = Services.DocumentService.Configuration.DocType.FirstOrDefault(r => r.Value.Equals(docType, StringComparison.OrdinalIgnoreCase)); + FileUtility.InternalExtension.TryGetValue(tmpFileType.Key, out var tmpFileExt); + if (!string.IsNullOrEmpty(tmpFileExt)) + fileExt = tmpFileExt; + } + + var templateName = "new" + fileExt; + + var templatePath = FileConstant.NewDocPath + lang + "/"; + if (!storeTemplate.IsDirectory(templatePath)) + templatePath = FileConstant.NewDocPath + "default/"; + templatePath += templateName; + + if (string.IsNullOrEmpty(fileTitle)) + { + fileTitle = templateName; + } + else + { + fileTitle += fileExt; + } + + var file = ServiceProvider.GetService(); + file.Title = fileTitle; + file.FolderID = folder.ID; + file.Comment = FilesCommonResource.CommentCreate; + + var fileDao = DaoFactory.FileDao; + var stream = storeTemplate.GetReadStream("", templatePath); + file.ContentLength = stream.CanSeek ? stream.Length : storeTemplate.GetFileSize(templatePath); + return fileDao.SaveFile(file, stream); + } + + private File CreateFileFromUri(Folder folder, string fileUri, string fileTitle) + { + if (string.IsNullOrEmpty(fileTitle)) + fileTitle = Path.GetFileName(HttpUtility.UrlDecode(fileUri)); + + var file = ServiceProvider.GetService(); + file.Title = fileTitle; + file.FolderID = folder.ID; + file.Comment = FilesCommonResource.CommentCreate; + + var req = (HttpWebRequest)WebRequest.Create(fileUri); + + // hack. http://ubuntuforums.org/showthread.php?t=1841740 + if (WorkContext.IsMono) + { + ServicePointManager.ServerCertificateValidationCallback += (s, ce, ca, p) => true; + } + + var fileDao = DaoFactory.FileDao; + var fileStream = new ResponseStream(req.GetResponse()); + file.ContentLength = fileStream.Length; + + return fileDao.SaveFile(file, fileStream); + } + + private void Redirect(HttpContext context) + { + if (!SecurityContext.AuthenticateMe(CookiesManager.GetCookies(CookiesType.AuthKey))) + { + context.Response.StatusCode = (int)HttpStatusCode.Forbidden; + return; + } + var urlRedirect = string.Empty; + var folderId = context.Request.Query[FilesLinkUtility.FolderId]; + if (!string.IsNullOrEmpty(folderId)) + { + try + { + urlRedirect = PathProvider.GetFolderUrl(folderId); + } + catch (ArgumentNullException e) + { + throw new HttpException((int)HttpStatusCode.BadRequest, e.Message); + } + } + + var fileId = context.Request.Query[FilesLinkUtility.FileId]; + if (!string.IsNullOrEmpty(fileId)) + { + var fileDao = DaoFactory.FileDao; + var file = fileDao.GetFile(fileId); + if (file == null) + { + context.Response.StatusCode = (int)HttpStatusCode.NotFound; + return; + } + + urlRedirect = FilesLinkUtility.GetFileWebPreviewUrl(FileUtility, file.Title, file.ID); + } + + if (string.IsNullOrEmpty(urlRedirect)) + throw new HttpException((int)HttpStatusCode.BadRequest, FilesCommonResource.ErrorMassage_BadRequest); + context.Response.Redirect(urlRedirect); + } + + private void TrackFile(HttpContext context) + { + var auth = context.Request.Query[FilesLinkUtility.AuthKey].FirstOrDefault(); + var fileId = context.Request.Query[FilesLinkUtility.FileId].FirstOrDefault(); + Logger.Debug("DocService track fileid: " + fileId); + + var callbackSpan = TimeSpan.FromDays(128); + var validateResult = EmailValidationKeyProvider.ValidateEmailKey(fileId, auth ?? "", callbackSpan); + if (validateResult != EmailValidationKeyProvider.ValidationResult.Ok) + { + Logger.ErrorFormat("DocService track auth error: {0}, {1}: {2}", validateResult.ToString(), FilesLinkUtility.AuthKey, auth); + throw new HttpException((int)HttpStatusCode.Forbidden, FilesCommonResource.ErrorMassage_SecurityException); + } + + DocumentServiceTracker.TrackerData fileData; + try + { + string body; + var receiveStream = context.Request.Body; + var readStream = new StreamReader(receiveStream); + body = readStream.ReadToEnd(); + + Logger.Debug("DocService track body: " + body); + if (string.IsNullOrEmpty(body)) + { + throw new ArgumentException("DocService request body is incorrect"); + } + + var data = JToken.Parse(body); + if (data == null) + { + throw new ArgumentException("DocService request is incorrect"); + } + fileData = data.ToObject(); + } + catch (Exception e) + { + Logger.Error("DocService track error read body", e); + throw new HttpException((int)HttpStatusCode.BadRequest, e.Message); + } + + if (!string.IsNullOrEmpty(FileUtility.SignatureSecret)) + { + JsonWebToken.JsonSerializer = new DocumentService.JwtSerializer(); + if (!string.IsNullOrEmpty(fileData.Token)) + { + try + { + var dataString = JsonWebToken.Decode(fileData.Token, FileUtility.SignatureSecret); + var data = JObject.Parse(dataString); + if (data == null) + { + throw new ArgumentException("DocService request token is incorrect"); + } + fileData = data.ToObject(); + } + catch (SignatureVerificationException ex) + { + Logger.Error("DocService track header", ex); + throw new HttpException((int)HttpStatusCode.Forbidden, ex.Message); + } + } + else + { + //todo: remove old scheme + var header = context.Request.Headers[FileUtility.SignatureHeader].FirstOrDefault(); + if (string.IsNullOrEmpty(header) || !header.StartsWith("Bearer ")) + { + Logger.Error("DocService track header is null"); + throw new HttpException((int)HttpStatusCode.Forbidden, FilesCommonResource.ErrorMassage_SecurityException); + } + header = header.Substring("Bearer ".Length); + + try + { + var stringPayload = JsonWebToken.Decode(header, FileUtility.SignatureSecret); + + Logger.Debug("DocService track payload: " + stringPayload); + var jsonPayload = JObject.Parse(stringPayload); + var data = jsonPayload["payload"]; + if (data == null) + { + throw new ArgumentException("DocService request header is incorrect"); + } + fileData = data.ToObject(); + } + catch (SignatureVerificationException ex) + { + Logger.Error("DocService track header", ex); + throw new HttpException((int)HttpStatusCode.Forbidden, ex.Message); + } + } + } + + DocumentServiceTracker.TrackResponse result; + try + { + result = DocumentServiceTrackerHelper.ProcessData(fileId, fileData); + } + catch (Exception e) + { + Logger.Error("DocService track:", e); + throw new HttpException((int)HttpStatusCode.BadRequest, e.Message); + } + result ??= new DocumentServiceTracker.TrackResponse(); + + context.Response.WriteAsync(DocumentServiceTracker.TrackResponse.Serialize(result)).Wait(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/HttpHandlers/SearchHandler.cs b/products/ASC.Files/Server/HttpHandlers/SearchHandler.cs new file mode 100644 index 0000000000..8ac968e37e --- /dev/null +++ b/products/ASC.Files/Server/HttpHandlers/SearchHandler.cs @@ -0,0 +1,180 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Files.Core; +using ASC.Files.Core.Security; +using ASC.Files.Resources; +using ASC.Web.Core.Files; +using ASC.Web.Core.ModuleManagement.Common; +using ASC.Web.Core.Utility.Skins; +using ASC.Web.Files.Classes; +using ASC.Web.Files.Helpers; +using ASC.Web.Files.Utils; +using ASC.Web.Studio.Core; + +namespace ASC.Web.Files.Configuration +{ + public class SearchHandler + { + public Guid ProductID + { + get { return ProductEntryPoint.ID; } + } + + public ImageOptions Logo + { + get { return new ImageOptions { ImageFileName = "common_search_icon.png" }; } + } + + public Guid ModuleID + { + get { return ProductID; } + } + + public string SearchName + { + get { return FilesCommonResource.Search; } + } + + public FileSecurity FileSecurity { get; } + public IDaoFactory DaoFactory { get; } + public Global Global { get; } + public EntryManager EntryManager { get; } + public GlobalFolderHelper GlobalFolderHelper { get; } + public FilesSettingsHelper FilesSettingsHelper { get; } + public FilesLinkUtility FilesLinkUtility { get; } + public FileUtility FileUtility { get; } + public PathProvider PathProvider { get; } + public ThirdpartyConfiguration ThirdpartyConfiguration { get; } + + public SearchHandler( + FileSecurity fileSecurity, + IDaoFactory daoFactory, + Global global, + EntryManager entryManager, + GlobalFolderHelper globalFolderHelper, + FilesSettingsHelper filesSettingsHelper, + FilesLinkUtility filesLinkUtility, + FileUtility fileUtility, + PathProvider pathProvider, + ThirdpartyConfiguration thirdpartyConfiguration) + { + FileSecurity = fileSecurity; + DaoFactory = daoFactory; + Global = global; + EntryManager = entryManager; + GlobalFolderHelper = globalFolderHelper; + FilesSettingsHelper = filesSettingsHelper; + FilesLinkUtility = filesLinkUtility; + FileUtility = fileUtility; + PathProvider = pathProvider; + ThirdpartyConfiguration = thirdpartyConfiguration; + } + + public IEnumerable SearchFiles(string text) + { + var security = FileSecurity; + var fileDao = DaoFactory.FileDao; + return fileDao.Search(text).Where(security.CanRead); + } + + public IEnumerable SearchFolders(string text) + { + var security = FileSecurity; + IEnumerable result; + var folderDao = DaoFactory.FolderDao; + result = folderDao.Search(text).Where(security.CanRead); + + if (ThirdpartyConfiguration.SupportInclusion + && (Global.IsAdministrator || FilesSettingsHelper.EnableThirdParty)) + { + var id = GlobalFolderHelper.FolderMy; + if (!Equals(id, 0)) + { + var folderMy = folderDao.GetFolder(id); + result = result.Concat(EntryManager.GetThirpartyFolders(folderMy, text)); + } + + id = GlobalFolderHelper.FolderCommon; + var folderCommon = folderDao.GetFolder(id); + result = result.Concat(EntryManager.GetThirpartyFolders(folderCommon, text)); + } + + return result; + } + + public SearchResultItem[] Search(string text) + { + var folderDao = DaoFactory.FolderDao; + var result = SearchFiles(text) + .Select(r => new SearchResultItem + { + Name = r.Title ?? string.Empty, + Description = string.Empty, + URL = FilesLinkUtility.GetFileWebPreviewUrl(FileUtility, r.Title, r.ID), + Date = r.ModifiedOn, + Additional = new Dictionary + { + { "Author", r.CreateByString.HtmlEncode() }, + { "Path", FolderPathBuilder(EntryManager.GetBreadCrumbs(r.FolderID, folderDao)) }, + { "Size", FileSizeComment.FilesSizeToString(r.ContentLength) } + } + } + ); + + var resultFolder = SearchFolders(text) + .Select(f => + new SearchResultItem + { + Name = f.Title ?? string.Empty, + Description = string.Empty, + URL = PathProvider.GetFolderUrl(f), + Date = f.ModifiedOn, + Additional = new Dictionary + { + { "Author", f.CreateByString.HtmlEncode() }, + { "Path", FolderPathBuilder(EntryManager.GetBreadCrumbs(f.ID, folderDao)) }, + { "IsFolder", true } + } + }); + + return result.Concat(resultFolder).ToArray(); + } + + private static string FolderPathBuilder(IEnumerable folders) + { + var titles = folders.Select(f => f.Title).ToList(); + const string separator = " \\ "; + return 4 < titles.Count + ? string.Join(separator, new[] { titles.First(), "...", titles.ElementAt(titles.Count - 2), titles.Last() }) + : string.Join(separator, titles.ToArray()); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/HttpHandlers/ThirdPartyAppHandler.ashx.cs b/products/ASC.Files/Server/HttpHandlers/ThirdPartyAppHandler.ashx.cs new file mode 100644 index 0000000000..6fa955b600 --- /dev/null +++ b/products/ASC.Files/Server/HttpHandlers/ThirdPartyAppHandler.ashx.cs @@ -0,0 +1,117 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using System.Web; + +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Common; +using ASC.Files.Resources; +using ASC.Web.Files.ThirdPartyApp; +using ASC.Web.Studio.Utility; + +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Options; + +namespace ASC.Web.Files.HttpHandlers +{ + public class ThirdPartyAppHandler //: IHttpHandler + { + public RequestDelegate Next { get; } + public AuthContext AuthContext { get; } + public BaseCommonLinkUtility BaseCommonLinkUtility { get; } + public CommonLinkUtility CommonLinkUtility { get; } + private ILog Log { get; set; } + + public string HandlerPath { get; set; } + + public ThirdPartyAppHandler( + RequestDelegate next, + IOptionsMonitor optionsMonitor, + AuthContext authContext, + BaseCommonLinkUtility baseCommonLinkUtility, + CommonLinkUtility commonLinkUtility) + { + Next = next; + AuthContext = authContext; + BaseCommonLinkUtility = baseCommonLinkUtility; + CommonLinkUtility = commonLinkUtility; + Log = optionsMonitor.CurrentValue; + HandlerPath = baseCommonLinkUtility.ToAbsolute("~/thirdpartyapp"); + } + + public async Task Invoke(HttpContext context) + { + Log.Debug("ThirdPartyApp: handler request - " + context.Request.Url()); + + var message = string.Empty; + + try + { + var app = ThirdPartySelector.GetApp(context.Request.Query[ThirdPartySelector.AppAttr]); + Log.Debug("ThirdPartyApp: app - " + app); + + if (app.Request(context)) + { + await Next.Invoke(context); + return; + } + } + catch (ThreadAbortException) + { + await Next.Invoke(context); + //Thats is responce ending + return; + } + catch (Exception e) + { + Log.Error("ThirdPartyApp", e); + message = e.Message; + } + + if (string.IsNullOrEmpty(message)) + { + if ((context.Request.Query["error"].FirstOrDefault() ?? "").ToLower() == "access_denied") + { + message = context.Request.Query["error_description"].FirstOrDefault() ?? FilesCommonResource.AppAccessDenied; + } + } + + var redirectUrl = CommonLinkUtility.GetDefault(); + if (!string.IsNullOrEmpty(message)) + { + redirectUrl += AuthContext.IsAuthenticated ? "#error/" : "?m="; + redirectUrl += HttpUtility.UrlEncode(message); + } + context.Response.Redirect(redirectUrl, true); + await Next.Invoke(context); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/HttpHandlers/docusignhandler.ashx b/products/ASC.Files/Server/HttpHandlers/docusignhandler.ashx new file mode 100644 index 0000000000..7143d3d59a --- /dev/null +++ b/products/ASC.Files/Server/HttpHandlers/docusignhandler.ashx @@ -0,0 +1 @@ +<%@ WebHandler Language="C#" CodeBehind="docusignhandler.ashx.cs" Class="ASC.Web.Files.HttpHandlers.DocuSignHandler" %> diff --git a/products/ASC.Files/Server/HttpHandlers/docusignhandler.ashx.cs b/products/ASC.Files/Server/HttpHandlers/docusignhandler.ashx.cs new file mode 100644 index 0000000000..867687e6f1 --- /dev/null +++ b/products/ASC.Files/Server/HttpHandlers/docusignhandler.ashx.cs @@ -0,0 +1,253 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using System.Web; +using System.Xml; + +using ASC.Common; +using ASC.Common.Logging; +using ASC.Common.Web; +using ASC.Core; +using ASC.Files.Resources; +using ASC.Web.Core.Files; +using ASC.Web.Files.Classes; +using ASC.Web.Files.Helpers; +using ASC.Web.Files.Services.NotifyService; +using ASC.Web.Studio.Utility; + +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Options; + +namespace ASC.Web.Files.HttpHandlers +{ + public class DocuSignHandler + { + public static string Path(FilesLinkUtility filesLinkUtility) + { + return filesLinkUtility.FilesBaseAbsolutePath + "httphandlers/docusignhandler.ashx"; + } + + private ILog Log { get; set; } + + public RequestDelegate Next { get; } + + public TenantExtra TenantExtra { get; } + public DocuSignHelper DocuSignHelper { get; } + public SecurityContext SecurityContext { get; } + public NotifyClient NotifyClient { get; } + + public DocuSignHandler( + RequestDelegate next, + IOptionsMonitor optionsMonitor, + TenantExtra tenantExtra, + DocuSignHelper docuSignHelper, + SecurityContext securityContext, + NotifyClient notifyClient) + { + Next = next; + TenantExtra = tenantExtra; + DocuSignHelper = docuSignHelper; + SecurityContext = securityContext; + NotifyClient = notifyClient; + Log = optionsMonitor.CurrentValue; + } + + public async Task Invoke(HttpContext context) + { + if (TenantExtra.IsNotPaid()) + { + context.Response.StatusCode = (int)HttpStatusCode.PaymentRequired; + context.Response.WriteAsync("Payment Required.").Wait(); + return; + } + + try + { + switch ((context.Request.Query[FilesLinkUtility.Action].FirstOrDefault() ?? "").ToLower()) + { + case "redirect": + Redirect(context); + break; + case "webhook": + Webhook(context); + break; + default: + throw new HttpException((int)HttpStatusCode.BadRequest, FilesCommonResource.ErrorMassage_BadRequest); + } + } + catch (InvalidOperationException e) + { + throw new HttpException((int)HttpStatusCode.InternalServerError, FilesCommonResource.ErrorMassage_BadRequest, e); + } + + await Next.Invoke(context); + } + + private void Redirect(HttpContext context) + { + Log.Info("DocuSign redirect query: " + context.Request.QueryString); + + var eventRedirect = context.Request.Query["event"].FirstOrDefault(); + switch (eventRedirect.ToLower()) + { + case "send": + context.Response.Redirect(PathProvider.StartURL + "#message/" + HttpUtility.UrlEncode(FilesCommonResource.DocuSignStatusSended), true); + break; + case "save": + case "cancel": + context.Response.Redirect(PathProvider.StartURL + "#error/" + HttpUtility.UrlEncode(FilesCommonResource.DocuSignStatusNotSended), true); + break; + case "error": + case "sessionend": + context.Response.Redirect(PathProvider.StartURL + "#error/" + HttpUtility.UrlEncode(FilesCommonResource.DocuSignStatusError), true); + break; + } + context.Response.Redirect(PathProvider.StartURL, true); + } + + private const string XmlPrefix = "docusign"; + + private void Webhook(HttpContext context) + { + Log.Info("DocuSign webhook: " + context.Request.QueryString); + try + { + var xmldoc = new XmlDocument(); + xmldoc.Load(context.Request.Body); + Log.Info("DocuSign webhook outerXml: " + xmldoc.OuterXml); + + var mgr = new XmlNamespaceManager(xmldoc.NameTable); + mgr.AddNamespace(XmlPrefix, "http://www.docusign.net/API/3.0"); + + var envelopeStatusNode = GetSingleNode(xmldoc, "DocuSignEnvelopeInformation/" + XmlPrefix + ":EnvelopeStatus", mgr); + var envelopeId = GetSingleNode(envelopeStatusNode, "EnvelopeID", mgr).InnerText; + var subject = GetSingleNode(envelopeStatusNode, "Subject", mgr).InnerText; + + var statusString = GetSingleNode(envelopeStatusNode, "Status", mgr).InnerText; + if (!Enum.TryParse(statusString, true, out DocuSignStatus status)) + { + throw new Exception("DocuSign webhook unknown status: " + statusString); + } + + Log.Info("DocuSign webhook: " + envelopeId + " " + subject + " " + status); + + var customFieldUserIdNode = GetSingleNode(envelopeStatusNode, "CustomFields/" + XmlPrefix + ":CustomField[" + XmlPrefix + ":Name='" + DocuSignHelper.UserField + "']", mgr); + var userIdString = GetSingleNode(customFieldUserIdNode, "Value", mgr).InnerText; + Auth(userIdString); + + switch (status) + { + case DocuSignStatus.Completed: + + var documentStatuses = GetSingleNode(envelopeStatusNode, "DocumentStatuses", mgr); + foreach (XmlNode documentStatus in documentStatuses.ChildNodes) + { + try + { + var documentId = GetSingleNode(documentStatus, "ID", mgr).InnerText; + var documentName = GetSingleNode(documentStatus, "Name", mgr).InnerText; + + string folderId = null; + string sourceTitle = null; + + var documentFiels = GetSingleNode(documentStatus, "DocumentFields", mgr, true); + if (documentFiels != null) + { + var documentFieldFolderNode = GetSingleNode(documentFiels, "DocumentField[" + XmlPrefix + ":Name='" + FilesLinkUtility.FolderId + "']", mgr, true); + if (documentFieldFolderNode != null) + { + folderId = GetSingleNode(documentFieldFolderNode, "Value", mgr).InnerText; + } + var documentFieldTitleNode = GetSingleNode(documentFiels, "DocumentField[" + XmlPrefix + ":Name='" + FilesLinkUtility.FileTitle + "']", mgr, true); + if (documentFieldTitleNode != null) + { + sourceTitle = GetSingleNode(documentFieldTitleNode, "Value", mgr).InnerText; + } + } + + var file = DocuSignHelper.SaveDocument(envelopeId, documentId, documentName, folderId); + + NotifyClient.SendDocuSignComplete(file, sourceTitle ?? documentName); + } + catch (Exception ex) + { + Log.Error("DocuSign webhook save document: " + documentStatus.InnerText, ex); + } + } + break; + case DocuSignStatus.Declined: + case DocuSignStatus.Voided: + var statusFromResource = status == DocuSignStatus.Declined + ? FilesCommonResource.DocuSignStatusDeclined + : FilesCommonResource.DocuSignStatusVoided; + NotifyClient.SendDocuSignStatus(subject, statusFromResource); + break; + } + } + catch (Exception e) + { + Log.Error("DocuSign webhook", e); + + throw new HttpException((int)HttpStatusCode.BadRequest, e.Message); + } + } + + private void Auth(string userIdString) + { + if (!Guid.TryParse(userIdString ?? "", out var userId)) + { + throw new Exception("DocuSign incorrect User ID: " + userIdString); + } + + SecurityContext.AuthenticateMe(userId); + } + + private static XmlNode GetSingleNode(XmlNode node, string xpath, XmlNamespaceManager mgr, bool canMiss = false) + { + var result = node.SelectSingleNode(XmlPrefix + ":" + xpath, mgr); + if (!canMiss && result == null) throw new Exception(xpath + " is null"); + return result; + } + } + + public static class DocuSignHandlerExtension + { + public static DIHelper AddDocuSignHandlerService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddFilesLinkUtilityService() + .AddTenantExtraService() + .AddDocuSignHelperService() + .AddSecurityContextService() + .AddNotifyClientService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/HttpHandlers/filehandler.ashx b/products/ASC.Files/Server/HttpHandlers/filehandler.ashx new file mode 100644 index 0000000000..264036f935 --- /dev/null +++ b/products/ASC.Files/Server/HttpHandlers/filehandler.ashx @@ -0,0 +1,2 @@ +<%@ Assembly Name="ASC.Web.Files" %> +<%@ WebHandler Language="C#" CodeBehind="FileHandler.ashx.cs" Class="ASC.Web.Files.FileHandler, ASC.Web.Files" %> diff --git a/products/ASC.Files/Server/HttpHandlers/thirdpartyapphandler.ashx b/products/ASC.Files/Server/HttpHandlers/thirdpartyapphandler.ashx new file mode 100644 index 0000000000..27efeb65af --- /dev/null +++ b/products/ASC.Files/Server/HttpHandlers/thirdpartyapphandler.ashx @@ -0,0 +1 @@ +<%@ WebHandler Language="C#" CodeBehind="ThirdPartyAppHandler.ashx.cs" Class="ASC.Web.Files.HttpHandlers.ThirdPartyAppHandler, ASC.Web.Files" %> diff --git a/products/ASC.Files/Server/Model/BatchModel.cs b/products/ASC.Files/Server/Model/BatchModel.cs new file mode 100644 index 0000000000..4640f15600 --- /dev/null +++ b/products/ASC.Files/Server/Model/BatchModel.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; + +using ASC.Api.Collections; +using ASC.Web.Files.Services.WCFService.FileOperations; + +namespace ASC.Files.Model +{ + public class BaseBatchModel + { + public IEnumerable FolderIds { get; set; } + public IEnumerable FileIds { get; set; } + } + + public class DownloadModel : BaseBatchModel + { + public IEnumerable> FileConvertIds { get; set; } + } + + public class DeleteBatchModel : BaseBatchModel + { + public bool DeleteAfter { get; set; } + public bool Immediately { get; set; } + } + + public class BatchModel : BaseBatchModel + { + public string DestFolderId { get; set; } + public FileConflictResolveType ConflictResolveType { get; set; } + public bool DeleteAfter { get; set; } + } +} diff --git a/products/ASC.Files/Server/Model/FileEntryWrapper.cs b/products/ASC.Files/Server/Model/FileEntryWrapper.cs new file mode 100644 index 0000000000..231bbbedc2 --- /dev/null +++ b/products/ASC.Files/Server/Model/FileEntryWrapper.cs @@ -0,0 +1,185 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.Runtime.Serialization; + +using ASC.Api.Core; +using ASC.Common; +using ASC.Files.Core; +using ASC.Files.Core.Security; +using ASC.Web.Api.Models; + +namespace ASC.Api.Documents +{ + /// + /// + [DataContract(Namespace = "")] + public abstract class FileEntryWrapper + { + /// + /// + [DataMember] + public object Id { get; set; } + + /// + /// + [DataMember(IsRequired = true)] + public string Title { get; set; } + + /// + /// + [DataMember] + public FileShare Access { get; set; } + + /// + /// + [DataMember] + public bool Shared { get; set; } + + /// + /// + [DataMember(Order = 50)] + public ApiDateTime Created { get; set; } + + /// + /// + [DataMember(Order = 51, EmitDefaultValue = false)] + public EmployeeWraper CreatedBy { get; set; } + + private ApiDateTime _updated; + + /// + /// + [DataMember(Order = 52, EmitDefaultValue = false)] + public ApiDateTime Updated + { + get + { + return _updated < Created ? Created : _updated; + } + set { _updated = value; } + } + + /// + /// + [DataMember(Order = 41, EmitDefaultValue = false)] + public FolderType RootFolderType { get; set; } + + /// + /// + [DataMember(Order = 41, EmitDefaultValue = false)] + public EmployeeWraper UpdatedBy { get; set; } + + /// + /// + [DataMember(Order = 55, EmitDefaultValue = false)] + public bool ProviderItem { get; set; } + + /// + /// + [DataMember(Order = 56, EmitDefaultValue = false)] + public string ProviderKey { get; set; } + + /// + /// + [DataMember(Order = 57, EmitDefaultValue = false)] + public int ProviderId { get; set; } + + + /// + /// + /// + /// + protected FileEntryWrapper(FileEntry entry, EmployeeWraperHelper employeeWraperHelper, ApiDateTimeHelper apiDateTimeHelper) + { + Id = entry.ID; + Title = entry.Title; + Access = entry.Access; + Shared = entry.Shared; + Created = apiDateTimeHelper.Get(entry.CreateOn); + CreatedBy = employeeWraperHelper.Get(entry.CreateBy); + Updated = apiDateTimeHelper.Get(entry.ModifiedOn); + UpdatedBy = employeeWraperHelper.Get(entry.ModifiedBy); + RootFolderType = entry.RootFolderType; + ProviderItem = entry.ProviderEntry; + ProviderKey = entry.ProviderKey; + ProviderId = entry.ProviderId; + } + + /// + /// + /// + protected FileEntryWrapper() + { + + } + } + + public class FileEntryWrapperHelper + { + public ApiDateTimeHelper ApiDateTimeHelper { get; } + public EmployeeWraperHelper EmployeeWraperHelper { get; } + + public FileEntryWrapperHelper( + ApiDateTimeHelper apiDateTimeHelper, + EmployeeWraperHelper employeeWraperHelper + ) + { + ApiDateTimeHelper = apiDateTimeHelper; + EmployeeWraperHelper = employeeWraperHelper; + } + + protected internal T Get(FileEntry entry) where T : FileEntryWrapper, new() + { + return new T + { + Id = entry.ID, + Title = entry.Title, + Access = entry.Access, + Shared = entry.Shared, + Created = ApiDateTimeHelper.Get(entry.CreateOn), + CreatedBy = EmployeeWraperHelper.Get(entry.CreateBy), + Updated = ApiDateTimeHelper.Get(entry.ModifiedOn), + UpdatedBy = EmployeeWraperHelper.Get(entry.ModifiedBy), + RootFolderType = entry.RootFolderType, + ProviderItem = entry.ProviderEntry, + ProviderKey = entry.ProviderKey, + ProviderId = entry.ProviderId + }; + } + } + + public static class FileEntryWrapperHelperExtention + { + public static DIHelper AddFileEntryWrapperHelperService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddApiDateTimeHelper() + .AddEmployeeWraper(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Model/FileOperationWraper.cs b/products/ASC.Files/Server/Model/FileOperationWraper.cs new file mode 100644 index 0000000000..b52ff4ccad --- /dev/null +++ b/products/ASC.Files/Server/Model/FileOperationWraper.cs @@ -0,0 +1,184 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; + +using ASC.Common; +using ASC.Files.Core; +using ASC.Files.Core.Data; +using ASC.Web.Files.Services.WCFService.FileOperations; +using ASC.Web.Studio.Utility; + +namespace ASC.Api.Documents +{ + /// + /// + [DataContract(Name = "operation_result", Namespace = "")] + public class FileOperationWraper + { + /// + /// + [DataMember(Name = "id", IsRequired = false)] + public string Id { get; set; } + + /// + /// + [DataMember(Name = "operation", IsRequired = false)] + public FileOperationType OperationType { get; set; } + + /// + /// + [DataMember(Name = "progress", IsRequired = false)] + public int Progress { get; set; } + + /// + /// + [DataMember(Name = "error", IsRequired = false)] + public string Error { get; set; } + + /// + /// + [DataMember(Name = "processed", IsRequired = false)] + public string Processed { get; set; } + + /// + /// + [DataMember(Name = "finished", IsRequired = false)] + public bool Finished { get; set; } + + /// + /// + [DataMember(Name = "url", IsRequired = false)] + public string Url { get; set; } + + /// + /// + [DataMember(Name = "files", IsRequired = true, EmitDefaultValue = true)] + public List Files { get; set; } + + /// + /// + [DataMember(Name = "folders", IsRequired = true, EmitDefaultValue = true)] + public List Folders { get; set; } + + /// + /// + public FileOperationWraper() + { + } + + /// + /// + /// + public static FileOperationWraper GetSample() + { + return new FileOperationWraper + { + Id = Guid.NewGuid().ToString(), + OperationType = FileOperationType.Move, + Progress = 100, + //Source = "folder_1,file_1", + //Result = "folder_1,file_1", + Error = "", + Processed = "1", + Files = new List { FileWrapper.GetSample() }, + Folders = new List { FolderWrapper.GetSample() } + }; + } + } + + public class FileOperationWraperHelper + { + private FolderWrapperHelper FolderWrapperHelper { get; } + private FileWrapperHelper FilesWrapperHelper { get; } + private IDaoFactory DaoFactory { get; } + private CommonLinkUtility CommonLinkUtility { get; } + + public FileOperationWraperHelper( + FolderWrapperHelper folderWrapperHelper, + FileWrapperHelper filesWrapperHelper, + IDaoFactory daoFactory, + CommonLinkUtility commonLinkUtility) + { + FolderWrapperHelper = folderWrapperHelper; + FilesWrapperHelper = filesWrapperHelper; + DaoFactory = daoFactory; + CommonLinkUtility = commonLinkUtility; + } + + public FileOperationWraper Get(FileOperationResult o) + { + var result = new FileOperationWraper + { + Id = o.Id, + OperationType = o.OperationType, + Progress = o.Progress, + Error = o.Error, + Processed = o.Processed, + Finished = o.Finished + }; + + if (!string.IsNullOrEmpty(o.Result) && result.OperationType != FileOperationType.Delete) + { + var arr = o.Result.Split(':'); + var folders = arr.Where(s => s.StartsWith("folder_")).Select(s => s.Substring(7)); + if (folders.Any()) + { + var folderDao = DaoFactory.FolderDao; + result.Folders = folderDao.GetFolders(folders.ToArray()).Select(FolderWrapperHelper.Get).ToList(); + } + var files = arr.Where(s => s.StartsWith("file_")).Select(s => s.Substring(5)); + if (files.Any()) + { + var fileDao = DaoFactory.FileDao; + result.Files = fileDao.GetFiles(files.ToArray()).Select(FilesWrapperHelper.Get).ToList(); + } + + if (result.OperationType == FileOperationType.Download) + { + result.Url = CommonLinkUtility.GetFullAbsolutePath(o.Result); + } + } + + return result; + } + } + public static class FileOperationWraperHelperExtention + { + public static DIHelper AddFileOperationWraperHelperService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddFolderWrapperHelperService() + .AddFileWrapperHelperService() + .AddDaoFactoryService() + .AddCommonLinkUtilityService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Model/FileShareParams.cs b/products/ASC.Files/Server/Model/FileShareParams.cs new file mode 100644 index 0000000000..fa01d5e6b8 --- /dev/null +++ b/products/ASC.Files/Server/Model/FileShareParams.cs @@ -0,0 +1,85 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Runtime.Serialization; + +using ASC.Common; +using ASC.Core; +using ASC.Files.Core.Security; +using ASC.Web.Files.Services.WCFService; + +namespace ASC.Api.Documents +{ + /// + /// + [DataContract(Name = "share", Namespace = "")] + public class FileShareParams + { + /// + /// + [DataMember(Name = "shareTo", Order = 0)] + public Guid ShareTo { get; set; } + + /// + /// + [DataMember(Name = "access", Order = 1)] + public FileShare Access { get; set; } + + /// + /// + /// + + } + + public class FileShareParamsHelper + { + public UserManager UserManager { get; } + public FileShareParamsHelper(UserManager userManager) + { + UserManager = userManager; + } + public AceWrapper ToAceObject(FileShareParams fileShareParams) + { + return new AceWrapper + { + Share = fileShareParams.Access, + SubjectId = fileShareParams.ShareTo, + SubjectGroup = !UserManager.UserExists(fileShareParams.ShareTo) + }; + } + } + + public static class FileShareParamsExtention + { + public static DIHelper AddFileShareParamsService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddUserManagerService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Model/FileShareWrapper.cs b/products/ASC.Files/Server/Model/FileShareWrapper.cs new file mode 100644 index 0000000000..883c5d7f40 --- /dev/null +++ b/products/ASC.Files/Server/Model/FileShareWrapper.cs @@ -0,0 +1,150 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; + +using ASC.Common; +using ASC.Core; +using ASC.Files.Core; +using ASC.Files.Core.Security; +using ASC.Web.Api.Models; +using ASC.Web.Files.Services.WCFService; + +namespace ASC.Api.Documents +{ + /// + /// + public class FileShareWrapper + { + /// + /// + /// + public FileShareWrapper() + { + + } + + /// + /// + public FileShare Access { get; set; } + + /// + /// + public object SharedTo { get; set; } + + /// + /// + public bool IsLocked { get; set; } + + /// + /// + public bool IsOwner { get; set; } + + /// + /// + /// + public static FileShareWrapper GetSample() + { + return new FileShareWrapper + { + Access = FileShare.ReadWrite, + IsLocked = false, + IsOwner = true, + //SharedTo = EmployeeWraper.GetSample() + }; + } + } + + + /// + /// + public class FileShareLink + { + /// + /// + public Guid Id; + + /// + /// + public string ShareLink; + } + public class FileShareWrapperHelper + { + private UserManager UserManager { get; } + private EmployeeWraperFullHelper EmployeeWraperFullHelper { get; } + + public FileShareWrapperHelper( + UserManager userManager, + EmployeeWraperFullHelper employeeWraperFullHelper) + { + UserManager = userManager; + EmployeeWraperFullHelper = employeeWraperFullHelper; + } + + public FileShareWrapper Get(AceWrapper aceWrapper) + { + var result = new FileShareWrapper + { + IsOwner = aceWrapper.Owner, + IsLocked = aceWrapper.LockedRights + }; + + if (aceWrapper.SubjectGroup) + { + if (aceWrapper.SubjectId == FileConstant.ShareLinkId) + { + result.SharedTo = new FileShareLink + { + Id = aceWrapper.SubjectId, + ShareLink = aceWrapper.Link + }; + } + else + { + //Shared to group + result.SharedTo = new GroupWrapperSummary(UserManager.GetGroupInfo(aceWrapper.SubjectId), UserManager); + } + } + else + { + result.SharedTo = EmployeeWraperFullHelper.GetFull(UserManager.GetUsers(aceWrapper.SubjectId)); + } + result.Access = aceWrapper.Share; + + return result; + } + } + public static class FileShareWrapperExtention + { + public static DIHelper AddFileShareWrapperService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddUserManagerService() + .AddEmployeeWraperFull(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Model/FileWrapper.cs b/products/ASC.Files/Server/Model/FileWrapper.cs new file mode 100644 index 0000000000..da38e2a061 --- /dev/null +++ b/products/ASC.Files/Server/Model/FileWrapper.cs @@ -0,0 +1,236 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Globalization; +using System.Runtime.Serialization; + +using ASC.Api.Core; +using ASC.Common; +using ASC.Core; +using ASC.Files.Core; +using ASC.Files.Core.Data; +using ASC.Files.Core.Security; +using ASC.Web.Api.Models; +using ASC.Web.Core.Files; +using ASC.Web.Files.Classes; +using ASC.Web.Studio.Utility; + +using File = ASC.Files.Core.File; +using FileShare = ASC.Files.Core.Security.FileShare; + +namespace ASC.Api.Documents +{ + /// + /// + [DataContract(Name = "file", Namespace = "")] + public class FileWrapper : FileEntryWrapper + { + /// + /// + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public object FolderId { get; set; } + + /// + /// + [DataMember(EmitDefaultValue = true, IsRequired = false)] + public int Version { get; set; } + + /// + /// + [DataMember(EmitDefaultValue = true, IsRequired = false)] + public int VersionGroup { get; set; } + + /// + /// + [DataMember(EmitDefaultValue = false, IsRequired = true)] + public string ContentLength { get; set; } + + /// + /// + [DataMember(EmitDefaultValue = false, IsRequired = true)] + public long PureContentLength { get; set; } + + /// + /// + [DataMember(EmitDefaultValue = true, IsRequired = false)] + public FileStatus FileStatus { get; set; } + + /// + /// + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public string ViewUrl { get; set; } + + /// + /// + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public string WebUrl { get; set; } + + /// + /// + /// + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public FileType FileType { get; set; } + + /// + /// + /// + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public string FileExst { get; set; } + + /// + /// + [DataMember(EmitDefaultValue = true, IsRequired = false)] + public string Comment { get; set; } + + /// + /// + [DataMember(EmitDefaultValue = false)] + public bool Encrypted { get; set; } + + /// + /// + /// + public FileWrapper() + { + } + + /// + /// + /// + public static FileWrapper GetSample() + { + return new FileWrapper + { + Access = FileShare.ReadWrite, + //Updated = ApiDateTime.GetSample(), + //Created = ApiDateTime.GetSample(), + //CreatedBy = EmployeeWraper.GetSample(), + Id = new Random().Next(), + RootFolderType = FolderType.BUNCH, + Shared = false, + Title = "Some titile.txt", + FileExst = ".txt", + FileType = FileType.Document, + //UpdatedBy = EmployeeWraper.GetSample(), + ContentLength = 12345.ToString(CultureInfo.InvariantCulture), + FileStatus = FileStatus.IsNew, + FolderId = 12334, + Version = 3, + VersionGroup = 1, + ViewUrl = "http://www.onlyoffice.com/viewfile?fileid=2221" + }; + } + } + public class FileWrapperHelper : FileEntryWrapperHelper + { + public AuthContext AuthContext { get; } + public IDaoFactory DaoFactory { get; } + public FileSecurity FileSecurity { get; } + public GlobalFolderHelper GlobalFolderHelper { get; } + public CommonLinkUtility CommonLinkUtility { get; } + public FilesLinkUtility FilesLinkUtility { get; } + public FileUtility FileUtility { get; } + + public FileWrapperHelper( + ApiDateTimeHelper apiDateTimeHelper, + EmployeeWraperHelper employeeWrapperHelper, + AuthContext authContext, + IDaoFactory daoFactory, + FileSecurity fileSecurity, + GlobalFolderHelper globalFolderHelper, + CommonLinkUtility commonLinkUtility, + FilesLinkUtility filesLinkUtility, + FileUtility fileUtility) + : base(apiDateTimeHelper, employeeWrapperHelper) + { + AuthContext = authContext; + DaoFactory = daoFactory; + FileSecurity = fileSecurity; + GlobalFolderHelper = globalFolderHelper; + CommonLinkUtility = commonLinkUtility; + FilesLinkUtility = filesLinkUtility; + FileUtility = fileUtility; + } + + public FileWrapper Get(File file) + { + var result = Get(file); + result.FolderId = file.FolderID; + if (file.RootFolderType == FolderType.USER + && !Equals(file.RootFolderCreator, AuthContext.CurrentAccount.ID)) + { + result.RootFolderType = FolderType.SHARE; + var folderDao = DaoFactory.FolderDao; + var parentFolder = folderDao.GetFolder(file.FolderID); + if (!FileSecurity.CanRead(parentFolder)) + { + result.FolderId = GlobalFolderHelper.FolderShare; + } + } + + result.FileExst = FileUtility.GetFileExtension(file.Title); + result.FileType = FileUtility.GetFileTypeByExtention(result.FileExst); + + result.Version = file.Version; + result.VersionGroup = file.VersionGroup; + result.ContentLength = file.ContentLengthString; + result.FileStatus = file.FileStatus; + result.PureContentLength = file.ContentLength; + result.Comment = file.Comment; + result.Encrypted = file.Encrypted; + try + { + result.ViewUrl = CommonLinkUtility.GetFullAbsolutePath(file.DownloadUrl); + + result.WebUrl = CommonLinkUtility.GetFullAbsolutePath(FilesLinkUtility.GetFileWebPreviewUrl(FileUtility, file.Title, file.ID)); + } + catch (Exception) + { + //Don't catch anything here because of httpcontext + } + + return result; + } + } + + public static class FileWrapperHelperExtention + { + public static DIHelper AddFileWrapperHelperService(this DIHelper services) + { + services.TryAddScoped(); + + return services + .AddFileEntryWrapperHelperService() + .AddAuthContextService() + .AddDaoFactoryService() + .AddFileSecurityService() + .AddGlobalFolderHelperService() + .AddFilesLinkUtilityService() + .AddFileUtilityService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Model/FolderContentWrapper.cs b/products/ASC.Files/Server/Model/FolderContentWrapper.cs new file mode 100644 index 0000000000..71e84909e5 --- /dev/null +++ b/products/ASC.Files/Server/Model/FolderContentWrapper.cs @@ -0,0 +1,149 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; + +using ASC.Common; +using ASC.Files.Core; +using ASC.Web.Files.Services.WCFService; + +namespace ASC.Api.Documents +{ + /// + /// + [DataContract(Name = "content", Namespace = "")] + public class FolderContentWrapper + { + /// + /// + [DataMember(IsRequired = false, EmitDefaultValue = false)] + public List Files { get; set; } + + /// + /// + [DataMember(IsRequired = false, EmitDefaultValue = false)] + public List Folders { get; set; } + + /// + /// + [DataMember(IsRequired = false, EmitDefaultValue = true)] + public FolderWrapper Current { get; set; } + + /// + /// + [DataMember(IsRequired = false, EmitDefaultValue = true)] + public object PathParts { get; set; } + + /// + /// + [DataMember(IsRequired = false, EmitDefaultValue = true)] + public int StartIndex { get; set; } + + /// + /// + [DataMember(IsRequired = false, EmitDefaultValue = true)] + public int Count { get; set; } + + /// + /// + [DataMember(IsRequired = false, EmitDefaultValue = true)] + public int Total { get; set; } + + /// + /// + /// + /// + public FolderContentWrapper() + { + + } + + /// + /// + /// + public static FolderContentWrapper GetSample() + { + return new FolderContentWrapper + { + Current = FolderWrapper.GetSample(), + Files = new List(new[] { FileWrapper.GetSample(), FileWrapper.GetSample() }), + Folders = new List(new[] { FolderWrapper.GetSample(), FolderWrapper.GetSample() }), + PathParts = new + { + key = "Key", + path = "//path//to//folder" + }, + + StartIndex = 0, + Count = 4, + Total = 4, + }; + } + } + + public class FolderContentWrapperHelper + { + private FileWrapperHelper FileWrapperHelper { get; } + private FolderWrapperHelper FolderWrapperHelper { get; } + + public FolderContentWrapperHelper( + FileWrapperHelper fileWrapperHelper, + FolderWrapperHelper folderWrapperHelper) + { + FileWrapperHelper = fileWrapperHelper; + FolderWrapperHelper = folderWrapperHelper; + } + + public FolderContentWrapper Get(DataWrapper folderItems, int startIndex) + { + var result = new FolderContentWrapper + { + Files = folderItems.Entries.OfType().Select(FileWrapperHelper.Get).ToList(), + Folders = folderItems.Entries.OfType().Select(FolderWrapperHelper.Get).ToList(), + PathParts = folderItems.FolderPathParts, + StartIndex = startIndex + }; + + result.Current = FolderWrapperHelper.Get(folderItems.FolderInfo); + result.Count = result.Files.Count + result.Folders.Count; + result.Total = folderItems.Total; + + return result; + } + } + public static class FolderContentWrapperHelperExtention + { + public static DIHelper AddFolderContentWrapperHelperService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddFileWrapperHelperService() + .AddFolderWrapperHelperService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Model/FolderWrapper.cs b/products/ASC.Files/Server/Model/FolderWrapper.cs new file mode 100644 index 0000000000..3db69610d5 --- /dev/null +++ b/products/ASC.Files/Server/Model/FolderWrapper.cs @@ -0,0 +1,156 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Runtime.Serialization; + +using ASC.Api.Core; +using ASC.Common; +using ASC.Core; +using ASC.Files.Core; +using ASC.Files.Core.Data; +using ASC.Files.Core.Security; +using ASC.Web.Api.Models; +using ASC.Web.Files.Classes; + +namespace ASC.Api.Documents +{ + /// + /// + [DataContract(Name = "folder", Namespace = "")] + public class FolderWrapper : FileEntryWrapper + { + /// + /// + [DataMember(IsRequired = true, EmitDefaultValue = true)] + public object ParentId { get; set; } + + /// + /// + [DataMember(EmitDefaultValue = true, IsRequired = false)] + public int FilesCount { get; set; } + + /// + /// + [DataMember(EmitDefaultValue = true, IsRequired = false)] + public int FoldersCount { get; set; } + + /// + /// + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public bool IsShareable { get; set; } + + /// + /// + /// + public FolderWrapper() + { + } + + /// + /// + /// + public static FolderWrapper GetSample() + { + return new FolderWrapper + { + Access = FileShare.ReadWrite, + //Updated = ApiDateTime.GetSample(), + //Created = ApiDateTime.GetSample(), + //CreatedBy = EmployeeWraper.GetSample(), + Id = new Random().Next(), + RootFolderType = FolderType.BUNCH, + Shared = false, + Title = "Some titile", + //UpdatedBy = EmployeeWraper.GetSample(), + FilesCount = new Random().Next(), + FoldersCount = new Random().Next(), + ParentId = new Random().Next(), + IsShareable = false + }; + } + } + + public class FolderWrapperHelper : FileEntryWrapperHelper + { + public AuthContext AuthContext { get; } + public IDaoFactory DaoFactory { get; } + public FileSecurity FileSecurity { get; } + public GlobalFolderHelper GlobalFolderHelper { get; } + + public FolderWrapperHelper( + ApiDateTimeHelper apiDateTimeHelper, + EmployeeWraperHelper employeeWrapperHelper, + AuthContext authContext, + IDaoFactory daoFactory, + FileSecurity fileSecurity, + GlobalFolderHelper globalFolderHelper) + : base(apiDateTimeHelper, employeeWrapperHelper) + { + AuthContext = authContext; + DaoFactory = daoFactory; + FileSecurity = fileSecurity; + GlobalFolderHelper = globalFolderHelper; + } + + public FolderWrapper Get(Folder folder) + { + var result = Get(folder); + result.ParentId = folder.ParentFolderID; + if (folder.RootFolderType == FolderType.USER + && !Equals(folder.RootFolderCreator, AuthContext.CurrentAccount.ID)) + { + result.RootFolderType = FolderType.SHARE; + + var folderDao = DaoFactory.FolderDao; + var parentFolder = folderDao.GetFolder(folder.ParentFolderID); + if (!FileSecurity.CanRead(parentFolder)) + result.ParentId = GlobalFolderHelper.FolderShare; + } + + result.FilesCount = folder.TotalFiles; + result.FoldersCount = folder.TotalSubFolders; + result.IsShareable = folder.Shareable; + + return result; + } + } + + public static class FolderWrapperHelperExtention + { + public static DIHelper AddFolderWrapperHelperService(this DIHelper services) + { + services.TryAddScoped(); + + return services + .AddFileEntryWrapperHelperService() + .AddAuthContextService() + .AddDaoFactoryService() + .AddFileSecurityService() + .AddGlobalFolderHelperService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Model/UploadModel.cs b/products/ASC.Files/Server/Model/UploadModel.cs new file mode 100644 index 0000000000..3059860011 --- /dev/null +++ b/products/ASC.Files/Server/Model/UploadModel.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using System.IO; +using System.Net.Mime; + +using Microsoft.AspNetCore.Http; + +namespace ASC.Files.Model +{ + public class UploadModel + { + public Stream File { get; set; } + public ContentType ContentType { get; set; } + public ContentDisposition ContentDisposition { get; set; } + public IEnumerable Files { get; set; } + public bool? CreateNewIfExist { get; set; } + public bool? StoreOriginalFileFlag { get; set; } + public bool KeepConvertStatus { get; set; } + } +} diff --git a/products/ASC.Files/Server/Program.cs b/products/ASC.Files/Server/Program.cs new file mode 100644 index 0000000000..66c89c500e --- /dev/null +++ b/products/ASC.Files/Server/Program.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using System.IO; + +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; + +namespace ASC.Files +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }) + .ConfigureAppConfiguration((hostingContext, config) => + { + var buided = config.Build(); + var path = buided["pathToConf"]; + if (!Path.IsPathRooted(path)) + { + path = Path.GetFullPath(Path.Combine(hostingContext.HostingEnvironment.ContentRootPath, path)); + } + + config.SetBasePath(path); + config + .AddInMemoryCollection(new Dictionary + { + {"pathToConf", path} + }) + .AddJsonFile("appsettings.json") + .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true) + .AddJsonFile("storage.json") + .AddJsonFile("kafka.json") + .AddJsonFile($"kafka.{hostingContext.HostingEnvironment.EnvironmentName}.json", true) + .AddEnvironmentVariables(); + }); + } +} diff --git a/products/ASC.Files/Server/Properties/launchSettings.json b/products/ASC.Files/Server/Properties/launchSettings.json new file mode 100644 index 0000000000..d45db3c074 --- /dev/null +++ b/products/ASC.Files/Server/Properties/launchSettings.json @@ -0,0 +1,33 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:5007", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": false, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "$STORAGE_ROOT": "../../../Data", + "log__name": "files", + "log__dir": "../../../Logs" + } + }, + "ASC.Files": { + "commandName": "Project", + "launchBrowser": false, + "applicationUrl": "http://localhost:5007", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "$STORAGE_ROOT": "../../../Data", + "log__name": "files", + "log__dir": "../../../Logs" + } + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Resources/FilesCommonResource.Designer.cs b/products/ASC.Files/Server/Resources/FilesCommonResource.Designer.cs new file mode 100644 index 0000000000..7a3dad4aef --- /dev/null +++ b/products/ASC.Files/Server/Resources/FilesCommonResource.Designer.cs @@ -0,0 +1,1584 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ASC.Files.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class FilesCommonResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal FilesCommonResource() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ASC.Files.Resources.FilesCommonResource", typeof(FilesCommonResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Access. + /// + public static string Access { + get { + return ResourceManager.GetString("Access", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Outside portal. + /// + public static string AceShareLink { + get { + return ResourceManager.GetString("AceShareLink", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Comment. + /// + public static string AceStatusEnum_Comment { + get { + return ResourceManager.GetString("AceStatusEnum_Comment", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Form Filling. + /// + public static string AceStatusEnum_FillForms { + get { + return ResourceManager.GetString("AceStatusEnum_FillForms", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Read Only. + /// + public static string AceStatusEnum_Read { + get { + return ResourceManager.GetString("AceStatusEnum_Read", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Full Access. + /// + public static string AceStatusEnum_ReadWrite { + get { + return ResourceManager.GetString("AceStatusEnum_ReadWrite", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Deny Access. + /// + public static string AceStatusEnum_Restrict { + get { + return ResourceManager.GetString("AceStatusEnum_Restrict", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Review. + /// + public static string AceStatusEnum_Review { + get { + return ResourceManager.GetString("AceStatusEnum_Review", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Operation. + /// + public static string Action { + get { + return ResourceManager.GetString("Action", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Admin. + /// + public static string Admin { + get { + return ResourceManager.GetString("Admin", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to To work with the file you must grant access to it. + /// + public static string AppAccessDenied { + get { + return ResourceManager.GetString("AppAccessDenied", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to CONTINUE. + /// + public static string AppButtonConvert { + get { + return ResourceManager.GetString("AppButtonConvert", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Opening this file in ONLYOFFICE™ will create a new copy of this file in Office Open XML format in your Google Drive. This way the original file will not be modified. To further edit the file in ONLYOFFICE™ you will have to open a new copy.. + /// + public static string AppConvertCopy { + get { + return ResourceManager.GetString("AppConvertCopy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Do not show this again. + /// + public static string AppConvertCopyHide { + get { + return ResourceManager.GetString("AppConvertCopyHide", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enter Filename. + /// + public static string AppEnterName { + get { + return ResourceManager.GetString("AppEnterName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select File Type. + /// + public static string AppEnterType { + get { + return ResourceManager.GetString("AppEnterType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Author. + /// + public static string Author { + get { + return ResourceManager.GetString("Author", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Me. + /// + public static string Author_Me { + get { + return ResourceManager.GetString("Author_Me", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cancel. + /// + public static string ButtonCancel { + get { + return ResourceManager.GetString("ButtonCancel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Close. + /// + public static string ButtonClose { + get { + return ResourceManager.GetString("ButtonClose", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Convert & Open. + /// + public static string ButtonConvertOpen { + get { + return ResourceManager.GetString("ButtonConvertOpen", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete. + /// + public static string ButtonDelete { + get { + return ResourceManager.GetString("ButtonDelete", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Download. + /// + public static string ButtonDownload { + get { + return ResourceManager.GetString("ButtonDownload", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit. + /// + public static string ButtonEdit { + get { + return ResourceManager.GetString("ButtonEdit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save. + /// + public static string ButtonSave { + get { + return ResourceManager.GetString("ButtonSave", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Comment. + /// + public static string Comment { + get { + return ResourceManager.GetString("Comment", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Autosaved. + /// + public static string CommentAutosave { + get { + return ResourceManager.GetString("CommentAutosave", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Changed owner. + /// + public static string CommentChangeOwner { + get { + return ResourceManager.GetString("CommentChangeOwner", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Converted from {0}. + /// + public static string CommentConvert { + get { + return ResourceManager.GetString("CommentConvert", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Copied. + /// + public static string CommentCopy { + get { + return ResourceManager.GetString("CommentCopy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Created. + /// + public static string CommentCreate { + get { + return ResourceManager.GetString("CommentCreate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Created by DocuSign. + /// + public static string CommentCreateByDocuSign { + get { + return ResourceManager.GetString("CommentCreateByDocuSign", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edited. + /// + public static string CommentEdit { + get { + return ResourceManager.GetString("CommentEdit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edited with encrypt. + /// + public static string CommentEditEncrypt { + get { + return ResourceManager.GetString("CommentEditEncrypt", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Encrypted. + /// + public static string CommentEncrypted { + get { + return ResourceManager.GetString("CommentEncrypted", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Saved. + /// + public static string CommentForcesave { + get { + return ResourceManager.GetString("CommentForcesave", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Overwrited. + /// + public static string CommentOverwrite { + get { + return ResourceManager.GetString("CommentOverwrite", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Restored from revision of {0}. + /// + public static string CommentRevert { + get { + return ResourceManager.GetString("CommentRevert", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Restored from editing session revision of {0}. + /// + public static string CommentRevertChanges { + get { + return ResourceManager.GetString("CommentRevertChanges", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Uploaded. + /// + public static string CommentUpload { + get { + return ResourceManager.GetString("CommentUpload", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Connection url. + /// + public static string ConnectionUrl { + get { + return ResourceManager.GetString("ConnectionUrl", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Convert. + /// + public static string Convert { + get { + return ResourceManager.GetString("Convert", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The document converted to {0} to allow its editing. + /// + public static string ConvertForEdit { + get { + return ResourceManager.GetString("ConvertForEdit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Convert into. + /// + public static string ConvertTo { + get { + return ResourceManager.GetString("ConvertTo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Creation date. + /// + public static string CreatingDate { + get { + return ResourceManager.GetString("CreatingDate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Department. + /// + public static string Department { + get { + return ResourceManager.GetString("Department", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Document Service is not available. Please contact support. + /// + public static string DocsAPIundefined { + get { + return ResourceManager.GetString("DocsAPIundefined", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Document Name. + /// + public static string DocumentName { + get { + return ResourceManager.GetString("DocumentName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Documents. + /// + public static string Documents { + get { + return ResourceManager.GetString("Documents", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Declined. + /// + public static string DocuSignStatusDeclined { + get { + return ResourceManager.GetString("DocuSignStatusDeclined", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to There was an error during the send operation. + /// + public static string DocuSignStatusError { + get { + return ResourceManager.GetString("DocuSignStatusError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sending the document via DocuSign has been canceled. + /// + public static string DocuSignStatusNotSended { + get { + return ResourceManager.GetString("DocuSignStatusNotSended", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Document was sent to sign via DocuSign. + /// + public static string DocuSignStatusSended { + get { + return ResourceManager.GetString("DocuSignStatusSended", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Voided. + /// + public static string DocuSignStatusVoided { + get { + return ResourceManager.GetString("DocuSignStatusVoided", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Notify when mentioned in document. + /// + public static string EditorMentions { + get { + return ResourceManager.GetString("EditorMentions", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Employees. + /// + public static string Employees { + get { + return ResourceManager.GetString("Employees", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enter a comment. + /// + public static string EnterComment { + get { + return ResourceManager.GetString("EnterComment", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error. + /// + public static string ErrorEntry { + get { + return ResourceManager.GetString("ErrorEntry", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Bad request.. + /// + public static string ErrorMassage_BadRequest { + get { + return ResourceManager.GetString("ErrorMassage_BadRequest", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You can not select this user. + /// + public static string ErrorMassage_ChangeOwner { + get { + return ResourceManager.GetString("ErrorMassage_ChangeOwner", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Conversion takes too much time.. + /// + public static string ErrorMassage_ConvertTimeout { + get { + return ResourceManager.GetString("ErrorMassage_ConvertTimeout", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You are not allowed to create folders in the Recycle Bin. + /// + public static string ErrorMassage_CreateNewFolderInTrash { + get { + return ResourceManager.GetString("ErrorMassage_CreateNewFolderInTrash", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error occurred in the Documents Service. + /// + public static string ErrorMassage_DocServiceException { + get { + return ResourceManager.GetString("ErrorMassage_DocServiceException", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The file is open for editing by {0}. Currently, the co-editing feature is not supported for this file format.. + /// + public static string ErrorMassage_EditingCoauth { + get { + return ResourceManager.GetString("ErrorMassage_EditingCoauth", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The file is open for editing in a mobile application by {0}. + /// + public static string ErrorMassage_EditingMobile { + get { + return ResourceManager.GetString("ErrorMassage_EditingMobile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Empty file. + /// + public static string ErrorMassage_EmptyFile { + get { + return ResourceManager.GetString("ErrorMassage_EmptyFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The required file was not found. + /// + public static string ErrorMassage_FileNotFound { + get { + return ResourceManager.GetString("ErrorMassage_FileNotFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to File larger than {0} will not be converted. + /// + public static string ErrorMassage_FileSizeConvert { + get { + return ResourceManager.GetString("ErrorMassage_FileSizeConvert", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to File larger than {0} will not be copied. + /// + public static string ErrorMassage_FileSizeCopy { + get { + return ResourceManager.GetString("ErrorMassage_FileSizeCopy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to File larger than {0} will not be edited. + /// + public static string ErrorMassage_FileSizeEdit { + get { + return ResourceManager.GetString("ErrorMassage_FileSizeEdit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to File larger than {0} will not be moved. + /// + public static string ErrorMassage_FileSizeMove { + get { + return ResourceManager.GetString("ErrorMassage_FileSizeMove", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to File larger than {0} will not be archived. + /// + public static string ErrorMassage_FileSizeZip { + get { + return ResourceManager.GetString("ErrorMassage_FileSizeZip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You cannot copy the folder to its subfolder. + /// + public static string ErrorMassage_FolderCopyError { + get { + return ResourceManager.GetString("ErrorMassage_FolderCopyError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The required folder was not found. + /// + public static string ErrorMassage_FolderNotFound { + get { + return ResourceManager.GetString("ErrorMassage_FolderNotFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Incorrect title. + /// + public static string ErrorMassage_InvalidTitle { + get { + return ResourceManager.GetString("ErrorMassage_InvalidTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The file is currently locked by another user. + /// + public static string ErrorMassage_LockedFile { + get { + return ResourceManager.GetString("ErrorMassage_LockedFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to There are no available accounts to send the message. Go to Mail and connect a mailbox.. + /// + public static string ErrorMassage_MailAccountNotFound { + get { + return ResourceManager.GetString("ErrorMassage_MailAccountNotFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to At least {0} mail messages could not be sent. Please visit the Mail module to see what the reason for the failure could be.. + /// + public static string ErrorMassage_MailMergeCount { + get { + return ResourceManager.GetString("ErrorMassage_MailMergeCount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Too many downloads.. + /// + public static string ErrorMassage_ManyDownloads { + get { + return ResourceManager.GetString("ErrorMassage_ManyDownloads", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sorry, this file format isn't supported. + /// + public static string ErrorMassage_NotSupportedFormat { + get { + return ResourceManager.GetString("ErrorMassage_NotSupportedFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The operation was canceled. + /// + public static string ErrorMassage_OperationCanceledException { + get { + return ResourceManager.GetString("ErrorMassage_OperationCanceledException", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Author of the save is unknown. + /// + public static string ErrorMassage_SaveAnonymous { + get { + return ResourceManager.GetString("ErrorMassage_SaveAnonymous", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The file was corrupted when you tried to save it. + /// + public static string ErrorMassage_SaveCorrupted { + get { + return ResourceManager.GetString("ErrorMassage_SaveCorrupted", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to get the file to be saved. + /// + public static string ErrorMassage_SaveUrlLost { + get { + return ResourceManager.GetString("ErrorMassage_SaveUrlLost", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You don't have enough permission to perform the operation. + /// + public static string ErrorMassage_SecurityException { + get { + return ResourceManager.GetString("ErrorMassage_SecurityException", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Can't authorize at {0} provider with given credentials.. + /// + public static string ErrorMassage_SecurityException_Auth { + get { + return ResourceManager.GetString("ErrorMassage_SecurityException_Auth", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You don't have enough permission to create. + /// + public static string ErrorMassage_SecurityException_Create { + get { + return ResourceManager.GetString("ErrorMassage_SecurityException_Create", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot delete the file because it is opened for editing. + /// + public static string ErrorMassage_SecurityException_DeleteEditingFile { + get { + return ResourceManager.GetString("ErrorMassage_SecurityException_DeleteEditingFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot delete the folder because one of the files is opened for editing. + /// + public static string ErrorMassage_SecurityException_DeleteEditingFolder { + get { + return ResourceManager.GetString("ErrorMassage_SecurityException_DeleteEditingFolder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You don't have enough permission to delete the file. + /// + public static string ErrorMassage_SecurityException_DeleteFile { + get { + return ResourceManager.GetString("ErrorMassage_SecurityException_DeleteFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You don't have enough permission to delete the folder. + /// + public static string ErrorMassage_SecurityException_DeleteFolder { + get { + return ResourceManager.GetString("ErrorMassage_SecurityException_DeleteFolder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You do not have enough permissions to edit the file. + /// + public static string ErrorMassage_SecurityException_EditFile { + get { + return ResourceManager.GetString("ErrorMassage_SecurityException_EditFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This document is being edited by you in another tab. + /// + public static string ErrorMassage_SecurityException_EditFileTwice { + get { + return ResourceManager.GetString("ErrorMassage_SecurityException_EditFileTwice", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You don't have enough permission to move the file. + /// + public static string ErrorMassage_SecurityException_MoveFile { + get { + return ResourceManager.GetString("ErrorMassage_SecurityException_MoveFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You don't have enough permission to move the folder. + /// + public static string ErrorMassage_SecurityException_MoveFolder { + get { + return ResourceManager.GetString("ErrorMassage_SecurityException_MoveFolder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You don't have enough permission to view the file. + /// + public static string ErrorMassage_SecurityException_ReadFile { + get { + return ResourceManager.GetString("ErrorMassage_SecurityException_ReadFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You don't have enough permission to view the folder content. + /// + public static string ErrorMassage_SecurityException_ReadFolder { + get { + return ResourceManager.GetString("ErrorMassage_SecurityException_ReadFolder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You don't have enough permission to rename the file. + /// + public static string ErrorMassage_SecurityException_RenameFile { + get { + return ResourceManager.GetString("ErrorMassage_SecurityException_RenameFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You don't have enough permission to rename the folder. + /// + public static string ErrorMassage_SecurityException_RenameFolder { + get { + return ResourceManager.GetString("ErrorMassage_SecurityException_RenameFolder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot update the file because it's opened for editing. + /// + public static string ErrorMassage_SecurityException_UpdateEditingFile { + get { + return ResourceManager.GetString("ErrorMassage_SecurityException_UpdateEditingFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You don't have enough permission to view the folder content. + /// + public static string ErrorMassage_SecurityException_ViewFolder { + get { + return ResourceManager.GetString("ErrorMassage_SecurityException_ViewFolder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The content of third party folder are not available. Try to reconnect the account.. + /// + public static string ErrorMassage_SharpBoxException { + get { + return ResourceManager.GetString("ErrorMassage_SharpBoxException", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to File editing start error. + /// + public static string ErrorMassage_StartEditing { + get { + return ResourceManager.GetString("ErrorMassage_StartEditing", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You cannot update a file which is opened for editing. + /// + public static string ErrorMassage_UpdateEditingFile { + get { + return ResourceManager.GetString("ErrorMassage_UpdateEditingFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to User not found. + /// + public static string ErrorMassage_UserNotFound { + get { + return ResourceManager.GetString("ErrorMassage_UserNotFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You are not allowed to preview the elements in the Recycle Bin. + /// + public static string ErrorMassage_ViewTrashItem { + get { + return ResourceManager.GetString("ErrorMassage_ViewTrashItem", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Everyone. + /// + public static string Everyone { + get { + return ResourceManager.GetString("Everyone", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Favorites. + /// + public static string Favorites { + get { + return ResourceManager.GetString("Favorites", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to File. + /// + public static string File { + get { + return ResourceManager.GetString("File", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to b,KB,MB,GB. + /// + public static string FileSizePostfix { + get { + return ResourceManager.GetString("FileSizePostfix", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Folder. + /// + public static string Folder { + get { + return ResourceManager.GetString("Folder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Folder Name. + /// + public static string FolderName { + get { + return ResourceManager.GetString("FolderName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Group. + /// + public static string Group { + get { + return ResourceManager.GetString("Group", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Anonymous. + /// + public static string Guest { + get { + return ResourceManager.GetString("Guest", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Import from Google Drive. + /// + public static string ImportFromGoogle { + get { + return ResourceManager.GetString("ImportFromGoogle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Search by contents of documents. + /// + public static string IndexTitle { + get { + return ResourceManager.GetString("IndexTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to new. + /// + public static string IsNew { + get { + return ResourceManager.GetString("IsNew", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Login. + /// + public static string Login { + get { + return ResourceManager.GetString("Login", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Restore. + /// + public static string MakeCurrent { + get { + return ResourceManager.GetString("MakeCurrent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to File created in folder "{0}". + /// + public static string MessageFileCreated { + get { + return ResourceManager.GetString("MessageFileCreated", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Documents. + /// + public static string ModuleName { + get { + return ResourceManager.GetString("ModuleName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Preview. + /// + public static string OpenFile { + get { + return ResourceManager.GetString("OpenFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Password. + /// + public static string Password { + get { + return ResourceManager.GetString("Password", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Do the same as a user|Link Dropbox, Box and other accounts in the 'Common Documents' section|Set up access rights to the documents and folders in the 'Common Documents' section. + /// + public static string ProductAdminOpportunities { + get { + return ResourceManager.GetString("ProductAdminOpportunities", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create, edit, save and share documents. Connect your Google Drive, Dropbox and Box accounts and have all documents in one place.. + /// + public static string ProductDescription { + get { + return ResourceManager.GetString("ProductDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create, edit, save and share documents. Connect your Google Drive, Dropbox and Box accounts and have all documents in one place.. + /// + public static string ProductDescriptionEx { + get { + return ResourceManager.GetString("ProductDescriptionEx", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create, edit, save and share documents. Connect your Google Drive, Dropbox and Box accounts and have all documents in one place.. + /// + public static string ProductDescriptionShort { + get { + return ResourceManager.GetString("ProductDescriptionShort", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Documents. + /// + public static string ProductName { + get { + return ResourceManager.GetString("ProductName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create and edit own documents as well as the shared documents with the proper access rights|Give reading/editing access to other users to the documents and folders|Link Dropbox, Box and other accounts in the 'My documents' section. + /// + public static string ProductUserOpportunities { + get { + return ResourceManager.GetString("ProductUserOpportunities", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {!User} removed. + /// + public static string ProfileRemoved { + get { + return ResourceManager.GetString("ProfileRemoved", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Mark as Read. + /// + public static string RemoveIsNew { + get { + return ResourceManager.GetString("RemoveIsNew", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Search. + /// + public static string Search { + get { + return ResourceManager.GetString("Search", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Make sure all words are spelled correctly. Try using different keywords.. + /// + public static string SearchNotFoundDescript { + get { + return ResourceManager.GetString("SearchNotFoundDescript", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Search. + /// + public static string SearchText { + get { + return ResourceManager.GetString("SearchText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Share. + /// + public static string Share { + get { + return ResourceManager.GetString("Share", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Shared with Me. + /// + public static string SharedForMe { + get { + return ResourceManager.GetString("SharedForMe", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Notify when a folder is shared. + /// + public static string ShareFolder { + get { + return ResourceManager.GetString("ShareFolder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show Versions. + /// + public static string ShowVersions { + get { + return ResourceManager.GetString("ShowVersions", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Size. + /// + public static string Size { + get { + return ResourceManager.GetString("Size", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Notify when document signed via DocuSign. + /// + public static string SubscriptDocuSignComplete { + get { + return ResourceManager.GetString("SubscriptDocuSignComplete", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Notify when document in DocuSign changed status. + /// + public static string SubscriptDocuSignStatus { + get { + return ResourceManager.GetString("SubscriptDocuSignStatus", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Notify when a document is shared. + /// + public static string SubscriptForAccess { + get { + return ResourceManager.GetString("SubscriptForAccess", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Notify when mailing is complete. + /// + public static string SubscriptForMailMerge { + get { + return ResourceManager.GetString("SubscriptForMailMerge", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Specify for change. + /// + public static string ThirdPartyCorrect { + get { + return ResourceManager.GetString("ThirdPartyCorrect", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Folder title. + /// + public static string ThirdPartyFolderTitle { + get { + return ResourceManager.GetString("ThirdPartyFolderTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Reconnect. + /// + public static string ThirdPartyReconnect { + get { + return ResourceManager.GetString("ThirdPartyReconnect", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Account. + /// + public static string ThirdPartyReconnectTitle { + get { + return ResourceManager.GetString("ThirdPartyReconnectTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Make shared and put into the 'Common Documents' folder. + /// + public static string ThirdPartySetCorporate { + get { + return ResourceManager.GetString("ThirdPartySetCorporate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Created. + /// + public static string TitleCreated { + get { + return ResourceManager.GetString("TitleCreated", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Documents of the {!user} {0}. + /// + public static string TitleDeletedUserFolder { + get { + return ResourceManager.GetString("TitleDeletedUserFolder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to It is currently being edited. + /// + public static string TitleEditingFile { + get { + return ResourceManager.GetString("TitleEditingFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Documents. + /// + public static string TitleFiles { + get { + return ResourceManager.GetString("TitleFiles", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Updated. + /// + public static string TitleModified { + get { + return ResourceManager.GetString("TitleModified", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Documents. + /// + public static string TitlePage { + get { + return ResourceManager.GetString("TitlePage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Deleted. + /// + public static string TitleRemoved { + get { + return ResourceManager.GetString("TitleRemoved", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select. + /// + public static string TitleSelectFile { + get { + return ResourceManager.GetString("TitleSelectFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Permission Settings. + /// + public static string TitleShareFile { + get { + return ResourceManager.GetString("TitleShareFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show File Actions. + /// + public static string TitleShowActions { + get { + return ResourceManager.GetString("TitleShowActions", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show Folder Actions. + /// + public static string TitleShowFolderActions { + get { + return ResourceManager.GetString("TitleShowFolderActions", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Subfolders. + /// + public static string TitleSubfolders { + get { + return ResourceManager.GetString("TitleSubfolders", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Uploaded. + /// + public static string TitleUploaded { + get { + return ResourceManager.GetString("TitleUploaded", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Mark as version. + /// + public static string TitleVersionComplete { + get { + return ResourceManager.GetString("TitleVersionComplete", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Mark as revision. + /// + public static string TitleVersionContinue { + get { + return ResourceManager.GetString("TitleVersionContinue", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Version history:. + /// + public static string TitleVersionHistory { + get { + return ResourceManager.GetString("TitleVersionHistory", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to First Name. + /// + public static string UnknownFirstName { + get { + return ResourceManager.GetString("UnknownFirstName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Last Name. + /// + public static string UnknownLastName { + get { + return ResourceManager.GetString("UnknownLastName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The file is unlocked. + /// + public static string UnlockComment { + get { + return ResourceManager.GetString("UnlockComment", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to User. + /// + public static string User { + get { + return ResourceManager.GetString("User", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to ver.. + /// + public static string Version { + get { + return ResourceManager.GetString("Version", resourceCulture); + } + } + } +} diff --git a/products/ASC.Files/Server/Resources/FilesCommonResource.resx b/products/ASC.Files/Server/Resources/FilesCommonResource.resx new file mode 100644 index 0000000000..7fd5c332e1 --- /dev/null +++ b/products/ASC.Files/Server/Resources/FilesCommonResource.resx @@ -0,0 +1,627 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Access + + + Outside portal + + + Comment + + + Form Filling + + + Read Only + + + Full Access + + + Deny Access + + + Review + + + Operation + + + Admin + + + To work with the file you must grant access to it + + + CONTINUE + + + Opening this file in ONLYOFFICE™ will create a new copy of this file in Office Open XML format in your Google Drive. This way the original file will not be modified. To further edit the file in ONLYOFFICE™ you will have to open a new copy. + + + Do not show this again + + + Enter Filename + + + Select File Type + + + Author + + + Me + + + Cancel + + + Close + + + Convert & Open + + + Delete + + + Download + + + Edit + + + Save + + + Comment + + + Autosaved + + + Converted from {0} + + + Copied + + + Created + + + Created by DocuSign + + + Edited + + + Edited with encrypt + + + Encrypted + + + Saved + + + Overwrited + + + Restored from revision of {0} + + + Restored from editing session revision of {0} + + + Changed owner + + + Uploaded + + + Connection url + + + Convert + + + The document converted to {0} to allow its editing + + + Convert into + + + Creation date + + + Department + + + Document Name + + + Documents + + + Document Service is not available. Please contact support + + + Declined + + + There was an error during the send operation + + + Sending the document via DocuSign has been canceled + + + Document was sent to sign via DocuSign + + + Voided + + + Notify when mentioned in document + + + Employees + + + Enter a comment + + + Error + + + Bad request. + + + Conversion takes too much time. + + + You are not allowed to create folders in the Recycle Bin + + + Error occurred in the Documents Service + + + The file is open for editing by {0}. Currently, the co-editing feature is not supported for this file format. + + + The file is open for editing in a mobile application by {0} + + + Empty file + + + The required file was not found + + + File larger than {0} will not be converted + + + File larger than {0} will not be copied + + + File larger than {0} will not be edited + + + File larger than {0} will not be moved + + + File larger than {0} will not be archived + + + You cannot copy the folder to its subfolder + + + The required folder was not found + + + Incorrect title + + + The file is currently locked by another user + + + There are no available accounts to send the message. Go to Mail and connect a mailbox. + + + At least {0} mail messages could not be sent. Please visit the Mail module to see what the reason for the failure could be. + + + Too many downloads. + + + Sorry, this file format isn't supported + + + Author of the save is unknown + + + The file was corrupted when you tried to save it + + + Failed to get the file to be saved + + + You don't have enough permission to perform the operation + + + Can't authorize at {0} provider with given credentials. + + + You don't have enough permission to create + + + Cannot delete the file because it is opened for editing + + + Cannot delete the folder because one of the files is opened for editing + + + You don't have enough permission to delete the file + + + You don't have enough permission to delete the folder + + + You do not have enough permissions to edit the file + + + This document is being edited by you in another tab + + + You don't have enough permission to move the file + + + You don't have enough permission to move the folder + + + You don't have enough permission to view the file + + + You don't have enough permission to view the folder content + + + You don't have enough permission to rename the file + + + You don't have enough permission to rename the folder + + + Cannot update the file because it's opened for editing + + + You don't have enough permission to view the folder content + + + The content of third party folder are not available. Try to reconnect the account. + + + File editing start error + + + You cannot update a file which is opened for editing + + + User not found + + + You are not allowed to preview the elements in the Recycle Bin + + + You can not select this user + + + Everyone + + + Favorites + + + File + + + b,KB,MB,GB + + + Folder + + + Folder Name + + + Group + + + Anonymous + + + Import from Google Drive + + + new + + + Login + + + Restore + + + File created in folder "{0}" + + + Documents + + + Preview + + + Password + + + Do the same as a user|Link Dropbox, Box and other accounts in the 'Common Documents' section|Set up access rights to the documents and folders in the 'Common Documents' section + + + Create, edit, save and share documents. Connect your Google Drive, Dropbox and Box accounts and have all documents in one place. + + + Create, edit, save and share documents. Connect your Google Drive, Dropbox and Box accounts and have all documents in one place. + + + Documents + + + Create and edit own documents as well as the shared documents with the proper access rights|Give reading/editing access to other users to the documents and folders|Link Dropbox, Box and other accounts in the 'My documents' section + + + {!User} removed + + + Mark as Read + + + Search + + + Make sure all words are spelled correctly. Try using different keywords. + + + Search + + + Share + + + Shared with Me + + + Notify when a folder is shared + + + Show Versions + + + Size + + + Notify when a document is shared + + + Notify when document signed via DocuSign + + + Notify when document in DocuSign changed status + + + Notify when mailing is complete + + + Specify for change + + + Folder title + + + Account + + + Reconnect + + + Make shared and put into the 'Common Documents' folder + + + Created + + + Documents of the {!user} {0} + + + It is currently being edited + + + Documents + + + Updated + + + Documents + + + Deleted + + + Select + + + Permission Settings + + + Show File Actions + + + Show Folder Actions + + + Subfolders + + + Uploaded + + + Mark as version + + + Mark as revision + + + Version history: + + + First Name + + + Last Name + + + The file is unlocked + + + User + + + ver. + + + Search by contents of documents + + + The operation was canceled + + + Create, edit, save and share documents. Connect your Google Drive, Dropbox and Box accounts and have all documents in one place. + + \ No newline at end of file diff --git a/products/ASC.Files/Server/Resources/FilesJSResource.Designer.cs b/products/ASC.Files/Server/Resources/FilesJSResource.Designer.cs new file mode 100644 index 0000000000..83a1dab302 --- /dev/null +++ b/products/ASC.Files/Server/Resources/FilesJSResource.Designer.cs @@ -0,0 +1,1656 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ASC.Files.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class FilesJSResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal FilesJSResource() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ASC.Files.Resources.FilesJSResource", typeof(FilesJSResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Access Settings {0}. + /// + public static string AccessSettingsTitle { + get { + return ResourceManager.GetString("AccessSettingsTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Comment. + /// + public static string AceStatusEnum_Comment { + get { + return ResourceManager.GetString("AceStatusEnum_Comment", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Form Filling. + /// + public static string AceStatusEnum_FillForms { + get { + return ResourceManager.GetString("AceStatusEnum_FillForms", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Owner. + /// + public static string AceStatusEnum_Owner { + get { + return ResourceManager.GetString("AceStatusEnum_Owner", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Read Only. + /// + public static string AceStatusEnum_Read { + get { + return ResourceManager.GetString("AceStatusEnum_Read", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Full Access. + /// + public static string AceStatusEnum_ReadWrite { + get { + return ResourceManager.GetString("AceStatusEnum_ReadWrite", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Deny Access. + /// + public static string AceStatusEnum_Restrict { + get { + return ResourceManager.GetString("AceStatusEnum_Restrict", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Review. + /// + public static string AceStatusEnum_Review { + get { + return ResourceManager.GetString("AceStatusEnum_Review", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Varies. + /// + public static string AceStatusEnum_Varies { + get { + return ResourceManager.GetString("AceStatusEnum_Varies", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Archives. + /// + public static string ButtonFilterArchive { + get { + return ResourceManager.GetString("ButtonFilterArchive", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Author. + /// + public static string ButtonFilterAuthor { + get { + return ResourceManager.GetString("ButtonFilterAuthor", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No subfolders. + /// + public static string ButtonFilterCurrentFolderOnly { + get { + return ResourceManager.GetString("ButtonFilterCurrentFolderOnly", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Documents. + /// + public static string ButtonFilterDocument { + get { + return ResourceManager.GetString("ButtonFilterDocument", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to All files. + /// + public static string ButtonFilterFiles { + get { + return ResourceManager.GetString("ButtonFilterFiles", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Folders. + /// + public static string ButtonFilterFolder { + get { + return ResourceManager.GetString("ButtonFilterFolder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Images. + /// + public static string ButtonFilterImage { + get { + return ResourceManager.GetString("ButtonFilterImage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to In content. + /// + public static string ButtonFilterInContent { + get { + return ResourceManager.GetString("ButtonFilterInContent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Media. + /// + public static string ButtonFilterMedia { + get { + return ResourceManager.GetString("ButtonFilterMedia", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Search. + /// + public static string ButtonFilterOther { + get { + return ResourceManager.GetString("ButtonFilterOther", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Presentations. + /// + public static string ButtonFilterPresentation { + get { + return ResourceManager.GetString("ButtonFilterPresentation", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Spreadsheets. + /// + public static string ButtonFilterSpreadsheet { + get { + return ResourceManager.GetString("ButtonFilterSpreadsheet", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Type. + /// + public static string ButtonFilterType { + get { + return ResourceManager.GetString("ButtonFilterType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show more {0}. + /// + public static string ButtonShowMore { + get { + return ResourceManager.GetString("ButtonShowMore", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Uploading.... + /// + public static string ButtonShowMoreLoad { + get { + return ResourceManager.GetString("ButtonShowMoreLoad", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show more {0} of {1}. + /// + public static string ButtonShowMoreOf { + get { + return ResourceManager.GetString("ButtonShowMoreOf", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Author. + /// + public static string ButtonSortAuthor { + get { + return ResourceManager.GetString("ButtonSortAuthor", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Created. + /// + public static string ButtonSortCreated { + get { + return ResourceManager.GetString("ButtonSortCreated", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Modified. + /// + public static string ButtonSortModifiedNew { + get { + return ResourceManager.GetString("ButtonSortModifiedNew", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Title. + /// + public static string ButtonSortName { + get { + return ResourceManager.GetString("ButtonSortName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Size. + /// + public static string ButtonSortSize { + get { + return ResourceManager.GetString("ButtonSortSize", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Type. + /// + public static string ButtonSortType { + get { + return ResourceManager.GetString("ButtonSortType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Code has been copied to the clipboard. + /// + public static string CodeCopySuccess { + get { + return ResourceManager.GetString("CodeCopySuccess", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Are you sure you want to remove the {0} from the 'Documents' module? This will not affect your {1} account in any way.. + /// + public static string ConfirmDeleteThirdParty { + get { + return ResourceManager.GetString("ConfirmDeleteThirdParty", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Are you sure you want to empty the recycle bin?. + /// + public static string ConfirmEmptyTrash { + get { + return ResourceManager.GetString("ConfirmEmptyTrash", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Are you sure you want to leave the current page? Downloading of files will be interrupted.. + /// + public static string ConfirmLeavePage { + get { + return ResourceManager.GetString("ConfirmLeavePage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Are you sure you want to delete the document?. + /// + public static string ConfirmRemoveFile { + get { + return ResourceManager.GetString("ConfirmRemoveFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Are you sure you want to delete the folder?. + /// + public static string ConfirmRemoveFolder { + get { + return ResourceManager.GetString("ConfirmRemoveFolder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Are you sure you want to delete these elements?. + /// + public static string ConfirmRemoveList { + get { + return ResourceManager.GetString("ConfirmRemoveList", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You are about to move elements from the {1} directory. In such a case, they will be deleted from your {1} account and will no longer be accessible to other users, in case these elements have previously been shared. {0}Are you sure you want to move the elements?. + /// + public static string ConfirmThirdPartyMoveMessage { + get { + return ResourceManager.GetString("ConfirmThirdPartyMoveMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Copy link. + /// + public static string CopyToClipboard { + get { + return ResourceManager.GetString("CopyToClipboard", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Common Documents. + /// + public static string CorporateFiles { + get { + return ResourceManager.GetString("CorporateFiles", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Custom format. + /// + public static string CustomFormat { + get { + return ResourceManager.GetString("CustomFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Groups. + /// + public static string Departments { + get { + return ResourceManager.GetString("Departments", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Archiving Data. + /// + public static string DescriptBulkdownload { + get { + return ResourceManager.GetString("DescriptBulkdownload", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Change data. + /// + public static string DescriptChangeInfo { + get { + return ResourceManager.GetString("DescriptChangeInfo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Finalizing version. + /// + public static string DescriptCompleteVersion { + get { + return ResourceManager.GetString("DescriptCompleteVersion", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Copy. + /// + public static string DescriptCopy { + get { + return ResourceManager.GetString("DescriptCopy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Creation. + /// + public static string DescriptCreate { + get { + return ResourceManager.GetString("DescriptCreate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sign via DocuSign. + /// + public static string DescriptDocuSign { + get { + return ResourceManager.GetString("DescriptDocuSign", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Loading versions.... + /// + public static string DescriptLoadVersion { + get { + return ResourceManager.GetString("DescriptLoadVersion", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Marking as Read. + /// + public static string DescriptMarkAsRead { + get { + return ResourceManager.GetString("DescriptMarkAsRead", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Move. + /// + public static string DescriptMove { + get { + return ResourceManager.GetString("DescriptMove", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Deleting.... + /// + public static string DescriptRemove { + get { + return ResourceManager.GetString("DescriptRemove", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Rename. + /// + public static string DescriptRename { + get { + return ResourceManager.GetString("DescriptRename", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Updating version. + /// + public static string DescriptSetVersion { + get { + return ResourceManager.GetString("DescriptSetVersion", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Preparing file for encryption. + /// + public static string DesktopMessageDownloading { + get { + return ResourceManager.GetString("DesktopMessageDownloading", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Encrypting file. + /// + public static string DesktopMessageEncrypting { + get { + return ResourceManager.GetString("DesktopMessageEncrypting", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Saving encrypted file. + /// + public static string DesktopMessageStoring { + get { + return ResourceManager.GetString("DesktopMessageStoring", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Documents. + /// + public static string Documents { + get { + return ResourceManager.GetString("Documents", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sign document ({0}). + /// + public static string DocuSignDialogHeader { + get { + return ResourceManager.GetString("DocuSignDialogHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select at least one folder/document to download. + /// + public static string EmptyListSelectedForDownload { + get { + return ResourceManager.GetString("EmptyListSelectedForDownload", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enter a comment. + /// + public static string EnterComment { + get { + return ResourceManager.GetString("EnterComment", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You cannot upload a folder or an empty file. + /// + public static string ErrorMassage_EmptyFile { + get { + return ResourceManager.GetString("ErrorMassage_EmptyFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Conversion Error. + /// + public static string ErrorMassage_ErrorConvert { + get { + return ResourceManager.GetString("ErrorMassage_ErrorConvert", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to All fields are required to be filled in. + /// + public static string ErrorMassage_FieldsIsEmpty { + get { + return ResourceManager.GetString("ErrorMassage_FieldsIsEmpty", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The maximum file size is exceeded. + /// + public static string ErrorMassage_FileSize { + get { + return ResourceManager.GetString("ErrorMassage_FileSize", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You need to login to be able to add. + /// + public static string ErrorMassage_MustLogin { + get { + return ResourceManager.GetString("ErrorMassage_MustLogin", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sorry, this file format isn't supported. + /// + public static string ErrorMassage_NotSupportedFormat { + get { + return ResourceManager.GetString("ErrorMassage_NotSupportedFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The password you entered is incorrect. Please try again.. + /// + public static string ErrorMassage_PasswordFile { + get { + return ResourceManager.GetString("ErrorMassage_PasswordFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Please wait for the previous download to complete or cancel it. + /// + public static string ErrorMassage_SecondDownload { + get { + return ResourceManager.GetString("ErrorMassage_SecondDownload", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You don't have enough permission to perform the operation. + /// + public static string ErrorMassage_SecurityException { + get { + return ResourceManager.GetString("ErrorMassage_SecurityException", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You can not performed this action in this folder. + /// + public static string ErrorMassage_SecurityException_PrivateRoot { + get { + return ResourceManager.GetString("ErrorMassage_SecurityException_PrivateRoot", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The title cannot contain any of the following characters: {0}. + /// + public static string ErrorMassage_SpecCharacter { + get { + return ResourceManager.GetString("ErrorMassage_SpecCharacter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The portal storage has run out of free space. You cannot upload files larger than {0}.. + /// + public static string ErrorMassage_StorageSize { + get { + return ResourceManager.GetString("ErrorMassage_StorageSize", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The file with the name {0} already exists in the folder {1}.. + /// + public static string FileAlreadyExist { + get { + return ResourceManager.GetString("FileAlreadyExist", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Converted. + /// + public static string FileConverted { + get { + return ResourceManager.GetString("FileConverted", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Converting. + /// + public static string FileConverting { + get { + return ResourceManager.GetString("FileConverting", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} documents with the same name already exist in the folder {1}.. + /// + public static string FilesAlreadyExist { + get { + return ResourceManager.GetString("FilesAlreadyExist", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Uploaded. + /// + public static string FileUploaded { + get { + return ResourceManager.GetString("FileUploaded", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Uploading. + /// + public static string FileUploading { + get { + return ResourceManager.GetString("FileUploading", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Box directory. + /// + public static string FolderTitleBoxNet { + get { + return ResourceManager.GetString("FolderTitleBoxNet", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to DocuSign account. + /// + public static string FolderTitleDocuSign { + get { + return ResourceManager.GetString("FolderTitleDocuSign", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Dropbox directory. + /// + public static string FolderTitleDropBox { + get { + return ResourceManager.GetString("FolderTitleDropBox", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Google directory. + /// + public static string FolderTitleGoogle { + get { + return ResourceManager.GetString("FolderTitleGoogle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to SharePoint directory. + /// + public static string FolderTitleSharePoint { + get { + return ResourceManager.GetString("FolderTitleSharePoint", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to OneDrive directory. + /// + public static string FolderTitleSkyDrive { + get { + return ResourceManager.GetString("FolderTitleSkyDrive", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to WebDAV Directory. + /// + public static string FolderTitleWebDav { + get { + return ResourceManager.GetString("FolderTitleWebDav", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Yandex Directory. + /// + public static string FolderTitleYandex { + get { + return ResourceManager.GetString("FolderTitleYandex", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Shorten link. + /// + public static string GetShortenLink { + get { + return ResourceManager.GetString("GetShortenLink", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Connect your Google Drive, Dropbox, Box and OneDrive accounts and have all documents in one place.. + /// + public static string HelpContentConnectAccount { + get { + return ResourceManager.GetString("HelpContentConnectAccount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use this button to create new documents, spreadsheets and presentations directly on your portal.. + /// + public static string HelpContentCreateFiles { + get { + return ResourceManager.GetString("HelpContentCreateFiles", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Click the file name to download and save the document to your hard disk drive.. + /// + public static string HelpContentDownloadFiles { + get { + return ResourceManager.GetString("HelpContentDownloadFiles", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Share your files with other portal users or make them available to people outside your portal setting the appropriate access rights.. + /// + public static string HelpContentShareFiles { + get { + return ResourceManager.GetString("HelpContentShareFiles", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use this button to upload files: documents, spreadsheets, presentations, etc. stored on your hard disk drive.. + /// + public static string HelpContentUploadFiles { + get { + return ResourceManager.GetString("HelpContentUploadFiles", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Click the file name to view the document, or use the pencil icon to open it for editing. For faster processing your file will be converted to the Office Open XML format.. + /// + public static string HelpContentViewFiles { + get { + return ResourceManager.GetString("HelpContentViewFiles", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Connect Your Account. + /// + public static string HelpTitleConnectAccount { + get { + return ResourceManager.GetString("HelpTitleConnectAccount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create New Files. + /// + public static string HelpTitleCreateFiles { + get { + return ResourceManager.GetString("HelpTitleCreateFiles", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Download Your Files. + /// + public static string HelpTitleDownloadFiles { + get { + return ResourceManager.GetString("HelpTitleDownloadFiles", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Share Your Files. + /// + public static string HelpTitleShareFiles { + get { + return ResourceManager.GetString("HelpTitleShareFiles", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Upload Your Files. + /// + public static string HelpTitleUploadFiles { + get { + return ResourceManager.GetString("HelpTitleUploadFiles", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to View & Edit Your Files. + /// + public static string HelpTitleViewFiles { + get { + return ResourceManager.GetString("HelpTitleViewFiles", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Import from Google Drive. + /// + public static string ImportFromGoogle { + get { + return ResourceManager.GetString("ImportFromGoogle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Now '{0}' is the owner of '{1}'. + /// + public static string InfoChangeOwner { + get { + return ResourceManager.GetString("InfoChangeOwner", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Now '{0}' is the owner of {1} items. + /// + public static string InfoChangeOwnerArray { + get { + return ResourceManager.GetString("InfoChangeOwnerArray", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to '{0}' changed. + /// + public static string InfoChangeThirdParty { + get { + return ResourceManager.GetString("InfoChangeThirdParty", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Copy {1}{0}{2}. + /// + public static string InfoCopyDescribe { + get { + return ResourceManager.GetString("InfoCopyDescribe", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} copied elements. + /// + public static string InfoCopyGroup { + get { + return ResourceManager.GetString("InfoCopyGroup", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to '{0}' copied. + /// + public static string InfoCopyItem { + get { + return ResourceManager.GetString("InfoCopyItem", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} elements. + /// + public static string InfoCountDescribe { + get { + return ResourceManager.GetString("InfoCountDescribe", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New file '{0}' is created. + /// + public static string InfoCrateFile { + get { + return ResourceManager.GetString("InfoCrateFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The new '{0}' file is created in '{1}'. + /// + public static string InfoCrateFileIn { + get { + return ResourceManager.GetString("InfoCrateFileIn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New folder '{0}' is created. + /// + public static string InfoCrateFolder { + get { + return ResourceManager.GetString("InfoCrateFolder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You cannot copy the folder to its subfolder. + /// + public static string InfoFolderCopyError { + get { + return ResourceManager.GetString("InfoFolderCopyError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You cannot move the folder to its subfolder. + /// + public static string InfoFolderMoveError { + get { + return ResourceManager.GetString("InfoFolderMoveError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You do not have access rights to move some file(s). + /// + public static string InfoMoveAccessError { + get { + return ResourceManager.GetString("InfoMoveAccessError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Move {1}{0}{2}. + /// + public static string InfoMoveDescribe { + get { + return ResourceManager.GetString("InfoMoveDescribe", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} moved elements. + /// + public static string InfoMoveGroup { + get { + return ResourceManager.GetString("InfoMoveGroup", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to '{0}' moved. + /// + public static string InfoMoveItem { + get { + return ResourceManager.GetString("InfoMoveItem", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The document '{0}' is deleted. + /// + public static string InfoRemoveFile { + get { + return ResourceManager.GetString("InfoRemoveFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The folder '{0}' is deleted. + /// + public static string InfoRemoveFolder { + get { + return ResourceManager.GetString("InfoRemoveFolder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to It was successfully deleted {0} from {1}. + /// + public static string InfoRemoveGroup { + get { + return ResourceManager.GetString("InfoRemoveGroup", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Third Party '{0}' is deleted. + /// + public static string InfoRemoveThirdParty { + get { + return ResourceManager.GetString("InfoRemoveThirdParty", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The document '{0}' is renamed to '{1}'. + /// + public static string InfoRenameFile { + get { + return ResourceManager.GetString("InfoRenameFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The folder '{0}' is renamed to '{1}'. + /// + public static string InfoRenameFolder { + get { + return ResourceManager.GetString("InfoRenameFolder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to '{0}' added to '{1}'. + /// + public static string InfoSaveThirdParty { + get { + return ResourceManager.GetString("InfoSaveThirdParty", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} documents are successfully uploaded. + /// + public static string InfoUploadedSuccess { + get { + return ResourceManager.GetString("InfoUploadedSuccess", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Loading.... + /// + public static string LoadingProcessing { + get { + return ResourceManager.GetString("LoadingProcessing", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Please wait.... + /// + public static string LoadingWait { + get { + return ResourceManager.GetString("LoadingWait", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to My Documents. + /// + public static string MyFiles { + get { + return ResourceManager.GetString("MyFiles", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Original format. + /// + public static string OriginalFormat { + get { + return ResourceManager.GetString("OriginalFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Other. + /// + public static string Other { + get { + return ResourceManager.GetString("Other", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Overall Progress. + /// + public static string OverallProgress { + get { + return ResourceManager.GetString("OverallProgress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Presentations. + /// + public static string Presentations { + get { + return ResourceManager.GetString("Presentations", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to upload file {0} to display. + /// + public static string PreviewError { + get { + return ResourceManager.GetString("PreviewError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Project Documents. + /// + public static string ProjectFiles { + get { + return ResourceManager.GetString("ProjectFiles", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to revisions: {0}. + /// + public static string RevisionCount { + get { + return ResourceManager.GetString("RevisionCount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You have been granted access to the {0} document. Click the link below to open the document right now: {1}. + /// + public static string shareLinkMailBody { + get { + return ResourceManager.GetString("shareLinkMailBody", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You have been granted access to the {0} document. + /// + public static string shareLinkMailSubject { + get { + return ResourceManager.GetString("shareLinkMailSubject", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to elements: {0}. + /// + public static string SharingSettingsCount { + get { + return ResourceManager.GetString("SharingSettingsCount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add users or groups and give them permissions{0} to read, review or edit the documents.. + /// + public static string SharingSettingsEmpty { + get { + return ResourceManager.GetString("SharingSettingsEmpty", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sharing Settings {0}. + /// + public static string SharingSettingsTitle { + get { + return ResourceManager.GetString("SharingSettingsTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Spreadsheets. + /// + public static string Spreedsheets { + get { + return ResourceManager.GetString("Spreedsheets", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Archiving Data. + /// + public static string TasksOperationBulkdownload { + get { + return ResourceManager.GetString("TasksOperationBulkdownload", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Copying. + /// + public static string TasksOperationCopy { + get { + return ResourceManager.GetString("TasksOperationCopy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Deleting. + /// + public static string TasksOperationDelete { + get { + return ResourceManager.GetString("TasksOperationDelete", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Marking as Read. + /// + public static string TasksOperationMarkAsRead { + get { + return ResourceManager.GetString("TasksOperationMarkAsRead", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} operations. + /// + public static string TasksOperationMixed { + get { + return ResourceManager.GetString("TasksOperationMixed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Move. + /// + public static string TasksOperationMove { + get { + return ResourceManager.GetString("TasksOperationMove", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Change {0} connection settings. + /// + public static string ThirdPartyEditorCaption { + get { + return ResourceManager.GetString("ThirdPartyEditorCaption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cancel. + /// + public static string TitleCancel { + get { + return ResourceManager.GetString("TitleCancel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create. + /// + public static string TitleCreate { + get { + return ResourceManager.GetString("TitleCreate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to It is currently being edited. + /// + public static string TitleEditingFile { + get { + return ResourceManager.GetString("TitleEditingFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to It is currently being edited by: {0}. + /// + public static string TitleEditingFileBy { + get { + return ResourceManager.GetString("TitleEditingFileBy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Locked. + /// + public static string TitleLockedFile { + get { + return ResourceManager.GetString("TitleLockedFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Locked by: {0}. + /// + public static string TitleLockedFileBy { + get { + return ResourceManager.GetString("TitleLockedFileBy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to More Features. + /// + public static string TitleMoreFeaturs { + get { + return ResourceManager.GetString("TitleMoreFeaturs", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New Presentation. + /// + public static string TitleNewFilePresentation { + get { + return ResourceManager.GetString("TitleNewFilePresentation", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New Spreadsheet. + /// + public static string TitleNewFileSpreadsheet { + get { + return ResourceManager.GetString("TitleNewFileSpreadsheet", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New Document. + /// + public static string TitleNewFileText { + get { + return ResourceManager.GetString("TitleNewFileText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New Folder. + /// + public static string TitleNewFolder { + get { + return ResourceManager.GetString("TitleNewFolder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Common Settings. + /// + public static string TitleSettingsCommon { + get { + return ResourceManager.GetString("TitleSettingsCommon", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Help. + /// + public static string TitleSettingsHelp { + get { + return ResourceManager.GetString("TitleSettingsHelp", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Connect accounts. + /// + public static string TitleSettingsThirdParty { + get { + return ResourceManager.GetString("TitleSettingsThirdParty", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Box. + /// + public static string TypeTitleBoxNet { + get { + return ResourceManager.GetString("TypeTitleBoxNet", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to DocuSign. + /// + public static string TypeTitleDocuSign { + get { + return ResourceManager.GetString("TypeTitleDocuSign", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Dropbox. + /// + public static string TypeTitleDropBox { + get { + return ResourceManager.GetString("TypeTitleDropBox", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to google.com. + /// + public static string TypeTitleGoogle { + get { + return ResourceManager.GetString("TypeTitleGoogle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to sharepoint.com. + /// + public static string TypeTitleSharePoint { + get { + return ResourceManager.GetString("TypeTitleSharePoint", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to onedrive.live.com. + /// + public static string TypeTitleSkyDrive { + get { + return ResourceManager.GetString("TypeTitleSkyDrive", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to WebDAV. + /// + public static string TypeTitleWebDav { + get { + return ResourceManager.GetString("TypeTitleWebDav", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to yandex.com. + /// + public static string TypeTitleYandex { + get { + return ResourceManager.GetString("TypeTitleYandex", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unknown error.... + /// + public static string UnknownErrorText { + get { + return ResourceManager.GetString("UnknownErrorText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Upload complete. + /// + public static string UploadComplete { + get { + return ResourceManager.GetString("UploadComplete", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Uploading .... + /// + public static string Uploading { + get { + return ResourceManager.GetString("Uploading", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Uploading {0} files ({1}% done). + /// + public static string UploadingProgress { + get { + return ResourceManager.GetString("UploadingProgress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} of {1} used. + /// + public static string UsedSize { + get { + return ResourceManager.GetString("UsedSize", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Users. + /// + public static string Users { + get { + return ResourceManager.GetString("Users", resourceCulture); + } + } + } +} diff --git a/products/ASC.Files/Server/Resources/FilesJSResource.resx b/products/ASC.Files/Server/Resources/FilesJSResource.resx new file mode 100644 index 0000000000..214c446dff --- /dev/null +++ b/products/ASC.Files/Server/Resources/FilesJSResource.resx @@ -0,0 +1,651 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Access Settings {0} + + + Comment + + + Form Filling + + + Owner + + + Read Only + + + Full Access + + + Deny Access + + + Review + + + Varies + + + Author + + + Archives + + + No subfolders + + + Documents + + + All files + + + Folders + + + Images + + + In content + + + Media + + + Search + + + Presentations + + + Spreadsheets + + + Type + + + Show more {0} + + + Uploading... + + + Show more {0} of {1} + + + Author + + + Created + + + Modified + + + Title + + + Size + + + Type + + + Code has been copied to the clipboard + + + Are you sure you want to remove the {0} from the 'Documents' module? This will not affect your {1} account in any way. + + + Are you sure you want to empty the recycle bin? + + + Are you sure you want to leave the current page? Downloading of files will be interrupted. + + + Are you sure you want to delete the document? + + + Are you sure you want to delete the folder? + + + Are you sure you want to delete these elements? + + + You are about to move elements from the {1} directory. In such a case, they will be deleted from your {1} account and will no longer be accessible to other users, in case these elements have previously been shared. {0}Are you sure you want to move the elements? + + + Copy link + + + Common Documents + + + Custom format + + + Groups + + + Archiving Data + + + Change data + + + Finalizing version + + + Copy + + + Creation + + + Sign via DocuSign + + + Loading versions... + + + Marking as Read + + + Move + + + Deleting... + + + Rename + + + Updating version + + + Preparing file for encryption + + + Encrypting file + + + Saving encrypted file + + + Documents + + + Sign document ({0}) + + + Select at least one folder/document to download + + + Enter a comment + + + You cannot upload a folder or an empty file + + + Conversion Error + + + All fields are required to be filled in + + + The maximum file size is exceeded + + + You need to login to be able to add + + + Sorry, this file format isn't supported + + + The password you entered is incorrect. Please try again. + + + Please wait for the previous download to complete or cancel it + + + You don't have enough permission to perform the operation + + + You can not performed this action in this folder + + + The title cannot contain any of the following characters: {0} + + + The portal storage has run out of free space. You cannot upload files larger than {0}. + + + The file with the name {0} already exists in the folder {1}. + + + Converted + + + Converting + + + {0} documents with the same name already exist in the folder {1}. + + + Uploaded + + + Uploading + + + Box directory + + + Dropbox directory + + + DocuSign account + + + Google directory + + + SharePoint directory + + + OneDrive directory + + + WebDAV Directory + + + Yandex Directory + + + Shorten link + + + Connect your Google Drive, Dropbox, Box and OneDrive accounts and have all documents in one place. + + + Use this button to create new documents, spreadsheets and presentations directly on your portal. + + + Click the file name to download and save the document to your hard disk drive. + + + Share your files with other portal users or make them available to people outside your portal setting the appropriate access rights. + + + Use this button to upload files: documents, spreadsheets, presentations, etc. stored on your hard disk drive. + + + Click the file name to view the document, or use the pencil icon to open it for editing. For faster processing your file will be converted to the Office Open XML format. + + + Connect Your Account + + + Create New Files + + + Download Your Files + + + Share Your Files + + + Upload Your Files + + + View & Edit Your Files + + + Import from Google Drive + + + '{0}' changed + + + Copy {1}{0}{2} + + + {0} copied elements + + + '{0}' copied + + + {0} elements + + + New file '{0}' is created + + + The new '{0}' file is created in '{1}' + + + New folder '{0}' is created + + + You cannot copy the folder to its subfolder + + + You cannot move the folder to its subfolder + + + You do not have access rights to move some file(s) + + + Move {1}{0}{2} + + + {0} moved elements + + + '{0}' moved + + + The document '{0}' is deleted + + + The folder '{0}' is deleted + + + It was successfully deleted {0} from {1} + + + Third Party '{0}' is deleted + + + The document '{0}' is renamed to '{1}' + + + The folder '{0}' is renamed to '{1}' + + + Now '{0}' is the owner of '{1}' + + + Now '{0}' is the owner of {1} items + + + '{0}' added to '{1}' + + + {0} documents are successfully uploaded + + + Loading... + + + Please wait... + + + My Documents + + + Original format + + + Other + + + Overall Progress + + + Presentations + + + Failed to upload file {0} to display + + + Project Documents + + + revisions: {0} + + + You have been granted access to the {0} document. Click the link below to open the document right now: {1} + + + You have been granted access to the {0} document + + + elements: {0} + + + Add users or groups and give them permissions{0} to read, review or edit the documents. + + + Sharing Settings {0} + + + Spreadsheets + + + Archiving Data + + + Copying + + + Deleting + + + Marking as Read + + + {0} operations + + + Move + + + Change {0} connection settings + + + Cancel + + + Create + + + It is currently being edited + + + It is currently being edited by: {0} + + + Locked + + + Locked by: {0} + + + More Features + + + New Presentation + + + New Spreadsheet + + + New Document + + + New Folder + + + Common Settings + + + Help + + + Connect accounts + + + Box + + + Dropbox + + + DocuSign + + + google.com + + + sharepoint.com + + + onedrive.live.com + + + WebDAV + + + yandex.com + + + Unknown error... + + + Upload complete + + + Uploading ... + + + Uploading {0} files ({1}% done) + + + {0} of {1} used + + + Users + + \ No newline at end of file diff --git a/products/ASC.Files/Server/Resources/FilesUCResource.Designer.cs b/products/ASC.Files/Server/Resources/FilesUCResource.Designer.cs new file mode 100644 index 0000000000..49c88daa77 --- /dev/null +++ b/products/ASC.Files/Server/Resources/FilesUCResource.Designer.cs @@ -0,0 +1,2466 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ASC.Files.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class FilesUCResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal FilesUCResource() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ASC.Files.Resources.FilesUCResource", typeof(FilesUCResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Comment. + /// + public static string AceStatusEnum_Comment { + get { + return ResourceManager.GetString("AceStatusEnum_Comment", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Form Filling. + /// + public static string AceStatusEnum_FillForms { + get { + return ResourceManager.GetString("AceStatusEnum_FillForms", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Read Only. + /// + public static string AceStatusEnum_Read { + get { + return ResourceManager.GetString("AceStatusEnum_Read", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Full Access. + /// + public static string AceStatusEnum_ReadWrite { + get { + return ResourceManager.GetString("AceStatusEnum_ReadWrite", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Deny Access. + /// + public static string AceStatusEnum_Restrict { + get { + return ResourceManager.GetString("AceStatusEnum_Restrict", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Review. + /// + public static string AceStatusEnum_Review { + get { + return ResourceManager.GetString("AceStatusEnum_Review", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add account. + /// + public static string AddAccount { + get { + return ResourceManager.GetString("AddAccount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add message. + /// + public static string AddShareMessage { + get { + return ResourceManager.GetString("AddShareMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Mobile Documents {0} available on the {1}App Store{2}. + /// + public static string AppStore { + get { + return ResourceManager.GetString("AppStore", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Author. + /// + public static string Author { + get { + return ResourceManager.GetString("Author", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Access. + /// + public static string ButtonAccess { + get { + return ResourceManager.GetString("ButtonAccess", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add Box account. + /// + public static string ButtonAddBoxNet { + get { + return ResourceManager.GetString("ButtonAddBoxNet", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add DropBox account. + /// + public static string ButtonAddDropBox { + get { + return ResourceManager.GetString("ButtonAddDropBox", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add Google account. + /// + public static string ButtonAddGoogle { + get { + return ResourceManager.GetString("ButtonAddGoogle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add Nextcloud account. + /// + public static string ButtonAddNextcloud { + get { + return ResourceManager.GetString("ButtonAddNextcloud", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add OneDrive account. + /// + public static string ButtonAddSkyDrive { + get { + return ResourceManager.GetString("ButtonAddSkyDrive", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Other account. + /// + public static string ButtonAddWebDav { + get { + return ResourceManager.GetString("ButtonAddWebDav", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cancel. + /// + public static string ButtonCancel { + get { + return ResourceManager.GetString("ButtonCancel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cancel all. + /// + public static string ButtonCancelAll { + get { + return ResourceManager.GetString("ButtonCancelAll", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Change owner. + /// + public static string ButtonChangeOwner { + get { + return ResourceManager.GetString("ButtonChangeOwner", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Change the third party info. + /// + public static string ButtonChangeThirdParty { + get { + return ResourceManager.GetString("ButtonChangeThirdParty", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Clear Filter. + /// + public static string ButtonClearFilter { + get { + return ResourceManager.GetString("ButtonClearFilter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Close. + /// + public static string ButtonClose { + get { + return ResourceManager.GetString("ButtonClose", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create a copy. + /// + public static string ButtonCopy { + get { + return ResourceManager.GetString("ButtonCopy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Copy. + /// + public static string ButtonCopyTo { + get { + return ResourceManager.GetString("ButtonCopyTo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Copy. + /// + public static string ButtonCopyToClipboard { + get { + return ResourceManager.GetString("ButtonCopyToClipboard", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create.... + /// + public static string ButtonCreate { + get { + return ResourceManager.GetString("ButtonCreate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create Document. + /// + public static string ButtonCreateDocument2 { + get { + return ResourceManager.GetString("ButtonCreateDocument2", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Folder. + /// + public static string ButtonCreateFolder { + get { + return ResourceManager.GetString("ButtonCreateFolder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Presentation. + /// + public static string ButtonCreatePresentation { + get { + return ResourceManager.GetString("ButtonCreatePresentation", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create Presentation. + /// + public static string ButtonCreatePresentation2 { + get { + return ResourceManager.GetString("ButtonCreatePresentation2", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Spreadsheet. + /// + public static string ButtonCreateSpreadsheet { + get { + return ResourceManager.GetString("ButtonCreateSpreadsheet", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create Spreadsheet. + /// + public static string ButtonCreateSpreadsheet2 { + get { + return ResourceManager.GetString("ButtonCreateSpreadsheet2", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Document. + /// + public static string ButtonCreateText { + get { + return ResourceManager.GetString("ButtonCreateText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete. + /// + public static string ButtonDelete { + get { + return ResourceManager.GetString("ButtonDelete", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete third party. + /// + public static string ButtonDeleteThirdParty { + get { + return ResourceManager.GetString("ButtonDeleteThirdParty", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Download. + /// + public static string ButtonDownload { + get { + return ResourceManager.GetString("ButtonDownload", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create file copy. + /// + public static string ButtonDuplicate { + get { + return ResourceManager.GetString("ButtonDuplicate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Edit. + /// + public static string ButtonEdit { + get { + return ResourceManager.GetString("ButtonEdit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Embedding document. + /// + public static string ButtonEmbedd { + get { + return ResourceManager.GetString("ButtonEmbedd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Empty Recycle Bin. + /// + public static string ButtonEmptyTrash { + get { + return ResourceManager.GetString("ButtonEmptyTrash", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Archives. + /// + public static string ButtonFilterArchive { + get { + return ResourceManager.GetString("ButtonFilterArchive", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Documents. + /// + public static string ButtonFilterDocument { + get { + return ResourceManager.GetString("ButtonFilterDocument", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to All files. + /// + public static string ButtonFilterFiles { + get { + return ResourceManager.GetString("ButtonFilterFiles", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Folders. + /// + public static string ButtonFilterFolder { + get { + return ResourceManager.GetString("ButtonFilterFolder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Images. + /// + public static string ButtonFilterImage { + get { + return ResourceManager.GetString("ButtonFilterImage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Media. + /// + public static string ButtonFilterMedia { + get { + return ResourceManager.GetString("ButtonFilterMedia", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Presentations. + /// + public static string ButtonFilterPresentation { + get { + return ResourceManager.GetString("ButtonFilterPresentation", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Spreadsheets. + /// + public static string ButtonFilterSpreadsheet { + get { + return ResourceManager.GetString("ButtonFilterSpreadsheet", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Actual Size. + /// + public static string ButtonFullScale { + get { + return ResourceManager.GetString("ButtonFullScale", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Go to '{0}'. + /// + public static string ButtonGotoFolder { + get { + return ResourceManager.GetString("ButtonGotoFolder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Go to My Documents. + /// + public static string ButtonGotoMy { + get { + return ResourceManager.GetString("ButtonGotoMy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Learn more.... + /// + public static string ButtonLearnMore { + get { + return ResourceManager.GetString("ButtonLearnMore", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Block. + /// + public static string ButtonLock { + get { + return ResourceManager.GetString("ButtonLock", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Move or Copy. + /// + public static string ButtonMoveCopy { + get { + return ResourceManager.GetString("ButtonMoveCopy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Move to. + /// + public static string ButtonMoveTo { + get { + return ResourceManager.GetString("ButtonMoveTo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Next Image. + /// + public static string ButtonNextImg { + get { + return ResourceManager.GetString("ButtonNextImg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to OK. + /// + public static string ButtonOk { + get { + return ResourceManager.GetString("ButtonOk", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open. + /// + public static string ButtonOpenFile { + get { + return ResourceManager.GetString("ButtonOpenFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to More Actions. + /// + public static string ButtonOtherAction { + get { + return ResourceManager.GetString("ButtonOtherAction", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Overwrite with version update. + /// + public static string ButtonOverwrite { + get { + return ResourceManager.GetString("ButtonOverwrite", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Previous Image. + /// + public static string ButtonPrevImg { + get { + return ResourceManager.GetString("ButtonPrevImg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Rename. + /// + public static string ButtonRename { + get { + return ResourceManager.GetString("ButtonRename", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Restore. + /// + public static string ButtonRestore { + get { + return ResourceManager.GetString("ButtonRestore", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Rotate Counterclockwise. + /// + public static string ButtonRotateLeft { + get { + return ResourceManager.GetString("ButtonRotateLeft", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Rotate Clockwise. + /// + public static string ButtonRotateRight { + get { + return ResourceManager.GetString("ButtonRotateRight", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save. + /// + public static string ButtonSave { + get { + return ResourceManager.GetString("ButtonSave", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to All. + /// + public static string ButtonSelectAll { + get { + return ResourceManager.GetString("ButtonSelectAll", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Send with DocuSign. + /// + public static string ButtonSendDocuSign { + get { + return ResourceManager.GetString("ButtonSendDocuSign", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Send. + /// + public static string ButtonSendDocuSignDialog { + get { + return ResourceManager.GetString("ButtonSendDocuSignDialog", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Send by email. + /// + public static string ButtonSendInEmail { + get { + return ResourceManager.GetString("ButtonSendInEmail", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sharing Settings. + /// + public static string ButtonShareAccess { + get { + return ResourceManager.GetString("ButtonShareAccess", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sharing Settings. + /// + public static string ButtonShareAccessShort { + get { + return ResourceManager.GetString("ButtonShareAccessShort", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show more. + /// + public static string ButtonShowMore { + get { + return ResourceManager.GetString("ButtonShowMore", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show version history. + /// + public static string ButtonShowVersions { + get { + return ResourceManager.GetString("ButtonShowVersions", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Skip. + /// + public static string ButtonSkip { + get { + return ResourceManager.GetString("ButtonSkip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Back to parent folder. + /// + public static string ButtonToParentFolder { + get { + return ResourceManager.GetString("ButtonToParentFolder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unblock. + /// + public static string ButtonUnlock { + get { + return ResourceManager.GetString("ButtonUnlock", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to On top. + /// + public static string ButtonUp { + get { + return ResourceManager.GetString("ButtonUp", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Upload. + /// + public static string ButtonUpload { + get { + return ResourceManager.GetString("ButtonUpload", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cancel uploading and close. + /// + public static string ButtonUploadCancelAndClose { + get { + return ResourceManager.GetString("ButtonUploadCancelAndClose", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Upload files. + /// + public static string ButtonUploadFiles { + get { + return ResourceManager.GetString("ButtonUploadFiles", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Upload folder. + /// + public static string ButtonUploadFolder { + get { + return ResourceManager.GetString("ButtonUploadFolder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Maximize dialog. + /// + public static string ButtonUploadMaximize { + get { + return ResourceManager.GetString("ButtonUploadMaximize", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Minimize dialog. + /// + public static string ButtonUploadMinimize { + get { + return ResourceManager.GetString("ButtonUploadMinimize", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Version history. + /// + public static string ButtonVersion { + get { + return ResourceManager.GetString("ButtonVersion", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Finalize version. + /// + public static string ButtonVersionComplete { + get { + return ResourceManager.GetString("ButtonVersionComplete", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Facebook. + /// + public static string ButtonViaFacebook { + get { + return ResourceManager.GetString("ButtonViaFacebook", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Twitter. + /// + public static string ButtonViaTwitter { + get { + return ResourceManager.GetString("ButtonViaTwitter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Zoom In. + /// + public static string ButtonZoomIn { + get { + return ResourceManager.GetString("ButtonZoomIn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Zoom Out. + /// + public static string ButtonZoomOut { + get { + return ResourceManager.GetString("ButtonZoomOut", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Canceled. + /// + public static string Canceled { + get { + return ResourceManager.GetString("Canceled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The file is password protected. Enter the password for conversion.. + /// + public static string CaptionConvertPassword { + get { + return ResourceManager.GetString("CaptionConvertPassword", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Convert and open document. + /// + public static string CaptionCopyConvertOpen { + get { + return ResourceManager.GetString("CaptionCopyConvertOpen", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Document saving. + /// + public static string CaptionCopyConvertOpen2 { + get { + return ResourceManager.GetString("CaptionCopyConvertOpen2", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Document uploading. + /// + public static string CaptionCopyConvertSave { + get { + return ResourceManager.GetString("CaptionCopyConvertSave", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Once the changes are applied, the current folder owner gets access rights to the access settings of the group he/she belongs to or according to the default folder permissions.. + /// + public static string ChangeOwnerDescription { + get { + return ResourceManager.GetString("ChangeOwnerDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Change owner. + /// + public static string ChangeOwnerHeader { + get { + return ResourceManager.GetString("ChangeOwnerHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Choose format for each file to be downloaded. + /// + public static string ChooseFormatToDownload { + get { + return ResourceManager.GetString("ChooseFormatToDownload", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Common Settings. + /// + public static string CommonSettings { + get { + return ResourceManager.GetString("CommonSettings", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Confirmation dialog for deletion to recycle bin. + /// + public static string ConfirmDelete { + get { + return ResourceManager.GetString("ConfirmDelete", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Do not show this message again. + /// + public static string ConfirmDontShow { + get { + return ResourceManager.GetString("ConfirmDontShow", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to There will be two different files in the folder.. + /// + public static string ConfirmDuplicateDescr { + get { + return ResourceManager.GetString("ConfirmDuplicateDescr", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Overwrite confirmation. + /// + public static string ConfirmOverwrite { + get { + return ResourceManager.GetString("ConfirmOverwrite", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The file will be added to the file with the same name as a version.. + /// + public static string ConfirmOverwriteDescr { + get { + return ResourceManager.GetString("ConfirmOverwriteDescr", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Confirmation. + /// + public static string ConfirmRemove { + get { + return ResourceManager.GetString("ConfirmRemove", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Note: This action can not be undone. . + /// + public static string ConfirmRemoveDescription { + get { + return ResourceManager.GetString("ConfirmRemoveDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Note: removal from your account can not be undone.. + /// + public static string ConfirmRemoveSharpBoxDescription { + get { + return ResourceManager.GetString("ConfirmRemoveSharpBoxDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No file will be copied. The orginal file be in the destination folder.. + /// + public static string ConfirmSkipDescr { + get { + return ResourceManager.GetString("ConfirmSkipDescr", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save the file copy in the original format as well. + /// + public static string ConfirmStoreOriginalOpenCbxLabelText { + get { + return ResourceManager.GetString("ConfirmStoreOriginalOpenCbxLabelText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to To edit the file its copy in the Office Open XML format (docx, xlsx or pptx) will be created on the portal. The source file will not be changed.. + /// + public static string ConfirmStoreOriginalOpenTitle { + get { + return ResourceManager.GetString("ConfirmStoreOriginalOpenTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The file copy will be created in the {0}My documents{1} folder. + /// + public static string ConfirmStoreOriginalOpenToMyText { + get { + return ResourceManager.GetString("ConfirmStoreOriginalOpenToMyText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to All the documents you upload will be converted into the corresponding Office Open XML format (docx, xlsx or pptx) for faster editing.. + /// + public static string ConfirmStoreOriginalSaveTitle { + get { + return ResourceManager.GetString("ConfirmStoreOriginalSaveTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Upload the documents in original format as well. + /// + public static string ConfirmStoreOriginalUploadCbxLabelText { + get { + return ResourceManager.GetString("ConfirmStoreOriginalUploadCbxLabelText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to All the documents you upload will be converted to the Office Open XML format for faster editing.. + /// + public static string ConfirmStoreOriginalUploadTitle { + get { + return ResourceManager.GetString("ConfirmStoreOriginalUploadTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Move confirmation. + /// + public static string ConfirmThirdPartyMove { + get { + return ResourceManager.GetString("ConfirmThirdPartyMove", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Are you sure you want to remove these elements from list?. + /// + public static string ConfirmUnsubscribe { + get { + return ResourceManager.GetString("ConfirmUnsubscribe", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Update the file version for the existing file with the same name. {0}Otherwise, a copy of the file will be created.{1}. + /// + public static string ConfirmUpdateIfExist { + get { + return ResourceManager.GetString("ConfirmUpdateIfExist", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Connection url. + /// + public static string ConnectionUrl { + get { + return ResourceManager.GetString("ConnectionUrl", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Convert. + /// + public static string Convert { + get { + return ResourceManager.GetString("Convert", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Download as. + /// + public static string ConvertAndDownload { + get { + return ResourceManager.GetString("ConvertAndDownload", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The conversion was successful.. + /// + public static string CopyConvertEnd { + get { + return ResourceManager.GetString("CopyConvertEnd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The file has been created in the '{0}' folder. + /// + public static string CopyConvertEndTo { + get { + return ResourceManager.GetString("CopyConvertEndTo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Conversion Error. + /// + public static string CopyConvertError { + get { + return ResourceManager.GetString("CopyConvertError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Is converting .... + /// + public static string CopyConvertExec { + get { + return ResourceManager.GetString("CopyConvertExec", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Common Documents. + /// + public static string CorporateFiles { + get { + return ResourceManager.GetString("CorporateFiles", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Creation date. + /// + public static string CreatingDate { + get { + return ResourceManager.GetString("CreatingDate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Department. + /// + public static string Department { + get { + return ResourceManager.GetString("Department", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Departments. + /// + public static string Departments { + get { + return ResourceManager.GetString("Departments", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You have just created your ONLYOFFICE portal.{0} Upload your offline files to the portal to collaborate on them without leaving the app.{0}{0} Open your portal in browser to find more features, including {1}Projects{2}, {1}CRM{2}, {1}Calendar{2}, {1}Mail{2} and more.. + /// + public static string DesktopDescr { + get { + return ResourceManager.GetString("DesktopDescr", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open in browser. + /// + public static string DesktopOpenBrowser { + get { + return ResourceManager.GetString("DesktopOpenBrowser", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to For any purchase questions, email us at {0}.. + /// + public static string DesktopSales { + get { + return ResourceManager.GetString("DesktopSales", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to If you have some technical problems, contact us at {0}.. + /// + public static string DesktopSupport { + get { + return ResourceManager.GetString("DesktopSupport", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Welcome to your portal!. + /// + public static string DesktopWelcome { + get { + return ResourceManager.GetString("DesktopWelcome", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Congratulations. + /// + public static string DesktopWelcomeHeader { + get { + return ResourceManager.GetString("DesktopWelcomeHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Document Name. + /// + public static string DocumentName { + get { + return ResourceManager.GetString("DocumentName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Documents. + /// + public static string Documents { + get { + return ResourceManager.GetString("Documents", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save to folder:. + /// + public static string DocuSignFolder { + get { + return ResourceManager.GetString("DocuSignFolder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Message. + /// + public static string DocuSignMessage { + get { + return ResourceManager.GetString("DocuSignMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add message. + /// + public static string DocuSignMessageAdd { + get { + return ResourceManager.GetString("DocuSignMessageAdd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Remove message. + /// + public static string DocuSignMessageRemove { + get { + return ResourceManager.GetString("DocuSignMessageRemove", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Document name. + /// + public static string DocuSignName { + get { + return ResourceManager.GetString("DocuSignName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Add {!user}. + /// + public static string DocuSignRecipientAdd { + get { + return ResourceManager.GetString("DocuSignRecipientAdd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Signer recipient list. + /// + public static string DocuSignRecipients { + get { + return ResourceManager.GetString("DocuSignRecipients", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Download as. + /// + public static string DownloadAs { + get { + return ResourceManager.GetString("DownloadAs", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Download. + /// + public static string DownloadFile { + get { + return ResourceManager.GetString("DownloadFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Download. + /// + public static string DownloadFolder { + get { + return ResourceManager.GetString("DownloadFolder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Size. + /// + public static string EmbeddedSize { + get { + return ResourceManager.GetString("EmbeddedSize", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Auto. + /// + public static string EmbedSizeAuto { + get { + return ResourceManager.GetString("EmbedSizeAuto", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Height. + /// + public static string EmbedSizeHeight { + get { + return ResourceManager.GetString("EmbedSizeHeight", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Width. + /// + public static string EmbedSizeWidth { + get { + return ResourceManager.GetString("EmbedSizeWidth", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The 'Common Documents' section shows all the documents shared by portal administrator for common access. Only portal administrator can create folders in this section, but with the access granted the portal users can also upload their files here.. + /// + public static string EmptyScreenDescrCorporate { + get { + return ResourceManager.GetString("EmptyScreenDescrCorporate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Drag-and-drop the files from your computer here to upload them to your portal even more easily.. + /// + public static string EmptyScreenDescrDragDrop { + get { + return ResourceManager.GetString("EmptyScreenDescrDragDrop", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The 'Shared with Me' section is used to show the files which your friends or colleagues gave you access to. In case you haven't seen the latest changes in the documents they are marked 'new'. You can remove the files from the list clicking the appropriate button.. + /// + public static string EmptyScreenDescrForme { + get { + return ResourceManager.GetString("EmptyScreenDescrForme", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The documents and image files you {0}create{1} or {2}upload{3} to the portal are kept here in 'My Documents' section. You can {4}open{5} and {6}edit{7} them using the ONLYOFFICE™ portal editor, share them with friends or colleagues, organize into folders.. + /// + public static string EmptyScreenDescrMy { + get { + return ResourceManager.GetString("EmptyScreenDescrMy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The documents and image files you {2}upload{3} to the portal are kept here in 'My Documents' section. You can share them with friends or colleagues, organize into folders.. + /// + public static string EmptyScreenDescrMyPoor { + get { + return ResourceManager.GetString("EmptyScreenDescrMyPoor", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The documents and image files you create or upload for your project are kept in this section. You can open and edit them using the ONLYOFFICE™ portal editor, organize into folders.. + /// + public static string EmptyScreenDescrProject { + get { + return ResourceManager.GetString("EmptyScreenDescrProject", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The documents and image files uploaded for the project are kept in this section. You can view them using the ONLYOFFICE portal editor.. + /// + public static string EmptyScreenDescrProjectVisitor { + get { + return ResourceManager.GetString("EmptyScreenDescrProjectVisitor", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The 'Recycle Bin' section is where all the deleted files are moved. You can either restore them in case they are deleted by mistake or delete them permanently. Please note, that when you delete the files from the 'Recycle Bin' they cannot be restored any longer.. + /// + public static string EmptyScreenDescrTrash { + get { + return ResourceManager.GetString("EmptyScreenDescrTrash", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No files to be displayed for this filter here. + /// + public static string EmptyScreenFilter { + get { + return ResourceManager.GetString("EmptyScreenFilter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No files or folders matching your filter can be displayed in this section. Please select other filter options or clear filter to view all the files in this section. You can also look for the file you need in other sections.. + /// + public static string EmptyScreenFilterDescr { + get { + return ResourceManager.GetString("EmptyScreenFilterDescr", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No files in this folder. + /// + public static string EmptyScreenFolderHeader { + get { + return ResourceManager.GetString("EmptyScreenFolderHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No files to be displayed in this section. + /// + public static string EmptyScreenHeader { + get { + return ResourceManager.GetString("EmptyScreenHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No connected accounts. + /// + public static string EmptyScreenThirdPartyDscr { + get { + return ResourceManager.GetString("EmptyScreenThirdPartyDscr", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error. + /// + public static string Error { + get { + return ResourceManager.GetString("Error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The uploaded file was not found. + /// + public static string ErrorEmptyUploadFileSelected { + get { + return ResourceManager.GetString("ErrorEmptyUploadFileSelected", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to All Users. + /// + public static string Everyone { + get { + return ResourceManager.GetString("Everyone", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select the file in {0} format. + /// + public static string FileChoiceFormat { + get { + return ResourceManager.GetString("FileChoiceFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select files of type: {0}. + /// + public static string FileChoiceType { + get { + return ResourceManager.GetString("FileChoiceType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Files will be compressed into the {0}.zip file{1}. + /// + public static string FilesWillBeCompressed { + get { + return ResourceManager.GetString("FilesWillBeCompressed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Filter. + /// + public static string Filter { + get { + return ResourceManager.GetString("Filter", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Folders. + /// + public static string Folders { + get { + return ResourceManager.GetString("Folders", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Start Now. + /// + public static string GetGreeAccountBtn { + get { + return ResourceManager.GetString("GetGreeAccountBtn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Shorten. + /// + public static string GetShorten { + get { + return ResourceManager.GetString("GetShorten", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Group. + /// + public static string Group { + get { + return ResourceManager.GetString("Group", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Import from Google Drive. + /// + public static string ImportFromGoogle { + get { + return ResourceManager.GetString("ImportFromGoogle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to new. + /// + public static string IsNew { + get { + return ResourceManager.GetString("IsNew", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Link. + /// + public static string Link { + get { + return ResourceManager.GetString("Link", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to via email. + /// + public static string LinkViaMail { + get { + return ResourceManager.GetString("LinkViaMail", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Login. + /// + public static string Login { + get { + return ResourceManager.GetString("Login", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to If you choose to convert the file to the format different from the original, some data might be lost.. + /// + public static string LostDataConvert { + get { + return ResourceManager.GetString("LostDataConvert", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select all. + /// + public static string MainHeaderSelectAll { + get { + return ResourceManager.GetString("MainHeaderSelectAll", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The maximum file size is {0}. + /// + public static string MaxFileSize { + get { + return ResourceManager.GetString("MaxFileSize", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Have a team {0}to collaborate with? {0}{1}Learn more{2}. + /// + public static string MoreFeatures { + get { + return ResourceManager.GetString("MoreFeatures", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Document management. + /// + public static string MoreFeaturesItem1 { + get { + return ResourceManager.GetString("MoreFeaturesItem1", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to CRM and Invoicing. + /// + public static string MoreFeaturesItem2 { + get { + return ResourceManager.GetString("MoreFeaturesItem2", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Task management and Gantt Chart. + /// + public static string MoreFeaturesItem3 { + get { + return ResourceManager.GetString("MoreFeaturesItem3", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Email Aggregator. + /// + public static string MoreFeaturesItem4 { + get { + return ResourceManager.GetString("MoreFeaturesItem4", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Have a project and a team to collaborate with?{0}We've prepared something for you.{1}. + /// + public static string MoreFeaturesText { + get { + return ResourceManager.GetString("MoreFeaturesText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invite your colleagues or subcontractors and benefit from additional features:. + /// + public static string MoreFeaturesTextList { + get { + return ResourceManager.GetString("MoreFeaturesTextList", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Welcome ONLYOFFICE™ for teams!. + /// + public static string MoreFeaturesTitle { + get { + return ResourceManager.GetString("MoreFeaturesTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to My Documents. + /// + public static string MyFiles { + get { + return ResourceManager.GetString("MyFiles", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New File. + /// + public static string NewFile { + get { + return ResourceManager.GetString("NewFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Preview. + /// + public static string OpenFile { + get { + return ResourceManager.GetString("OpenFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open. + /// + public static string OpenFolder { + get { + return ResourceManager.GetString("OpenFolder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open location. + /// + public static string OpenParent { + get { + return ResourceManager.GetString("OpenParent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Original format. + /// + public static string OriginalFormat { + get { + return ResourceManager.GetString("OriginalFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Hide documents list. + /// + public static string OverwriteListHide { + get { + return ResourceManager.GetString("OverwriteListHide", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show documents list. + /// + public static string OverwriteListShow { + get { + return ResourceManager.GetString("OverwriteListShow", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Password. + /// + public static string Password { + get { + return ResourceManager.GetString("Password", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Warning! Possibly this file is a renamed executable file and it cannot be previewed.. + /// + public static string PreviewError { + get { + return ResourceManager.GetString("PreviewError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This is a web application for internal use designed to store and share files and work on them in team.. + /// + public static string ProductDescription { + get { + return ResourceManager.GetString("ProductDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Project Documents. + /// + public static string ProjectFiles { + get { + return ResourceManager.GetString("ProjectFiles", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to delete. + /// + public static string Remove { + get { + return ResourceManager.GetString("Remove", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Mark as Read. + /// + public static string RemoveIsNew { + get { + return ResourceManager.GetString("RemoveIsNew", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Mark all as read. + /// + public static string RemoveIsNewAll { + get { + return ResourceManager.GetString("RemoveIsNewAll", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Mark as Read. + /// + public static string RemoveIsNewShort { + get { + return ResourceManager.GetString("RemoveIsNewShort", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Remove message. + /// + public static string RemoveShareMessage { + get { + return ResourceManager.GetString("RemoveShareMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to File name. + /// + public static string SaveAsFileTitle { + get { + return ResourceManager.GetString("SaveAsFileTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open saved document in new tab. + /// + public static string SaveAsOpenTab { + get { + return ResourceManager.GetString("SaveAsOpenTab", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Search. + /// + public static string Search { + get { + return ResourceManager.GetString("Search", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Search. + /// + public static string SearchText { + get { + return ResourceManager.GetString("SearchText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select folder. + /// + public static string SelectFolder { + get { + return ResourceManager.GetString("SelectFolder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Please select the action:. + /// + public static string SelectOverwrite { + get { + return ResourceManager.GetString("SelectOverwrite", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Send notification. + /// + public static string SendShareNotify { + get { + return ResourceManager.GetString("SendShareNotify", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Keep intermediate versions when editing. + /// + public static string SettingForcesave { + get { + return ResourceManager.GetString("SettingForcesave", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Keep all saved intermediate versions. + /// + public static string SettingStoreForcesave { + get { + return ResourceManager.GetString("SettingStoreForcesave", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Common Settings. + /// + public static string SettingUpdateIfExist { + get { + return ResourceManager.GetString("SettingUpdateIfExist", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Storing file versions. + /// + public static string SettingVersions { + get { + return ResourceManager.GetString("SettingVersions", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Share. + /// + public static string Share { + get { + return ResourceManager.GetString("Share", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Shared with Me. + /// + public static string SharedForMe { + get { + return ResourceManager.GetString("SharedForMe", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Set up the access right for the link. + /// + public static string SharingLinkCaption { + get { + return ResourceManager.GetString("SharingLinkCaption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Provides access to the link. + /// + public static string SharingLinkDescr { + get { + return ResourceManager.GetString("SharingLinkDescr", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show more. + /// + public static string ShowMore { + get { + return ResourceManager.GetString("ShowMore", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show this window minimized. + /// + public static string ShowThisWindowMinimized { + get { + return ResourceManager.GetString("ShowThisWindowMinimized", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Settings. + /// + public static string SideCaptionSettings { + get { + return ResourceManager.GetString("SideCaptionSettings", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Switch to compact view. + /// + public static string SwitchViewToCompact { + get { + return ResourceManager.GetString("SwitchViewToCompact", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Switch to common view. + /// + public static string SwitchViewToNormal { + get { + return ResourceManager.GetString("SwitchViewToNormal", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Third-party accounts. + /// + public static string ThirdPartyAccounts { + get { + return ResourceManager.GetString("ThirdPartyAccounts", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Box. + /// + public static string ThirdPartyBoxNet { + get { + return ResourceManager.GetString("ThirdPartyBoxNet", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Connect account. + /// + public static string ThirdPartyConnectAccount { + get { + return ResourceManager.GetString("ThirdPartyConnectAccount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Connect Accounts. + /// + public static string ThirdPartyConnectAccounts { + get { + return ResourceManager.GetString("ThirdPartyConnectAccounts", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You can connect the following accounts to the ONLYOFFICE™ Documents. They will be displayed in 'My Documents' folder and you will be able to edit and save them right on the portal all in one place.. + /// + public static string ThirdPartyConnectAccountsDescription { + get { + return ResourceManager.GetString("ThirdPartyConnectAccountsDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to For successful connection enter the necessary data at {0}this page{1}.. + /// + public static string ThirdPartyConnectAccountsKeys { + get { + return ResourceManager.GetString("ThirdPartyConnectAccountsKeys", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Connecting account. + /// + public static string ThirdPartyConnectingAccount { + get { + return ResourceManager.GetString("ThirdPartyConnectingAccount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Specify for change. + /// + public static string ThirdPartyCorrect { + get { + return ResourceManager.GetString("ThirdPartyCorrect", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Delete Third Party. + /// + public static string ThirdPartyDeleteCaption { + get { + return ResourceManager.GetString("ThirdPartyDeleteCaption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to DocuSign. + /// + public static string ThirdPartyDocuSign { + get { + return ResourceManager.GetString("ThirdPartyDocuSign", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to DropBox. + /// + public static string ThirdPartyDropBox { + get { + return ResourceManager.GetString("ThirdPartyDropBox", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enable third party settings for users. + /// + public static string ThirdPartyEnableSettings { + get { + return ResourceManager.GetString("ThirdPartyEnableSettings", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Folder title. + /// + public static string ThirdPartyFolderTitle { + get { + return ResourceManager.GetString("ThirdPartyFolderTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Google Drive. + /// + public static string ThirdPartyGoogleDrive { + get { + return ResourceManager.GetString("ThirdPartyGoogleDrive", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Nextcloud. + /// + public static string ThirdPartyNextcloud { + get { + return ResourceManager.GetString("ThirdPartyNextcloud", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to ownCloud. + /// + public static string ThirdPartyOwnCloud { + get { + return ResourceManager.GetString("ThirdPartyOwnCloud", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Reconnect. + /// + public static string ThirdPartyReconnect { + get { + return ResourceManager.GetString("ThirdPartyReconnect", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Account. + /// + public static string ThirdPartyReconnectTitle { + get { + return ResourceManager.GetString("ThirdPartyReconnectTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Make shared and put into Common Documents. + /// + public static string ThirdPartySetCorporate { + get { + return ResourceManager.GetString("ThirdPartySetCorporate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to SharePoint. + /// + public static string ThirdPartySharePoint { + get { + return ResourceManager.GetString("ThirdPartySharePoint", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to SharePoint / OneDrive for Business. + /// + public static string ThirdPartySharePointDescr { + get { + return ResourceManager.GetString("ThirdPartySharePointDescr", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to OneDrive. + /// + public static string ThirdPartySkyDrive { + get { + return ResourceManager.GetString("ThirdPartySkyDrive", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to WebDAV. + /// + public static string ThirdPartyWebDav { + get { + return ResourceManager.GetString("ThirdPartyWebDav", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Yandex. + /// + public static string ThirdPartyYandex { + get { + return ResourceManager.GetString("ThirdPartyYandex", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Search in. + /// + public static string TitleSearchIn { + get { + return ResourceManager.GetString("TitleSearchIn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to and {0}all subfolders{1}. + /// + public static string TitleSearchSubfolder { + get { + return ResourceManager.GetString("TitleSearchSubfolder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Click for searchibg without subfolders. + /// + public static string TitleSearchWithoutSubfolder { + get { + return ResourceManager.GetString("TitleSearchWithoutSubfolder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Select. + /// + public static string TitleSelectFile { + get { + return ResourceManager.GetString("TitleSelectFile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You can create documents ({0}), spreadsheets ({1}), presentations ({2}) and download them to most popular formats.. + /// + public static string TooltipCreate { + get { + return ResourceManager.GetString("TooltipCreate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You can edit the following files: {0}.. + /// + public static string TooltipEdit { + get { + return ResourceManager.GetString("TooltipEdit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You can open the files with the following extentions: {0}.. + /// + public static string TooltipOpen { + get { + return ResourceManager.GetString("TooltipOpen", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You can upload any files you want to Documents.. + /// + public static string TooltipUpload { + get { + return ResourceManager.GetString("TooltipUpload", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Recycle Bin. + /// + public static string Trash { + get { + return ResourceManager.GetString("Trash", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Types. + /// + public static string Types { + get { + return ResourceManager.GetString("Types", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Remove from List. + /// + public static string Unsubscribe { + get { + return ResourceManager.GetString("Unsubscribe", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Overwrite existing file with the same name. + /// + public static string UpdateIfExist { + get { + return ResourceManager.GetString("UpdateIfExist", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Upgrade your plan. + /// + public static string UpgradeYourPlan { + get { + return ResourceManager.GetString("UpgradeYourPlan", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to User. + /// + public static string User { + get { + return ResourceManager.GetString("User", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Users. + /// + public static string Users { + get { + return ResourceManager.GetString("Users", resourceCulture); + } + } + } +} diff --git a/products/ASC.Files/Server/Resources/FilesUCResource.resx b/products/ASC.Files/Server/Resources/FilesUCResource.resx new file mode 100644 index 0000000000..e715d0d359 --- /dev/null +++ b/products/ASC.Files/Server/Resources/FilesUCResource.resx @@ -0,0 +1,921 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Comment + + + Form Filling + + + Read Only + + + Full Access + + + Deny Access + + + Review + + + Add account + + + Add message + + + Mobile Documents {0} available on the {1}App Store{2} + + + Author + + + Access + + + Add Box account + + + Add DropBox account + + + Add Google account + + + Add Nextcloud account + + + Add OneDrive account + + + Other account + + + Cancel + + + Cancel all + + + Change the third party info + + + Clear Filter + + + Close + + + Create a copy + + + Copy + + + Copy + + + Create... + + + Folder + + + Create Document + + + Presentation + + + Create Presentation + + + Spreadsheet + + + Create Spreadsheet + + + Document + + + Delete + + + Delete third party + + + Download + + + Edit + + + Embedding document + + + Empty Recycle Bin + + + Archives + + + Documents + + + All files + + + Folders + + + Images + + + Media + + + Presentations + + + Spreadsheets + + + Actual Size + + + Go to '{0}' + + + Go to My Documents + + + Learn more... + + + Block + + + Move or Copy + + + Move to + + + Next Image + + + OK + + + Open + + + More Actions + + + Overwrite with version update + + + Previous Image + + + Rename + + + Restore + + + Rotate Counterclockwise + + + Rotate Clockwise + + + Save + + + All + + + Send by email + + + Sharing Settings + + + Sharing Settings + + + Change owner + + + Show more + + + Show version history + + + Skip + + + Create file copy + + + Cancel uploading and close + + + Minimize dialog + + + Maximize dialog + + + Version history + + + Please select the action: + + + Show documents list + + + Hide documents list + + + The file will be added to the file with the same name as a version. + + + There will be two different files in the folder. + + + No file will be copied. The orginal file be in the destination folder. + + + Connection url + + + Send with DocuSign + + + Send + + + Back to parent folder + + + Unblock + + + On top + + + Upload + + + Finalize version + + + Facebook + + + Twitter + + + Zoom In + + + Zoom Out + + + Canceled + + + Convert and open document + + + Document saving + + + Document uploading + + + The file is password protected. Enter the password for conversion. + + + Choose format for each file to be downloaded + + + Common Settings + + + Confirmation dialog for deletion to recycle bin + + + Do not show this message again + + + Overwrite confirmation + + + Confirmation + + + Note: This action can not be undone. + + + Note: removal from your account can not be undone. + + + Save the file copy in the original format as well + + + To edit the file its copy in the Office Open XML format (docx, xlsx or pptx) will be created on the portal. The source file will not be changed. + + + All the documents you upload will be converted into the corresponding Office Open XML format (docx, xlsx or pptx) for faster editing. + + + The file copy will be created in the {0}My documents{1} folder + + + Upload the documents in original format as well + + + All the documents you upload will be converted to the Office Open XML format for faster editing. + + + Move confirmation + + + Are you sure you want to remove these elements from list? + + + Update the file version for the existing file with the same name. {0}Otherwise, a copy of the file will be created.{1} + + + Convert + + + Download as + + + The conversion was successful. + + + The file has been created in the '{0}' folder + + + Conversion Error + + + Is converting ... + + + Common Documents + + + Creation date + + + Department + + + Departments + + + Document Name + + + Documents + + + Save to folder: + + + Message + + + Add message + + + Remove message + + + Document name + + + Add {!user} + + + Signer recipient list + + + Download as + + + Download + + + Download + + + Size + + + Auto + + + Height + + + Width + + + The 'Common Documents' section shows all the documents shared by portal administrator for common access. Only portal administrator can create folders in this section, but with the access granted the portal users can also upload their files here. + + + Drag-and-drop the files from your computer here to upload them to your portal even more easily. + + + The 'Shared with Me' section is used to show the files which your friends or colleagues gave you access to. In case you haven't seen the latest changes in the documents they are marked 'new'. You can remove the files from the list clicking the appropriate button. + + + The documents and image files you {0}create{1} or {2}upload{3} to the portal are kept here in 'My Documents' section. You can {4}open{5} and {6}edit{7} them using the ONLYOFFICE™ portal editor, share them with friends or colleagues, organize into folders. + + + The documents and image files you {2}upload{3} to the portal are kept here in 'My Documents' section. You can share them with friends or colleagues, organize into folders. + + + The documents and image files you create or upload for your project are kept in this section. You can open and edit them using the ONLYOFFICE™ portal editor, organize into folders. + + + The documents and image files uploaded for the project are kept in this section. You can view them using the ONLYOFFICE portal editor. + + + The 'Recycle Bin' section is where all the deleted files are moved. You can either restore them in case they are deleted by mistake or delete them permanently. Please note, that when you delete the files from the 'Recycle Bin' they cannot be restored any longer. + + + No files to be displayed for this filter here + + + No files or folders matching your filter can be displayed in this section. Please select other filter options or clear filter to view all the files in this section. You can also look for the file you need in other sections. + + + No files to be displayed in this section + + + No files in this folder + + + No connected accounts + + + Error + + + The uploaded file was not found + + + All Users + + + Select the file in {0} format + + + Select files of type: {0} + + + Files will be compressed into the {0}.zip file{1} + + + Filter + + + Folders + + + Start Now + + + Shorten + + + Group + + + Import from Google Drive + + + new + + + Link + + + via email + + + Login + + + If you choose to convert the file to the format different from the original, some data might be lost. + + + Select all + + + The maximum file size is {0} + + + Have a team {0}to collaborate with? {0}{1}Learn more{2} + + + Document management + + + CRM and Invoicing + + + Task management and Gantt Chart + + + Email Aggregator + + + Have a project and a team to collaborate with?{0}We've prepared something for you.{1} + + + Invite your colleagues or subcontractors and benefit from additional features: + + + Welcome ONLYOFFICE™ for teams! + + + My Documents + + + New File + + + Preview + + + Open + + + Open location + + + Original format + + + Password + + + Warning! Possibly this file is a renamed executable file and it cannot be previewed. + + + This is a web application for internal use designed to store and share files and work on them in team. + + + Project Documents + + + delete + + + Mark as Read + + + Mark as Read + + + Mark all as read + + + Remove message + + + File name + + + Open saved document in new tab + + + Search + + + Search + + + Select folder + + + Send notification + + + Keep intermediate versions when editing + + + Keep all saved intermediate versions + + + Common Settings + + + Storing file versions + + + Share + + + Shared with Me + + + Set up the access right for the link + + + Provides access to the link + + + Show more + + + Show this window minimized + + + Settings + + + Switch to compact view + + + Switch to common view + + + Third-party accounts + + + Box + + + Connect account + + + Connect Accounts + + + You can connect the following accounts to the ONLYOFFICE™ Documents. They will be displayed in 'My Documents' folder and you will be able to edit and save them right on the portal all in one place. + + + For successful connection enter the necessary data at {0}this page{1}. + + + Connecting account + + + Specify for change + + + Delete Third Party + + + DropBox + + + DocuSign + + + Enable third party settings for users + + + Folder title + + + Google Drive + + + Nextcloud + + + ownCloud + + + Account + + + Reconnect + + + Make shared and put into Common Documents + + + SharePoint + + + SharePoint / OneDrive for Business + + + OneDrive + + + WebDAV + + + Yandex + + + Search in + + + and {0}all subfolders{1} + + + Click for searchibg without subfolders + + + Select + + + You can create documents ({0}), spreadsheets ({1}), presentations ({2}) and download them to most popular formats. + + + You can edit the following files: {0}. + + + You can open the files with the following extentions: {0}. + + + You can upload any files you want to Documents. + + + Recycle Bin + + + Types + + + Remove from List + + + Overwrite existing file with the same name + + + Upgrade your plan + + + User + + + Users + + + Change owner + + + Once the changes are applied, the current folder owner gets access rights to the access settings of the group he/she belongs to or according to the default folder permissions. + + + Congratulations + + + Open in browser + + + Welcome to your portal! + + + You have just created your ONLYOFFICE portal.{0} Upload your offline files to the portal to collaborate on them without leaving the app.{0}{0} Open your portal in browser to find more features, including {1}Projects{2}, {1}CRM{2}, {1}Calendar{2}, {1}Mail{2} and more. + + + If you have some technical problems, contact us at {0}. + + + For any purchase questions, email us at {0}. + + + Upload files + + + Upload folder + + \ No newline at end of file diff --git a/products/ASC.Files/Server/Services/DocumentService/Configuration.cs b/products/ASC.Files/Server/Services/DocumentService/Configuration.cs new file mode 100644 index 0000000000..d2fab72044 --- /dev/null +++ b/products/ASC.Files/Server/Services/DocumentService/Configuration.cs @@ -0,0 +1,1034 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Json; +using System.Text; +using System.Web; + +using ASC.Common; +using ASC.Core; +using ASC.Core.Common; +using ASC.Core.Common.Configuration; +using ASC.Core.Common.Settings; +using ASC.Core.Users; +using ASC.FederatedLogin.LoginProviders; +using ASC.Files.Core; +using ASC.Files.Core.Data; +using ASC.Files.Core.Security; +using ASC.Files.Resources; +using ASC.Web.Core.Files; +using ASC.Web.Core.Users; +using ASC.Web.Core.Utility.Skins; +using ASC.Web.Core.WhiteLabel; +using ASC.Web.Files.Classes; +using ASC.Web.Files.Helpers; +using ASC.Web.Files.Services.WCFService; +using ASC.Web.Files.ThirdPartyApp; +using ASC.Web.Files.Utils; +using ASC.Web.Studio.Utility; + +using Microsoft.Extensions.DependencyInjection; + +using static ASC.Web.Files.Services.DocumentService.Configuration; +using static ASC.Web.Files.Services.DocumentService.Configuration.DocumentConfig; +using static ASC.Web.Files.Services.DocumentService.Configuration.EditorConfiguration; +using static ASC.Web.Files.Services.DocumentService.Configuration.EditorConfiguration.CustomizationConfig; + +using File = ASC.Files.Core.File; + +namespace ASC.Web.Files.Services.DocumentService +{ + [DataContract(Name = "editorConfig", Namespace = "")] + public class Configuration + { + public static readonly Dictionary DocType = new Dictionary + { + { FileType.Document, "text" }, + { FileType.Spreadsheet, "spreadsheet" }, + { FileType.Presentation, "presentation" } + }; + + public enum EditorType + { + Desktop, + Mobile, + Embedded, + External, + } + + private FileType _fileTypeCache = FileType.Unknown; + + public Configuration( + File file, + IServiceProvider serviceProvider + ) + { + Document = serviceProvider.GetService(); + Document.Info.File = file; + EditorConfig = serviceProvider.GetService(); + EditorConfig.SetConfiguration(this); + } + + public EditorType Type + { + set { Document.Info.Type = value; } + get { return Document.Info.Type; } + } + + #region Property + + [DataMember(Name = "document")] + public DocumentConfig Document; + + [DataMember(Name = "documentType")] + public string DocumentType + { + set { } + get + { + DocType.TryGetValue(GetFileType, out var documentType); + return documentType; + } + } + + [DataMember(Name = "editorConfig")] + public EditorConfiguration EditorConfig; + + [DataMember(Name = "token", EmitDefaultValue = false)] + public string Token; + + [DataMember(Name = "type")] + public string TypeString + { + set { Type = (EditorType)Enum.Parse(typeof(EditorType), value, true); } + get { return Type.ToString().ToLower(); } + } + + private FileType GetFileType + { + set { } + get + { + if (_fileTypeCache == FileType.Unknown) + _fileTypeCache = FileUtility.GetFileTypeByFileName(Document.Info.File.Title); + return _fileTypeCache; + } + } + + [DataMember(Name = "error", EmitDefaultValue = false)] + public string ErrorMessage; + + #endregion + + public static string Serialize(Configuration configuration) + { + using var ms = new MemoryStream(); + var serializer = new DataContractJsonSerializer(typeof(Configuration)); + serializer.WriteObject(ms, configuration); + ms.Seek(0, SeekOrigin.Begin); + return Encoding.UTF8.GetString(ms.GetBuffer(), 0, (int)ms.Length); + } + + #region Nested Classes + + [DataContract(Name = "document", Namespace = "")] + public class DocumentConfig + { + public string SharedLinkKey; + + public DocumentConfig(DocumentServiceConnector documentServiceConnector, PathProvider pathProvider, InfoConfig infoConfig) + { + Info = infoConfig; + Permissions = new PermissionsConfig(); + DocumentServiceConnector = documentServiceConnector; + PathProvider = pathProvider; + } + + private string _key = string.Empty; + private string _fileUri; + private string _title = null; + + + [DataMember(Name = "fileType")] + public string FileType + { + set { } + get { return Info.File.ConvertedExtension.Trim('.'); } + } + + [DataMember(Name = "info")] + public InfoConfig Info; + + [DataMember(Name = "key")] + public string Key + { + set { _key = value; } + get { return DocumentServiceConnector.GenerateRevisionId(_key); } + } + + [DataMember(Name = "permissions")] + public PermissionsConfig Permissions; + + [DataMember(Name = "title")] + public string Title + { + set { _title = value; } + get { return _title ?? Info.File.Title; } + } + + [DataMember(Name = "url")] + public string Url + { + set { _fileUri = DocumentServiceConnector.ReplaceCommunityAdress(value); } + get + { + if (!string.IsNullOrEmpty(_fileUri)) + return _fileUri; + var last = Permissions.Edit || Permissions.Review || Permissions.Comment; + _fileUri = DocumentServiceConnector.ReplaceCommunityAdress(PathProvider.GetFileStreamUrl(Info.File, SharedLinkKey, last)); + return _fileUri; + } + } + + public DocumentServiceConnector DocumentServiceConnector { get; } + public PathProvider PathProvider { get; } + + + #region Nested Classes + + [DataContract(Name = "info", Namespace = "")] + public class InfoConfig + { + public File File; + + public EditorType Type = EditorType.Desktop; + private string _breadCrumbs; + + public InfoConfig(BreadCrumbsManager breadCrumbsManager, FileSharing fileSharing) + { + BreadCrumbsManager = breadCrumbsManager; + FileSharing = fileSharing; + } + + [Obsolete("Use owner (since v5.4)")] + [DataMember(Name = "author")] + public string Aouthor + { + set { } + get { return File.CreateByString; } + } + + [Obsolete("Use uploaded (since v5.4)")] + [DataMember(Name = "created")] + public string Created + { + set { } + get { return File.CreateOnString; } + } + + [DataMember(Name = "folder", EmitDefaultValue = false)] + public string Folder + { + set { } + get + { + if (Type == EditorType.Embedded || Type == EditorType.External) return null; + if (string.IsNullOrEmpty(_breadCrumbs)) + { + const string crumbsSeporator = " \\ "; + + var breadCrumbsList = BreadCrumbsManager.GetBreadCrumbs(File.FolderID); + _breadCrumbs = string.Join(crumbsSeporator, breadCrumbsList.Select(folder => folder.Title).ToArray()); + } + + return _breadCrumbs; + } + } + + [DataMember(Name = "owner")] + public string Owner + { + set { } + get { return File.CreateByString; } + } + + [DataMember(Name = "uploaded")] + public string Uploaded + { + set { } + get { return File.CreateOnString; } + } + + [DataMember(Name = "sharingSettings", EmitDefaultValue = false)] + public ItemList SharingSettings + { + set { } + get + { + if (Type == EditorType.Embedded + || Type == EditorType.External + || !FileSharing.CanSetAccess(File)) return null; + + try + { + return FileSharing.GetSharedInfoShort(File.UniqID); + } + catch + { + return null; + } + } + } + + public BreadCrumbsManager BreadCrumbsManager { get; } + public FileSharing FileSharing { get; } + } + + [DataContract(Name = "permissions", Namespace = "")] + public class PermissionsConfig + { + [Obsolete("Since DS v5.5")] + [DataMember(Name = "changeHistory")] + public bool ChangeHistory = false; + + [DataMember(Name = "comment")] + public bool Comment = true; + + [DataMember(Name = "download")] + public bool Download = true; + + [DataMember(Name = "edit")] + public bool Edit = true; + + [DataMember(Name = "fillForms")] + public bool FillForms = true; + + [DataMember(Name = "print")] + public bool Print = true; + + [DataMember(Name = "rename")] + public bool Rename = false; + + [DataMember(Name = "review")] + public bool Review = true; + } + + #endregion + } + + [DataContract(Name = "editorConfig", Namespace = "")] + public class EditorConfiguration + { + public EditorConfiguration( + UserManager userManager, + AuthContext authContext, + DisplayUserSettingsHelper displayUserSettingsHelper, + FilesLinkUtility filesLinkUtility, + BaseCommonLinkUtility baseCommonLinkUtility, + PluginsConfig pluginsConfig, + EmbeddedConfig embeddedConfig, + CustomizationConfig customizationConfig) + { + UserManager = userManager; + AuthContext = authContext; + FilesLinkUtility = filesLinkUtility; + BaseCommonLinkUtility = baseCommonLinkUtility; + Customization = customizationConfig; + Plugins = pluginsConfig; + Embedded = embeddedConfig; + _userInfo = userManager.GetUsers(authContext.CurrentAccount.ID); + + User = _userInfo.ID.Equals(ASC.Core.Configuration.Constants.Guest.ID) + ? new UserConfig + { + Id = Guid.NewGuid().ToString(), + Name = FilesCommonResource.Guest, + } + : new UserConfig + { + Id = _userInfo.ID.ToString(), + Name = _userInfo.DisplayUserName(false, displayUserSettingsHelper), + }; + } + + public bool ModeWrite = false; + + private Configuration _configuration; + + internal void SetConfiguration(Configuration configuration) + { + _configuration = configuration; + Customization.SetConfiguration(_configuration); + } + + private readonly UserInfo _userInfo; + private EmbeddedConfig _embeddedConfig; + + [DataMember(Name = "actionLink", EmitDefaultValue = false)] + public ActionLinkConfig ActionLink; + + public string ActionLinkString + { + get { return null; } + set + { + try + { + using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(value))) + { + var serializer = new DataContractJsonSerializer(typeof(ActionLinkConfig)); + ActionLink = (ActionLinkConfig)serializer.ReadObject(ms); + } + } + catch (Exception) + { + ActionLink = null; + } + } + } + + [DataMember(Name = "callbackUrl", EmitDefaultValue = false)] + public string CallbackUrl; + + [DataMember(Name = "createUrl", EmitDefaultValue = false)] + public string CreateUrl + { + set { } + get + { + if (_configuration.Document.Info.Type != EditorType.Desktop) return null; + if (!AuthContext.IsAuthenticated || UserManager.GetUsers(AuthContext.CurrentAccount.ID).IsVisitor(UserManager)) return null; + + return GetCreateUrl(_configuration.GetFileType); + } + } + + [DataMember(Name = "plugins", EmitDefaultValue = false)] + public PluginsConfig Plugins; + + [DataMember(Name = "customization", EmitDefaultValue = false)] + public CustomizationConfig Customization; + + [DataMember(Name = "embedded", EmitDefaultValue = false)] + public EmbeddedConfig Embedded + { + set { _embeddedConfig = value; } + get { return _configuration.Document.Info.Type == EditorType.Embedded ? _embeddedConfig : null; } + } + + [DataMember(Name = "fileChoiceUrl", EmitDefaultValue = false)] + public string FileChoiceUrl; + + [DataMember(Name = "lang")] + public string Lang + { + set { } + get { return _userInfo.GetCulture().Name; } + } + + //todo: remove old feild after release 5.2+ + [DataMember(Name = "mergeFolderUrl", EmitDefaultValue = false)] + public string MergeFolderUrl; + + [DataMember(Name = "mode")] + public string Mode + { + set { } + get { return ModeWrite ? "edit" : "view"; } + } + + public UserManager UserManager { get; } + public AuthContext AuthContext { get; } + public FilesLinkUtility FilesLinkUtility { get; } + public BaseCommonLinkUtility BaseCommonLinkUtility { get; } + + [DataMember(Name = "saveAsUrl", EmitDefaultValue = false)] + public string SaveAsUrl; + + [DataMember(Name = "sharingSettingsUrl", EmitDefaultValue = false)] + public string SharingSettingsUrl; + + [DataMember(Name = "user")] + public UserConfig User; + + private string GetCreateUrl(FileType fileType) + { + string title; + switch (fileType) + { + case FileType.Document: + title = FilesJSResource.TitleNewFileText; + break; + case FileType.Spreadsheet: + title = FilesJSResource.TitleNewFileSpreadsheet; + break; + case FileType.Presentation: + title = FilesJSResource.TitleNewFilePresentation; + break; + default: + return null; + } + + DocType.TryGetValue(fileType, out var documentType); + + return BaseCommonLinkUtility.GetFullAbsolutePath(FilesLinkUtility.FileHandlerPath) + + "?" + FilesLinkUtility.Action + "=create" + + "&doctype=" + documentType + + "&" + FilesLinkUtility.FileTitle + "=" + HttpUtility.UrlEncode(title); + } + + #region Nested Classes + + [DataContract(Name = "actionLink", Namespace = "")] + public class ActionLinkConfig + { + [DataMember(Name = "action", EmitDefaultValue = false)] + public ActionConfig Action; + + + [DataContract(Name = "action", Namespace = "")] + public class ActionConfig + { + [DataMember(Name = "type", EmitDefaultValue = false)] + public string Type; + + [DataMember(Name = "data", EmitDefaultValue = false)] + public string Data; + } + + + public static string Serialize(ActionLinkConfig actionLinkConfig) + { + using (var ms = new MemoryStream()) + { + var serializer = new DataContractJsonSerializer(typeof(ActionLinkConfig)); + serializer.WriteObject(ms, actionLinkConfig); + ms.Seek(0, SeekOrigin.Begin); + return Encoding.UTF8.GetString(ms.GetBuffer(), 0, (int)ms.Length); + } + } + } + + [DataContract(Name = "embedded", Namespace = "")] + public class EmbeddedConfig + { + public string ShareLinkParam; + + [DataMember(Name = "embedUrl", EmitDefaultValue = false)] + public string EmbedUrl + { + set { } + get { return BaseCommonLinkUtility.GetFullAbsolutePath(FilesLinkUtility.FilesBaseAbsolutePath + FilesLinkUtility.EditorPage + "?" + FilesLinkUtility.Action + "=embedded" + ShareLinkParam); } + } + + [DataMember(Name = "saveUrl", EmitDefaultValue = false)] + public string SaveUrl + { + set { } + get { return BaseCommonLinkUtility.GetFullAbsolutePath(FilesLinkUtility.FileHandlerPath + "?" + FilesLinkUtility.Action + "=download" + ShareLinkParam); } + } + + [DataMember(Name = "shareUrl", EmitDefaultValue = false)] + public string ShareUrl + { + set { } + get { return BaseCommonLinkUtility.GetFullAbsolutePath(FilesLinkUtility.FilesBaseAbsolutePath + FilesLinkUtility.EditorPage + "?" + FilesLinkUtility.Action + "=view" + ShareLinkParam); } + } + + public BaseCommonLinkUtility BaseCommonLinkUtility { get; } + public FilesLinkUtility FilesLinkUtility { get; } + + [DataMember(Name = "toolbarDocked")] + public string ToolbarDocked = "top"; + + public EmbeddedConfig(BaseCommonLinkUtility baseCommonLinkUtility, FilesLinkUtility filesLinkUtility) + { + BaseCommonLinkUtility = baseCommonLinkUtility; + FilesLinkUtility = filesLinkUtility; + } + } + + [DataContract(Name = "plugins", Namespace = "")] + public class PluginsConfig + { + [DataMember(Name = "pluginsData", EmitDefaultValue = false)] + public string[] PluginsData + { + set { } + get + { + var plugins = new List(); + + var easyBibHelper = ConsumerFactory.Get(); + if (!string.IsNullOrEmpty(easyBibHelper.AppKey)) + { + plugins.Add(BaseCommonLinkUtility.GetFullAbsolutePath("ThirdParty/plugin/easybib/config.json")); + } + + var wordpressLoginProvider = ConsumerFactory.Get(); + if (!string.IsNullOrEmpty(wordpressLoginProvider.ClientID) && + !string.IsNullOrEmpty(wordpressLoginProvider.ClientSecret) && + !string.IsNullOrEmpty(wordpressLoginProvider.RedirectUri)) + { + plugins.Add(BaseCommonLinkUtility.GetFullAbsolutePath("ThirdParty/plugin/wordpress/config.json")); + } + + return plugins.ToArray(); + } + } + + public ConsumerFactory ConsumerFactory { get; } + public BaseCommonLinkUtility BaseCommonLinkUtility { get; } + + public PluginsConfig(ConsumerFactory consumerFactory, BaseCommonLinkUtility baseCommonLinkUtility) + { + ConsumerFactory = consumerFactory; + BaseCommonLinkUtility = baseCommonLinkUtility; + } + } + + [DataContract(Name = "customization", Namespace = "")] + public class CustomizationConfig + { + public CustomizationConfig( + CoreBaseSettings coreBaseSettings, + SettingsManager settingsManager, + FileUtility fileUtility, + FilesSettingsHelper filesSettingsHelper, + AuthContext authContext, + FileSecurity fileSecurity, + IDaoFactory daoFactory, + GlobalFolderHelper globalFolderHelper, + PathProvider pathProvider, + WebImageSupplier webImageSupplier, + BaseCommonLinkUtility baseCommonLinkUtility, + CustomerConfig customerConfig, + LogoConfig logoConfig) + { + CoreBaseSettings = coreBaseSettings; + SettingsManager = settingsManager; + FileUtility = fileUtility; + FilesSettingsHelper = filesSettingsHelper; + AuthContext = authContext; + FileSecurity = fileSecurity; + DaoFactory = daoFactory; + GlobalFolderHelper = globalFolderHelper; + PathProvider = pathProvider; + WebImageSupplier = webImageSupplier; + BaseCommonLinkUtility = baseCommonLinkUtility; + Customer = customerConfig; + Logo = logoConfig; + } + + private Configuration _configuration; + + internal void SetConfiguration(Configuration configuration) + { + _configuration = configuration; + Customer.SetConfiguration(_configuration); + Logo.SetConfiguration(_configuration); + } + + public string GobackUrl; + public bool IsRetina = false; + + + [DataMember(Name = "about")] + public bool About + { + set { } + get { return !CoreBaseSettings.Standalone && !CoreBaseSettings.CustomMode; } + } + + [DataMember(Name = "customer")] + public CustomerConfig Customer; + + [DataMember(Name = "feedback", EmitDefaultValue = false)] + public FeedbackConfig Feedback + { + set { } + get + { + if (CoreBaseSettings.Standalone) return null; + if (!AdditionalWhiteLabelSettings.Instance(SettingsManager).FeedbackAndSupportEnabled) return null; + + return new FeedbackConfig + { + Url = BaseCommonLinkUtility.GetRegionalUrl( + AdditionalWhiteLabelSettings.Instance(SettingsManager).FeedbackAndSupportUrl, + CultureInfo.CurrentCulture.TwoLetterISOLanguageName), + }; + } + } + + [DataMember(Name = "forcesave", EmitDefaultValue = false)] + public bool Forcesave + { + set { } + get + { + return FileUtility.CanForcesave + && !_configuration.Document.Info.File.ProviderEntry + && ThirdPartySelector.GetAppByFileId(_configuration.Document.Info.File.ID.ToString()) == null + && FilesSettingsHelper.Forcesave; + } + } + + [DataMember(Name = "goback", EmitDefaultValue = false)] + public GobackConfig Goback + { + set { } + get + { + if (_configuration.Type == EditorType.Embedded || _configuration.Type == EditorType.External) return null; + if (!AuthContext.IsAuthenticated) return null; + if (GobackUrl != null) + { + return new GobackConfig + { + Url = GobackUrl, + }; + } + + var folderDao = DaoFactory.FolderDao; + try + { + var parent = folderDao.GetFolder(_configuration.Document.Info.File.FolderID); + var fileSecurity = FileSecurity; + if (_configuration.Document.Info.File.RootFolderType == FolderType.USER + && !Equals(_configuration.Document.Info.File.RootFolderId, GlobalFolderHelper.FolderMy) + && !fileSecurity.CanRead(parent)) + { + if (fileSecurity.CanRead(_configuration.Document.Info.File)) + { + return new GobackConfig + { + Url = PathProvider.GetFolderUrl(GlobalFolderHelper.FolderShare), + }; + } + return null; + } + + return new GobackConfig + { + Url = PathProvider.GetFolderUrl(parent), + }; + } + catch (Exception) + { + return null; + } + } + } + + [DataMember(Name = "loaderLogo", EmitDefaultValue = false)] + public string LoaderLogo + { + set { } + get + { + return CoreBaseSettings.CustomMode + ? BaseCommonLinkUtility.GetFullAbsolutePath(WebImageSupplier.GetAbsoluteWebPath("loader.svg").ToLower()) + : null; + } + } + + [DataMember(Name = "loaderName", EmitDefaultValue = false)] + public string LoaderName + { + set { } + get + { + return CoreBaseSettings.CustomMode + ? " " + : null; + } + } + + [DataMember(Name = "logo")] + public LogoConfig Logo; + + [DataMember(Name = "reviewDisplay", EmitDefaultValue = false)] + public string ReviewDisplay + { + set { } + get { return _configuration.EditorConfig.ModeWrite ? null : "markup"; } + } + + public CoreBaseSettings CoreBaseSettings { get; } + public SettingsManager SettingsManager { get; } + public FileUtility FileUtility { get; } + public FilesSettingsHelper FilesSettingsHelper { get; } + public AuthContext AuthContext { get; } + public FileSecurity FileSecurity { get; } + public IDaoFactory DaoFactory { get; } + public GlobalFolderHelper GlobalFolderHelper { get; } + public PathProvider PathProvider { get; } + public WebImageSupplier WebImageSupplier { get; } + public BaseCommonLinkUtility BaseCommonLinkUtility { get; } + + [DataContract(Name = "customer", Namespace = "")] + public class CustomerConfig + { + public CustomerConfig( + SettingsManager settingsManager, + BaseCommonLinkUtility baseCommonLinkUtility, + TenantLogoHelper tenantLogoHelper) + { + SettingsManager = settingsManager; + BaseCommonLinkUtility = baseCommonLinkUtility; + TenantLogoHelper = tenantLogoHelper; + } + + private Configuration _configuration; + + internal void SetConfiguration(Configuration configuration) + { + _configuration = configuration; + } + + [DataMember(Name = "logo")] + public string Logo + { + set { } + get { return BaseCommonLinkUtility.GetFullAbsolutePath(TenantLogoHelper.GetLogo(WhiteLabelLogoTypeEnum.Dark, !_configuration.EditorConfig.Customization.IsRetina)); } + } + + [DataMember(Name = "name")] + public string Name + { + set { } + get + { + return (SettingsManager.Load().GetLogoText(SettingsManager) ?? "") + .Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("/", "\\/"); + } + } + + public SettingsManager SettingsManager { get; } + public BaseCommonLinkUtility BaseCommonLinkUtility { get; } + public TenantLogoHelper TenantLogoHelper { get; } + } + + [DataContract(Name = "feedback", Namespace = "")] + public class FeedbackConfig + { + [DataMember(Name = "url")] + public string Url; + + [DataMember(Name = "visible")] + public bool Visible = true; + } + + [DataContract(Name = "goback", Namespace = "")] + public class GobackConfig + { + [DataMember(Name = "url", EmitDefaultValue = false)] + public string Url; + } + + [DataContract(Name = "logo", Namespace = "")] + public class LogoConfig + { + public LogoConfig( + SettingsManager settingsManager, + BaseCommonLinkUtility baseCommonLinkUtility, + TenantLogoHelper tenantLogoHelper) + { + BaseCommonLinkUtility = baseCommonLinkUtility; + TenantLogoHelper = tenantLogoHelper; + SettingsManager = settingsManager; + } + + private Configuration _configuration; + internal void SetConfiguration(Configuration configuration) + { + _configuration = configuration; + } + + [DataMember(Name = "image")] + public string Image + { + set { } + get + { + return + _configuration.Type == EditorType.Embedded + ? null + : BaseCommonLinkUtility.GetFullAbsolutePath(TenantLogoHelper.GetLogo(WhiteLabelLogoTypeEnum.DocsEditor, !_configuration.EditorConfig.Customization.IsRetina)); + } + } + + [DataMember(Name = "imageEmbedded", EmitDefaultValue = false)] + public string ImageEmbedded + { + set { } + get + { + return + _configuration.Type != EditorType.Embedded + ? null + : BaseCommonLinkUtility.GetFullAbsolutePath(TenantLogoHelper.GetLogo(WhiteLabelLogoTypeEnum.Dark, !_configuration.EditorConfig.Customization.IsRetina)); + } + } + + [DataMember(Name = "url")] + public string Url + { + set { } + get { return CompanyWhiteLabelSettings.Instance(SettingsManager).Site; } + } + + public BaseCommonLinkUtility BaseCommonLinkUtility { get; } + public TenantLogoHelper TenantLogoHelper { get; } + public SettingsManager SettingsManager { get; } + } + } + + [DataContract(Name = "user", Namespace = "")] + public class UserConfig + { + [DataMember(Name = "id", EmitDefaultValue = false)] + public string Id; + + [DataMember(Name = "name", EmitDefaultValue = false)] + public string Name; + } + + #endregion + } + + #endregion + } + + public static class ConfigurationExtention + { + public static DIHelper AddConfigurationService(this DIHelper services) + { + return services + .AddDocumentConfigService() + .AddEditorConfigurationService(); + } + + public static DIHelper AddDocumentConfigService(this DIHelper services) + { + services.TryAddTransient(); + + return services + .AddDocumentServiceConnectorService() + .AddPathProviderService() + .AddInfoConfigService(); + } + + public static DIHelper AddInfoConfigService(this DIHelper services) + { + services.TryAddTransient(); + + return services + .AddBreadCrumbsManagerService() + .AddFileSharingService(); + } + + public static DIHelper AddEditorConfigurationService(this DIHelper services) + { + services.TryAddTransient(); + + return services + .AddUserManagerService() + .AddAuthContextService() + .AddDisplayUserSettingsService() + .AddFilesLinkUtilityService() + .AddBaseCommonLinkUtilityService() + .AddPluginsConfigService() + .AddEmbeddedConfigService() + .AddCustomizationConfigService(); + } + + public static DIHelper AddPluginsConfigService(this DIHelper services) + { + services.TryAddTransient(); + + return services + .AddConsumerFactoryService() + .AddBaseCommonLinkUtilityService(); + } + + public static DIHelper AddEmbeddedConfigService(this DIHelper services) + { + services.TryAddTransient(); + + return services + .AddFilesLinkUtilityService() + .AddBaseCommonLinkUtilityService(); + } + + public static DIHelper AddCustomizationConfigService(this DIHelper services) + { + services.TryAddTransient(); + + return services + .AddCoreBaseSettingsService() + .AddSettingsManagerService() + .AddFileUtilityService() + .AddFilesSettingsHelperService() + .AddAuthContextService() + .AddFileSecurityService() + .AddDaoFactoryService() + .AddGlobalFolderHelperService() + .AddPathProviderService() + .AddWebImageSupplierService() + .AddBaseCommonLinkUtilityService() + .AddCustomerConfigService() + .AddLogoConfigService(); + } + + public static DIHelper AddCustomerConfigService(this DIHelper services) + { + services.TryAddTransient(); + + return services + .AddSettingsManagerService() + .AddBaseCommonLinkUtilityService() + .AddTenantLogoHelperService(); + } + + public static DIHelper AddLogoConfigService(this DIHelper services) + { + services.TryAddTransient(); + + return services + .AddSettingsManagerService() + .AddBaseCommonLinkUtilityService() + .AddTenantLogoHelperService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Services/DocumentService/DocbuilderReportsUtility.cs b/products/ASC.Files/Server/Services/DocumentService/DocbuilderReportsUtility.cs new file mode 100644 index 0000000000..f6e56021c6 --- /dev/null +++ b/products/ASC.Files/Server/Services/DocumentService/DocbuilderReportsUtility.cs @@ -0,0 +1,364 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using System.Web; + +using ASC.Common.Logging; +using ASC.Common.Threading; +using ASC.Core; + +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace ASC.Web.Files.Services.DocumentService +{ + public enum ReportOrigin + { + CRM, + Projects, + ProjectsAuto, + } + + public enum ReportStatus + { + Queued, + Started, + Done, + Failed + } + + public class ReportStateData + { + public string FileName { get; } + public string TmpFileName { get; } + public string Script { get; } + public int ReportType { get; } + public ReportOrigin Origin { get; } + public Action SaveFileAction { get; } + public object Obj { get; } + public int TenantId { get; } + public Guid UserId { get; } + + public ReportStateData(string fileName, string tmpFileName, string script, int reportType, ReportOrigin origin, + Action saveFileAction, object obj, + int tenantId, Guid userId) + { + FileName = fileName; + TmpFileName = tmpFileName; + Script = script; + ReportType = reportType; + Origin = origin; + SaveFileAction = saveFileAction; + Obj = obj; + TenantId = tenantId; + UserId = userId; + } + } + + public class ReportState + { + public string Id { get; set; } + public string FileName { get; set; } + public int FileId { get; set; } + public int ReportType { get; set; } + + public string Exception { get; set; } + public ReportStatus Status { get; set; } + public ReportOrigin Origin { get; set; } + + internal string BuilderKey { get; set; } + internal string Script { get; set; } + internal string TmpFileName { get; set; } + internal Action SaveFileAction { get; set; } + + internal int TenantId { get; set; } + internal Guid UserId { get; set; } + internal string ContextUrl { get; set; } + + public object Obj { get; set; } + + protected DistributedTask TaskInfo { get; private set; } + public IServiceProvider ServiceProvider { get; } + + public ReportState(IServiceProvider serviceProvider, ReportStateData reportStateData, IHttpContextAccessor httpContextAccessor) + { + TaskInfo = new DistributedTask(); + ServiceProvider = serviceProvider; + FileName = reportStateData.FileName; + TmpFileName = reportStateData.TmpFileName; + Script = reportStateData.Script; + ReportType = reportStateData.ReportType; + Origin = reportStateData.Origin; + SaveFileAction = reportStateData.SaveFileAction; + Obj = reportStateData.Obj; + TenantId = reportStateData.TenantId; + UserId = reportStateData.UserId; + ContextUrl = httpContextAccessor.HttpContext?.Request.GetUrlRewriter().ToString(); + } + + public static ReportState FromTask( + DistributedTask task, + IHttpContextAccessor httpContextAccessor, + int tenantId, + Guid userId) + { + var data = new ReportStateData( + task.GetProperty("fileName"), + task.GetProperty("tmpFileName"), + task.GetProperty("script"), + task.GetProperty("reportType"), + task.GetProperty("reportOrigin"), + null, + null, + tenantId, + userId + ); + return new ReportState(null, data, httpContextAccessor) + { + Id = task.GetProperty("id"), + FileId = task.GetProperty("fileId"), + Status = task.GetProperty("status"), + Exception = task.GetProperty("exception") + }; + + } + + public void GenerateReport(DistributedTask task, CancellationToken cancellationToken) + { + using var scope = ServiceProvider.CreateScope(); + var logger = scope.ServiceProvider.GetService>().CurrentValue; + + try + { + var tenantManager = scope.ServiceProvider.GetService(); + tenantManager.SetCurrentTenant(TenantId); + var authContext = scope.ServiceProvider.GetService(); + var securityContext = scope.ServiceProvider.GetService(); + var documentServiceConnector = scope.ServiceProvider.GetService(); + + Status = ReportStatus.Started; + PublishTaskInfo(logger); + + //if (HttpContext.Current == null && !WorkContext.IsMono && !string.IsNullOrEmpty(ContextUrl)) + //{ + // HttpContext.Current = new HttpContext( + // new HttpRequest("hack", ContextUrl, string.Empty), + // new HttpResponse(new System.IO.StringWriter())); + //} + + tenantManager.SetCurrentTenant(TenantId); + securityContext.AuthenticateMe(UserId); + + BuilderKey = documentServiceConnector.DocbuilderRequest(null, Script, true, out var urls); + + while (true) + { + if (cancellationToken.IsCancellationRequested) + { + throw new OperationCanceledException(); + } + + Task.Delay(1500, cancellationToken).Wait(cancellationToken); + var builderKey = documentServiceConnector.DocbuilderRequest(BuilderKey, null, true, out urls); + if (builderKey == null) + throw new NullReferenceException(); + + if (urls != null && !urls.Any()) throw new Exception("Empty response"); + + if (urls != null && urls.ContainsKey(TmpFileName)) + break; + } + + if (cancellationToken.IsCancellationRequested) + { + throw new OperationCanceledException(); + } + + SaveFileAction(this, urls[TmpFileName]); + + Status = ReportStatus.Done; + } + catch (Exception e) + { + logger.Error("DocbuilderReportsUtility error", e); + Exception = e.Message; + Status = ReportStatus.Failed; + } + + PublishTaskInfo(logger); + } + + public DistributedTask GetDistributedTask() + { + FillDistributedTask(); + return TaskInfo; + } + + protected void PublishTaskInfo(ILog logger) + { + var tries = 3; + while (tries-- > 0) + { + try + { + FillDistributedTask(); + TaskInfo.PublishChanges(); + return; + } + catch (Exception e) + { + logger.Error(" PublishTaskInfo DocbuilderReportsUtility", e); + if (tries == 0) throw; + } + } + } + + protected void FillDistributedTask() + { + TaskInfo.SetProperty("id", Id); + TaskInfo.SetProperty("fileName", FileName); + TaskInfo.SetProperty("tmpFileName", TmpFileName); + TaskInfo.SetProperty("reportType", ReportType); + TaskInfo.SetProperty("fileId", FileId); + TaskInfo.SetProperty("status", Status); + TaskInfo.SetProperty("reportOrigin", Origin); + TaskInfo.SetProperty("exception", Exception); + } + } + + public class DocbuilderReportsUtility + { + private readonly DistributedTaskQueue tasks; + private readonly object Locker; + + public static string TmpFileName + { + get + { + return string.Format("tmp{0}.xlsx", DateTime.UtcNow.Ticks); + } + } + + public DocbuilderReportsUtility(DistributedTaskCacheNotify distributedTaskCacheNotify) + { + tasks = new DistributedTaskQueue(distributedTaskCacheNotify, "DocbuilderReportsUtility", 10); + Locker = new object(); + } + + public void Enqueue(ReportState state) + { + lock (Locker) + { + tasks.QueueTask(state.GenerateReport, state.GetDistributedTask()); + } + } + + public void Terminate(ReportOrigin origin, int tenantId, Guid userId) + { + lock (Locker) + { + var result = tasks.GetTasks().Where(Predicate(origin, tenantId, userId)); + + foreach (var t in result) + { + tasks.CancelTask(t.Id); + } + } + } + + public ReportState Status(ReportOrigin origin, IHttpContextAccessor httpContextAccessor, int tenantId, Guid userId) + { + lock (Locker) + { + var task = tasks.GetTasks().LastOrDefault(Predicate(origin, tenantId, userId)); + if (task == null) return null; + + var result = ReportState.FromTask(task, httpContextAccessor, tenantId, userId); + var status = task.GetProperty("status"); + var id = task.GetProperty("status"); + + if ((int)status > 1) + { + tasks.RemoveTask(task.Id); + } + + return result; + } + } + + private static Func Predicate(ReportOrigin origin, int tenantId, Guid userId) + { + return t => t.GetProperty("id") == GetCacheKey(origin, tenantId, userId); + } + + internal static string GetCacheKey(ReportOrigin origin, int tenantId, Guid userId) + { + return $"{tenantId}_{userId}_{(int)origin}"; + } + } + + public class DocbuilderReportsUtilityHelper + { + public DocbuilderReportsUtility DocbuilderReportsUtility { get; } + public AuthContext AuthContext { get; } + public TenantManager TenantManager { get; } + public IHttpContextAccessor HttpContextAccessor { get; } + + public DocbuilderReportsUtilityHelper( + DocbuilderReportsUtility docbuilderReportsUtility, + AuthContext authContext, + TenantManager tenantManager) + { + DocbuilderReportsUtility = docbuilderReportsUtility; + AuthContext = authContext; + TenantManager = tenantManager; + TenantId = TenantManager.GetCurrentTenant().TenantId; + UserId = AuthContext.CurrentAccount.ID; + } + + public DocbuilderReportsUtilityHelper( + DocbuilderReportsUtility docbuilderReportsUtility, + AuthContext authContext, + TenantManager tenantManager, + IHttpContextAccessor httpContextAccessor) + : this(docbuilderReportsUtility, authContext, tenantManager) + { + HttpContextAccessor = httpContextAccessor; + } + + private int TenantId { get; set; } + private Guid UserId { get; set; } + + public void Enqueue(ReportState state) => DocbuilderReportsUtility.Enqueue(state); + public void Terminate(ReportOrigin origin) => DocbuilderReportsUtility.Terminate(origin, TenantId, UserId); + public ReportState Status(ReportOrigin origin) => DocbuilderReportsUtility.Status(origin, HttpContextAccessor, TenantId, UserId); + } +} diff --git a/products/ASC.Files/Server/Services/DocumentService/DocumentServiceConnector.cs b/products/ASC.Files/Server/Services/DocumentService/DocumentServiceConnector.cs new file mode 100644 index 0000000000..917c9928c0 --- /dev/null +++ b/products/ASC.Files/Server/Services/DocumentService/DocumentServiceConnector.cs @@ -0,0 +1,398 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Net; +using System.Web; + +using ASC.Common; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Common; +using ASC.Core.Tenants; +using ASC.Files.Resources; +using ASC.Web.Core.Files; +using ASC.Web.Files.Classes; +using ASC.Web.Studio.Utility; + +using Microsoft.Extensions.Options; + +using Newtonsoft.Json; + +using CommandMethod = ASC.Web.Core.Files.DocumentService.CommandMethod; + +namespace ASC.Web.Files.Services.DocumentService +{ + public class DocumentServiceConnector + { + public ILog Logger { get; } + public FilesLinkUtility FilesLinkUtility { get; } + public FileUtility FileUtility { get; } + public GlobalStore GlobalStore { get; } + public BaseCommonLinkUtility BaseCommonLinkUtility { get; } + public TenantManager TenantManager { get; } + public TenantExtra TenantExtra { get; } + public CoreSettings CoreSettings { get; } + public PathProvider PathProvider { get; } + + public DocumentServiceConnector( + IOptionsMonitor optionsMonitor, + FilesLinkUtility filesLinkUtility, + FileUtility fileUtility, + PathProvider pathProvider, + GlobalStore globalStore, + BaseCommonLinkUtility baseCommonLinkUtility, + TenantManager tenantManager, + TenantExtra tenantExtra, + CoreSettings coreSettings) + { + Logger = optionsMonitor.CurrentValue; + FilesLinkUtility = filesLinkUtility; + FileUtility = fileUtility; + GlobalStore = globalStore; + BaseCommonLinkUtility = baseCommonLinkUtility; + TenantManager = tenantManager; + TenantExtra = tenantExtra; + CoreSettings = coreSettings; + PathProvider = pathProvider; + } + + public static string GenerateRevisionId(string expectedKey) + { + return Web.Core.Files.DocumentService.GenerateRevisionId(expectedKey); + } + + public int GetConvertedUri(string documentUri, + string fromExtension, + string toExtension, + string documentRevisionId, + string password, + bool isAsync, + out string convertedDocumentUri) + { + Logger.DebugFormat("DocService convert from {0} to {1} - {2}", fromExtension, toExtension, documentUri); + try + { + return Web.Core.Files.DocumentService.GetConvertedUri( + FileUtility, + FilesLinkUtility.DocServiceConverterUrl, + documentUri, + fromExtension, + toExtension, + GenerateRevisionId(documentRevisionId), + password, + isAsync, + FileUtility.SignatureSecret, + out convertedDocumentUri); + } + catch (Exception ex) + { + throw CustomizeError(ex); + } + } + + public bool Command(CommandMethod method, + string docKeyForTrack, + object fileId = null, + string callbackUrl = null, + string[] users = null, + Web.Core.Files.DocumentService.MetaData meta = null) + { + Logger.DebugFormat("DocService command {0} fileId '{1}' docKey '{2}' callbackUrl '{3}' users '{4}' meta '{5}'", method, fileId, docKeyForTrack, callbackUrl, users != null ? string.Join(", ", users) : null, JsonConvert.SerializeObject(meta)); + try + { + var result = Web.Core.Files.DocumentService.CommandRequest( + FileUtility, + FilesLinkUtility.DocServiceCommandUrl, + method, + GenerateRevisionId(docKeyForTrack), + callbackUrl, + users, + meta, + FileUtility.SignatureSecret, + out var version); + + if (result == Web.Core.Files.DocumentService.CommandResultTypes.NoError) + { + return true; + } + + Logger.ErrorFormat("DocService command response: '{0}'", result); + } + catch (Exception e) + { + Logger.Error("DocService command error", e); + } + return false; + } + + public string DocbuilderRequest(string requestKey, + string inputScript, + bool isAsync, + out Dictionary urls) + { + string scriptUrl = null; + if (!string.IsNullOrEmpty(inputScript)) + { + using (var stream = new MemoryStream()) + using (var writer = new StreamWriter(stream)) + { + writer.Write(inputScript); + writer.Flush(); + stream.Position = 0; + scriptUrl = PathProvider.GetTempUrl(stream, ".docbuilder"); + } + scriptUrl = ReplaceCommunityAdress(scriptUrl); + requestKey = null; + } + + Logger.DebugFormat("DocService builder requestKey {0} async {1}", requestKey, isAsync); + try + { + return Web.Core.Files.DocumentService.DocbuilderRequest( + FileUtility, + FilesLinkUtility.DocServiceDocbuilderUrl, + GenerateRevisionId(requestKey), + scriptUrl, + isAsync, + FileUtility.SignatureSecret, + out urls); + } + catch (Exception ex) + { + throw CustomizeError(ex); + } + } + + public string GetVersion() + { + Logger.DebugFormat("DocService request version"); + try + { + var result = Web.Core.Files.DocumentService.CommandRequest( + FileUtility, + FilesLinkUtility.DocServiceCommandUrl, + CommandMethod.Version, + GenerateRevisionId(null), + null, + null, + null, + FileUtility.SignatureSecret, + out var version); + + if (result == Web.Core.Files.DocumentService.CommandResultTypes.NoError) + { + return version; + } + + Logger.ErrorFormat("DocService command response: '{0}'", result); + } + catch (Exception e) + { + Logger.Error("DocService command error", e); + } + return "4.1.5.1"; + } + + public void CheckDocServiceUrl() + { + if (!string.IsNullOrEmpty(FilesLinkUtility.DocServiceHealthcheckUrl)) + { + try + { + if (!Web.Core.Files.DocumentService.HealthcheckRequest(FilesLinkUtility.DocServiceHealthcheckUrl)) + { + throw new Exception("bad status"); + } + } + catch (Exception ex) + { + Logger.Error("Healthcheck DocService check error", ex); + throw new Exception("Healthcheck url: " + ex.Message); + } + } + + if (!string.IsNullOrEmpty(FilesLinkUtility.DocServiceConverterUrl)) + { + string convertedFileUri; + try + { + const string fileExtension = ".docx"; + var toExtension = FileUtility.GetInternalExtension(fileExtension); + var url = PathProvider.GetEmptyFileUrl(fileExtension); + + var fileUri = ReplaceCommunityAdress(url); + + var key = GenerateRevisionId(Guid.NewGuid().ToString()); + Web.Core.Files.DocumentService.GetConvertedUri(FileUtility, FilesLinkUtility.DocServiceConverterUrl, fileUri, fileExtension, toExtension, key, null, false, FileUtility.SignatureSecret, out convertedFileUri); + } + catch (Exception ex) + { + Logger.Error("Converter DocService check error", ex); + throw new Exception("Converter url: " + ex.Message); + } + + try + { + var request = (HttpWebRequest)WebRequest.Create(convertedFileUri); + using var response = (HttpWebResponse)request.GetResponse(); + if (response.StatusCode != HttpStatusCode.OK) + { + throw new Exception("Converted url is not available"); + } + } + catch (Exception ex) + { + Logger.Error("Document DocService check error", ex); + throw new Exception("Document server: " + ex.Message); + } + } + + if (!string.IsNullOrEmpty(FilesLinkUtility.DocServiceCommandUrl)) + { + try + { + var key = GenerateRevisionId(Guid.NewGuid().ToString()); + Web.Core.Files.DocumentService.CommandRequest(FileUtility, FilesLinkUtility.DocServiceCommandUrl, CommandMethod.Version, key, null, null, null, FileUtility.SignatureSecret, out var version); + } + catch (Exception ex) + { + Logger.Error("Command DocService check error", ex); + throw new Exception("Command url: " + ex.Message); + } + } + + if (!string.IsNullOrEmpty(FilesLinkUtility.DocServiceDocbuilderUrl)) + { + try + { + var storeTemplate = GlobalStore.GetStoreTemplate(); + var scriptUri = storeTemplate.GetUri("", "test.docbuilder"); + var scriptUrl = BaseCommonLinkUtility.GetFullAbsolutePath(scriptUri.ToString()); + scriptUrl = ReplaceCommunityAdress(scriptUrl); + + Web.Core.Files.DocumentService.DocbuilderRequest(FileUtility, FilesLinkUtility.DocServiceDocbuilderUrl, null, scriptUrl, false, FileUtility.SignatureSecret, out var urls); + } + catch (Exception ex) + { + Logger.Error("DocService check error", ex); + throw new Exception("Docbuilder url: " + ex.Message); + } + } + } + + public string ReplaceCommunityAdress(string url) + { + var docServicePortalUrl = FilesLinkUtility.DocServicePortalUrl; + + if (string.IsNullOrEmpty(url)) + { + return url; + } + + if (string.IsNullOrEmpty(docServicePortalUrl)) + { + Tenant tenant; + if (!TenantExtra.Saas + || string.IsNullOrEmpty((tenant = TenantManager.GetCurrentTenant()).MappedDomain) + || !url.StartsWith("https://" + tenant.MappedDomain)) + { + return url; + } + + docServicePortalUrl = "https://" + tenant.GetTenantDomain(CoreSettings, false); + } + + var uri = new UriBuilder(url); + if (new UriBuilder(BaseCommonLinkUtility.ServerRootPath).Host != uri.Host) + { + return url; + } + + var urlRewriterQuery = uri.Scheme + Uri.SchemeDelimiter + uri.Host + ":" + uri.Port; + var query = HttpUtility.ParseQueryString(uri.Query); + query[HttpRequestExtensions.UrlRewriterHeader] = urlRewriterQuery; + uri.Query = query.ToString(); + + var communityUrl = new UriBuilder(docServicePortalUrl); + uri.Scheme = communityUrl.Scheme; + uri.Host = communityUrl.Host; + uri.Port = communityUrl.Port; + + return uri.ToString(); + } + + public string ReplaceDocumentAdress(string url) + { + if (string.IsNullOrEmpty(url)) + { + return url; + } + + var uri = new UriBuilder(url).ToString(); + var externalUri = new UriBuilder(BaseCommonLinkUtility.GetFullAbsolutePath(FilesLinkUtility.DocServiceUrl)).ToString(); + var internalUri = new UriBuilder(BaseCommonLinkUtility.GetFullAbsolutePath(FilesLinkUtility.DocServiceUrlInternal)).ToString(); + if (uri.StartsWith(internalUri, true, CultureInfo.InvariantCulture) || !uri.StartsWith(externalUri, true, CultureInfo.InvariantCulture)) + { + return url; + } + + uri = uri.Replace(externalUri, FilesLinkUtility.DocServiceUrlInternal); + + return uri; + } + + private Exception CustomizeError(Exception ex) + { + var error = FilesCommonResource.ErrorMassage_DocServiceException; + if (!string.IsNullOrEmpty(ex.Message)) + error += string.Format(" ({0})", ex.Message); + + Logger.Error("DocService error", ex); + return new Exception(error, ex); + } + } + public static class DocumentServiceConnectorExtension + { + public static DIHelper AddDocumentServiceConnectorService(this DIHelper services) + { + services.TryAddScoped(); + + return services + .AddFilesLinkUtilityService() + .AddFileUtilityService() + .AddPathProviderService() + .AddGlobalStoreService() + .AddBaseCommonLinkUtilityService() + .AddTenantManagerService() + .AddTenantExtraService() + .AddCoreSettingsService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Services/DocumentService/DocumentServiceHelper.cs b/products/ASC.Files/Server/Services/DocumentService/DocumentServiceHelper.cs new file mode 100644 index 0000000000..84616f3b68 --- /dev/null +++ b/products/ASC.Files/Server/Services/DocumentService/DocumentServiceHelper.cs @@ -0,0 +1,398 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.IO; +using System.Linq; +using System.Security; +using System.Text; + +using ASC.Common; +using ASC.Core; +using ASC.Core.Users; +using ASC.Files.Core; +using ASC.Files.Core.Data; +using ASC.Files.Core.Security; +using ASC.Files.Resources; +using ASC.Security.Cryptography; +using ASC.Web.Core.Files; +using ASC.Web.Files.Classes; +using ASC.Web.Files.Utils; +using ASC.Web.Studio.Core; + +using JWT; + +using File = ASC.Files.Core.File; +using FileShare = ASC.Files.Core.Security.FileShare; + +namespace ASC.Web.Files.Services.DocumentService +{ + public class DocumentServiceHelper + { + public IDaoFactory DaoFactory { get; } + public FileShareLink FileShareLink { get; } + public UserManager UserManager { get; } + public AuthContext AuthContext { get; } + public FileSecurity FileSecurity { get; } + public SetupInfo SetupInfo { get; } + public FileUtility FileUtility { get; } + public MachinePseudoKeys MachinePseudoKeys { get; } + public Global Global { get; } + public DocumentServiceConnector DocumentServiceConnector { get; } + public LockerManager LockerManager { get; } + public IServiceProvider ServiceProvider { get; } + + public DocumentServiceHelper( + IDaoFactory daoFactory, + FileShareLink fileShareLink, + UserManager userManager, + AuthContext authContext, + FileSecurity fileSecurity, + SetupInfo setupInfo, + FileUtility fileUtility, + MachinePseudoKeys machinePseudoKeys, + Global global, + DocumentServiceConnector documentServiceConnector, + LockerManager lockerManager, + IServiceProvider serviceProvider) + { + DaoFactory = daoFactory; + FileShareLink = fileShareLink; + UserManager = userManager; + AuthContext = authContext; + FileSecurity = fileSecurity; + SetupInfo = setupInfo; + FileUtility = fileUtility; + MachinePseudoKeys = machinePseudoKeys; + Global = global; + DocumentServiceConnector = documentServiceConnector; + LockerManager = lockerManager; + ServiceProvider = serviceProvider; + } + + public File GetParams(object fileId, int version, string doc, bool editPossible, bool tryEdit, bool tryCoauth, out Configuration configuration) + { + var lastVersion = true; + FileShare linkRight; + + var fileDao = DaoFactory.FileDao; + + linkRight = FileShareLink.Check(doc, fileDao, out var file); + + if (file == null) + { + var curFile = fileDao.GetFile(fileId); + + if (curFile != null && 0 < version && version < curFile.Version) + { + file = fileDao.GetFile(fileId, version); + lastVersion = false; + } + else + { + file = curFile; + } + } + + return GetParams(file, lastVersion, linkRight, true, true, editPossible, tryEdit, tryCoauth, out configuration); + } + + public File GetParams(File file, bool lastVersion, FileShare linkRight, bool rightToRename, bool rightToEdit, bool editPossible, bool tryEdit, bool tryCoauth, out Configuration configuration) + { + if (file == null) throw new FileNotFoundException(FilesCommonResource.ErrorMassage_FileNotFound); + if (!string.IsNullOrEmpty(file.Error)) throw new Exception(file.Error); + + var rightToReview = rightToEdit; + var reviewPossible = editPossible; + + var rightToFillForms = rightToEdit; + var fillFormsPossible = editPossible; + + var rightToComment = rightToEdit; + var commentPossible = editPossible; + + if (linkRight == FileShare.Restrict && UserManager.GetUsers(AuthContext.CurrentAccount.ID).IsVisitor(UserManager)) + { + rightToEdit = false; + rightToReview = false; + rightToFillForms = false; + rightToComment = false; + } + + var fileSecurity = FileSecurity; + rightToEdit = rightToEdit + && (linkRight == FileShare.ReadWrite + || fileSecurity.CanEdit(file)); + if (editPossible && !rightToEdit) + { + editPossible = false; + } + + rightToRename = rightToRename && rightToEdit && fileSecurity.CanEdit(file); + + rightToReview = rightToReview + && (linkRight == FileShare.Review || linkRight == FileShare.ReadWrite + || fileSecurity.CanReview(file)); + if (reviewPossible && !rightToReview) + { + reviewPossible = false; + } + + rightToFillForms = rightToFillForms + && (linkRight == FileShare.FillForms || linkRight == FileShare.Review || linkRight == FileShare.ReadWrite + || fileSecurity.CanFillForms(file)); + if (fillFormsPossible && !rightToFillForms) + { + fillFormsPossible = false; + } + + rightToComment = rightToComment + && (linkRight == FileShare.Comment || linkRight == FileShare.Review || linkRight == FileShare.ReadWrite + || fileSecurity.CanComment(file)); + if (commentPossible && !rightToComment) + { + commentPossible = false; + } + + if (linkRight == FileShare.Restrict + && !(editPossible || reviewPossible || fillFormsPossible || commentPossible) + && !fileSecurity.CanRead(file)) throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException_ReadFile); + + if (file.RootFolderType == FolderType.TRASH) throw new Exception(FilesCommonResource.ErrorMassage_ViewTrashItem); + + if (file.ContentLength > SetupInfo.AvailableFileSize) throw new Exception(string.Format(FilesCommonResource.ErrorMassage_FileSizeEdit, FileSizeComment.FilesSizeToString(SetupInfo.AvailableFileSize))); + + string strError = null; + if ((editPossible || reviewPossible || fillFormsPossible || commentPossible) + && LockerManager.FileLockedForMe(file.ID)) + { + if (tryEdit) + { + strError = FilesCommonResource.ErrorMassage_LockedFile; + } + rightToRename = false; + rightToEdit = editPossible = false; + rightToReview = reviewPossible = false; + rightToFillForms = fillFormsPossible = false; + rightToComment = commentPossible = false; + } + + if (editPossible + && !FileUtility.CanWebEdit(file.Title)) + { + rightToEdit = editPossible = false; + } + + if (!editPossible && !FileUtility.CanWebView(file.Title)) throw new Exception(string.Format("{0} ({1})", FilesCommonResource.ErrorMassage_NotSupportedFormat, FileUtility.GetFileExtension(file.Title))); + + if (reviewPossible && + !FileUtility.CanWebReview(file.Title)) + { + rightToReview = reviewPossible = false; + } + + if (fillFormsPossible && + !FileUtility.CanWebRestrictedEditing(file.Title)) + { + rightToFillForms = fillFormsPossible = false; + } + + if (commentPossible && + !FileUtility.CanWebComment(file.Title)) + { + rightToComment = commentPossible = false; + } + + var rightChangeHistory = rightToEdit; + + if (FileTracker.IsEditing(file.ID)) + { + rightChangeHistory = false; + + bool coauth; + if ((editPossible || reviewPossible || fillFormsPossible || commentPossible) + && tryCoauth + && (!(coauth = FileUtility.CanCoAuhtoring(file.Title)) || FileTracker.IsEditingAlone(file.ID))) + { + if (tryEdit) + { + var editingBy = FileTracker.GetEditingBy(file.ID).FirstOrDefault(); + strError = string.Format(!coauth + ? FilesCommonResource.ErrorMassage_EditingCoauth + : FilesCommonResource.ErrorMassage_EditingMobile, + Global.GetUserName(editingBy, true)); + } + rightToEdit = editPossible = reviewPossible = fillFormsPossible = commentPossible = false; + } + } + + var fileStable = file; + if (lastVersion && file.Forcesave != ForcesaveType.None && tryEdit) + { + var fileDao = DaoFactory.FileDao; + fileStable = fileDao.GetFileStable(file.ID, file.Version); + } + + var docKey = GetDocKey(fileStable); + var modeWrite = (editPossible || reviewPossible || fillFormsPossible || commentPossible) && tryEdit; + + configuration = new Configuration(file, ServiceProvider) + { + Document = + { + Key = docKey, + Permissions = + { + Edit = rightToEdit && lastVersion, + Rename = rightToRename && lastVersion && !file.ProviderEntry, + Review = rightToReview && lastVersion, + FillForms = rightToFillForms && lastVersion, + Comment = rightToComment && lastVersion, + ChangeHistory = rightChangeHistory, + } + }, + EditorConfig = + { + ModeWrite = modeWrite, + }, + ErrorMessage = strError, + }; + + if (!lastVersion) + { + configuration.Document.Title += string.Format(" ({0})", file.CreateOnString); + } + + return file; + } + + + public string GetSignature(object payload) + { + if (string.IsNullOrEmpty(FileUtility.SignatureSecret)) return null; + + JsonWebToken.JsonSerializer = new Web.Core.Files.DocumentService.JwtSerializer(); + return JsonWebToken.Encode(payload, FileUtility.SignatureSecret, JwtHashAlgorithm.HS256); + } + + + public string GetDocKey(File file) + { + return GetDocKey(file.ID, file.Version, file.ProviderEntry ? file.ModifiedOn : file.CreateOn); + } + + public string GetDocKey(object fileId, int fileVersion, DateTime modified) + { + var str = string.Format("teamlab_{0}_{1}_{2}_{3}", + fileId, + fileVersion, + modified.GetHashCode(), + Global.GetDocDbKey()); + + var keyDoc = Encoding.UTF8.GetBytes(str) + .ToList() + .Concat(MachinePseudoKeys.GetMachineConstant()) + .ToArray(); + + return DocumentServiceConnector.GenerateRevisionId(Hasher.Base64Hash(keyDoc, HashAlg.SHA256)); + } + + + public void CheckUsersForDrop(File file) + { + var fileSecurity = FileSecurity; + var sharedLink = + fileSecurity.CanEdit(file, FileConstant.ShareLinkId) + || fileSecurity.CanReview(file, FileConstant.ShareLinkId) + || fileSecurity.CanFillForms(file, FileConstant.ShareLinkId) + || fileSecurity.CanComment(file, FileConstant.ShareLinkId); + + var usersDrop = FileTracker.GetEditingBy(file.ID) + .Where(uid => + { + if (!UserManager.UserExists(uid)) + { + return !sharedLink; + } + return !fileSecurity.CanEdit(file, uid) && !fileSecurity.CanReview(file, uid) && !fileSecurity.CanFillForms(file, uid) && !fileSecurity.CanComment(file, uid); + }) + .Select(u => u.ToString()).ToArray(); + + if (!usersDrop.Any()) return; + + var fileStable = file; + if (file.Forcesave != ForcesaveType.None) + { + var fileDao = DaoFactory.FileDao; + fileStable = fileDao.GetFileStable(file.ID, file.Version); + } + + var docKey = GetDocKey(fileStable); + DropUser(docKey, usersDrop, file.ID); + } + + public bool DropUser(string docKeyForTrack, string[] users, object fileId = null) + { + return DocumentServiceConnector.Command(Web.Core.Files.DocumentService.CommandMethod.Drop, docKeyForTrack, fileId, null, users); + } + + public bool RenameFile(File file, IFileDao fileDao) + { + if (!FileUtility.CanWebView(file.Title) + && !FileUtility.CanWebEdit(file.Title) + && !FileUtility.CanWebReview(file.Title) + && !FileUtility.CanWebRestrictedEditing(file.Title) + && !FileUtility.CanWebComment(file.Title)) + return true; + + var fileStable = file.Forcesave == ForcesaveType.None ? file : fileDao.GetFileStable(file.ID, file.Version); + var docKeyForTrack = GetDocKey(fileStable); + + var meta = new Web.Core.Files.DocumentService.MetaData { Title = file.Title }; + return DocumentServiceConnector.Command(Web.Core.Files.DocumentService.CommandMethod.Meta, docKeyForTrack, file.ID, meta: meta); + } + } + public static class DocumentServiceHelperExtention + { + public static DIHelper AddDocumentServiceHelperService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddDaoFactoryService() + .AddFileShareLinkService() + .AddUserManagerService() + .AddAuthContextService() + .AddFileSecurityService() + .AddSetupInfo() + .AddLockerManagerService() + .AddFileUtilityService() + .AddMachinePseudoKeysService() + .AddGlobalService() + .AddDocumentServiceConnectorService() + .AddConfigurationService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Services/DocumentService/DocumentServiceParams.cs b/products/ASC.Files/Server/Services/DocumentService/DocumentServiceParams.cs new file mode 100644 index 0000000000..b1e41b02ec --- /dev/null +++ b/products/ASC.Files/Server/Services/DocumentService/DocumentServiceParams.cs @@ -0,0 +1,94 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.IO; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Json; +using System.Text; + +namespace ASC.Web.Files.Services.DocumentService +{ + [DataContract(Name = "docServiceParams", Namespace = "")] + public class DocumentServiceParams + { + [DataMember(Name = "displayName")] + public string DisplayName; + + [DataMember(Name = "docKeyForTrack")] + public string DocKeyForTrack; + + [DataMember(Name = "editByUrl")] + public bool EditByUrl; + + [DataMember(Name = "email")] + public string Email; + + [DataMember(Name = "fileId", EmitDefaultValue = false)] + public string FileId; + + [DataMember(Name = "fileProviderKey", EmitDefaultValue = false)] + public string FileProviderKey; + + [DataMember(Name = "fileVersion", EmitDefaultValue = false)] + public int FileVersion; + + [DataMember(Name = "linkToEdit")] + public string LinkToEdit; + + [DataMember(Name = "openHistory", EmitDefaultValue = false)] + public bool OpenHistory; + + [DataMember(Name = "openinigDate")] + public string OpeninigDate; + + [DataMember(Name = "serverErrorMessage")] + public string ServerErrorMessage; + + [DataMember(Name = "shareLinkParam")] + public string ShareLinkParam; + + [DataMember(Name = "tabId")] + public string TabId; + + [DataMember(Name = "thirdPartyApp")] + public bool ThirdPartyApp; + + [DataMember(Name = "canGetUsers")] + public bool CanGetUsers; + + + public static string Serialize(DocumentServiceParams docServiceParams) + { + using (var ms = new MemoryStream()) + { + var serializer = new DataContractJsonSerializer(typeof (DocumentServiceParams)); + serializer.WriteObject(ms, docServiceParams); + ms.Seek(0, SeekOrigin.Begin); + return Encoding.UTF8.GetString(ms.GetBuffer(), 0, (int)ms.Length); + } + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Services/DocumentService/DocumentServiceTracker.cs b/products/ASC.Files/Server/Services/DocumentService/DocumentServiceTracker.cs new file mode 100644 index 0000000000..afe143ba01 --- /dev/null +++ b/products/ASC.Files/Server/Services/DocumentService/DocumentServiceTracker.cs @@ -0,0 +1,688 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Json; +using System.Text; +using System.Threading; +using System.Web; + +using ASC.Common; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Common; +using ASC.Core.Users; +using ASC.Files.Core; +using ASC.Files.Core.Data; +using ASC.Files.Resources; +using ASC.MessagingSystem; +using ASC.Security.Cryptography; +using ASC.Web.Core.Files; +using ASC.Web.Core.Users; +using ASC.Web.Files.Classes; +using ASC.Web.Files.Core; +using ASC.Web.Files.Core.Entries; +using ASC.Web.Files.Helpers; +using ASC.Web.Files.Services.NotifyService; +using ASC.Web.Files.ThirdPartyApp; +using ASC.Web.Files.Utils; + +using Microsoft.Extensions.Options; + +using static ASC.Web.Files.Services.DocumentService.DocumentServiceTracker; + +using CommandMethod = ASC.Web.Core.Files.DocumentService.CommandMethod; +using File = ASC.Files.Core.File; + +namespace ASC.Web.Files.Services.DocumentService +{ + public class DocumentServiceTracker + { + #region Class + + public enum TrackerStatus + { + NotFound = 0, + Editing = 1, + MustSave = 2, + Corrupted = 3, + Closed = 4, + MailMerge = 5, + ForceSave = 6, + CorruptedForceSave = 7, + } + + [DebuggerDisplay("{Status} - {Key}")] + public class TrackerData + { + public List Actions; + public string ChangesUrl; + public ForceSaveInitiator ForceSaveType; + public object History; + public string Key; + public MailMergeData MailMerge; + public TrackerStatus Status; + public string Token; + public string Url; + public List Users; + public string UserData; + public bool Encrypted; + + [DebuggerDisplay("{Type} - {UserId}")] + public class Action + { + public string Type; + public string UserId; + } + + public enum ForceSaveInitiator + { + Command = 0, + User = 1, + Timer = 2 + } + } + + public enum MailMergeType + { + Html = 0, + AttachDocx = 1, + AttachPdf = 2, + } + + [DebuggerDisplay("{From}")] + public class MailMergeData + { + public int RecordCount; + public int RecordErrorCount; + public int RecordIndex; + + public string From; + public string Subject; + public string To; + public MailMergeType Type; + + public string Title; //attach + public string Message; //attach + } + + [Serializable] + [DataContract(Name = "response", Namespace = "")] + public class TrackResponse + { + [DataMember(Name = "error")] + public int Error + { + set { } + get + { + return string.IsNullOrEmpty(Message) + ? 0 //error:0 - sended + : 1; //error:1 - some error + } + } + + [DataMember(Name = "message", EmitDefaultValue = false)] + public string Message = null; + + [DataMember(Name = "addresses", EmitDefaultValue = false)] + public string[] Addresses = null; + + public static string Serialize(TrackResponse response) + { + using (var ms = new MemoryStream()) + { + var serializer = new DataContractJsonSerializer(typeof(TrackResponse)); + serializer.WriteObject(ms, response); + ms.Seek(0, SeekOrigin.Begin); + return Encoding.UTF8.GetString(ms.GetBuffer(), 0, (int)ms.Length); + } + } + } + + #endregion + } + + public class DocumentServiceTrackerHelper + { + public SecurityContext SecurityContext { get; } + public UserManager UserManager { get; } + public TenantManager TenantManager { get; } + public FilesLinkUtility FilesLinkUtility { get; } + public EmailValidationKeyProvider EmailValidationKeyProvider { get; } + public BaseCommonLinkUtility BaseCommonLinkUtility { get; } + public SocketManager SocketManager { get; } + public GlobalStore GlobalStore { get; } + public DisplayUserSettingsHelper DisplayUserSettingsHelper { get; } + public IDaoFactory DaoFactory { get; } + public DocumentServiceHelper DocumentServiceHelper { get; } + public EntryManager EntryManager { get; } + public FileShareLink FileShareLink { get; } + public FilesMessageService FilesMessageService { get; } + public EncryptionAddressHelper EncryptionAddressHelper { get; } + public DocumentServiceConnector DocumentServiceConnector { get; } + public NotifyClient NotifyClient { get; } + public MailMergeTaskRunner MailMergeTaskRunner { get; } + public ILog Logger { get; } + + public DocumentServiceTrackerHelper( + SecurityContext securityContext, + UserManager userManager, + TenantManager tenantManager, + FilesLinkUtility filesLinkUtility, + EmailValidationKeyProvider emailValidationKeyProvider, + BaseCommonLinkUtility baseCommonLinkUtility, + SocketManager socketManager, + GlobalStore globalStore, + DisplayUserSettingsHelper displayUserSettingsHelper, + IDaoFactory daoFactory, + IOptionsMonitor options, + DocumentServiceHelper documentServiceHelper, + EntryManager entryManager, + FileShareLink fileShareLink, + FilesMessageService filesMessageService, + EncryptionAddressHelper encryptionAddressHelper, + DocumentServiceConnector documentServiceConnector, + NotifyClient notifyClient, + MailMergeTaskRunner mailMergeTaskRunner) + { + SecurityContext = securityContext; + UserManager = userManager; + TenantManager = tenantManager; + FilesLinkUtility = filesLinkUtility; + EmailValidationKeyProvider = emailValidationKeyProvider; + BaseCommonLinkUtility = baseCommonLinkUtility; + SocketManager = socketManager; + GlobalStore = globalStore; + DisplayUserSettingsHelper = displayUserSettingsHelper; + DaoFactory = daoFactory; + DocumentServiceHelper = documentServiceHelper; + EntryManager = entryManager; + FileShareLink = fileShareLink; + FilesMessageService = filesMessageService; + EncryptionAddressHelper = encryptionAddressHelper; + DocumentServiceConnector = documentServiceConnector; + NotifyClient = notifyClient; + MailMergeTaskRunner = mailMergeTaskRunner; + Logger = options.CurrentValue; + } + + public string GetCallbackUrl(string fileId) + { + var callbackUrl = BaseCommonLinkUtility.GetFullAbsolutePath(FilesLinkUtility.FileHandlerPath + + "?" + FilesLinkUtility.Action + "=track" + + "&" + FilesLinkUtility.FileId + "=" + HttpUtility.UrlEncode(fileId) + + "&" + FilesLinkUtility.AuthKey + "=" + EmailValidationKeyProvider.GetEmailKey(fileId)); + callbackUrl = DocumentServiceConnector.ReplaceCommunityAdress(callbackUrl); + return callbackUrl; + } + + public bool StartTrack(string fileId, string docKeyForTrack) + { + var callbackUrl = GetCallbackUrl(fileId); + return DocumentServiceConnector.Command(CommandMethod.Info, docKeyForTrack, fileId, callbackUrl); + } + + public TrackResponse ProcessData(string fileId, TrackerData fileData) + { + switch (fileData.Status) + { + case TrackerStatus.NotFound: + case TrackerStatus.Closed: + FileTracker.Remove(fileId); + SocketManager.FilesChangeEditors(fileId, true); + break; + + case TrackerStatus.Editing: + ProcessEdit(fileId, fileData); + break; + + case TrackerStatus.MustSave: + case TrackerStatus.Corrupted: + case TrackerStatus.ForceSave: + case TrackerStatus.CorruptedForceSave: + return ProcessSave(fileId, fileData); + + case TrackerStatus.MailMerge: + return ProcessMailMerge(fileId, fileData); + } + return null; + } + + private void ProcessEdit(string fileId, TrackerData fileData) + { + if (ThirdPartySelector.GetAppByFileId(fileId) != null) + { + return; + } + + var users = FileTracker.GetEditingBy(fileId); + var usersDrop = new List(); + + string docKey; + var app = ThirdPartySelector.GetAppByFileId(fileId); + if (app == null) + { + File fileStable; + fileStable = DaoFactory.FileDao.GetFileStable(fileId); + + docKey = DocumentServiceHelper.GetDocKey(fileStable); + } + else + { + docKey = fileData.Key; + } + + if (!fileData.Key.Equals(docKey)) + { + Logger.InfoFormat("DocService editing file {0} ({1}) with key {2} for {3}", fileId, docKey, fileData.Key, string.Join(", ", fileData.Users)); + usersDrop = fileData.Users; + } + else + { + foreach (var user in fileData.Users) + { + if (!Guid.TryParse(user, out var userId)) + { + Logger.Error("DocService userId is not Guid: " + user); + continue; + } + users.Remove(userId); + + try + { + var doc = FileShareLink.CreateKey(fileId); + EntryManager.TrackEditing(fileId, userId, userId, doc); + } + catch (Exception e) + { + Logger.DebugFormat("Drop command: fileId '{0}' docKey '{1}' for user {2} : {3}", fileId, fileData.Key, user, e.Message); + usersDrop.Add(userId.ToString()); + } + } + } + + if (usersDrop.Any()) + { + if (!DocumentServiceHelper.DropUser(fileData.Key, usersDrop.ToArray(), fileId)) + { + Logger.Error("DocService drop failed for users " + string.Join(",", usersDrop)); + } + } + + foreach (var removeUserId in users) + { + FileTracker.Remove(fileId, userId: removeUserId); + } + SocketManager.FilesChangeEditors(fileId); + } + + private TrackResponse ProcessSave(string fileId, TrackerData fileData) + { + var comments = new List(); + if (fileData.Status == TrackerStatus.Corrupted + || fileData.Status == TrackerStatus.CorruptedForceSave) + comments.Add(FilesCommonResource.ErrorMassage_SaveCorrupted); + + var forcesave = fileData.Status == TrackerStatus.ForceSave || fileData.Status == TrackerStatus.CorruptedForceSave; + + if (fileData.Users == null || fileData.Users.Count == 0 || !Guid.TryParse(fileData.Users[0], out var userId)) + { + userId = FileTracker.GetEditingBy(fileId).FirstOrDefault(); + } + + var app = ThirdPartySelector.GetAppByFileId(fileId); + if (app == null) + { + File fileStable; + fileStable = DaoFactory.FileDao.GetFileStable(fileId); + + var docKey = DocumentServiceHelper.GetDocKey(fileStable); + if (!fileData.Key.Equals(docKey)) + { + Logger.ErrorFormat("DocService saving file {0} ({1}) with key {2}", fileId, docKey, fileData.Key); + + StoringFileAfterError(fileId, userId.ToString(), DocumentServiceConnector.ReplaceDocumentAdress(fileData.Url)); + return new TrackResponse { Message = "Expected key " + docKey }; + } + } + + UserInfo user = null; + try + { + SecurityContext.AuthenticateMe(userId); + + user = UserManager.GetUsers(userId); + var culture = string.IsNullOrEmpty(user.CultureName) ? TenantManager.GetCurrentTenant().GetCulture() : CultureInfo.GetCultureInfo(user.CultureName); + Thread.CurrentThread.CurrentCulture = culture; + Thread.CurrentThread.CurrentUICulture = culture; + } + catch (Exception ex) + { + Logger.Info("DocService save error: anonymous author - " + userId, ex); + if (!userId.Equals(ASC.Core.Configuration.Constants.Guest.ID)) + { + comments.Add(FilesCommonResource.ErrorMassage_SaveAnonymous); + } + } + + File file = null; + var saveMessage = "Not saved"; + + if (string.IsNullOrEmpty(fileData.Url)) + { + try + { + comments.Add(FilesCommonResource.ErrorMassage_SaveUrlLost); + + file = EntryManager.CompleteVersionFile(fileId, 0, false, false); + + DaoFactory.FileDao.UpdateComment(file.ID, file.Version, string.Join("; ", comments)); + + file = null; + Logger.ErrorFormat("DocService save error. Empty url. File id: '{0}'. UserId: {1}. DocKey '{2}'", fileId, userId, fileData.Key); + } + catch (Exception ex) + { + Logger.Error(string.Format("DocService save error. Version update. File id: '{0}'. UserId: {1}. DocKey '{2}'", fileId, userId, fileData.Key), ex); + } + } + else + { + if (fileData.Encrypted) + { + comments.Add(FilesCommonResource.CommentEditEncrypt); + } + + var forcesaveType = ForcesaveType.None; + if (forcesave) + { + switch (fileData.ForceSaveType) + { + case TrackerData.ForceSaveInitiator.Command: + forcesaveType = ForcesaveType.Command; + break; + case TrackerData.ForceSaveInitiator.Timer: + forcesaveType = ForcesaveType.Timer; + break; + case TrackerData.ForceSaveInitiator.User: + forcesaveType = ForcesaveType.User; + break; + } + comments.Add(fileData.ForceSaveType == TrackerData.ForceSaveInitiator.User + ? FilesCommonResource.CommentForcesave + : FilesCommonResource.CommentAutosave); + } + + try + { + file = EntryManager.SaveEditing(fileId, null, DocumentServiceConnector.ReplaceDocumentAdress(fileData.Url), null, string.Empty, string.Join("; ", comments), false, fileData.Encrypted, forcesaveType); + saveMessage = fileData.Status == TrackerStatus.MustSave || fileData.Status == TrackerStatus.ForceSave ? null : "Status " + fileData.Status; + } + catch (Exception ex) + { + Logger.Error(string.Format("DocService save error. File id: '{0}'. UserId: {1}. DocKey '{2}'. DownloadUri: {3}", fileId, userId, fileData.Key, fileData.Url), ex); + saveMessage = ex.Message; + + StoringFileAfterError(fileId, userId.ToString(), DocumentServiceConnector.ReplaceDocumentAdress(fileData.Url)); + } + } + + if (!forcesave) + FileTracker.Remove(fileId); + + if (file != null) + { + if (user != null) + FilesMessageService.Send(file, MessageInitiator.DocsService, MessageAction.UserFileUpdated, user.DisplayUserName(false, DisplayUserSettingsHelper), file.Title); + + if (!forcesave) + SaveHistory(file, (fileData.History ?? "").ToString(), DocumentServiceConnector.ReplaceDocumentAdress(fileData.ChangesUrl)); + } + + SocketManager.FilesChangeEditors(fileId, !forcesave); + + var result = new TrackResponse { Message = saveMessage }; + if (string.IsNullOrEmpty(saveMessage) && file != null && file.Encrypted) + { + result.Addresses = EncryptionAddressHelper.GetAddresses(file.ID.ToString()).ToArray(); + } + return result; + } + + private TrackResponse ProcessMailMerge(string fileId, TrackerData fileData) + { + if (fileData.Users == null || fileData.Users.Count == 0 || !Guid.TryParse(fileData.Users[0], out var userId)) + { + userId = FileTracker.GetEditingBy(fileId).FirstOrDefault(); + } + + string saveMessage; + + try + { + SecurityContext.AuthenticateMe(userId); + + var user = UserManager.GetUsers(userId); + var culture = string.IsNullOrEmpty(user.CultureName) ? TenantManager.GetCurrentTenant().GetCulture() : CultureInfo.GetCultureInfo(user.CultureName); + Thread.CurrentThread.CurrentCulture = culture; + Thread.CurrentThread.CurrentUICulture = culture; + + if (string.IsNullOrEmpty(fileData.Url)) throw new ArgumentException("emptry url"); + + if (fileData.MailMerge == null) throw new ArgumentException("MailMerge is null"); + + var message = fileData.MailMerge.Message; + Stream attach = null; + switch (fileData.MailMerge.Type) + { + case MailMergeType.AttachDocx: + case MailMergeType.AttachPdf: + var downloadRequest = (HttpWebRequest)WebRequest.Create(DocumentServiceConnector.ReplaceDocumentAdress(fileData.Url)); + + // hack. http://ubuntuforums.org/showthread.php?t=1841740 + if (WorkContext.IsMono) + { + ServicePointManager.ServerCertificateValidationCallback += (s, ce, ca, p) => true; + } + + using (var downloadStream = new ResponseStream(downloadRequest.GetResponse())) + { + const int bufferSize = 2048; + var buffer = new byte[bufferSize]; + int readed; + attach = new MemoryStream(); + while ((readed = downloadStream.Read(buffer, 0, bufferSize)) > 0) + { + attach.Write(buffer, 0, readed); + } + attach.Position = 0; + } + + if (string.IsNullOrEmpty(fileData.MailMerge.Title)) + { + fileData.MailMerge.Title = "Attach"; + } + + var attachExt = fileData.MailMerge.Type == MailMergeType.AttachDocx ? ".docx" : ".pdf"; + var curExt = FileUtility.GetFileExtension(fileData.MailMerge.Title); + if (curExt != attachExt) + { + fileData.MailMerge.Title += attachExt; + } + + break; + + case MailMergeType.Html: + var httpWebRequest = (HttpWebRequest)WebRequest.Create(DocumentServiceConnector.ReplaceDocumentAdress(fileData.Url)); + + // hack. http://ubuntuforums.org/showthread.php?t=1841740 + if (WorkContext.IsMono) + { + ServicePointManager.ServerCertificateValidationCallback += (s, ce, ca, p) => true; + } + + using (var httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse()) + using (var stream = httpWebResponse.GetResponseStream()) + if (stream != null) + using (var reader = new StreamReader(stream, Encoding.GetEncoding(Encoding.UTF8.WebName))) + { + message = reader.ReadToEnd(); + } + break; + } + + using (var mailMergeTask = + new MailMergeTask + { + From = fileData.MailMerge.From, + Subject = fileData.MailMerge.Subject, + To = fileData.MailMerge.To, + Message = message, + AttachTitle = fileData.MailMerge.Title, + Attach = attach + }) + { + var response = MailMergeTaskRunner.Run(mailMergeTask); + Logger.InfoFormat("DocService mailMerge {0}/{1} send: {2}", + fileData.MailMerge.RecordIndex + 1, fileData.MailMerge.RecordCount, response); + } + saveMessage = null; + } + catch (Exception ex) + { + Logger.Error( + string.Format("DocService mailMerge{0} error: userId - {1}, url - {2}", + (fileData.MailMerge == null ? "" : " " + fileData.MailMerge.RecordIndex + "/" + fileData.MailMerge.RecordCount), + userId, fileData.Url), + ex); + saveMessage = ex.Message; + } + + if (fileData.MailMerge != null && + fileData.MailMerge.RecordIndex == fileData.MailMerge.RecordCount - 1) + { + var errorCount = fileData.MailMerge.RecordErrorCount; + if (!string.IsNullOrEmpty(saveMessage)) errorCount++; + + NotifyClient.SendMailMergeEnd(userId, fileData.MailMerge.RecordCount, errorCount); + } + + return new TrackResponse { Message = saveMessage }; + } + + + private void StoringFileAfterError(string fileId, string userId, string downloadUri) + { + try + { + var fileName = Global.ReplaceInvalidCharsAndTruncate(fileId + FileUtility.GetFileExtension(downloadUri)); + var path = string.Format(@"save_crash\{0}\{1}_{2}", + DateTime.UtcNow.ToString("yyyy_MM_dd"), + userId, + fileName); + + var store = GlobalStore.GetStore(); + var req = (HttpWebRequest)WebRequest.Create(downloadUri); + + // hack. http://ubuntuforums.org/showthread.php?t=1841740 + if (WorkContext.IsMono) + { + ServicePointManager.ServerCertificateValidationCallback += (s, ce, ca, p) => true; + } + + using (var fileStream = new ResponseStream(req.GetResponse())) + { + store.Save(FileConstant.StorageDomainTmp, path, fileStream); + } + Logger.DebugFormat("DocService storing to {0}", path); + } + catch (Exception ex) + { + Logger.Error("DocService Error on save file to temp store", ex); + } + } + + private void SaveHistory(File file, string changes, string differenceUrl) + { + if (file == null) return; + if (file.ProviderEntry) return; + if (string.IsNullOrEmpty(changes) || string.IsNullOrEmpty(differenceUrl)) return; + + try + { + var fileDao = DaoFactory.FileDao; + var req = (HttpWebRequest)WebRequest.Create(differenceUrl); + + // hack. http://ubuntuforums.org/showthread.php?t=1841740 + if (WorkContext.IsMono) + { + ServicePointManager.ServerCertificateValidationCallback += (s, ce, ca, p) => true; + } + + using var differenceStream = new ResponseStream(req.GetResponse()); + fileDao.SaveEditHistory(file, changes, differenceStream); + } + catch (Exception ex) + { + Logger.Error("DocService save history error", ex); + } + } + } + public static class DocumentServiceTrackerHelperExtension + { + public static DIHelper AddDocumentServiceTrackerHelperService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddSecurityContextService() + .AddUserManagerService() + .AddTenantManagerService() + .AddFilesLinkUtilityService() + .AddEmailValidationKeyProviderService() + .AddBaseCommonLinkUtilityService() + .AddGlobalStoreService() + .AddDisplayUserSettingsService() + .AddDaoFactoryService() + .AddDocumentServiceHelperService() + .AddEntryManagerService() + .AddFileShareLinkService() + .AddFilesMessageService() + .AddDocumentServiceConnectorService() + .AddNotifyClientService() + .AddEncryptionAddressHelperService() + .AddSocketManagerService() + .AddMailMergeTaskRunnerService(); + } + } +} diff --git a/products/ASC.Files/Server/Services/FFmpegService/FFmpeg.cs b/products/ASC.Files/Server/Services/FFmpegService/FFmpeg.cs new file mode 100644 index 0000000000..e29b5d488e --- /dev/null +++ b/products/ASC.Files/Server/Services/FFmpegService/FFmpeg.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using ASC.Common.Logging; +using ASC.Core; + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Options; + +namespace ASC.Web.Files.Services.FFmpegService +{ + public class FFmpegService + { + public List MustConvertable + { + get + { + if (string.IsNullOrEmpty(FFmpegPath)) return new List(); + return ConvertableMedia.ToList(); + } + } + + public bool IsConvertable(string extension) + { + return MustConvertable.Contains(extension.TrimStart('.')); + } + + public Stream Convert(Stream inputStream, string inputFormat) + { + if (inputStream == null) throw new ArgumentException(); + if (string.IsNullOrEmpty(inputFormat)) throw new ArgumentException(); + + var startInfo = PrepareFFmpeg(inputFormat); + + Process process; + using (process = new Process { StartInfo = startInfo }) + { + process.Start(); + + var _ = StreamCopyToAsync(inputStream, process.StandardInput.BaseStream, closeDst: true); + + ProcessLog(process.StandardError.BaseStream); + + return process.StandardOutput.BaseStream; + } + } + + public FFmpegService(IOptionsMonitor optionsMonitor, IConfiguration configuration) + { + logger = optionsMonitor.CurrentValue; + FFmpegPath = configuration["files:ffmpeg"]; + FFmpegArgs = configuration["files:ffmpeg:args"] ?? "-i - -preset ultrafast -movflags frag_keyframe+empty_moov -f {0} -"; + + var exts = configuration["files:ffmpeg:exts"]; + ConvertableMedia = new List(); + + if (!string.IsNullOrEmpty(exts)) + { + try + { + ConvertableMedia = exts.Split('|').ToList(); + } + catch (Exception e) + { + logger.Error("Couldn't parse 'files.ffmpeg.exts' setting.", e); + } + } + + if (string.IsNullOrEmpty(FFmpegPath)) + { + var pathvar = Environment.GetEnvironmentVariable("PATH"); + var folders = pathvar.Split(WorkContext.IsMono ? ':' : ';').Distinct(); + foreach (var folder in folders) + { + if (!Directory.Exists(folder)) continue; + + foreach (var name in FFmpegExecutables) + { + var path = Path.Combine(folder, WorkContext.IsMono ? name : name + ".exe"); + if (File.Exists(path)) + { + FFmpegPath = path; + logger.InfoFormat("FFmpeg found in {0}", path); + break; + } + } + + if (!string.IsNullOrEmpty(FFmpegPath)) break; + } + } + } + + private readonly List ConvertableMedia; + private readonly List FFmpegExecutables = new List() { "ffmpeg", "avconv" }; + private readonly string FFmpegPath; + private readonly string FFmpegArgs; + + private readonly ILog logger; + + private ProcessStartInfo PrepareFFmpeg(string inputFormat) + { + if (!ConvertableMedia.Contains(inputFormat.TrimStart('.'))) throw new ArgumentException(); + + var startInfo = new ProcessStartInfo(); + + if (string.IsNullOrEmpty(FFmpegPath)) + { + logger.Error("FFmpeg/avconv was not found in PATH or 'files.ffmpeg' setting"); + throw new Exception("no ffmpeg"); + } + + startInfo.FileName = FFmpegPath; + startInfo.WorkingDirectory = Path.GetDirectoryName(FFmpegPath); + startInfo.Arguments = string.Format(FFmpegArgs, "mp4"); + startInfo.UseShellExecute = false; + startInfo.RedirectStandardOutput = true; + startInfo.RedirectStandardInput = true; + startInfo.RedirectStandardError = true; + startInfo.CreateNoWindow = true; + startInfo.WindowStyle = ProcessWindowStyle.Normal; + return startInfo; + } + + private static async Task StreamCopyToAsync(Stream srcStream, Stream dstStream, bool closeSrc = false, bool closeDst = false) + { + const int bufs = 2048 * 4; + + if (srcStream == null) throw new ArgumentNullException("srcStream"); + if (dstStream == null) throw new ArgumentNullException("dstStream"); + + var buffer = new byte[bufs]; + int readed; + var total = 0; + while ((readed = await srcStream.ReadAsync(buffer, 0, bufs)) > 0) + { + await dstStream.WriteAsync(buffer, 0, readed); + await dstStream.FlushAsync(); + total += readed; + } + + if (closeSrc) + { + srcStream.Dispose(); + srcStream.Close(); + } + + if (closeDst) + { + await dstStream.FlushAsync(); + dstStream.Dispose(); + dstStream.Close(); + } + + return total; + } + + private async void ProcessLog(Stream stream) + { + using var reader = new StreamReader(stream, Encoding.UTF8); + string line; + while ((line = await reader.ReadLineAsync()) != null) + { + logger.Info(line); + } + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Services/NotifyService/FilesPatternResource.Designer.cs b/products/ASC.Files/Server/Services/NotifyService/FilesPatternResource.Designer.cs new file mode 100644 index 0000000000..51e27ab065 --- /dev/null +++ b/products/ASC.Files/Server/Services/NotifyService/FilesPatternResource.Designer.cs @@ -0,0 +1,215 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ASC.Web.Files.Services.NotifyService { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class FilesPatternResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal FilesPatternResource() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ASC.Web.Files.Services.NotifyService.FilesPatternResource", typeof(FilesPatternResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to h1. All signers completed $Message + /// + ///This is a mail message to notify you that all signers completed "$DocumentTitle":"$DocumentURL". + /// + ///^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal.^. + /// + public static string pattern_DocuSignComplete { + get { + return ResourceManager.GetString("pattern_DocuSignComplete", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to h1. $Message: $DocumentTitle + /// + ///Watch your DocuSign account for more information. + /// + ///^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal.^. + /// + public static string pattern_DocuSignStatus { + get { + return ResourceManager.GetString("pattern_DocuSignStatus", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to h1. "$DocumentTitle":"$DocumentURL" + /// + ///This is a mail message to notify you that you have mentioned by "$__AuthorName":"$__AuthorUrl". + /// + ///$Message + /// + ///^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal.^. + /// + public static string pattern_EditorMentions { + get { + return ResourceManager.GetString("pattern_EditorMentions", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to h1. Mailing completed + /// + ///This is a mail message to notify you that you have requested the mailing of $MailsCount messages and the process is now complete. $Message + /// + ///The successfully sent mail messages can be found in your "Sent":"$__VirtualRootPath/addons/mail/#sent" folder of the Mail module. + /// + ///^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal.^. + /// + public static string pattern_MailMergeEnd { + get { + return ResourceManager.GetString("pattern_MailMergeEnd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to h1. Access granted to document: "$DocumentTitle":"$DocumentURL" + /// + ///$__DateTime "$__AuthorName":"$__AuthorUrl" granted you the access to the "$DocumentTitle":"$DocumentURL" document with the following access rights: "$AccessRights". + /// + ///$Message. + /// + public static string pattern_ShareDocument { + get { + return ResourceManager.GetString("pattern_ShareDocument", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to h1. Access granted to folder: "$DocumentTitle":"$__VirtualRootPath/products/files/#$FolderID" + /// + ///$__DateTime "$__AuthorName":"$__AuthorUrl" granted you the access to the "$DocumentTitle":"$__VirtualRootPath/products/files/#$FolderID" folder with the following access rights: "$AccessRights". + /// + ///$Message. + /// + public static string pattern_ShareFolder { + get { + return ResourceManager.GetString("pattern_ShareFolder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to <patterns> + /// <formatter type="ASC.Notify.Patterns.NVelocityPatternFormatter, ASC.Common" /> + /// + /// <pattern id="DocuSignComplete" sender="email.sender"> + /// <subject resource="|subject_DocuSignComplete|ASC.Web.Files.Services.NotifyService.FilesPatternResource,ASC.Web.Files" /> + /// <body styler="ASC.Notify.Textile.TextileStyler,ASC.Notify.Textile" resource="|pattern_DocuSignComplete|ASC.Web.Files.Services.NotifyService.FilesPatternResource,ASC.Web.Files" /> + /// </pattern> + /// <pattern id="DocuSignComplete" se [rest of string was truncated]";. + /// + public static string patterns { + get { + return ResourceManager.GetString("patterns", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Documents. All signers completed $DocumentTitle. + /// + public static string subject_DocuSignComplete { + get { + return ResourceManager.GetString("subject_DocuSignComplete", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Documents. Sign status changed. + /// + public static string subject_DocuSignStatus { + get { + return ResourceManager.GetString("subject_DocuSignStatus", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Documents. Mentioned in document. + /// + public static string subject_EditorMentions { + get { + return ResourceManager.GetString("subject_EditorMentions", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Documents. Mailing is complete.. + /// + public static string subject_MailMergeEnd { + get { + return ResourceManager.GetString("subject_MailMergeEnd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Documents. Access granted to document: $DocumentTitle. + /// + public static string subject_ShareDocument { + get { + return ResourceManager.GetString("subject_ShareDocument", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Documents. Access granted to folder: $DocumentTitle. + /// + public static string subject_ShareFolder { + get { + return ResourceManager.GetString("subject_ShareFolder", resourceCulture); + } + } + } +} diff --git a/products/ASC.Files/Server/Services/NotifyService/FilesPatternResource.resx b/products/ASC.Files/Server/Services/NotifyService/FilesPatternResource.resx new file mode 100644 index 0000000000..e4a2590eb7 --- /dev/null +++ b/products/ASC.Files/Server/Services/NotifyService/FilesPatternResource.resx @@ -0,0 +1,188 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + h1. All signers completed $Message + +This is a mail message to notify you that all signers completed "$DocumentTitle":"$DocumentURL". + +^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal.^ + + + h1. $Message: $DocumentTitle + +Watch your DocuSign account for more information. + +^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal.^ + + + h1. "$DocumentTitle":"$DocumentURL" + +This is a mail message to notify you that you have mentioned by "$__AuthorName":"$__AuthorUrl". + +$Message + +^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal.^ + + + h1. Mailing completed + +This is a mail message to notify you that you have requested the mailing of $MailsCount messages and the process is now complete. $Message + +The successfully sent mail messages can be found in your "Sent":"$__VirtualRootPath/addons/mail/#sent" folder of the Mail module. + +^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal.^ + + + h1. Access granted to document: "$DocumentTitle":"$DocumentURL" + +$__DateTime "$__AuthorName":"$__AuthorUrl" granted you the access to the "$DocumentTitle":"$DocumentURL" document with the following access rights: "$AccessRights". + +$Message + + + h1. Access granted to folder: "$DocumentTitle":"$__VirtualRootPath/products/files/#$FolderID" + +$__DateTime "$__AuthorName":"$__AuthorUrl" granted you the access to the "$DocumentTitle":"$__VirtualRootPath/products/files/#$FolderID" folder with the following access rights: "$AccessRights". + +$Message + + + Documents. All signers completed $DocumentTitle + + + Documents. Sign status changed + + + Documents. Mentioned in document + + + Documents. Mailing is complete. + + + Documents. Access granted to document: $DocumentTitle + + + Documents. Access granted to folder: $DocumentTitle + + + + patterns.xml;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;windows-1251 + + \ No newline at end of file diff --git a/products/ASC.Files/Server/Services/NotifyService/NotifyClient.cs b/products/ASC.Files/Server/Services/NotifyService/NotifyClient.cs new file mode 100644 index 0000000000..b504c25785 --- /dev/null +++ b/products/ASC.Files/Server/Services/NotifyService/NotifyClient.cs @@ -0,0 +1,232 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Globalization; + +using ASC.Common; +using ASC.Core; +using ASC.Core.Common; +using ASC.Files.Core; +using ASC.Files.Core.Data; +using ASC.Files.Core.Security; +using ASC.Files.Resources; +using ASC.Notify.Patterns; +using ASC.Web.Core.Files; +using ASC.Web.Files.Classes; + +using Microsoft.Extensions.DependencyInjection; + +namespace ASC.Web.Files.Services.NotifyService +{ + public class NotifyClient + { + public IServiceProvider ServiceProvider { get; } + + public NotifyClient(IServiceProvider serviceProvider) + { + ServiceProvider = serviceProvider; + } + + public void SendDocuSignComplete(File file, string sourceTitle) + { + using var scope = ServiceProvider.CreateScope(); + var notifySource = scope.ServiceProvider.GetService(); + var securityContext = scope.ServiceProvider.GetService(); + var filesLinkUtility = scope.ServiceProvider.GetService(); + var fileUtility = scope.ServiceProvider.GetService(); + var baseCommonLinkUtility = scope.ServiceProvider.GetService(); + var client = WorkContext.NotifyContext.NotifyService.RegisterClient(notifySource, scope); + + var recipient = notifySource.GetRecipientsProvider().GetRecipient(securityContext.CurrentAccount.ID.ToString()); + + client.SendNoticeAsync( + NotifyConstants.Event_DocuSignComplete, + file.UniqID, + recipient, + true, + new TagValue(NotifyConstants.Tag_DocumentUrl, baseCommonLinkUtility.GetFullAbsolutePath(filesLinkUtility.GetFileWebPreviewUrl(fileUtility, file.Title, file.ID))), + new TagValue(NotifyConstants.Tag_DocumentTitle, file.Title), + new TagValue(NotifyConstants.Tag_Message, sourceTitle) + ); + } + + public void SendDocuSignStatus(string subject, string status) + { + using var scope = ServiceProvider.CreateScope(); + var notifySource = scope.ServiceProvider.GetService(); + var securityContext = scope.ServiceProvider.GetService(); + var client = WorkContext.NotifyContext.NotifyService.RegisterClient(notifySource, scope); + + var recipient = notifySource.GetRecipientsProvider().GetRecipient(securityContext.CurrentAccount.ID.ToString()); + + client.SendNoticeAsync( + NotifyConstants.Event_DocuSignStatus, + null, + recipient, + true, + new TagValue(NotifyConstants.Tag_DocumentTitle, subject), + new TagValue(NotifyConstants.Tag_Message, status) + ); + } + + public void SendMailMergeEnd(Guid userId, int countMails, int countError) + { + using var scope = ServiceProvider.CreateScope(); + var notifySource = scope.ServiceProvider.GetService(); + var client = WorkContext.NotifyContext.NotifyService.RegisterClient(notifySource, scope); + + var recipient = notifySource.GetRecipientsProvider().GetRecipient(userId.ToString()); + + client.SendNoticeAsync( + NotifyConstants.Event_MailMergeEnd, + null, + recipient, + true, + new TagValue(NotifyConstants.Tag_MailsCount, countMails), + new TagValue(NotifyConstants.Tag_Message, countError > 0 ? string.Format(FilesCommonResource.ErrorMassage_MailMergeCount, countError) : string.Empty) + ); + } + + public void SendShareNotice(FileEntry fileEntry, Dictionary recipients, string message) + { + if (fileEntry == null || recipients.Count == 0) return; + + using var scope = ServiceProvider.CreateScope(); + var notifySource = scope.ServiceProvider.GetService(); + var daoFactory = scope.ServiceProvider.GetService(); + var filesLinkUtility = scope.ServiceProvider.GetService(); + var fileUtility = scope.ServiceProvider.GetService(); + var pathProvider = scope.ServiceProvider.GetService(); + var userManager = scope.ServiceProvider.GetService(); + var tenantManager = scope.ServiceProvider.GetService(); + var baseCommonLinkUtility = scope.ServiceProvider.GetService(); + var client = WorkContext.NotifyContext.NotifyService.RegisterClient(notifySource, scope); + + var folderDao = daoFactory.FolderDao; + if (fileEntry.FileEntryType == FileEntryType.File && folderDao.GetFolder(((File)fileEntry).FolderID) == null) return; + + var url = fileEntry.FileEntryType == FileEntryType.File + ? filesLinkUtility.GetFileWebPreviewUrl(fileUtility, fileEntry.Title, fileEntry.ID) + : pathProvider.GetFolderUrl(((Folder)fileEntry)); + + var recipientsProvider = notifySource.GetRecipientsProvider(); + + foreach (var recipientPair in recipients) + { + var u = userManager.GetUsers(recipientPair.Key); + var culture = string.IsNullOrEmpty(u.CultureName) + ? tenantManager.GetCurrentTenant().GetCulture() + : CultureInfo.GetCultureInfo(u.CultureName); + + var aceString = GetAccessString(recipientPair.Value, culture); + var recipient = recipientsProvider.GetRecipient(u.ID.ToString()); + + client.SendNoticeAsync( + fileEntry.FileEntryType == FileEntryType.File ? NotifyConstants.Event_ShareDocument : NotifyConstants.Event_ShareFolder, + fileEntry.UniqID, + recipient, + true, + new TagValue(NotifyConstants.Tag_DocumentTitle, fileEntry.Title), + new TagValue(NotifyConstants.Tag_FolderID, fileEntry.ID), + new TagValue(NotifyConstants.Tag_DocumentUrl, baseCommonLinkUtility.GetFullAbsolutePath(url)), + new TagValue(NotifyConstants.Tag_AccessRights, aceString), + new TagValue(NotifyConstants.Tag_Message, message.HtmlEncode()) + ); + } + } + + public void SendEditorMentions(FileEntry file, string documentUrl, List recipientIds, string message) + { + if (file == null || recipientIds.Count == 0) return; + + using var scope = ServiceProvider.CreateScope(); + var notifySource = scope.ServiceProvider.GetService(); + var userManager = scope.ServiceProvider.GetService(); + var baseCommonLinkUtility = scope.ServiceProvider.GetService(); + var client = WorkContext.NotifyContext.NotifyService.RegisterClient(notifySource, scope); + + var recipientsProvider = notifySource.GetRecipientsProvider(); + + foreach (var recipientId in recipientIds) + { + var u = userManager.GetUsers(recipientId); + + var recipient = recipientsProvider.GetRecipient(u.ID.ToString()); + + client.SendNoticeAsync( + NotifyConstants.Event_EditorMentions, + file.UniqID, + recipient, + true, + new TagValue(NotifyConstants.Tag_DocumentTitle, file.Title), + new TagValue(NotifyConstants.Tag_DocumentUrl, baseCommonLinkUtility.GetFullAbsolutePath(documentUrl)), + new TagValue(NotifyConstants.Tag_Message, message.HtmlEncode()) + ); + } + } + + private static string GetAccessString(FileShare fileShare, CultureInfo cultureInfo) + { + switch (fileShare) + { + case FileShare.Read: + return FilesCommonResource.ResourceManager.GetString("AceStatusEnum_Read", cultureInfo); + case FileShare.ReadWrite: + return FilesCommonResource.ResourceManager.GetString("AceStatusEnum_ReadWrite", cultureInfo); + case FileShare.Review: + return FilesCommonResource.ResourceManager.GetString("AceStatusEnum_Review", cultureInfo); + case FileShare.FillForms: + return FilesCommonResource.ResourceManager.GetString("AceStatusEnum_FillForms", cultureInfo); + case FileShare.Comment: + return FilesCommonResource.ResourceManager.GetString("AceStatusEnum_Comment", cultureInfo); + default: + return string.Empty; + } + } + } + + public static class NotifyClientExtension + { + public static DIHelper AddNotifyClientService(this DIHelper services) + { + services.TryAddScoped(); + + return services + .AddFilesNotifySourceService() + .AddBaseCommonLinkUtilityService() + .AddUserManagerService() + .AddSecurityContextService() + .AddFilesLinkUtilityService() + .AddFileUtilityService() + .AddPathProviderService() + .AddTenantManagerService() + .AddDaoFactoryService() + ; + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Services/NotifyService/NotifyConstants.cs b/products/ASC.Files/Server/Services/NotifyService/NotifyConstants.cs new file mode 100644 index 0000000000..c3f051b10a --- /dev/null +++ b/products/ASC.Files/Server/Services/NotifyService/NotifyConstants.cs @@ -0,0 +1,55 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using ASC.Notify.Model; + +namespace ASC.Web.Files.Services.NotifyService +{ + public static class NotifyConstants + { + #region Events + + public static readonly INotifyAction Event_DocuSignComplete = new NotifyAction("DocuSignComplete", "docusign complete"); + public static readonly INotifyAction Event_DocuSignStatus = new NotifyAction("DocuSignStatus", "docusign status"); + public static readonly INotifyAction Event_MailMergeEnd = new NotifyAction("MailMergeEnd", "mail merge end"); + public static readonly INotifyAction Event_ShareDocument = new NotifyAction("ShareDocument", "share document"); + public static readonly INotifyAction Event_ShareFolder = new NotifyAction("ShareFolder", "share folder"); + public static readonly INotifyAction Event_EditorMentions = new NotifyAction("EditorMentions", "editor mentions"); + + #endregion + + #region Tags + + public static readonly string Tag_FolderID = "FolderID"; + public static readonly string Tag_DocumentTitle = "DocumentTitle"; + public static readonly string Tag_DocumentUrl = "DocumentURL"; + public static readonly string Tag_AccessRights = "AccessRights"; + public static readonly string Tag_Message = "Message"; + public static readonly string Tag_MailsCount = "MailsCount"; + + #endregion + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Services/NotifyService/NotifySource.cs b/products/ASC.Files/Server/Services/NotifyService/NotifySource.cs new file mode 100644 index 0000000000..be85be13ee --- /dev/null +++ b/products/ASC.Files/Server/Services/NotifyService/NotifySource.cs @@ -0,0 +1,72 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; + +using ASC.Common; +using ASC.Core; +using ASC.Core.Notify; +using ASC.Notify.Model; +using ASC.Notify.Patterns; +using ASC.Notify.Recipients; + +using NotifySourceBase = ASC.Core.Notify.NotifySource; + +namespace ASC.Web.Files.Services.NotifyService +{ + public class NotifySource : NotifySourceBase + { + public NotifySource(UserManager userManager, IRecipientProvider recipientsProvider, SubscriptionManager subscriptionManager) + : base(new Guid("6FE286A4-479E-4c25-A8D9-0156E332B0C0"), userManager, recipientsProvider, subscriptionManager) + { + } + + protected override IActionProvider CreateActionProvider() + { + return new ConstActionProvider( + NotifyConstants.Event_ShareFolder, + NotifyConstants.Event_ShareDocument); + } + + protected override IPatternProvider CreatePatternsProvider() + { + return new XmlPatternProvider2(FilesPatternResource.patterns); + } + } + + public static class FilesNotifySourceExtension + { + public static DIHelper AddFilesNotifySourceService(this DIHelper services) + { + services.TryAddScoped(); + + return services + .AddUserManagerService() + .AddRecipientProviderImplService() + .AddSubscriptionManagerService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Services/NotifyService/patterns.xml b/products/ASC.Files/Server/Services/NotifyService/patterns.xml new file mode 100644 index 0000000000..b35b612d15 --- /dev/null +++ b/products/ASC.Files/Server/Services/NotifyService/patterns.xml @@ -0,0 +1,81 @@ + + + + + + + + + + $DocumentURL + + + + + + + + + $Message: $DocumentTitle + + + + + + + + + $__AuthorName + +$Message + + + + + + + + + + $__AuthorName + +$AccessRights + +$Message + +$DocumentURL + + + + + + + + + + $__AuthorName + +$AccessRights + +$Message + +$__VirtualRootPath/products/files/#$FolderID + + + + + + + + + + $__AuthorName + +$DocumentTitle + +$Message + +$DocumentURL + + + \ No newline at end of file diff --git a/products/ASC.Files/Server/Services/WCFService/FileEntrySerializer.cs b/products/ASC.Files/Server/Services/WCFService/FileEntrySerializer.cs new file mode 100644 index 0000000000..a352d200d2 --- /dev/null +++ b/products/ASC.Files/Server/Services/WCFService/FileEntrySerializer.cs @@ -0,0 +1,141 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Xml; + +using ASC.Files.Core; + +namespace ASC.Web.Files.Services.WCFService +{ + public class FileEntrySerializer + { + private static readonly IDictionary serializers = new Dictionary(); + private static bool oldMonoSerializer = false; + + + static FileEntrySerializer() + { + serializers[typeof(File)] = new DataContractSerializer(typeof(File)); + serializers[typeof(ItemList)] = new DataContractSerializer(typeof(ItemList)); + serializers[typeof(DataWrapper)] = new DataContractSerializer(typeof(DataWrapper)); + + //if (WorkContext.IsMono && !string.IsNullOrEmpty(WorkContext.MonoVersion)) + //{ + // // in version higher 4.0 use standard DataContractResolver + // var version = WorkContext.MonoVersion.Split('.', ' '); + // if (2 <= version.Length && (Convert.ToInt32(version[0]) * 1000 + Convert.ToInt32(version[1])) < 4002) + // { + // oldMonoSerializer = true; + // serializers[typeof(ItemList)] = new DataContractSerializer(typeof(ItemList), Type.EmptyTypes, ushort.MaxValue, false, false, null, new FileEntryResolver()); + // serializers[typeof(DataWrapper)] = new DataContractSerializer(typeof(DataWrapper), Type.EmptyTypes, ushort.MaxValue, false, false, null, new FileEntryResolver()); + // } + //} + } + + public System.IO.MemoryStream ToXml(object o) + { + var result = new System.IO.MemoryStream(); + if (o == null) + { + return result; + } + + using (var writer = XmlDictionaryWriter.CreateTextWriter(result, Encoding.UTF8, false)) + { + var serializer = serializers[o.GetType()]; + serializer.WriteObject(writer, o); + } + result.Seek(0, System.IO.SeekOrigin.Begin); + + if (oldMonoSerializer) + { + var xml = new XmlDocument(); + xml.PreserveWhitespace = true; + xml.Load(result); + result.Close(); + + //remove incorrect ns + foreach (XmlNode entry in xml.SelectNodes("//entry")) + { + var nsattr = entry.Attributes.Cast().FirstOrDefault(a => a.Value == typeof(FileEntry).Name); + if (nsattr != null) + { + foreach (XmlAttribute a in entry.Attributes) + { + if (a.Value.StartsWith(nsattr.LocalName + ":")) + { + a.Value = a.Value.Substring(nsattr.LocalName.Length + 1); + } + } + entry.Attributes.Remove(nsattr); + } + } + + //http://stackoverflow.com/questions/13483138/mono-does-not-honor-system-runtime-serialization-datamemberattribute-emitdefault + var nsmanager = new XmlNamespaceManager(xml.NameTable); + nsmanager.AddNamespace("i", "http://www.w3.org/2001/XMLSchema-instance"); + foreach (XmlNode nil in xml.SelectNodes("//*[@i:nil='true']", nsmanager)) + { + nil.ParentNode.RemoveChild(nil); + } + + result = new System.IO.MemoryStream(); + xml.Save(result); + result.Seek(0, System.IO.SeekOrigin.Begin); + } + + return result; + } + + + private class FileEntryResolver : DataContractResolver + { + public override Type ResolveName(string typeName, string typeNamespace, Type declaredType, DataContractResolver knownTypeResolver) + { + return null; + } + + public override bool TryResolveType(Type type, Type declaredType, DataContractResolver knownTypeResolver, out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace) + { + typeName = XmlDictionaryString.Empty; + typeNamespace = XmlDictionaryString.Empty; + + if (declaredType == typeof(FileEntry)) + { + typeName = new XmlDictionaryString(XmlDictionary.Empty, type.Name.ToLower(), 0); + typeNamespace = new XmlDictionaryString(XmlDictionary.Empty, declaredType.Name, 0); + return true; + } + return false; + } + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Services/WCFService/FileExceptionFilterAttribute.cs b/products/ASC.Files/Server/Services/WCFService/FileExceptionFilterAttribute.cs new file mode 100644 index 0000000000..020156e29b --- /dev/null +++ b/products/ASC.Files/Server/Services/WCFService/FileExceptionFilterAttribute.cs @@ -0,0 +1,123 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + + + +using System; +using System.Diagnostics; +using System.Net; +using System.Runtime.Serialization; + +using ASC.Common.Logging; +using ASC.Files.Resources; + +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.Options; + +namespace ASC.Web.Files.Services.WCFService +{ + class FileExceptionFilterAttribute : IExceptionFilter + { + private readonly ILog log; + + public FileExceptionFilterAttribute(IOptionsMonitor options) + { + log = options.Get("ASC.Files"); + } + + public void OnException(ExceptionContext actionExecutedContext) + { + if (actionExecutedContext.Exception != null) + { + var fileError = new FileError(actionExecutedContext.Exception); + actionExecutedContext.Result = new ObjectResult(fileError) + { + StatusCode = (int)HttpStatusCode.BadRequest + }; + } + LogException(actionExecutedContext.Exception); + } + + + [Conditional("DEBUG")] + private void LogException(Exception err) + { + while (err != null) + { + log.Error(err); + err = err.InnerException; + } + } + + + [DataContract(Name = "error", Namespace = "")] + class FileError + { + [DataMember(Name = "Detail")] + public string Detail { get; set; } + + [DataMember(Name = "message")] + public string Message { get; set; } + + [DataMember(Name = "inner")] + public FileErrorInner Inner { get; set; } + + [DataContract(Name = "error", Namespace = "")] + internal class FileErrorInner + { + [DataMember(Name = "message")] + public string Message { get; set; } + + [DataMember(Name = "type")] + public string Type { get; set; } + + [DataMember(Name = "source")] + public string Source { get; set; } + + [DataMember(Name = "stack")] + public string Stack { get; set; } + } + + public FileError() + { + } + + public FileError(Exception error) + { + Detail = error.Message; + Message = FilesCommonResource.ErrorMassage_BadRequest; + Inner = new FileErrorInner + { + Message = error.Message, + Type = error.GetType().FullName, + Source = error.Source ?? string.Empty, + Stack = error.StackTrace ?? string.Empty, + }; + } + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Services/WCFService/FileOperations/FileConflictResolveType.cs b/products/ASC.Files/Server/Services/WCFService/FileOperations/FileConflictResolveType.cs new file mode 100644 index 0000000000..45d0130d44 --- /dev/null +++ b/products/ASC.Files/Server/Services/WCFService/FileOperations/FileConflictResolveType.cs @@ -0,0 +1,38 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.Runtime.Serialization; + +namespace ASC.Web.Files.Services.WCFService.FileOperations +{ + [DataContract] + public enum FileConflictResolveType + { + [EnumMember] Skip = 0, + [EnumMember] Overwrite = 1, + [EnumMember] Duplicate = 2 + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Services/WCFService/FileOperations/FileDeleteOperation.cs b/products/ASC.Files/Server/Services/WCFService/FileOperations/FileDeleteOperation.cs new file mode 100644 index 0000000000..51f023db7e --- /dev/null +++ b/products/ASC.Files/Server/Services/WCFService/FileOperations/FileDeleteOperation.cs @@ -0,0 +1,258 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Core.Tenants; +using ASC.Files.Core; +using ASC.Files.Resources; +using ASC.MessagingSystem; +using ASC.Web.Files.Helpers; +using ASC.Web.Files.Utils; + +using Microsoft.Extensions.DependencyInjection; + +namespace ASC.Web.Files.Services.WCFService.FileOperations +{ + internal class FileDeleteOperationData : FileOperationData + { + public bool IgnoreException { get; } + public bool Immediately { get; } + public Dictionary Headers { get; } + + public FileDeleteOperationData(List folders, List files, Tenant tenant, + bool holdResult = true, bool ignoreException = false, bool immediately = false, Dictionary headers = null) + : base(folders, files, tenant, holdResult) + { + IgnoreException = ignoreException; + Immediately = immediately; + Headers = headers; + } + } + + class FileDeleteOperation : FileOperation + { + private object _trashId; + private readonly bool _ignoreException; + private readonly bool _immediately; + private readonly Dictionary _headers; + + public override FileOperationType OperationType + { + get { return FileOperationType.Delete; } + } + + + public FileDeleteOperation(IServiceProvider serviceProvider, FileDeleteOperationData fileOperationData) + : base(serviceProvider, fileOperationData) + { + _ignoreException = fileOperationData.IgnoreException; + _immediately = fileOperationData.Immediately; + _headers = fileOperationData.Headers; + } + + + protected override void Do(IServiceScope scope) + { + _trashId = FolderDao.GetFolderIDTrash(true); + + Folder root = null; + if (0 < Folders.Count) + { + root = FolderDao.GetRootFolder(Folders[0]); + } + else if (0 < Files.Count) + { + root = FolderDao.GetRootFolderByFile(Files[0]); + } + if (root != null) + { + Status += string.Format("folder_{0}{1}", root.ID, FileOperation.SPLIT_CHAR); + } + + DeleteFiles(Files, scope); + DeleteFolders(Folders, scope); + } + + private void DeleteFolders(IEnumerable folderIds, IServiceScope scope) + { + var fileMarker = scope.ServiceProvider.GetService(); + var filesMessageService = scope.ServiceProvider.GetService(); + + foreach (var folderId in folderIds) + { + CancellationToken.ThrowIfCancellationRequested(); + + var folder = FolderDao.GetFolder(folderId); + object canCalculate = null; + if (folder == null) + { + Error = FilesCommonResource.ErrorMassage_FolderNotFound; + } + else if (folder.FolderType != FolderType.DEFAULT && folder.FolderType != FolderType.BUNCH) + { + Error = FilesCommonResource.ErrorMassage_SecurityException_DeleteFolder; + } + else if (!_ignoreException && !FilesSecurity.CanDelete(folder)) + { + canCalculate = FolderDao.CanCalculateSubitems(folderId) ? null : folderId; + + Error = FilesCommonResource.ErrorMassage_SecurityException_DeleteFolder; + } + else + { + canCalculate = FolderDao.CanCalculateSubitems(folderId) ? null : folderId; + + fileMarker.RemoveMarkAsNewForAll(folder); + if (folder.ProviderEntry && folder.ID.Equals(folder.RootFolderId)) + { + if (ProviderDao != null) + { + ProviderDao.RemoveProviderInfo(folder.ProviderId); + filesMessageService.Send(folder, _headers, MessageAction.ThirdPartyDeleted, folder.ID.ToString(), folder.ProviderKey); + } + + ProcessedFolder(folderId); + } + else + { + var immediately = _immediately || !FolderDao.UseTrashForRemove(folder); + if (immediately && FolderDao.UseRecursiveOperation(folder.ID, null)) + { + DeleteFiles(FileDao.GetFiles(folder.ID), scope); + DeleteFolders(FolderDao.GetFolders(folder.ID).Select(f => f.ID).ToList(), scope); + + if (FolderDao.IsEmpty(folder.ID)) + { + FolderDao.DeleteFolder(folder.ID); + filesMessageService.Send(folder, _headers, MessageAction.FolderDeleted, folder.Title); + + ProcessedFolder(folderId); + } + } + else + { + var files = FileDao.GetFiles(folder.ID, new OrderBy(SortedByType.AZ, true), FilterType.FilesOnly, false, Guid.Empty, string.Empty, false, true); + if (!_ignoreException && WithError(scope, files, true, out var tmpError)) + { + Error = tmpError; + } + else + { + if (immediately) + { + FolderDao.DeleteFolder(folder.ID); + filesMessageService.Send(folder, _headers, MessageAction.FolderDeleted, folder.Title); + } + else + { + FolderDao.MoveFolder(folder.ID, _trashId, CancellationToken); + filesMessageService.Send(folder, _headers, MessageAction.FolderMovedToTrash, folder.Title); + } + + ProcessedFolder(folderId); + } + } + } + } + ProgressStep(canCalculate); + } + } + + private void DeleteFiles(IEnumerable fileIds, IServiceScope scope) + { + var fileMarker = scope.ServiceProvider.GetService(); + var filesMessageService = scope.ServiceProvider.GetService(); + + foreach (var fileId in fileIds) + { + CancellationToken.ThrowIfCancellationRequested(); + + var file = FileDao.GetFile(fileId); + if (file == null) + { + Error = FilesCommonResource.ErrorMassage_FileNotFound; + } + else if (!_ignoreException && WithError(scope, new[] { file }, false, out var tmpError)) + { + Error = tmpError; + } + else + { + fileMarker.RemoveMarkAsNewForAll(file); + if (!_immediately && FileDao.UseTrashForRemove(file)) + { + FileDao.MoveFile(file.ID, _trashId); + filesMessageService.Send(file, _headers, MessageAction.FileMovedToTrash, file.Title); + } + else + { + try + { + FileDao.DeleteFile(file.ID); + filesMessageService.Send(file, _headers, MessageAction.FileDeleted, file.Title); + } + catch (Exception ex) + { + Error = ex.Message; + Logger.Error(Error, ex); + } + } + ProcessedFile(fileId); + } + ProgressStep(fileId: FolderDao.CanCalculateSubitems(fileId) ? null : fileId); + } + } + + private bool WithError(IServiceScope scope, IEnumerable files, bool folder, out string error) + { + var entryManager = scope.ServiceProvider.GetService(); + + error = null; + foreach (var file in files) + { + if (!FilesSecurity.CanDelete(file)) + { + error = FilesCommonResource.ErrorMassage_SecurityException_DeleteFile; + return true; + } + if (entryManager.FileLockedForMe(file.ID)) + { + error = FilesCommonResource.ErrorMassage_LockedFile; + return true; + } + if (FileTracker.IsEditing(file.ID)) + { + error = folder ? FilesCommonResource.ErrorMassage_SecurityException_DeleteEditingFolder : FilesCommonResource.ErrorMassage_SecurityException_DeleteEditingFile; + return true; + } + } + return false; + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Services/WCFService/FileOperations/FileDownloadOperation.cs b/products/ASC.Files/Server/Services/WCFService/FileOperations/FileDownloadOperation.cs new file mode 100644 index 0000000000..94764f3a35 --- /dev/null +++ b/products/ASC.Files/Server/Services/WCFService/FileOperations/FileDownloadOperation.cs @@ -0,0 +1,382 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; + +using ASC.Common.Security.Authentication; +using ASC.Core.Tenants; +using ASC.Data.Storage; +using ASC.Files.Core; +using ASC.Files.Resources; +using ASC.MessagingSystem; +using ASC.Web.Core.Files; +using ASC.Web.Files.Classes; +using ASC.Web.Files.Helpers; +using ASC.Web.Files.Utils; +using ASC.Web.Studio.Core; + +using Ionic.Zip; + +using Microsoft.Extensions.DependencyInjection; + +using File = ASC.Files.Core.File; + +namespace ASC.Web.Files.Services.WCFService.FileOperations +{ + internal class FileDownloadOperationData : FileOperationData + { + public Dictionary FilesDownload { get; } + public Dictionary Headers { get; } + + public FileDownloadOperationData(Dictionary folders, Dictionary files, Tenant tenant, Dictionary headers, bool holdResult = true) + : base(folders.Select(f => f.Key).ToList(), files.Select(f => f.Key).ToList(), tenant, holdResult) + { + FilesDownload = files; + Headers = headers; + } + } + + class FileDownloadOperation : FileOperation + { + private readonly Dictionary files; + private readonly Dictionary headers; + + public override FileOperationType OperationType + { + get { return FileOperationType.Download; } + } + + + public FileDownloadOperation(IServiceProvider serviceProvider, FileDownloadOperationData fileDownloadOperationData) + : base(serviceProvider, fileDownloadOperationData) + { + files = fileDownloadOperationData.FilesDownload; + headers = fileDownloadOperationData.Headers; + } + + + protected override void Do(IServiceScope scope) + { + var entriesPathId = GetEntriesPathId(scope); + if (entriesPathId == null || entriesPathId.Count == 0) + { + if (0 < Files.Count) + throw new FileNotFoundException(FilesCommonResource.ErrorMassage_FileNotFound); + throw new DirectoryNotFoundException(FilesCommonResource.ErrorMassage_FolderNotFound); + } + + var globalStore = scope.ServiceProvider.GetService(); + var filesLinkUtility = scope.ServiceProvider.GetService(); + + ReplaceLongPath(entriesPathId); + + using var stream = CompressToZip(scope, entriesPathId); + if (stream != null) + { + stream.Position = 0; + const string fileName = FileConstant.DownloadTitle + ".zip"; + var store = globalStore.GetStore(); + store.Save( + FileConstant.StorageDomainTmp, + string.Format(@"{0}\{1}", ((IAccount)Thread.CurrentPrincipal.Identity).ID, fileName), + stream, + "application/zip", + "attachment; filename=\"" + fileName + "\""); + Status = string.Format("{0}?{1}=bulk", filesLinkUtility.FileHandlerPath, FilesLinkUtility.Action); + } + } + + private ItemNameValueCollection ExecPathFromFile(IServiceScope scope, File file, string path) + { + var fileMarker = scope.ServiceProvider.GetService(); + fileMarker.RemoveMarkAsNew(file); + + var title = file.Title; + + if (files.ContainsKey(file.ID.ToString())) + { + var convertToExt = files[file.ID.ToString()]; + + if (!string.IsNullOrEmpty(convertToExt)) + { + title = FileUtility.ReplaceFileExtension(title, convertToExt); + } + } + + var entriesPathId = new ItemNameValueCollection(); + entriesPathId.Add(path + title, file.ID.ToString()); + + return entriesPathId; + } + + private ItemNameValueCollection GetEntriesPathId(IServiceScope scope) + { + var fileMarker = scope.ServiceProvider.GetService(); + var entriesPathId = new ItemNameValueCollection(); + if (0 < Files.Count) + { + var files = FileDao.GetFiles(Files.ToArray()); + files = FilesSecurity.FilterRead(files).ToList(); + files.ForEach(file => entriesPathId.Add(ExecPathFromFile(scope, file, string.Empty))); + } + if (0 < Folders.Count) + { + FilesSecurity.FilterRead(FolderDao.GetFolders(Files.ToArray())).ToList().Cast().ToList() + .ForEach(folder => fileMarker.RemoveMarkAsNew(folder)); + + var filesInFolder = GetFilesInFolders(scope, Folders, string.Empty); + entriesPathId.Add(filesInFolder); + } + return entriesPathId; + } + + private ItemNameValueCollection GetFilesInFolders(IServiceScope scope, IEnumerable folderIds, string path) + { + var fileMarker = scope.ServiceProvider.GetService(); + + CancellationToken.ThrowIfCancellationRequested(); + + var entriesPathId = new ItemNameValueCollection(); + foreach (var folderId in folderIds) + { + CancellationToken.ThrowIfCancellationRequested(); + + var folder = FolderDao.GetFolder(folderId); + if (folder == null || !FilesSecurity.CanRead(folder)) continue; + var folderPath = path + folder.Title + "/"; + + var files = FileDao.GetFiles(folder.ID, null, FilterType.None, false, Guid.Empty, string.Empty, true); + files = FilesSecurity.FilterRead(files).ToList(); + files.ForEach(file => entriesPathId.Add(ExecPathFromFile(scope, file, folderPath))); + + fileMarker.RemoveMarkAsNew(folder); + + var nestedFolders = FolderDao.GetFolders(folder.ID); + nestedFolders = FilesSecurity.FilterRead(nestedFolders).ToList(); + if (files.Count == 0 && nestedFolders.Count == 0) + { + entriesPathId.Add(folderPath, string.Empty); + } + + var filesInFolder = GetFilesInFolders(scope, nestedFolders.ConvertAll(f => f.ID), folderPath); + entriesPathId.Add(filesInFolder); + } + return entriesPathId; + } + + private Stream CompressToZip(IServiceScope scope, ItemNameValueCollection entriesPathId) + { + var setupInfo = scope.ServiceProvider.GetService(); + var fileConverter = scope.ServiceProvider.GetService(); + var filesMessageService = scope.ServiceProvider.GetService(); + + var stream = TempStream.Create(); + using (var zip = new ZipOutputStream(stream, true)) + { + zip.CompressionLevel = Ionic.Zlib.CompressionLevel.Level3; + zip.AlternateEncodingUsage = Ionic.Zip.ZipOption.AsNecessary; + zip.AlternateEncoding = Encoding.UTF8; + + foreach (var path in entriesPathId.AllKeys) + { + var counter = 0; + foreach (var entryId in entriesPathId[path]) + { + if (CancellationToken.IsCancellationRequested) + { + zip.Dispose(); + stream.Dispose(); + CancellationToken.ThrowIfCancellationRequested(); + } + + var newtitle = path; + + File file = null; + var convertToExt = string.Empty; + + if (!string.IsNullOrEmpty(entryId)) + { + FileDao.InvalidateCache(entryId); + file = FileDao.GetFile(entryId); + + if (file == null) + { + Error = FilesCommonResource.ErrorMassage_FileNotFound; + continue; + } + + if (file.ContentLength > setupInfo.AvailableFileSize) + { + Error = string.Format(FilesCommonResource.ErrorMassage_FileSizeZip, FileSizeComment.FilesSizeToString(setupInfo.AvailableFileSize)); + continue; + } + + if (files.ContainsKey(file.ID.ToString())) + { + convertToExt = files[file.ID.ToString()]; + if (!string.IsNullOrEmpty(convertToExt)) + { + newtitle = FileUtility.ReplaceFileExtension(path, convertToExt); + } + } + } + + if (0 < counter) + { + var suffix = " (" + counter + ")"; + + if (!string.IsNullOrEmpty(entryId)) + { + newtitle = 0 < newtitle.IndexOf('.') ? newtitle.Insert(newtitle.LastIndexOf('.'), suffix) : newtitle + suffix; + } + else + { + break; + } + } + + zip.PutNextEntry(newtitle); + + if (!string.IsNullOrEmpty(entryId) && file != null) + { + try + { + if (fileConverter.EnableConvert(file, convertToExt)) + { + //Take from converter + using (var readStream = fileConverter.Exec(file, convertToExt)) + { + readStream.StreamCopyTo(zip); + if (!string.IsNullOrEmpty(convertToExt)) + { + filesMessageService.Send(file, headers, MessageAction.FileDownloadedAs, file.Title, convertToExt); + } + else + { + filesMessageService.Send(file, headers, MessageAction.FileDownloaded, file.Title); + } + } + } + else + { + using var readStream = FileDao.GetFileStream(file); + readStream.StreamCopyTo(zip); + filesMessageService.Send(file, headers, MessageAction.FileDownloaded, file.Title); + } + } + catch (Exception ex) + { + Error = ex.Message; + Logger.Error(Error, ex); + } + } + counter++; + } + + ProgressStep(); + } + } + return stream; + } + + private void ReplaceLongPath(ItemNameValueCollection entriesPathId) + { + foreach (var path in new List(entriesPathId.AllKeys)) + { + CancellationToken.ThrowIfCancellationRequested(); + + if (200 >= path.Length || 0 >= path.IndexOf('/')) continue; + + var ids = entriesPathId[path]; + entriesPathId.Remove(path); + + var newtitle = "LONG_FOLDER_NAME" + path.Substring(path.LastIndexOf('/')); + entriesPathId.Add(newtitle, ids); + } + } + + + class ItemNameValueCollection + { + private readonly Dictionary> dic = new Dictionary>(); + + + public IEnumerable AllKeys + { + get { return dic.Keys; } + } + + public IEnumerable this[string name] + { + get { return dic[name].ToArray(); } + } + + public int Count + { + get { return dic.Count; } + } + + public void Add(string name, string value) + { + if (!dic.ContainsKey(name)) + { + dic.Add(name, new List()); + } + dic[name].Add(value); + } + + public void Add(ItemNameValueCollection collection) + { + foreach (var key in collection.AllKeys) + { + foreach (var value in collection[key]) + { + Add(key, value); + } + } + } + + public void Add(string name, IEnumerable values) + { + if (!dic.ContainsKey(name)) + { + dic.Add(name, new List()); + } + dic[name].AddRange(values); + } + + public void Remove(string name) + { + dic.Remove(name); + } + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Services/WCFService/FileOperations/FileMarkAsReadOperation.cs b/products/ASC.Files/Server/Services/WCFService/FileOperations/FileMarkAsReadOperation.cs new file mode 100644 index 0000000000..48aeebf12c --- /dev/null +++ b/products/ASC.Files/Server/Services/WCFService/FileOperations/FileMarkAsReadOperation.cs @@ -0,0 +1,103 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; + +using ASC.Common.Security.Authentication; +using ASC.Core.Tenants; +using ASC.Files.Core; +using ASC.Web.Files.Utils; + +using Microsoft.Extensions.DependencyInjection; + +namespace ASC.Web.Files.Services.WCFService.FileOperations +{ + class FileMarkAsReadOperationData : FileOperationData + { + public FileMarkAsReadOperationData(List folders, List files, Tenant tenant, bool holdResult = true) : base(folders, files, tenant, holdResult) + { + } + } + + class FileMarkAsReadOperation : FileOperation + { + public override FileOperationType OperationType + { + get { return FileOperationType.MarkAsRead; } + } + + + public FileMarkAsReadOperation(IServiceProvider serviceProvider, FileMarkAsReadOperationData fileOperationData) + : base(serviceProvider, fileOperationData) + { + } + + + protected override int InitTotalProgressSteps() + { + return Files.Count + Folders.Count; + } + + protected override void Do(IServiceScope scope) + { + var fileMarker = scope.ServiceProvider.GetService(); + var entries = new List(); + if (Folders.Any()) + { + entries.AddRange(FolderDao.GetFolders(Folders.ToArray())); + } + if (Files.Any()) + { + entries.AddRange(FileDao.GetFiles(Files.ToArray())); + } + entries.ForEach(x => + { + CancellationToken.ThrowIfCancellationRequested(); + + fileMarker.RemoveMarkAsNew(x, ((IAccount)Thread.CurrentPrincipal.Identity).ID); + + if (x.FileEntryType == FileEntryType.File) + { + ProcessedFile(x.ID.ToString()); + } + else + { + ProcessedFolder(x.ID.ToString()); + } + ProgressStep(); + }); + + var newrootfolder = fileMarker + .GetRootFoldersIdMarkedAsNew() + .Select(item => string.Format("new_{{\"key\"? \"{0}\", \"value\"? \"{1}\"}}", item.Key, item.Value)); + + Status += string.Join(FileOperation.SPLIT_CHAR, newrootfolder.ToArray()); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Services/WCFService/FileOperations/FileMoveCopyOperation.cs b/products/ASC.Files/Server/Services/WCFService/FileOperations/FileMoveCopyOperation.cs new file mode 100644 index 0000000000..dc45bfa094 --- /dev/null +++ b/products/ASC.Files/Server/Services/WCFService/FileOperations/FileMoveCopyOperation.cs @@ -0,0 +1,483 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +using ASC.Core.Tenants; +using ASC.Files.Core; +using ASC.Files.Resources; +using ASC.MessagingSystem; +using ASC.Web.Core.Files; +using ASC.Web.Files.Classes; +using ASC.Web.Files.Helpers; +using ASC.Web.Files.Utils; + +using Microsoft.Extensions.DependencyInjection; + +namespace ASC.Web.Files.Services.WCFService.FileOperations +{ + internal class FileMoveCopyOperationData : FileOperationData + { + public string ToFolderId { get; } + public bool Copy { get; } + public FileConflictResolveType ResolveType { get; } + public Dictionary Headers { get; } + + public FileMoveCopyOperationData(List folders, List files, Tenant tenant, string toFolderId, bool copy, FileConflictResolveType resolveType, bool holdResult = true, Dictionary headers = null) + : base(folders, files, tenant, holdResult) + { + ToFolderId = toFolderId; + Copy = copy; + ResolveType = resolveType; + Headers = headers; + } + } + + class FileMoveCopyOperation : FileOperation + { + private readonly string _toFolderId; + private readonly bool _copy; + private readonly FileConflictResolveType _resolveType; + private readonly List _needToMark = new List(); + + private readonly Dictionary _headers; + + public override FileOperationType OperationType + { + get { return _copy ? FileOperationType.Copy : FileOperationType.Move; } + } + + public FileMoveCopyOperation(IServiceProvider serviceProvider, FileMoveCopyOperationData data) + : base(serviceProvider, data) + { + _toFolderId = data.ToFolderId; + _copy = data.Copy; + _resolveType = data.ResolveType; + + _headers = data.Headers; + } + + protected override void Do(IServiceScope scope) + { + var fileMarker = scope.ServiceProvider.GetService(); + + Status += string.Format("folder_{0}{1}", _toFolderId, FileOperation.SPLIT_CHAR); + + //TODO: check on each iteration? + var toFolder = FolderDao.GetFolder(_toFolderId); + if (toFolder == null) return; + if (!FilesSecurity.CanCreate(toFolder)) throw new System.Security.SecurityException(FilesCommonResource.ErrorMassage_SecurityException_Create); + + if (FolderDao.GetParentFolders(toFolder.ID).Any(parent => Folders.Contains(parent.ID.ToString()))) + { + Error = FilesCommonResource.ErrorMassage_FolderCopyError; + return; + } + + if (_copy) + { + Folder rootFrom = null; + if (0 < Folders.Count) rootFrom = FolderDao.GetRootFolder(Folders[0]); + if (0 < Files.Count) rootFrom = FolderDao.GetRootFolderByFile(Files[0]); + if (rootFrom != null && rootFrom.FolderType == FolderType.TRASH) throw new InvalidOperationException("Can not copy from Trash."); + if (toFolder.RootFolderType == FolderType.TRASH) throw new InvalidOperationException("Can not copy to Trash."); + } + + MoveOrCopyFolders(scope, Folders, toFolder, _copy); + MoveOrCopyFiles(scope, Files, toFolder, _copy); + + _needToMark.Distinct().ToList().ForEach(x => fileMarker.MarkAsNew(x)); + } + + private void MoveOrCopyFolders(IServiceScope scope, ICollection folderIds, Folder toFolder, bool copy) + { + if (folderIds.Count == 0) return; + + var filesMessageService = scope.ServiceProvider.GetService(); + var fileMarker = scope.ServiceProvider.GetService(); + var toFolderId = toFolder.ID; + var isToFolder = Equals(toFolderId.ToString(), _toFolderId); + + foreach (var folderId in folderIds) + { + CancellationToken.ThrowIfCancellationRequested(); + + var folder = FolderDao.GetFolder(folderId); + if (folder == null) + { + Error = FilesCommonResource.ErrorMassage_FolderNotFound; + } + else if (!FilesSecurity.CanRead(folder)) + { + Error = FilesCommonResource.ErrorMassage_SecurityException_ReadFolder; + } + else if (!Equals((folder.ParentFolderID ?? string.Empty).ToString(), toFolderId.ToString()) || _resolveType == FileConflictResolveType.Duplicate) + { + try + { + //if destination folder contains folder with same name then merge folders + var conflictFolder = FolderDao.GetFolder(folder.Title, toFolderId); + Folder newFolder; + + if (copy || conflictFolder != null) + { + if (conflictFolder != null) + { + newFolder = conflictFolder; + + if (isToFolder) + _needToMark.Add(conflictFolder); + } + else + { + newFolder = FolderDao.CopyFolder(folder.ID, toFolderId, CancellationToken); + filesMessageService.Send(newFolder, toFolder, _headers, MessageAction.FolderCopied, newFolder.Title, toFolder.Title); + + if (isToFolder) + _needToMark.Add(newFolder); + + if (ProcessedFolder(folderId)) + { + Status += string.Format("folder_{0}{1}", newFolder.ID, FileOperation.SPLIT_CHAR); + } + } + + if (FolderDao.UseRecursiveOperation(folder.ID, toFolderId)) + { + MoveOrCopyFiles(scope, FileDao.GetFiles(folder.ID), newFolder, copy); + MoveOrCopyFolders(scope, FolderDao.GetFolders(folder.ID).Select(f => f.ID).ToList(), newFolder, copy); + + if (!copy) + { + if (!FilesSecurity.CanDelete(folder)) + { + Error = FilesCommonResource.ErrorMassage_SecurityException_MoveFolder; + } + else if (FolderDao.IsEmpty(folder.ID)) + { + FolderDao.DeleteFolder(folder.ID); + if (ProcessedFolder(folderId)) + { + Status += string.Format("folder_{0}{1}", newFolder.ID, FileOperation.SPLIT_CHAR); + } + } + } + } + else + { + if (conflictFolder != null) + { + object newFolderId; + if (copy) + { + newFolder = FolderDao.CopyFolder(folder.ID, toFolderId, CancellationToken); + newFolderId = newFolder.ID; + filesMessageService.Send(newFolder, toFolder, _headers, MessageAction.FolderCopiedWithOverwriting, newFolder.Title, toFolder.Title); + + if (isToFolder) + _needToMark.Add(newFolder); + + if (ProcessedFolder(folderId)) + { + Status += string.Format("folder_{0}{1}", newFolderId, FileOperation.SPLIT_CHAR); + } + } + else if (!FilesSecurity.CanDelete(folder)) + { + Error = FilesCommonResource.ErrorMassage_SecurityException_MoveFolder; + } + else if (WithError(scope, FileDao.GetFiles(folder.ID, new OrderBy(SortedByType.AZ, true), FilterType.FilesOnly, false, Guid.Empty, string.Empty, false, true), out var tmpError)) + { + Error = tmpError; + } + else + { + fileMarker.RemoveMarkAsNewForAll(folder); + + newFolderId = FolderDao.MoveFolder(folder.ID, toFolderId, CancellationToken); + newFolder = FolderDao.GetFolder(newFolderId); + filesMessageService.Send(folder.RootFolderType != FolderType.USER ? folder : newFolder, toFolder, _headers, MessageAction.FolderMovedWithOverwriting, folder.Title, toFolder.Title); + + if (isToFolder) + _needToMark.Add(newFolder); + + if (ProcessedFolder(folderId)) + { + Status += string.Format("folder_{0}{1}", newFolderId, FileOperation.SPLIT_CHAR); + } + } + } + } + } + else + { + if (!FilesSecurity.CanDelete(folder)) + { + Error = FilesCommonResource.ErrorMassage_SecurityException_MoveFolder; + } + else if (WithError(scope, FileDao.GetFiles(folder.ID, new OrderBy(SortedByType.AZ, true), FilterType.FilesOnly, false, Guid.Empty, string.Empty, false, true), out var tmpError)) + { + Error = tmpError; + } + else + { + fileMarker.RemoveMarkAsNewForAll(folder); + + var newFolderId = FolderDao.MoveFolder(folder.ID, toFolderId, CancellationToken); + newFolder = FolderDao.GetFolder(newFolderId); + filesMessageService.Send(folder.RootFolderType != FolderType.USER ? folder : newFolder, toFolder, _headers, MessageAction.FolderMoved, folder.Title, toFolder.Title); + + if (isToFolder) + _needToMark.Add(newFolder); + + if (ProcessedFolder(folderId)) + { + Status += string.Format("folder_{0}{1}", newFolderId, FileOperation.SPLIT_CHAR); + } + } + } + } + catch (Exception ex) + { + Error = ex.Message; + + Logger.Error(Error, ex); + } + } + ProgressStep(FolderDao.CanCalculateSubitems(folderId) ? null : folderId); + } + } + + private void MoveOrCopyFiles(IServiceScope scope, ICollection fileIds, Folder toFolder, bool copy) + { + if (fileIds.Count == 0) return; + + var filesMessageService = scope.ServiceProvider.GetService(); + var fileMarker = scope.ServiceProvider.GetService(); + var fileUtility = scope.ServiceProvider.GetService(); + var global = scope.ServiceProvider.GetService(); + var entryManager = scope.ServiceProvider.GetService(); + + var toFolderId = toFolder.ID; + foreach (var fileId in fileIds) + { + CancellationToken.ThrowIfCancellationRequested(); + + var file = FileDao.GetFile(fileId); + if (file == null) + { + Error = FilesCommonResource.ErrorMassage_FileNotFound; + } + else if (!FilesSecurity.CanRead(file)) + { + Error = FilesCommonResource.ErrorMassage_SecurityException_ReadFile; + } + else if (global.EnableUploadFilter + && !fileUtility.ExtsUploadable.Contains(FileUtility.GetFileExtension(file.Title))) + { + Error = FilesCommonResource.ErrorMassage_NotSupportedFormat; + } + else + { + var parentFolder = FolderDao.GetFolder(file.FolderID); + try + { + var conflict = _resolveType == FileConflictResolveType.Duplicate + ? null + : FileDao.GetFile(toFolderId, file.Title); + if (conflict == null) + { + File newFile = null; + if (copy) + { + try + { + newFile = FileDao.CopyFile(file.ID, toFolderId); //Stream copy will occur inside dao + filesMessageService.Send(newFile, toFolder, _headers, MessageAction.FileCopied, newFile.Title, parentFolder.Title, toFolder.Title); + + if (Equals(newFile.FolderID.ToString(), _toFolderId)) + { + _needToMark.Add(newFile); + } + + if (ProcessedFile(fileId)) + { + Status += string.Format("file_{0}{1}", newFile.ID, FileOperation.SPLIT_CHAR); + } + } + catch + { + if (newFile != null) + { + FileDao.DeleteFile(newFile.ID); + } + throw; + } + } + else + { + if (WithError(scope, new[] { file }, out var tmpError)) + { + Error = tmpError; + } + else + { + fileMarker.RemoveMarkAsNewForAll(file); + + var newFileId = FileDao.MoveFile(file.ID, toFolderId); + newFile = FileDao.GetFile(newFileId); + filesMessageService.Send(file.RootFolderType != FolderType.USER ? file : newFile, toFolder, _headers, MessageAction.FileMoved, file.Title, parentFolder.Title, toFolder.Title); + + if (Equals(toFolderId.ToString(), _toFolderId)) + { + _needToMark.Add(newFile); + } + + if (ProcessedFile(fileId)) + { + Status += string.Format("file_{0}{1}", newFileId, FileOperation.SPLIT_CHAR); + } + } + } + } + else + { + if (_resolveType == FileConflictResolveType.Overwrite) + { + if (!FilesSecurity.CanEdit(conflict)) + { + Error = FilesCommonResource.ErrorMassage_SecurityException; + } + else if (entryManager.FileLockedForMe(conflict.ID)) + { + Error = FilesCommonResource.ErrorMassage_LockedFile; + } + else if (FileTracker.IsEditing(conflict.ID)) + { + Error = FilesCommonResource.ErrorMassage_SecurityException_UpdateEditingFile; + } + else + { + var newFile = conflict; + newFile.Version++; + newFile.VersionGroup++; + newFile.PureTitle = file.PureTitle; + newFile.ConvertedType = file.ConvertedType; + newFile.Comment = FilesCommonResource.CommentOverwrite; + newFile.Encrypted = file.Encrypted; + + using (var stream = FileDao.GetFileStream(file)) + { + newFile.ContentLength = stream.CanSeek ? stream.Length : file.ContentLength; + + newFile = FileDao.SaveFile(newFile, stream); + } + + _needToMark.Add(newFile); + + if (copy) + { + filesMessageService.Send(newFile, toFolder, _headers, MessageAction.FileCopiedWithOverwriting, newFile.Title, parentFolder.Title, toFolder.Title); + if (ProcessedFile(fileId)) + { + Status += string.Format("file_{0}{1}", newFile.ID, FileOperation.SPLIT_CHAR); + } + } + else + { + if (Equals(file.FolderID.ToString(), toFolderId.ToString())) + { + if (ProcessedFile(fileId)) + { + Status += string.Format("file_{0}{1}", newFile.ID, FileOperation.SPLIT_CHAR); + } + } + else + { + if (WithError(scope, new[] { file }, out var tmpError)) + { + Error = tmpError; + } + else + { + FileDao.DeleteFile(file.ID); + + filesMessageService.Send(file.RootFolderType != FolderType.USER ? file : newFile, toFolder, _headers, MessageAction.FileMovedWithOverwriting, file.Title, parentFolder.Title, toFolder.Title); + + if (ProcessedFile(fileId)) + { + Status += string.Format("file_{0}{1}", newFile.ID, FileOperation.SPLIT_CHAR); + } + } + } + } + } + } + else if (_resolveType == FileConflictResolveType.Skip) + { + //nothing + } + } + } + catch (Exception ex) + { + Error = ex.Message; + Logger.Error(Error, ex); + } + } + ProgressStep(fileId: FolderDao.CanCalculateSubitems(fileId) ? null : fileId); + } + } + + private bool WithError(IServiceScope scope, IEnumerable files, out string error) + { + var entryManager = scope.ServiceProvider.GetService(); + error = null; + foreach (var file in files) + { + if (!FilesSecurity.CanDelete(file)) + { + error = FilesCommonResource.ErrorMassage_SecurityException_MoveFile; + return true; + } + if (entryManager.FileLockedForMe(file.ID)) + { + error = FilesCommonResource.ErrorMassage_LockedFile; + return true; + } + if (FileTracker.IsEditing(file.ID)) + { + error = FilesCommonResource.ErrorMassage_SecurityException_UpdateEditingFile; + return true; + } + } + return false; + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Services/WCFService/FileOperations/FileOperation.cs b/products/ASC.Files/Server/Services/WCFService/FileOperations/FileOperation.cs new file mode 100644 index 0000000000..549ced883c --- /dev/null +++ b/products/ASC.Files/Server/Services/WCFService/FileOperations/FileOperation.cs @@ -0,0 +1,263 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Security; +using System.Security.Principal; +using System.Threading; +using System.Threading.Tasks; + +using ASC.Common.Logging; +using ASC.Common.Security.Authentication; +using ASC.Common.Security.Authorizing; +using ASC.Common.Threading; +using ASC.Core; +using ASC.Core.Tenants; +using ASC.Files.Core; +using ASC.Files.Core.Security; +using ASC.Files.Resources; + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace ASC.Web.Files.Services.WCFService.FileOperations +{ + abstract class FileOperationData + { + public List Folders { get; private set; } + + public List Files { get; private set; } + + public Tenant Tenant { get; } + + public bool HoldResult { get; private set; } + + protected FileOperationData(List folders, List files, Tenant tenant, bool holdResult = true) + { + Folders = folders ?? new List(); + Files = files ?? new List(); + Tenant = tenant; + HoldResult = holdResult; + } + } + + public static class FileOperation + { + public const string SPLIT_CHAR = ":"; + public const string OWNER = "Owner"; + public const string OPERATION_TYPE = "OperationType"; + public const string SOURCE = "Source"; + public const string PROGRESS = "Progress"; + public const string RESULT = "Result"; + public const string ERROR = "Error"; + public const string PROCESSED = "Processed"; + public const string FINISHED = "Finished"; + public const string HOLD = "Hold"; + } + + abstract class FileOperation where T : FileOperationData + { + private readonly IPrincipal principal; + private readonly string culture; + private int total; + private int processed; + private int successProcessed; + + + protected DistributedTask TaskInfo { get; private set; } + + protected string Status { get; set; } + + protected string Error { get; set; } + + protected Tenant CurrentTenant { get; private set; } + + protected FileSecurity FilesSecurity { get; private set; } + + protected IFolderDao FolderDao { get; private set; } + + protected IFileDao FileDao { get; private set; } + + protected ITagDao TagDao { get; private set; } + + protected IProviderDao ProviderDao { get; private set; } + + protected ILog Logger { get; private set; } + + protected CancellationToken CancellationToken { get; private set; } + + protected List Folders { get; private set; } + + protected List Files { get; private set; } + + protected bool HoldResult { get; private set; } + + public abstract FileOperationType OperationType { get; } + public IServiceProvider ServiceProvider { get; } + + protected FileOperation(IServiceProvider serviceProvider, T fileOperationData) + { + principal = Thread.CurrentPrincipal; + culture = Thread.CurrentThread.CurrentCulture.Name; + + TaskInfo = new DistributedTask(); + ServiceProvider = serviceProvider; + Files = fileOperationData.Files; + Folders = fileOperationData.Folders; + HoldResult = fileOperationData.HoldResult; + CurrentTenant = fileOperationData.Tenant; + } + + public void RunJob(DistributedTask _, CancellationToken cancellationToken) + { + try + { + CancellationToken = cancellationToken; + + using var scope = ServiceProvider.CreateScope(); + var tenantManager = scope.ServiceProvider.GetService(); + tenantManager.SetCurrentTenant(CurrentTenant); + var daoFactory = scope.ServiceProvider.GetService(); + var fileSecurity = scope.ServiceProvider.GetService(); + var logger = scope.ServiceProvider.GetService>().CurrentValue; + + + Thread.CurrentPrincipal = principal; + Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(culture); + Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(culture); + + FolderDao = daoFactory.FolderDao; + FileDao = daoFactory.FileDao; + TagDao = daoFactory.TagDao; + ProviderDao = daoFactory.ProviderDao; + FilesSecurity = fileSecurity; + + Logger = logger; + + total = InitTotalProgressSteps(); + + Do(scope); + } + catch (AuthorizingException authError) + { + Error = FilesCommonResource.ErrorMassage_SecurityException; + Logger.Error(Error, new SecurityException(Error, authError)); + } + catch (AggregateException ae) + { + ae.Flatten().Handle(e => e is TaskCanceledException || e is OperationCanceledException); + } + catch (Exception error) + { + Error = error is TaskCanceledException || error is OperationCanceledException + ? FilesCommonResource.ErrorMassage_OperationCanceledException + : error.Message; + Logger.Error(error, error); + } + finally + { + try + { + TaskInfo.SetProperty(FileOperation.FINISHED, true); + PublishTaskInfo(); + } + catch { /* ignore */ } + } + } + + public virtual DistributedTask GetDistributedTask() + { + FillDistributedTask(); + return TaskInfo; + } + + + protected virtual void FillDistributedTask() + { + var progress = total != 0 ? 100 * processed / total : 0; + + TaskInfo.SetProperty(FileOperation.SOURCE, string.Join(FileOperation.SPLIT_CHAR, Folders.Select(f => "folder_" + f).Concat(Files.Select(f => "file_" + f)).ToArray())); + TaskInfo.SetProperty(FileOperation.OPERATION_TYPE, OperationType); + TaskInfo.SetProperty(FileOperation.OWNER, ((IAccount)Thread.CurrentPrincipal.Identity).ID); + TaskInfo.SetProperty(FileOperation.PROGRESS, progress < 100 ? progress : 100); + TaskInfo.SetProperty(FileOperation.RESULT, Status); + TaskInfo.SetProperty(FileOperation.ERROR, Error); + TaskInfo.SetProperty(FileOperation.PROCESSED, successProcessed); + TaskInfo.SetProperty(FileOperation.HOLD, HoldResult); + } + + protected virtual int InitTotalProgressSteps() + { + var count = Files.Count; + Folders.ForEach(f => count += 1 + (FolderDao.CanCalculateSubitems(f) ? FolderDao.GetItemsCount(f) : 0)); + return count; + } + + protected void ProgressStep(object folderId = null, object fileId = null) + { + if (folderId == null && fileId == null + || folderId != null && Folders.Contains(folderId) + || fileId != null && Files.Contains(fileId)) + { + processed++; + PublishTaskInfo(); + } + } + + protected bool ProcessedFolder(object folderId) + { + successProcessed++; + if (Folders.Contains(folderId)) + { + Status += string.Format("folder_{0}{1}", folderId, FileOperation.SPLIT_CHAR); + return true; + } + return false; + } + + protected bool ProcessedFile(object fileId) + { + successProcessed++; + if (Files.Contains(fileId)) + { + Status += string.Format("file_{0}{1}", fileId, FileOperation.SPLIT_CHAR); + return true; + } + return false; + } + + protected void PublishTaskInfo() + { + FillDistributedTask(); + TaskInfo.PublishChanges(); + } + + protected abstract void Do(IServiceScope serviceScope); + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Services/WCFService/FileOperations/FileOperationResult.cs b/products/ASC.Files/Server/Services/WCFService/FileOperations/FileOperationResult.cs new file mode 100644 index 0000000000..f7c990f72d --- /dev/null +++ b/products/ASC.Files/Server/Services/WCFService/FileOperations/FileOperationResult.cs @@ -0,0 +1,58 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.Runtime.Serialization; + +namespace ASC.Web.Files.Services.WCFService.FileOperations +{ + [DataContract(Name = "operation_result", Namespace = "")] + public class FileOperationResult + { + [DataMember(Name = "id")] + public string Id { get; set; } + + [DataMember(Name = "operation")] + public FileOperationType OperationType { get; set; } + + [DataMember(Name = "progress")] + public int Progress { get; set; } + + [DataMember(Name = "source")] + public string Source { get; set; } + + [DataMember(Name = "result")] + public string Result { get; set; } + + [DataMember(Name = "error")] + public string Error { get; set; } + + [DataMember(Name = "processed")] + public string Processed { get; set; } + + [DataMember(Name = "finished")] + public bool Finished { get; set; } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Services/WCFService/FileOperations/FileOperationType.cs b/products/ASC.Files/Server/Services/WCFService/FileOperations/FileOperationType.cs new file mode 100644 index 0000000000..4217901858 --- /dev/null +++ b/products/ASC.Files/Server/Services/WCFService/FileOperations/FileOperationType.cs @@ -0,0 +1,39 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +namespace ASC.Web.Files.Services.WCFService.FileOperations +{ + public enum FileOperationType + { + Move, + Copy, + Delete, + Download, + MarkAsRead, + Import, + Convert + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Services/WCFService/FileOperations/FileOperationsManager.cs b/products/ASC.Files/Server/Services/WCFService/FileOperations/FileOperationsManager.cs new file mode 100644 index 0000000000..6e3cfb95fe --- /dev/null +++ b/products/ASC.Files/Server/Services/WCFService/FileOperations/FileOperationsManager.cs @@ -0,0 +1,186 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +using ASC.Common; +using ASC.Common.Threading; +using ASC.Core; +using ASC.Files.Resources; + +namespace ASC.Web.Files.Services.WCFService.FileOperations +{ + public class FileOperationsManager + { + private readonly DistributedTaskQueue tasks; + + public IServiceProvider ServiceProvider { get; } + + public FileOperationsManager(DistributedTaskCacheNotify distributedTaskCacheNotify, IServiceProvider serviceProvider) + { + tasks = new DistributedTaskQueue(distributedTaskCacheNotify, "fileOperations", 10); + ServiceProvider = serviceProvider; + } + + public ItemList GetOperationResults(AuthContext authContext) + { + var operations = tasks.GetTasks(); + var processlist = Process.GetProcesses(); + + foreach (var o in operations.Where(o => string.IsNullOrEmpty(o.InstanceId) + || processlist.All(p => p.Id != int.Parse(o.InstanceId)))) + { + o.SetProperty(FileOperation.PROGRESS, 100); + tasks.RemoveTask(o.Id); + } + + operations = operations.Where(t => t.GetProperty(FileOperation.OWNER) == authContext.CurrentAccount.ID); + foreach (var o in operations.Where(o => DistributedTaskStatus.Running < o.Status)) + { + o.SetProperty(FileOperation.PROGRESS, 100); + tasks.RemoveTask(o.Id); + } + + var results = operations + .Where(o => o.GetProperty(FileOperation.HOLD) || o.GetProperty(FileOperation.PROGRESS) != 100) + .Select(o => new FileOperationResult + { + Id = o.Id, + OperationType = o.GetProperty(FileOperation.OPERATION_TYPE), + Source = o.GetProperty(FileOperation.SOURCE), + Progress = o.GetProperty(FileOperation.PROGRESS), + Processed = o.GetProperty(FileOperation.PROCESSED).ToString(), + Result = o.GetProperty(FileOperation.RESULT), + Error = o.GetProperty(FileOperation.ERROR), + Finished = o.GetProperty(FileOperation.FINISHED), + }); + + return new ItemList(results); + } + + public ItemList CancelOperations(AuthContext authContext) + { + var operations = tasks.GetTasks() + .Where(t => t.GetProperty(FileOperation.OWNER) == authContext.CurrentAccount.ID); + + foreach (var o in operations) + { + tasks.CancelTask(o.Id); + } + + return GetOperationResults(authContext); + } + + + public ItemList MarkAsRead(AuthContext authContext, TenantManager tenantManager, List folderIds, List fileIds) + { + var op = new FileMarkAsReadOperation(ServiceProvider, new FileMarkAsReadOperationData(folderIds, fileIds, tenantManager.GetCurrentTenant())); + return QueueTask(authContext, op); + } + + public ItemList Download(AuthContext authContext, TenantManager tenantManager, Dictionary folders, Dictionary files, Dictionary headers) + { + var operations = tasks.GetTasks() + .Where(t => t.GetProperty(FileOperation.OWNER) == authContext.CurrentAccount.ID) + .Where(t => t.GetProperty(FileOperation.OPERATION_TYPE) == FileOperationType.Download); + + if (operations.Any(o => o.Status <= DistributedTaskStatus.Running)) + { + throw new InvalidOperationException(FilesCommonResource.ErrorMassage_ManyDownloads); + } + + var op = new FileDownloadOperation(ServiceProvider, new FileDownloadOperationData(folders, files, tenantManager.GetCurrentTenant(), headers)); + return QueueTask(authContext, op); + } + + public ItemList MoveOrCopy(AuthContext authContext, TenantManager tenantManager, List folders, List files, string destFolderId, bool copy, FileConflictResolveType resolveType, bool holdResult, Dictionary headers) + { + var op = new FileMoveCopyOperation(ServiceProvider, new FileMoveCopyOperationData(folders, files, tenantManager.GetCurrentTenant(), destFolderId, copy, resolveType, holdResult, headers)); + return QueueTask(authContext, op); + } + + public ItemList Delete(AuthContext authContext, TenantManager tenantManager, List folders, List files, bool ignoreException, bool holdResult, bool immediately, Dictionary headers) + { + var op = new FileDeleteOperation(ServiceProvider, new FileDeleteOperationData(folders, files, tenantManager.GetCurrentTenant(), holdResult, ignoreException, immediately, headers)); + return QueueTask(authContext, op); + } + + + private ItemList QueueTask(AuthContext authContext, FileOperation op) where T : FileOperationData + { + tasks.QueueTask(op.RunJob, op.GetDistributedTask()); + return GetOperationResults(authContext); + } + } + + public class FileOperationsManagerHelper + { + public FileOperationsManager FileOperationsManager { get; } + public AuthContext AuthContext { get; } + public TenantManager TenantManager { get; } + + public FileOperationsManagerHelper( + FileOperationsManager fileOperationsManager, + AuthContext authContext, + TenantManager tenantManager) + { + FileOperationsManager = fileOperationsManager; + AuthContext = authContext; + TenantManager = tenantManager; + } + + public ItemList GetOperationResults() => FileOperationsManager.GetOperationResults(AuthContext); + public ItemList CancelOperations() => FileOperationsManager.CancelOperations(AuthContext); + public ItemList MarkAsRead(List folderIds, List fileIds) + => FileOperationsManager.MarkAsRead(AuthContext, TenantManager, folderIds, fileIds); + public ItemList Download(Dictionary folders, Dictionary files, Dictionary headers) + => FileOperationsManager.Download(AuthContext, TenantManager, folders, files, headers); + + public ItemList MoveOrCopy(List folders, List files, string destFolderId, bool copy, FileConflictResolveType resolveType, bool holdResult, Dictionary headers) + => FileOperationsManager.MoveOrCopy(AuthContext, TenantManager, folders, files, destFolderId, copy, resolveType, holdResult, headers); + + public ItemList Delete(List folders, List files, bool ignoreException, bool holdResult, bool immediately, Dictionary headers) + => FileOperationsManager.Delete(AuthContext, TenantManager, folders, files, ignoreException, holdResult, immediately, headers); + } + + public static class FileOperationsManagerHelperExtention + { + public static DIHelper AddFileOperationsManagerHelperService(this DIHelper services) + { + services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddScoped(); + + return services + .AddAuthContextService() + .AddTenantManagerService() + ; + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Services/WCFService/IFileStorageService.cs b/products/ASC.Files/Server/Services/WCFService/IFileStorageService.cs new file mode 100644 index 0000000000..de1b66a587 --- /dev/null +++ b/products/ASC.Files/Server/Services/WCFService/IFileStorageService.cs @@ -0,0 +1,192 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.IO; + +using ASC.Files.Core; +using ASC.Web.Files.Helpers; +using ASC.Web.Files.Services.WCFService.FileOperations; + +using File = ASC.Files.Core.File; +using FileShare = ASC.Files.Core.Security.FileShare; + +namespace ASC.Web.Files.Services.WCFService +{ + public interface IFileStorageService + { + #region Folder Manager + + Folder GetFolder(string folderId); + + ItemList GetFolders(string parentId); + + ItemList GetPath(string folderId); + + Folder CreateNewFolder(string parentId, string title); + + Folder FolderRename(string folderId, string title); + + DataWrapper GetFolderItems(string parentId, int from, int count, FilterType filter, bool subjectGroup, string subjectID, string searchText, bool searchInContent, bool withSubfolders, OrderBy orderBy); + + object GetFolderItemsXml(string parentId, int from, int count, FilterType filter, bool subjectGroup, string subjectID, string searchText, bool searchInContent, bool withSubfolders, OrderBy orderBy); + + ItemList GetItems(ItemList items, FilterType filter, bool subjectGroup, string subjectID, string searchText); + + ItemDictionary MoveOrCopyFilesCheck(ItemList items, string destFolderId); + + ItemList MoveOrCopyItems(ItemList items, string destFolderId, FileConflictResolveType resolveType, bool isCopyOperation, bool deleteAfter = false); + + ItemList DeleteItems(string action, ItemList items, bool ignoreException = false, bool deleteAfter = false, bool immediately = false); + + void ReassignStorage(Guid userFromId, Guid userToId); + + void DeleteStorage(Guid userId); + + #endregion + + #region File Manager + + File GetFile(string fileId, int version); + + File CreateNewFile(FileModel fileWrapper); + + File FileRename(string fileId, string title); + + KeyValuePair> UpdateToVersion(string fileId, int version); + + KeyValuePair> CompleteVersion(string fileId, int version, bool continueVersion); + + string UpdateComment(string fileId, int version, string comment); + + ItemList GetFileHistory(string fileId); + + ItemList GetSiblingsFile(string fileId, string folderId, FilterType filter, bool subjectGroup, string subjectID, string searchText, bool searchInContent, bool withSubfolders, OrderBy orderBy); + + KeyValuePair TrackEditFile(string fileId, Guid tabId, string docKeyForTrack, string doc, bool isFinish); + + ItemDictionary CheckEditing(ItemList filesId); + + File SaveEditing(string fileId, string fileExtension, string fileuri, Stream stream, string doc, bool forcesave); + + File UpdateFileStream(string fileId, Stream stream, bool encrypted); + + string StartEdit(string fileId, bool editingAlone, string doc); + + ItemList CheckConversion(ItemList> filesIdVersion); + + File LockFile(string fileId, bool lockFile); + + ItemList GetEditHistory(string fileId, string doc); + + EditHistoryData GetEditDiffUrl(string fileId, int version, string doc = null); + + ItemList RestoreVersion(string fileId, int version, string url, string doc = null); + + Web.Core.Files.DocumentService.FileLink GetPresignedUri(string fileId); + + #endregion + + #region Utils + + ItemList ChangeOwner(ItemList items, Guid userId); + + ItemList BulkDownload(Dictionary items); + + ItemList GetTasksStatuses(); + + ItemList EmptyTrash(); + + ItemList TerminateTasks(); + + string GetShortenLink(string fileId); + + bool StoreOriginal(bool store); + + bool HideConfirmConvert(bool isForSave); + + bool UpdateIfExist(bool update); + + bool Forcesave(bool value); + + bool StoreForcesave(bool value); + + bool ChangeDeleteConfrim(bool update); + + string GetHelpCenter(); + + #endregion + + #region Ace Manager + + ItemList GetSharedInfo(ItemList objectId); + + ItemList GetSharedInfoShort(string objectId); + + ItemList SetAceObject(AceCollection aceCollection, bool notify); + + void RemoveAce(ItemList items); + + ItemList MarkAsRead(ItemList items); + + object GetNewItems(string folderId); + + bool SetAceLink(string fileId, FileShare share); + + ItemList SharedUsers(string fileId); + + ItemList SendEditorNotify(string fileId, MentionMessageWrapper mentionMessage); + + #endregion + + #region ThirdParty + + ItemList GetThirdParty(); + + ItemList GetThirdPartyFolder(int folderType); + + Folder SaveThirdParty(ThirdPartyParams thirdPartyParams); + + object DeleteThirdParty(string providerId); + + bool ChangeAccessToThirdparty(bool enableThirdpartySettings); + + bool SaveDocuSign(string code); + + object DeleteDocuSign(); + + string SendDocuSign(string fileId, DocuSignData docuSignData); + + #endregion + + #region MailMerge + + ItemList GetMailAccounts(); + + #endregion + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Services/WCFService/Wrappers/AceWrapper.cs b/products/ASC.Files/Server/Services/WCFService/Wrappers/AceWrapper.cs new file mode 100644 index 0000000000..aad06e5645 --- /dev/null +++ b/products/ASC.Files/Server/Services/WCFService/Wrappers/AceWrapper.cs @@ -0,0 +1,124 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Runtime.Serialization; + +using ASC.Files.Core; +using ASC.Files.Core.Security; +using ASC.Files.Resources; + +namespace ASC.Web.Files.Services.WCFService +{ + [DataContract(Name = "ace_collection", Namespace = "")] + public class AceCollection + { + [DataMember(Name = "entries", Order = 1)] + public ItemList Entries { get; set; } + + [DataMember(Name = "aces", Order = 2)] + public ItemList Aces { get; set; } + + [DataMember(Name = "message", Order = 3, IsRequired = false)] + public string Message { get; set; } + } + + [DataContract(Name = "ace_wrapper", Namespace = "")] + public class AceWrapper + { + [DataMember(Name = "id", Order = 1)] + public Guid SubjectId { get; set; } + + [DataMember(Name = "title", Order = 2, EmitDefaultValue = false)] + public string SubjectName { get; set; } + + [DataMember(Name = "link", Order = 3, EmitDefaultValue = false)] + public string Link { get; set; } + + [DataMember(Name = "is_group", Order = 4)] + public bool SubjectGroup { get; set; } + + [DataMember(Name = "owner", Order = 5)] + public bool Owner { get; set; } + + [DataMember(Name = "ace_status", Order = 6)] + public FileShare Share { get; set; } + + [DataMember(Name = "locked", Order = 7)] + public bool LockedRights { get; set; } + + [DataMember(Name = "disable_remove", Order = 8)] + public bool DisableRemove { get; set; } + } + + [DataContract(Name = "sharingSettings", Namespace = "")] + public class AceShortWrapper + { + [DataMember(Name = "user")] + public string User { get; set; } + + [DataMember(Name = "permissions")] + public string Permissions { get; set; } + + [DataMember(Name = "isLink", EmitDefaultValue = false, IsRequired = false)] + public bool IsLink { get; set; } + + public AceShortWrapper(AceWrapper aceWrapper) + { + var permission = string.Empty; + + switch (aceWrapper.Share) + { + case FileShare.Read: + permission = FilesCommonResource.AceStatusEnum_Read; + break; + case FileShare.ReadWrite: + permission = FilesCommonResource.AceStatusEnum_ReadWrite; + break; + case FileShare.Restrict: + permission = FilesCommonResource.AceStatusEnum_Restrict; + break; + case FileShare.Review: + permission = FilesCommonResource.AceStatusEnum_Review; + break; + case FileShare.FillForms: + permission = FilesCommonResource.AceStatusEnum_FillForms; + break; + case FileShare.Comment: + permission = FilesCommonResource.AceStatusEnum_Comment; + break; + } + + User = aceWrapper.SubjectName; + if (aceWrapper.SubjectId.Equals(FileConstant.ShareLinkId)) + { + IsLink = true; + User = FilesCommonResource.AceShareLink; + } + Permissions = permission; + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Services/WCFService/Wrappers/BaseTypeWrapper.cs b/products/ASC.Files/Server/Services/WCFService/Wrappers/BaseTypeWrapper.cs new file mode 100644 index 0000000000..a4fbad8367 --- /dev/null +++ b/products/ASC.Files/Server/Services/WCFService/Wrappers/BaseTypeWrapper.cs @@ -0,0 +1,59 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace ASC.Web.Files.Services.WCFService +{ + [CollectionDataContract(Name = "{0}List", ItemName = "entry", Namespace = "")] + public class ItemList : List + { + public ItemList() + : base() + { + } + + public ItemList(IEnumerable items) + : base(items) + { + } + } + + [CollectionDataContract(Name = "{1}Hash", ItemName = "entry", KeyName = "key", ValueName = "value", Namespace = "")] + public class ItemDictionary : Dictionary + { + public ItemDictionary() + : base() + { + } + + public ItemDictionary(IDictionary items) + : base(items) + { + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Services/WCFService/Wrappers/DataWrapper.cs b/products/ASC.Files/Server/Services/WCFService/Wrappers/DataWrapper.cs new file mode 100644 index 0000000000..ef1af79654 --- /dev/null +++ b/products/ASC.Files/Server/Services/WCFService/Wrappers/DataWrapper.cs @@ -0,0 +1,51 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.Collections.Generic; +using System.Runtime.Serialization; +using ASC.Files.Core; + +namespace ASC.Web.Files.Services.WCFService +{ + [DataContract(Name = "composite_data", Namespace = "")] + public class DataWrapper + { + [DataMember(IsRequired = false, Name = "entries", EmitDefaultValue = false)] + public ItemList Entries { get; set; } + + [DataMember(IsRequired = false, Name = "total")] + public int Total { get; set; } + + [DataMember(IsRequired = false, Name = "path_parts")] + public ItemList FolderPathParts { get; set; } + + [DataMember(IsRequired = false, Name = "folder_info")] + public Folder FolderInfo { get; set; } + + [DataMember(IsRequired = false, Name = "root_folders_id_marked_as_new")] + public Dictionary RootFoldersIdMarkedAsNew { get; set; } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Services/WCFService/Wrappers/MentionWrapper.cs b/products/ASC.Files/Server/Services/WCFService/Wrappers/MentionWrapper.cs new file mode 100644 index 0000000000..3eeacf3d29 --- /dev/null +++ b/products/ASC.Files/Server/Services/WCFService/Wrappers/MentionWrapper.cs @@ -0,0 +1,86 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.Collections.Generic; +using System.Runtime.Serialization; + +using ASC.Core.Users; +using ASC.Web.Core.Users; + +namespace ASC.Web.Files.Services.WCFService +{ + [DataContract(Name = "mention", Namespace = "")] + public class MentionWrapper + { + [DataMember(Name = "email", EmitDefaultValue = false)] + public string Email + { + get { return User.Email; } + set { } + } + + [DataMember(Name = "id", EmitDefaultValue = false)] + public string Id + { + get { return User.ID.ToString(); } + set { } + } + + [DataMember(Name = "hasAccess", EmitDefaultValue = false)] + public bool HasAccess { get; set; } + + [DataMember(Name = "name", EmitDefaultValue = false)] + public string Name + { + get { return User.DisplayUserName(false, DisplayUserSettingsHelper); } + set { } + } + + public DisplayUserSettingsHelper DisplayUserSettingsHelper { get; } + + public UserInfo User; + + public MentionWrapper(UserInfo user, DisplayUserSettingsHelper displayUserSettingsHelper) + { + User = user; + DisplayUserSettingsHelper = displayUserSettingsHelper; + } + } + + + [DataContract(Name = "message", Namespace = "")] + public class MentionMessageWrapper + { + [DataMember(Name = "actionLink")] + public DocumentService.Configuration.EditorConfiguration.ActionLinkConfig ActionLink { get; set; } + + [DataMember(Name = "emails")] + public List Emails { get; set; } + + [DataMember(Name = "message")] + public string Message { get; set; } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Services/WCFService/Wrappers/ThirdPartyParams.cs b/products/ASC.Files/Server/Services/WCFService/Wrappers/ThirdPartyParams.cs new file mode 100644 index 0000000000..7d12a40e0f --- /dev/null +++ b/products/ASC.Files/Server/Services/WCFService/Wrappers/ThirdPartyParams.cs @@ -0,0 +1,50 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.Runtime.Serialization; +using ASC.Files.Core; + +namespace ASC.Web.Files.Services.WCFService +{ + [DataContract(Name = "third_party", Namespace = "")] + public class ThirdPartyParams + { + [DataMember(Name = "auth_data", EmitDefaultValue = false)] + public AuthData AuthData { get; set; } + + [DataMember(Name = "corporate")] + public bool Corporate { get; set; } + + [DataMember(Name = "customer_title")] + public string CustomerTitle { get; set; } + + [DataMember(Name = "provider_id")] + public string ProviderId { get; set; } + + [DataMember(Name = "provider_key")] + public string ProviderKey { get; set; } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Startup.cs b/products/ASC.Files/Server/Startup.cs new file mode 100644 index 0000000000..1b7437319d --- /dev/null +++ b/products/ASC.Files/Server/Startup.cs @@ -0,0 +1,122 @@ + +using ASC.Api.Core.Auth; +using ASC.Api.Core.Core; +using ASC.Api.Core.Middleware; +using ASC.Api.Documents; +using ASC.Common; +using ASC.Common.DependencyInjection; +using ASC.Common.Logging; + +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.HttpOverrides; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Authorization; +using Microsoft.AspNetCore.Mvc.Formatters; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; + +namespace ASC.Files +{ + public class Startup + { + public IConfiguration Configuration { get; } + public IHostEnvironment HostEnvironment { get; } + + public Startup(IConfiguration configuration, IHostEnvironment hostEnvironment) + { + Configuration = configuration; + HostEnvironment = hostEnvironment; + } + + public void ConfigureServices(IServiceCollection services) + { + services.AddHttpContextAccessor(); + + services.AddControllers() + .AddNewtonsoftJson() + .AddXmlSerializerFormatters(); + + services.AddTransient, CustomJsonOptionsWrapper>(); + + services.AddMemoryCache(); + + services.AddAuthentication("cookie") + .AddScheme("cookie", a => { }); + + var builder = services.AddMvcCore(config => + { + var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build(); + config.Filters.Add(new AuthorizeFilter(policy)); + config.Filters.Add(new TypeFilterAttribute(typeof(TenantStatusFilter))); + config.Filters.Add(new TypeFilterAttribute(typeof(PaymentFilter))); + config.Filters.Add(new TypeFilterAttribute(typeof(IpSecurityFilter))); + config.Filters.Add(new TypeFilterAttribute(typeof(ProductSecurityFilter))); + config.Filters.Add(new CustomResponseFilterAttribute()); + config.Filters.Add(new CustomExceptionFilterAttribute()); + config.Filters.Add(new TypeFilterAttribute(typeof(FormatFilter))); + + config.OutputFormatters.RemoveType(); + config.OutputFormatters.Add(new XmlOutputFormatter()); + }); + + var diHelper = new DIHelper(services); + diHelper + .AddCookieAuthHandler() + .AddCultureMiddleware() + .AddIpSecurityFilter() + .AddPaymentFilter() + .AddProductSecurityFilter() + .AddTenantStatusFilter(); + + diHelper.AddNLogManager("ASC.Files"); + + diHelper + .AddDocumentsControllerService() + .AddEncryptionControllerService(); + + services.AddAutofac(Configuration, HostEnvironment.ContentRootPath); + } + + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseForwardedHeaders(new ForwardedHeadersOptions + { + ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto + }); + + app.UseCors(builder => + builder + .AllowAnyOrigin() + .AllowAnyHeader() + .AllowAnyMethod()); + + app.UseRouting(); + + app.UseAuthentication(); + + app.UseAuthorization(); + + app.UseCultureMiddleware(); + + app.UseDisposeMiddleware(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapCustom(); + }); + + app.UseStaticFiles(); + } + } +} diff --git a/products/ASC.Files/Server/ThirdPartyApp/BoxApp.cs b/products/ASC.Files/Server/ThirdPartyApp/BoxApp.cs new file mode 100644 index 0000000000..42ff57b05b --- /dev/null +++ b/products/ASC.Files/Server/ThirdPartyApp/BoxApp.cs @@ -0,0 +1,633 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Security; +using System.Text; +using System.Threading; +using System.Web; + +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Common.Web; +using ASC.Core; +using ASC.Core.Common; +using ASC.Core.Common.Configuration; +using ASC.Core.Common.Settings; +using ASC.Core.Tenants; +using ASC.Core.Users; +using ASC.FederatedLogin; +using ASC.FederatedLogin.Helpers; +using ASC.FederatedLogin.LoginProviders; +using ASC.Files.Resources; +using ASC.MessagingSystem; +using ASC.Security.Cryptography; +using ASC.Web.Core; +using ASC.Web.Core.Files; +using ASC.Web.Core.Users; +using ASC.Web.Files.Classes; +using ASC.Web.Files.Core; +using ASC.Web.Files.HttpHandlers; +using ASC.Web.Files.Services.DocumentService; +using ASC.Web.Studio.Core; +using ASC.Web.Studio.Utility; + +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +using Newtonsoft.Json.Linq; + +using File = ASC.Files.Core.File; +using SecurityContext = ASC.Core.SecurityContext; + +namespace ASC.Web.Files.ThirdPartyApp +{ + public class BoxApp : Consumer, IThirdPartyApp, IOAuthProvider + { + public const string AppAttr = "box"; + + private const string BoxUrlUserInfo = "https://api.box.com/2.0/users/me"; + private const string BoxUrlFile = "https://api.box.com/2.0/files/{fileId}"; + private const string BoxUrlUpload = "https://upload.box.com/api/2.0/files/{fileId}/content"; + + public string Scopes { get { return ""; } } + public string CodeUrl { get { return ""; } } + public string AccessTokenUrl { get { return "https://www.box.com/api/oauth2/token"; } } + public string RedirectUri { get { return ""; } } + public string ClientID { get { return this["boxAppClientId"]; } } + public string ClientSecret { get { return this["boxAppSecretKey"]; } } + + public bool IsEnabled + { + get { return !string.IsNullOrEmpty(ClientID) && !string.IsNullOrEmpty(ClientSecret); } + } + + public PathProvider PathProvider { get; } + public TenantUtil TenantUtil { get; } + public AuthContext AuthContext { get; } + public SecurityContext SecurityContext { get; } + public UserManager UserManager { get; } + public UserManagerWrapper UserManagerWrapper { get; } + public CookiesManager CookiesManager { get; } + public MessageService MessageService { get; } + public Global Global { get; } + public EmailValidationKeyProvider EmailValidationKeyProvider { get; } + public FilesLinkUtility FilesLinkUtility { get; } + public SettingsManager SettingsManager { get; } + public PersonalSettingsHelper PersonalSettingsHelper { get; } + public BaseCommonLinkUtility BaseCommonLinkUtility { get; } + public IOptionsSnapshot Snapshot { get; } + public SetupInfo SetupInfo { get; } + public TokenHelper TokenHelper { get; } + public DocumentServiceConnector DocumentServiceConnector { get; } + public ThirdPartyAppHandler ThirdPartyAppHandler { get; } + public IServiceProvider ServiceProvider { get; } + public ILog Logger { get; } + + public BoxApp() + { + } + + public BoxApp( + PathProvider pathProvider, + TenantUtil tenantUtil, + IOptionsMonitor option, + AuthContext authContext, + SecurityContext securityContext, + UserManager userManager, + UserManagerWrapper userManagerWrapper, + CookiesManager cookiesManager, + MessageService messageService, + Global global, + EmailValidationKeyProvider emailValidationKeyProvider, + FilesLinkUtility filesLinkUtility, + SettingsManager settingsManager, + PersonalSettingsHelper personalSettingsHelper, + BaseCommonLinkUtility baseCommonLinkUtility, + IOptionsSnapshot snapshot, + SetupInfo setupInfo, + TokenHelper tokenHelper, + DocumentServiceConnector documentServiceConnector, + ThirdPartyAppHandler thirdPartyAppHandler, + IServiceProvider serviceProvider, + TenantManager tenantManager, + CoreBaseSettings coreBaseSettings, + CoreSettings coreSettings, + ConsumerFactory consumerFactory, + IConfiguration configuration, + ICacheNotify cache, + string name, int order, Dictionary additional) + : base(tenantManager, coreBaseSettings, coreSettings, consumerFactory, configuration, cache, name, order, additional) + { + PathProvider = pathProvider; + TenantUtil = tenantUtil; + AuthContext = authContext; + SecurityContext = securityContext; + UserManager = userManager; + UserManagerWrapper = userManagerWrapper; + CookiesManager = cookiesManager; + MessageService = messageService; + Global = global; + EmailValidationKeyProvider = emailValidationKeyProvider; + FilesLinkUtility = filesLinkUtility; + SettingsManager = settingsManager; + PersonalSettingsHelper = personalSettingsHelper; + BaseCommonLinkUtility = baseCommonLinkUtility; + Snapshot = snapshot; + SetupInfo = setupInfo; + TokenHelper = tokenHelper; + DocumentServiceConnector = documentServiceConnector; + ThirdPartyAppHandler = thirdPartyAppHandler; + ServiceProvider = serviceProvider; + Logger = option.CurrentValue; + } + + public bool Request(HttpContext context) + { + if ((context.Request.Query[FilesLinkUtility.Action].FirstOrDefault() ?? "").Equals("stream", StringComparison.InvariantCultureIgnoreCase)) + { + StreamFile(context); + return true; + } + + if (!string.IsNullOrEmpty(context.Request.Query["code"])) + { + RequestCode(context); + return true; + } + + return false; + } + + public string GetRefreshUrl() + { + return AccessTokenUrl; + } + + public File GetFile(string fileId, out bool editable) + { + Logger.Debug("BoxApp: get file " + fileId); + fileId = ThirdPartySelector.GetFileId(fileId); + + var token = TokenHelper.GetToken(AppAttr); + + var boxFile = GetBoxFile(fileId, token); + editable = true; + + if (boxFile == null) return null; + + var jsonFile = JObject.Parse(boxFile); + + var file = ServiceProvider.GetService(); + file.ID = ThirdPartySelector.BuildAppFileId(AppAttr, jsonFile.Value("id")); + file.Title = Global.ReplaceInvalidCharsAndTruncate(jsonFile.Value("name")); + file.CreateOn = TenantUtil.DateTimeFromUtc(jsonFile.Value("created_at")); + file.ModifiedOn = TenantUtil.DateTimeFromUtc(jsonFile.Value("modified_at")); + file.ContentLength = Convert.ToInt64(jsonFile.Value("size")); + file.ProviderKey = "Box"; + + var modifiedBy = jsonFile.Value("modified_by"); + if (modifiedBy != null) + { + file.ModifiedByString = modifiedBy.Value("name"); + } + + var createdBy = jsonFile.Value("created_by"); + if (createdBy != null) + { + file.CreateByString = createdBy.Value("name"); + } + + + var locked = jsonFile.Value("lock"); + if (locked != null) + { + var lockedBy = locked.Value("created_by"); + if (lockedBy != null) + { + var lockedUserId = lockedBy.Value("id"); + Logger.Debug("BoxApp: locked by " + lockedUserId); + + editable = CurrentUser(lockedUserId); + } + } + + return file; + } + + public string GetFileStreamUrl(File file) + { + if (file == null) return string.Empty; + + var fileId = ThirdPartySelector.GetFileId(file.ID.ToString()); + + Logger.Debug("BoxApp: get file stream url " + fileId); + + var uriBuilder = new UriBuilder(BaseCommonLinkUtility.GetFullAbsolutePath(ThirdPartyAppHandler.HandlerPath)); + if (uriBuilder.Uri.IsLoopback) + { + uriBuilder.Host = Dns.GetHostName(); + } + var query = uriBuilder.Query; + query += FilesLinkUtility.Action + "=stream&"; + query += FilesLinkUtility.FileId + "=" + HttpUtility.UrlEncode(fileId) + "&"; + query += CommonLinkUtility.ParamName_UserUserID + "=" + HttpUtility.UrlEncode(AuthContext.CurrentAccount.ID.ToString()) + "&"; + query += FilesLinkUtility.AuthKey + "=" + EmailValidationKeyProvider.GetEmailKey(fileId + AuthContext.CurrentAccount.ID) + "&"; + query += ThirdPartySelector.AppAttr + "=" + AppAttr; + + return uriBuilder.Uri + "?" + query; + } + + public void SaveFile(string fileId, string fileType, string downloadUrl, Stream stream) + { + Logger.Debug("BoxApp: save file stream " + fileId + + (stream == null + ? " from - " + downloadUrl + : " from stream")); + fileId = ThirdPartySelector.GetFileId(fileId); + + var token = TokenHelper.GetToken(AppAttr); + + var boxFile = GetBoxFile(fileId, token); + if (boxFile == null) + { + Logger.Error("BoxApp: file is null"); + throw new Exception("File not found"); + } + + var jsonFile = JObject.Parse(boxFile); + var title = Global.ReplaceInvalidCharsAndTruncate(jsonFile.Value("name")); + var currentType = FileUtility.GetFileExtension(title); + if (!fileType.Equals(currentType)) + { + try + { + if (stream != null) + { + downloadUrl = PathProvider.GetTempUrl(stream, fileType); + downloadUrl = DocumentServiceConnector.ReplaceCommunityAdress(downloadUrl); + } + + Logger.Debug("BoxApp: GetConvertedUri from " + fileType + " to " + currentType + " - " + downloadUrl); + + var key = DocumentServiceConnector.GenerateRevisionId(downloadUrl); + DocumentServiceConnector.GetConvertedUri(downloadUrl, fileType, currentType, key, null, false, out downloadUrl); + stream = null; + } + catch (Exception e) + { + Logger.Error("BoxApp: Error convert", e); + } + } + + var request = (HttpWebRequest)WebRequest.Create(BoxUrlUpload.Replace("{fileId}", fileId)); + + using (var tmpStream = new MemoryStream()) + { + var boundary = DateTime.UtcNow.Ticks.ToString("x"); + + var metadata = string.Format("Content-Disposition: form-data; name=\"filename\"; filename=\"{0}\"\r\nContent-Type: application/octet-stream\r\n\r\n", title); + var metadataPart = string.Format("--{0}\r\n{1}", boundary, metadata); + var bytes = Encoding.UTF8.GetBytes(metadataPart); + tmpStream.Write(bytes, 0, bytes.Length); + + if (stream != null) + { + stream.CopyTo(tmpStream); + } + else + { + var downloadRequest = (HttpWebRequest)WebRequest.Create(downloadUrl); + using (var downloadStream = new ResponseStream(downloadRequest.GetResponse())) + { + downloadStream.CopyTo(tmpStream); + } + } + + var mediaPartEnd = string.Format("\r\n--{0}--\r\n", boundary); + bytes = Encoding.UTF8.GetBytes(mediaPartEnd); + tmpStream.Write(bytes, 0, bytes.Length); + + request.Method = "POST"; + request.Headers.Add("Authorization", "Bearer " + token); + request.ContentType = "multipart/form-data; boundary=" + boundary; + request.ContentLength = tmpStream.Length; + Logger.Debug("BoxApp: save file totalSize - " + tmpStream.Length); + + const int bufferSize = 2048; + var buffer = new byte[bufferSize]; + int readed; + tmpStream.Seek(0, SeekOrigin.Begin); + while ((readed = tmpStream.Read(buffer, 0, bufferSize)) > 0) + { + request.GetRequestStream().Write(buffer, 0, readed); + } + } + + try + { + using var response = request.GetResponse(); + using var responseStream = response.GetResponseStream(); + string result = null; + if (responseStream != null) + { + using var readStream = new StreamReader(responseStream); + result = readStream.ReadToEnd(); + } + + Logger.Debug("BoxApp: save file response - " + result); + } + catch (WebException e) + { + Logger.Error("BoxApp: Error save file", e); + request.Abort(); + var httpResponse = (HttpWebResponse)e.Response; + if (httpResponse.StatusCode == HttpStatusCode.Forbidden || httpResponse.StatusCode == HttpStatusCode.Unauthorized) + { + throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException, e); + } + throw; + } + } + + + private void RequestCode(HttpContext context) + { + var token = GetToken(context.Request.Query["code"]); + if (token == null) + { + Logger.Error("BoxApp: token is null"); + throw new SecurityException("Access token is null"); + } + + var boxUserId = context.Request.Query["userId"]; + + if (AuthContext.IsAuthenticated) + { + if (!CurrentUser(boxUserId)) + { + Logger.Debug("BoxApp: logout for " + boxUserId); + CookiesManager.ClearCookies(CookiesType.AuthKey); + AuthContext.Logout(); + } + } + + if (!AuthContext.IsAuthenticated) + { + var userInfo = GetUserInfo(token, out var isNew); + + if (userInfo == null) + { + Logger.Error("BoxApp: UserInfo is null"); + throw new Exception("Profile is null"); + } + + var cookiesKey = SecurityContext.AuthenticateMe(userInfo.ID); + CookiesManager.SetCookies(CookiesType.AuthKey, cookiesKey); + MessageService.Send(MessageAction.LoginSuccessViaSocialApp); + + if (isNew) + { + var userHelpTourSettings = SettingsManager.LoadForCurrentUser(); + userHelpTourSettings.IsNewUser = true; + SettingsManager.SaveForCurrentUser(userHelpTourSettings); + + PersonalSettingsHelper.IsNewUser = true; + PersonalSettingsHelper.IsNotActivated = true; + } + + if (!string.IsNullOrEmpty(boxUserId) && !CurrentUser(boxUserId)) + { + AddLinker(boxUserId); + } + } + + TokenHelper.SaveToken(token); + + var fileId = context.Request.Query["id"]; + + context.Response.Redirect(FilesLinkUtility.GetFileWebEditorUrl(ThirdPartySelector.BuildAppFileId(AppAttr, fileId)), true); + } + + private void StreamFile(HttpContext context) + { + try + { + var fileId = context.Request.Query[FilesLinkUtility.FileId]; + var auth = context.Request.Query[FilesLinkUtility.AuthKey]; + var userId = context.Request.Query[CommonLinkUtility.ParamName_UserUserID]; + + Logger.Debug("BoxApp: get file stream " + fileId); + + var validateResult = EmailValidationKeyProvider.ValidateEmailKey(fileId + userId, auth, Global.StreamUrlExpire); + if (validateResult != EmailValidationKeyProvider.ValidationResult.Ok) + { + var exc = new HttpException((int)HttpStatusCode.Forbidden, FilesCommonResource.ErrorMassage_SecurityException); + + Logger.Error(string.Format("BoxApp: validate error {0} {1}: {2}", FilesLinkUtility.AuthKey, validateResult, context.Request.Url()), exc); + + throw exc; + } + + Token token = null; + + if (Guid.TryParse(userId, out var userIdGuid)) + { + token = TokenHelper.GetToken(AppAttr, userIdGuid); + } + + if (token == null) + { + Logger.Error("BoxApp: token is null"); + throw new SecurityException("Access token is null"); + } + + var request = (HttpWebRequest)WebRequest.Create(BoxUrlFile.Replace("{fileId}", fileId) + "/content"); + request.Method = "GET"; + request.Headers.Add("Authorization", "Bearer " + token); + + using (var stream = new ResponseStream(request.GetResponse())) + { + stream.StreamCopyTo(context.Response.Body); + } + } + catch (Exception ex) + { + context.Response.StatusCode = (int)HttpStatusCode.BadRequest; + context.Response.WriteAsync(ex.Message).Wait(); + Logger.Error("BoxApp: Error request " + context.Request.Url(), ex); + } + + try + { + context.Response.Body.Flush(); + //TODO + //context.Response.Body.SuppressContent = true; + //context.ApplicationInstance.CompleteRequest(); + } + catch (HttpException ex) + { + Logger.Error("BoxApp StreamFile", ex); + } + } + + private bool CurrentUser(string boxUserId) + { + var linkedProfiles = Snapshot.Get("webstudio") + .GetLinkedObjectsByHashId(HashHelper.MD5(string.Format("{0}/{1}", ProviderConstants.Box, boxUserId))); + return linkedProfiles.Any(profileId => Guid.TryParse(profileId, out var tmp) && tmp == AuthContext.CurrentAccount.ID); + } + + private void AddLinker(string boxUserId) + { + Logger.Debug("BoxApp: AddLinker " + boxUserId); + var linker = Snapshot.Get("webstudio"); + linker.AddLink(AuthContext.CurrentAccount.ID.ToString(), boxUserId, ProviderConstants.Box); + } + + private UserInfo GetUserInfo(Token token, out bool isNew) + { + isNew = false; + if (token == null) + { + Logger.Error("BoxApp: token is null"); + throw new SecurityException("Access token is null"); + } + + var resultResponse = string.Empty; + try + { + resultResponse = RequestHelper.PerformRequest(BoxUrlUserInfo, + headers: new Dictionary { { "Authorization", "Bearer " + token } }); + Logger.Debug("BoxApp: userinfo response - " + resultResponse); + } + catch (Exception ex) + { + Logger.Error("BoxApp: userinfo request", ex); + } + + var boxUserInfo = JObject.Parse(resultResponse); + if (boxUserInfo == null) + { + Logger.Error("Error in userinfo request"); + return null; + } + + var email = boxUserInfo.Value("login"); + var userInfo = UserManager.GetUserByEmail(email); + if (Equals(userInfo, Constants.LostUser)) + { + userInfo = new UserInfo + { + FirstName = boxUserInfo.Value("name"), + Email = email, + MobilePhone = boxUserInfo.Value("phone"), + }; + + var cultureName = boxUserInfo.Value("language"); + if (string.IsNullOrEmpty(cultureName)) + cultureName = Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName; + var cultureInfo = SetupInfo.EnabledCultures.Find(c => string.Equals(c.TwoLetterISOLanguageName, cultureName, StringComparison.InvariantCultureIgnoreCase)); + if (cultureInfo != null) + { + userInfo.CultureName = cultureInfo.Name; + } + else + { + Logger.DebugFormat("From box app new personal user '{0}' without culture {1}", userInfo.Email, cultureName); + } + + if (string.IsNullOrEmpty(userInfo.FirstName)) + { + userInfo.FirstName = FilesCommonResource.UnknownFirstName; + } + if (string.IsNullOrEmpty(userInfo.LastName)) + { + userInfo.LastName = FilesCommonResource.UnknownLastName; + } + + try + { + SecurityContext.AuthenticateMe(ASC.Core.Configuration.Constants.CoreSystem); + userInfo = UserManagerWrapper.AddUser(userInfo, UserManagerWrapper.GeneratePassword()); + } + finally + { + AuthContext.Logout(); + } + + isNew = true; + + Logger.Debug("BoxApp: new user " + userInfo.ID); + } + + return userInfo; + } + + private string GetBoxFile(string boxFileId, Token token) + { + if (token == null) + { + Logger.Error("BoxApp: token is null"); + throw new SecurityException("Access token is null"); + } + + try + { + var resultResponse = RequestHelper.PerformRequest(BoxUrlFile.Replace("{fileId}", boxFileId), + headers: new Dictionary { { "Authorization", "Bearer " + token } }); + Logger.Debug("BoxApp: file response - " + resultResponse); + return resultResponse; + } + catch (Exception ex) + { + Logger.Error("BoxApp: file request", ex); + } + return null; + } + + private Token GetToken(string code) + { + try + { + Logger.Debug("BoxApp: GetAccessToken by code " + code); + var token = OAuth20TokenHelper.GetAccessToken(ConsumerFactory, code); + return new Token(token, AppAttr); + } + catch (Exception ex) + { + Logger.Error(ex); + } + return null; + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/ThirdPartyApp/GoogleDriveApp.cs b/products/ASC.Files/Server/ThirdPartyApp/GoogleDriveApp.cs new file mode 100644 index 0000000000..0bd0ff9668 --- /dev/null +++ b/products/ASC.Files/Server/ThirdPartyApp/GoogleDriveApp.cs @@ -0,0 +1,955 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Security; +using System.Text; +using System.Threading; +using System.Web; + +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Common.Web; +using ASC.Core; +using ASC.Core.Common; +using ASC.Core.Common.Configuration; +using ASC.Core.Common.Settings; +using ASC.Core.Tenants; +using ASC.Core.Users; +using ASC.FederatedLogin; +using ASC.FederatedLogin.Helpers; +using ASC.FederatedLogin.LoginProviders; +using ASC.FederatedLogin.Profile; +using ASC.Files.Core; +using ASC.Files.Resources; +using ASC.MessagingSystem; +using ASC.Security.Cryptography; +using ASC.Web.Core; +using ASC.Web.Core.Files; +using ASC.Web.Core.Users; +using ASC.Web.Files.Classes; +using ASC.Web.Files.Core; +using ASC.Web.Files.HttpHandlers; +using ASC.Web.Files.Services.DocumentService; +using ASC.Web.Studio.Core; +using ASC.Web.Studio.Utility; + +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +using Newtonsoft.Json.Linq; + +using File = ASC.Files.Core.File; +using MimeMapping = ASC.Common.Web.MimeMapping; +using SecurityContext = ASC.Core.SecurityContext; + +namespace ASC.Web.Files.ThirdPartyApp +{ + public class GoogleDriveApp : Consumer, IThirdPartyApp, IOAuthProvider + { + public const string AppAttr = "gdrive"; + + public string Scopes { get { return ""; } } + public string CodeUrl { get { return ""; } } + public string AccessTokenUrl { get { return GoogleLoginProvider.Instance.AccessTokenUrl; } } + public string RedirectUri { get { return this["googleDriveAppRedirectUrl"]; } } + public string ClientID { get { return this["googleDriveAppClientId"]; } } + public string ClientSecret { get { return this["googleDriveAppSecretKey"]; } } + + public bool IsEnabled + { + get + { + return !string.IsNullOrEmpty(ClientID) && + !string.IsNullOrEmpty(ClientSecret); + } + } + + public ILog Logger { get; } + public PathProvider PathProvider { get; } + public TenantUtil TenantUtil { get; } + public AuthContext AuthContext { get; } + public SecurityContext SecurityContext { get; } + public UserManager UserManager { get; } + public UserManagerWrapper UserManagerWrapper { get; } + public CookiesManager CookiesManager { get; } + public MessageService MessageService { get; } + public Global Global { get; } + public GlobalStore GlobalStore { get; } + public EmailValidationKeyProvider EmailValidationKeyProvider { get; } + public FilesLinkUtility FilesLinkUtility { get; } + public SettingsManager SettingsManager { get; } + public PersonalSettingsHelper PersonalSettingsHelper { get; } + public BaseCommonLinkUtility BaseCommonLinkUtility { get; } + public FileUtility FileUtility { get; } + public FilesSettingsHelper FilesSettingsHelper { get; } + public IOptionsSnapshot Snapshot { get; } + public SetupInfo SetupInfo { get; } + public GoogleLoginProvider GoogleLoginProvider { get; } + public TokenHelper TokenHelper { get; } + public DocumentServiceConnector DocumentServiceConnector { get; } + public ThirdPartyAppHandler ThirdPartyAppHandler { get; } + public IServiceProvider ServiceProvider { get; } + + public GoogleDriveApp() + { + } + + public GoogleDriveApp( + PathProvider pathProvider, + TenantUtil tenantUtil, + AuthContext authContext, + SecurityContext securityContext, + UserManager userManager, + UserManagerWrapper userManagerWrapper, + CookiesManager cookiesManager, + MessageService messageService, + Global global, + GlobalStore globalStore, + EmailValidationKeyProvider emailValidationKeyProvider, + FilesLinkUtility filesLinkUtility, + SettingsManager settingsManager, + PersonalSettingsHelper personalSettingsHelper, + BaseCommonLinkUtility baseCommonLinkUtility, + IOptionsMonitor option, + FileUtility fileUtility, + FilesSettingsHelper filesSettingsHelper, + IOptionsSnapshot snapshot, + SetupInfo setupInfo, + GoogleLoginProvider googleLoginProvider, + TokenHelper tokenHelper, + DocumentServiceConnector documentServiceConnector, + ThirdPartyAppHandler thirdPartyAppHandler, + IServiceProvider serviceProvider, + TenantManager tenantManager, + CoreBaseSettings coreBaseSettings, + CoreSettings coreSettings, + ConsumerFactory consumerFactory, + IConfiguration configuration, + ICacheNotify cache, + string name, int order, Dictionary additional) + : base(tenantManager, coreBaseSettings, coreSettings, consumerFactory, configuration, cache, name, order, additional) + { + Logger = option.CurrentValue; + PathProvider = pathProvider; + TenantUtil = tenantUtil; + AuthContext = authContext; + SecurityContext = securityContext; + UserManager = userManager; + UserManagerWrapper = userManagerWrapper; + CookiesManager = cookiesManager; + MessageService = messageService; + Global = global; + GlobalStore = globalStore; + EmailValidationKeyProvider = emailValidationKeyProvider; + FilesLinkUtility = filesLinkUtility; + SettingsManager = settingsManager; + PersonalSettingsHelper = personalSettingsHelper; + BaseCommonLinkUtility = baseCommonLinkUtility; + FileUtility = fileUtility; + FilesSettingsHelper = filesSettingsHelper; + Snapshot = snapshot; + SetupInfo = setupInfo; + GoogleLoginProvider = googleLoginProvider; + TokenHelper = tokenHelper; + DocumentServiceConnector = documentServiceConnector; + ThirdPartyAppHandler = thirdPartyAppHandler; + ServiceProvider = serviceProvider; + } + + public bool Request(HttpContext context) + { + switch ((context.Request.Query[FilesLinkUtility.Action].FirstOrDefault() ?? "").ToLower()) + { + case "stream": + StreamFile(context); + return true; + case "convert": + ConfirmConvertFile(context); + return true; + case "create": + CreateFile(context); + return true; + } + + if (!string.IsNullOrEmpty(context.Request.Query["code"])) + { + RequestCode(context); + return true; + } + + return false; + } + + public string GetRefreshUrl() + { + return AccessTokenUrl; + } + + public File GetFile(string fileId, out bool editable) + { + Logger.Debug("GoogleDriveApp: get file " + fileId); + fileId = ThirdPartySelector.GetFileId(fileId); + + var token = TokenHelper.GetToken(AppAttr); + var driveFile = GetDriveFile(fileId, token); + editable = false; + + if (driveFile == null) return null; + + var jsonFile = JObject.Parse(driveFile); + + var file = ServiceProvider.GetService(); + file.ID = ThirdPartySelector.BuildAppFileId(AppAttr, jsonFile.Value("id")); + file.Title = Global.ReplaceInvalidCharsAndTruncate(GetCorrectTitle(jsonFile)); + file.CreateOn = TenantUtil.DateTimeFromUtc(jsonFile.Value("createdTime")); + file.ModifiedOn = TenantUtil.DateTimeFromUtc(jsonFile.Value("modifiedTime")); + file.ContentLength = Convert.ToInt64(jsonFile.Value("size")); + file.ModifiedByString = jsonFile["lastModifyingUser"]["displayName"].Value(); + file.ProviderKey = "Google"; + + var owners = jsonFile["owners"]; + if (owners != null) + { + file.CreateByString = owners[0]["displayName"].Value(); + } + + editable = jsonFile["capabilities"]["canEdit"].Value(); + return file; + } + + public string GetFileStreamUrl(File file) + { + if (file == null) return string.Empty; + + var fileId = ThirdPartySelector.GetFileId(file.ID.ToString()); + return GetFileStreamUrl(fileId); + } + + private string GetFileStreamUrl(string fileId) + { + Logger.Debug("GoogleDriveApp: get file stream url " + fileId); + + var uriBuilder = new UriBuilder(BaseCommonLinkUtility.GetFullAbsolutePath(ThirdPartyAppHandler.HandlerPath)); + if (uriBuilder.Uri.IsLoopback) + { + uriBuilder.Host = Dns.GetHostName(); + } + var query = uriBuilder.Query; + query += FilesLinkUtility.Action + "=stream&"; + query += FilesLinkUtility.FileId + "=" + HttpUtility.UrlEncode(fileId) + "&"; + query += CommonLinkUtility.ParamName_UserUserID + "=" + HttpUtility.UrlEncode(AuthContext.CurrentAccount.ID.ToString()) + "&"; + query += FilesLinkUtility.AuthKey + "=" + EmailValidationKeyProvider.GetEmailKey(fileId + AuthContext.CurrentAccount.ID) + "&"; + query += ThirdPartySelector.AppAttr + "=" + AppAttr; + + return uriBuilder.Uri + "?" + query; + } + + public void SaveFile(string fileId, string fileType, string downloadUrl, Stream stream) + { + Logger.Debug("GoogleDriveApp: save file stream " + fileId + + (stream == null + ? " from - " + downloadUrl + : " from stream")); + fileId = ThirdPartySelector.GetFileId(fileId); + + var token = TokenHelper.GetToken(AppAttr); + + var driveFile = GetDriveFile(fileId, token); + if (driveFile == null) + { + Logger.Error("GoogleDriveApp: file is null"); + throw new Exception("File not found"); + } + + var jsonFile = JObject.Parse(driveFile); + var currentType = GetCorrectExt(jsonFile); + if (!fileType.Equals(currentType)) + { + try + { + if (stream != null) + { + downloadUrl = PathProvider.GetTempUrl(stream, fileType); + downloadUrl = DocumentServiceConnector.ReplaceCommunityAdress(downloadUrl); + } + + Logger.Debug("GoogleDriveApp: GetConvertedUri from " + fileType + " to " + currentType + " - " + downloadUrl); + + var key = DocumentServiceConnector.GenerateRevisionId(downloadUrl); + DocumentServiceConnector.GetConvertedUri(downloadUrl, fileType, currentType, key, null, false, out downloadUrl); + stream = null; + } + catch (Exception e) + { + Logger.Error("GoogleDriveApp: Error convert", e); + } + } + + var request = (HttpWebRequest)WebRequest.Create(GoogleLoginProvider.GoogleUrlFileUpload + "/{fileId}?uploadType=media".Replace("{fileId}", fileId)); + request.Method = "PATCH"; + request.Headers.Add("Authorization", "Bearer " + token); + request.ContentType = MimeMapping.GetMimeMapping(currentType); + + if (stream != null) + { + request.ContentLength = stream.Length; + + const int bufferSize = 2048; + var buffer = new byte[bufferSize]; + int readed; + while ((readed = stream.Read(buffer, 0, bufferSize)) > 0) + { + request.GetRequestStream().Write(buffer, 0, readed); + } + } + else + { + var downloadRequest = (HttpWebRequest)WebRequest.Create(downloadUrl); + using var downloadStream = new ResponseStream(downloadRequest.GetResponse()); + request.ContentLength = downloadStream.Length; + + const int bufferSize = 2048; + var buffer = new byte[bufferSize]; + int readed; + while ((readed = downloadStream.Read(buffer, 0, bufferSize)) > 0) + { + request.GetRequestStream().Write(buffer, 0, readed); + } + } + + try + { + using var response = request.GetResponse(); + using var responseStream = response.GetResponseStream(); + string result = null; + if (responseStream != null) + { + using var readStream = new StreamReader(responseStream); + result = readStream.ReadToEnd(); + } + + Logger.Debug("GoogleDriveApp: save file stream response - " + result); + } + catch (WebException e) + { + Logger.Error("GoogleDriveApp: Error save file stream", e); + request.Abort(); + var httpResponse = (HttpWebResponse)e.Response; + if (httpResponse.StatusCode == HttpStatusCode.Forbidden || httpResponse.StatusCode == HttpStatusCode.Unauthorized) + { + throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException, e); + } + throw; + } + } + + + private void RequestCode(HttpContext context) + { + var state = context.Request.Query["state"]; + Logger.Debug("GoogleDriveApp: state - " + state); + if (string.IsNullOrEmpty(state)) + { + Logger.Error("GoogleDriveApp: empty state"); + throw new Exception("Empty state"); + } + + var token = GetToken(context.Request.Query["code"]); + if (token == null) + { + Logger.Error("GoogleDriveApp: token is null"); + throw new SecurityException("Access token is null"); + } + + var stateJson = JObject.Parse(state); + + var googleUserId = stateJson.Value("userId"); + + if (AuthContext.IsAuthenticated) + { + if (!CurrentUser(googleUserId)) + { + Logger.Debug("GoogleDriveApp: logout for " + googleUserId); + CookiesManager.ClearCookies(CookiesType.AuthKey); + AuthContext.Logout(); + } + } + + if (!AuthContext.IsAuthenticated) + { + var userInfo = GetUserInfo(token, out var isNew); + + if (userInfo == null) + { + Logger.Error("GoogleDriveApp: UserInfo is null"); + throw new Exception("Profile is null"); + } + + var cookiesKey = SecurityContext.AuthenticateMe(userInfo.ID); + CookiesManager.SetCookies(CookiesType.AuthKey, cookiesKey); + MessageService.Send(MessageAction.LoginSuccessViaSocialApp); + + if (isNew) + { + var userHelpTourSettings = SettingsManager.LoadForCurrentUser(); + userHelpTourSettings.IsNewUser = true; + SettingsManager.SaveForCurrentUser(userHelpTourSettings); + + PersonalSettingsHelper.IsNewUser = true; + PersonalSettingsHelper.IsNotActivated = true; + } + + if (!string.IsNullOrEmpty(googleUserId) && !CurrentUser(googleUserId)) + { + AddLinker(googleUserId); + } + } + + TokenHelper.SaveToken(token); + + var action = stateJson.Value("action"); + switch (action) + { + case "create": + //var folderId = stateJson.Value("folderId"); + + //context.Response.Redirect(App.Location + "?" + FilesLinkUtility.FolderId + "=" + HttpUtility.UrlEncode(folderId), true); + return; + case "open": + var idsArray = stateJson.Value("ids") ?? stateJson.Value("exportIds"); + if (idsArray == null) + { + Logger.Error("GoogleDriveApp: ids is empty"); + throw new Exception("File id is null"); + } + var fileId = idsArray.ToObject>().FirstOrDefault(); + + var driveFile = GetDriveFile(fileId, token); + if (driveFile == null) + { + Logger.Error("GoogleDriveApp: file is null"); + throw new Exception("File not found"); + } + + var jsonFile = JObject.Parse(driveFile); + var ext = GetCorrectExt(jsonFile); + if (FileUtility.ExtsMustConvert.Contains(ext) + || GoogleLoginProvider.GoogleDriveExt.Contains(ext)) + { + Logger.Debug("GoogleDriveApp: file must be converted"); + if (FilesSettingsHelper.ConvertNotify) + { + //context.Response.Redirect(App.Location + "?" + FilesLinkUtility.FileId + "=" + HttpUtility.UrlEncode(fileId), true); + return; + } + + fileId = CreateConvertedFile(driveFile, token); + } + + context.Response.Redirect(FilesLinkUtility.GetFileWebEditorUrl(ThirdPartySelector.BuildAppFileId(AppAttr, fileId)), true); + return; + } + Logger.Error("GoogleDriveApp: Action not identified"); + throw new Exception("Action not identified"); + } + + private void StreamFile(HttpContext context) + { + try + { + var fileId = context.Request.Query[FilesLinkUtility.FileId]; + var auth = context.Request.Query[FilesLinkUtility.AuthKey]; + var userId = context.Request.Query[CommonLinkUtility.ParamName_UserUserID]; + + Logger.Debug("GoogleDriveApp: get file stream " + fileId); + + var validateResult = EmailValidationKeyProvider.ValidateEmailKey(fileId + userId, auth, Global.StreamUrlExpire); + if (validateResult != EmailValidationKeyProvider.ValidationResult.Ok) + { + var exc = new HttpException((int)HttpStatusCode.Forbidden, FilesCommonResource.ErrorMassage_SecurityException); + + Logger.Error(string.Format("GoogleDriveApp: validate error {0} {1}: {2}", FilesLinkUtility.AuthKey, validateResult, context.Request.Url()), exc); + + throw exc; + } + + Token token = null; + + if (Guid.TryParse(userId, out var userIdGuid)) + { + token = TokenHelper.GetToken(AppAttr, userIdGuid); + } + + if (token == null) + { + Logger.Error("BoxApp: token is null"); + throw new SecurityException("Access token is null"); + } + + var driveFile = GetDriveFile(fileId, token); + + var jsonFile = JObject.Parse(driveFile); + + var downloadUrl = GoogleLoginProvider.GoogleUrlFile + fileId + "?alt=media"; + + if (string.IsNullOrEmpty(downloadUrl)) + { + Logger.Error("GoogleDriveApp: downloadUrl is null"); + throw new Exception("downloadUrl is null"); + } + + Logger.Debug("GoogleDriveApp: get file stream downloadUrl - " + downloadUrl); + + var request = (HttpWebRequest)WebRequest.Create(downloadUrl); + request.Method = "GET"; + request.Headers.Add("Authorization", "Bearer " + token); + + using (var response = request.GetResponse()) + using (var stream = new ResponseStream(response)) + { + stream.StreamCopyTo(context.Response.Body); + + var contentLength = jsonFile.Value("size"); + Logger.Debug("GoogleDriveApp: get file stream contentLength - " + contentLength); + context.Response.Headers.Add("Content-Length", contentLength); + } + } + catch (Exception ex) + { + context.Response.StatusCode = (int)HttpStatusCode.BadRequest; + context.Response.WriteAsync(ex.Message).Wait(); + Logger.Error("GoogleDriveApp: Error request " + context.Request.Url(), ex); + } + try + { + context.Response.Body.Flush(); + //TODO + //context.Response.SuppressContent = true; + //context.ApplicationInstance.CompleteRequest(); + } + catch (HttpException ex) + { + Logger.Error("GoogleDriveApp StreamFile", ex); + } + } + + private void ConfirmConvertFile(HttpContext context) + { + var fileId = context.Request.Query[FilesLinkUtility.FileId]; + Logger.Debug("GoogleDriveApp: ConfirmConvertFile - " + fileId); + + var token = TokenHelper.GetToken(AppAttr); + + var driveFile = GetDriveFile(fileId, token); + if (driveFile == null) + { + Logger.Error("GoogleDriveApp: file is null"); + throw new Exception("File not found"); + } + + fileId = CreateConvertedFile(driveFile, token); + + context.Response.Redirect(FilesLinkUtility.GetFileWebEditorUrl(ThirdPartySelector.BuildAppFileId(AppAttr, fileId)), true); + } + + private void CreateFile(HttpContext context) + { + var folderId = context.Request.Query[FilesLinkUtility.FolderId]; + var fileName = context.Request.Query[FilesLinkUtility.FileTitle]; + Logger.Debug("GoogleDriveApp: CreateFile folderId - " + folderId + " fileName - " + fileName); + + var token = TokenHelper.GetToken(AppAttr); + + var culture = UserManager.GetUsers(AuthContext.CurrentAccount.ID).GetCulture(); + var storeTemplate = GlobalStore.GetStoreTemplate(); + + var path = FileConstant.NewDocPath + culture + "/"; + if (!storeTemplate.IsDirectory(path)) + { + path = FileConstant.NewDocPath + "default/"; + } + var ext = FileUtility.InternalExtension[FileUtility.GetFileTypeByFileName(fileName)]; + path += "new" + ext; + fileName = FileUtility.ReplaceFileExtension(fileName, ext); + + string driveFile; + using (var content = storeTemplate.GetReadStream("", path)) + { + driveFile = CreateFile(content, fileName, folderId, token); + } + if (driveFile == null) + { + Logger.Error("GoogleDriveApp: file is null"); + throw new Exception("File not created"); + } + + var jsonFile = JObject.Parse(driveFile); + var fileId = jsonFile.Value("id"); + + context.Response.Redirect(FilesLinkUtility.GetFileWebEditorUrl(ThirdPartySelector.BuildAppFileId(AppAttr, fileId)), true); + } + + private Token GetToken(string code) + { + try + { + Logger.Debug("GoogleDriveApp: GetAccessToken by code " + code); + var token = OAuth20TokenHelper.GetAccessToken(ConsumerFactory, code); + return new Token(token, AppAttr); + } + catch (Exception ex) + { + Logger.Error(ex); + } + return null; + } + + private bool CurrentUser(string googleId) + { + var linker = Snapshot.Get("webstudio"); + var linkedProfiles = linker.GetLinkedObjectsByHashId(HashHelper.MD5(string.Format("{0}/{1}", ProviderConstants.Google, googleId))); + linkedProfiles = linkedProfiles.Concat(linker.GetLinkedObjectsByHashId(HashHelper.MD5(string.Format("{0}/{1}", ProviderConstants.OpenId, googleId)))); + return linkedProfiles.Any(profileId => Guid.TryParse(profileId, out var tmp) && tmp == AuthContext.CurrentAccount.ID); + } + + private void AddLinker(string googleUserId) + { + Logger.Debug("GoogleDriveApp: AddLinker " + googleUserId); + var linker = Snapshot.Get("webstudio"); + linker.AddLink(AuthContext.CurrentAccount.ID.ToString(), googleUserId, ProviderConstants.Google); + } + + private UserInfo GetUserInfo(Token token, out bool isNew) + { + isNew = false; + if (token == null) + { + Logger.Error("GoogleDriveApp: token is null"); + throw new SecurityException("Access token is null"); + } + + LoginProfile loginProfile = null; + try + { + loginProfile = GoogleLoginProvider.Instance.GetLoginProfile(token.GetRefreshedToken(TokenHelper)); + } + catch (Exception ex) + { + Logger.Error("GoogleDriveApp: userinfo request", ex); + } + + if (loginProfile == null) + { + Logger.Error("Error in userinfo request"); + return null; + } + + var userInfo = UserManager.GetUserByEmail(loginProfile.EMail); + if (Equals(userInfo, Constants.LostUser)) + { + userInfo = loginProfile.ProfileToUserInfo(CoreBaseSettings); + + var cultureName = loginProfile.Locale; + if (string.IsNullOrEmpty(cultureName)) + cultureName = Thread.CurrentThread.CurrentUICulture.Name; + + var cultureInfo = SetupInfo.EnabledCultures.Find(c => string.Equals(c.Name, cultureName, StringComparison.InvariantCultureIgnoreCase)); + if (cultureInfo != null) + { + userInfo.CultureName = cultureInfo.Name; + } + else + { + Logger.DebugFormat("From google app new personal user '{0}' without culture {1}", userInfo.Email, cultureName); + } + + try + { + SecurityContext.AuthenticateMe(ASC.Core.Configuration.Constants.CoreSystem); + userInfo = UserManagerWrapper.AddUser(userInfo, UserManagerWrapper.GeneratePassword()); + } + finally + { + SecurityContext.Logout(); + } + + isNew = true; + + Logger.Debug("GoogleDriveApp: new user " + userInfo.ID); + } + + return userInfo; + } + + private string GetDriveFile(string googleFileId, Token token) + { + if (token == null) + { + Logger.Error("GoogleDriveApp: token is null"); + throw new SecurityException("Access token is null"); + } + try + { + var requestUrl = GoogleLoginProvider.GoogleUrlFile + googleFileId + "?fields=" + HttpUtility.UrlEncode(GoogleLoginProvider.FilesFields); + var resultResponse = RequestHelper.PerformRequest(requestUrl, + headers: new Dictionary { { "Authorization", "Bearer " + token } }); + Logger.Debug("GoogleDriveApp: file response - " + resultResponse); + return resultResponse; + } + catch (Exception ex) + { + Logger.Error("GoogleDriveApp: file request", ex); + } + return null; + } + + private string CreateFile(string contentUrl, string fileName, string folderId, Token token) + { + if (string.IsNullOrEmpty(contentUrl)) + { + Logger.Error("GoogleDriveApp: downloadUrl is null"); + throw new Exception("downloadUrl is null"); + } + Logger.Debug("GoogleDriveApp: create from - " + contentUrl); + + var request = (HttpWebRequest)WebRequest.Create(contentUrl); + + using (var content = new ResponseStream(request.GetResponse())) + { + return CreateFile(content, fileName, folderId, token); + } + } + + private string CreateFile(Stream content, string fileName, string folderId, Token token) + { + Logger.Debug("GoogleDriveApp: create file"); + + var request = (HttpWebRequest)WebRequest.Create(GoogleLoginProvider.GoogleUrlFileUpload + "?uploadType=multipart"); + + using (var tmpStream = new MemoryStream()) + { + var boundary = DateTime.UtcNow.Ticks.ToString("x"); + + var folderdata = string.IsNullOrEmpty(folderId) ? "" : string.Format(",\"parents\":[\"{0}\"]", folderId); + var metadata = string.Format("{{\"name\":\"{0}\"{1}}}", fileName, folderdata); + var metadataPart = string.Format("\r\n--{0}\r\nContent-Type: application/json; charset=UTF-8\r\n\r\n{1}", boundary, metadata); + var bytes = Encoding.UTF8.GetBytes(metadataPart); + tmpStream.Write(bytes, 0, bytes.Length); + + var mediaPartStart = string.Format("\r\n--{0}\r\nContent-Type: {1}\r\n\r\n", boundary, MimeMapping.GetMimeMapping(fileName)); + bytes = Encoding.UTF8.GetBytes(mediaPartStart); + tmpStream.Write(bytes, 0, bytes.Length); + + content.CopyTo(tmpStream); + + var mediaPartEnd = string.Format("\r\n--{0}--\r\n", boundary); + bytes = Encoding.UTF8.GetBytes(mediaPartEnd); + tmpStream.Write(bytes, 0, bytes.Length); + + request.Method = "POST"; + request.Headers.Add("Authorization", "Bearer " + token); + request.ContentType = "multipart/related; boundary=" + boundary; + request.ContentLength = tmpStream.Length; + Logger.Debug("GoogleDriveApp: create file totalSize - " + tmpStream.Length); + + const int bufferSize = 2048; + var buffer = new byte[bufferSize]; + int readed; + tmpStream.Seek(0, SeekOrigin.Begin); + while ((readed = tmpStream.Read(buffer, 0, bufferSize)) > 0) + { + request.GetRequestStream().Write(buffer, 0, readed); + } + } + + try + { + using (var response = request.GetResponse()) + using (var responseStream = response.GetResponseStream()) + { + string result = null; + if (responseStream != null) + { + using (var readStream = new StreamReader(responseStream)) + { + result = readStream.ReadToEnd(); + } + } + + Logger.Debug("GoogleDriveApp: create file response - " + result); + return result; + } + } + catch (WebException e) + { + Logger.Error("GoogleDriveApp: Error create file", e); + request.Abort(); + + var httpResponse = (HttpWebResponse)e.Response; + if (httpResponse.StatusCode == HttpStatusCode.Forbidden || httpResponse.StatusCode == HttpStatusCode.Unauthorized) + { + throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException, e); + } + } + return null; + } + + private string ConvertFile(string fileId, string fromExt) + { + Logger.Debug("GoogleDriveApp: convert file"); + + var downloadUrl = GetFileStreamUrl(fileId); + + var toExt = FileUtility.GetInternalExtension(fromExt); + try + { + Logger.Debug("GoogleDriveApp: GetConvertedUri- " + downloadUrl); + + var key = DocumentServiceConnector.GenerateRevisionId(downloadUrl); + DocumentServiceConnector.GetConvertedUri(downloadUrl, fromExt, toExt, key, null, false, out downloadUrl); + } + catch (Exception e) + { + Logger.Error("GoogleDriveApp: Error GetConvertedUri", e); + } + + return downloadUrl; + } + + private string CreateConvertedFile(string driveFile, Token token) + { + var jsonFile = JObject.Parse(driveFile); + var fileName = GetCorrectTitle(jsonFile); + + var folderId = (string)jsonFile.SelectToken("parents[0]"); + + Logger.Info("GoogleDriveApp: create copy - " + fileName); + + var ext = GetCorrectExt(jsonFile); + var fileId = jsonFile.Value("id"); + + if (GoogleLoginProvider.GoogleDriveExt.Contains(ext)) + { + var internalExt = FileUtility.GetGoogleDownloadableExtension(ext); + fileName = FileUtility.ReplaceFileExtension(fileName, internalExt); + var requiredMimeType = MimeMapping.GetMimeMapping(internalExt); + + var downloadUrl = GoogleLoginProvider.GoogleUrlFile + + string.Format("{0}/export?mimeType={1}", + fileId, + HttpUtility.UrlEncode(requiredMimeType)); + + var request = (HttpWebRequest)WebRequest.Create(downloadUrl); + request.Method = "GET"; + request.Headers.Add("Authorization", "Bearer " + token); + + Logger.Debug("GoogleDriveApp: download exportLink - " + downloadUrl); + try + { + using (var fileStream = new ResponseStream(request.GetResponse())) + { + driveFile = CreateFile(fileStream, fileName, folderId, token); + } + } + catch (WebException e) + { + Logger.Error("GoogleDriveApp: Error download exportLink", e); + request.Abort(); + + var httpResponse = (HttpWebResponse)e.Response; + if (httpResponse.StatusCode == HttpStatusCode.Forbidden || httpResponse.StatusCode == HttpStatusCode.Unauthorized) + { + throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException, e); + } + } + } + else + { + var convertedUrl = ConvertFile(fileId, ext); + + if (string.IsNullOrEmpty(convertedUrl)) + { + Logger.ErrorFormat("GoogleDriveApp: Error convertUrl. size {0}", FileSizeComment.FilesSizeToString(jsonFile.Value("size"))); + throw new Exception(FilesCommonResource.ErrorMassage_DocServiceException + " (convert)"); + } + + var toExt = FileUtility.GetInternalExtension(fileName); + fileName = FileUtility.ReplaceFileExtension(fileName, toExt); + driveFile = CreateFile(convertedUrl, fileName, folderId, token); + } + + jsonFile = JObject.Parse(driveFile); + return jsonFile.Value("id"); + } + + + private string GetCorrectTitle(JToken jsonFile) + { + var title = jsonFile.Value("name") ?? ""; + var extTitle = FileUtility.GetFileExtension(title); + var correctExt = GetCorrectExt(jsonFile); + + if (extTitle != correctExt) + { + title = title + correctExt; + } + return title; + } + + private string GetCorrectExt(JToken jsonFile) + { + var mimeType = (jsonFile.Value("mimeType") ?? "").ToLower(); + + var ext = MimeMapping.GetExtention(mimeType); + if (!GoogleLoginProvider.GoogleDriveExt.Contains(ext)) + { + var title = (jsonFile.Value("name") ?? "").ToLower(); + ext = FileUtility.GetFileExtension(title); + + if (MimeMapping.GetMimeMapping(ext) != mimeType) + { + var originalFilename = (jsonFile.Value("originalFilename") ?? "").ToLower(); + ext = FileUtility.GetFileExtension(originalFilename); + + if (MimeMapping.GetMimeMapping(ext) != mimeType) + { + ext = MimeMapping.GetExtention(mimeType); + + Logger.Debug("GoogleDriveApp: Try GetCorrectExt - " + ext + " for - " + mimeType); + } + } + } + return ext; + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/ThirdPartyApp/IThirdPartyApp.cs b/products/ASC.Files/Server/ThirdPartyApp/IThirdPartyApp.cs new file mode 100644 index 0000000000..d49b5662dd --- /dev/null +++ b/products/ASC.Files/Server/ThirdPartyApp/IThirdPartyApp.cs @@ -0,0 +1,86 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.IO; +using System.Text.RegularExpressions; + +using Microsoft.AspNetCore.Http; + +using File = ASC.Files.Core.File; + +namespace ASC.Web.Files.ThirdPartyApp +{ + public interface IThirdPartyApp + { + bool Request(HttpContext context); + + string GetRefreshUrl(); + + File GetFile(string fileId, out bool editable); + + string GetFileStreamUrl(File file); + + void SaveFile(string fileId, string fileType, string downloadUrl, Stream stream); + } + + public class ThirdPartySelector + { + public const string AppAttr = "app"; + public static readonly Regex AppRegex = new Regex("^" + AppAttr + @"-(\S+)\|(\S+)$", RegexOptions.Singleline | RegexOptions.Compiled); + + public static string BuildAppFileId(string app, object fileId) + { + return AppAttr + "-" + app + "|" + fileId; + } + + public static string GetFileId(string appFileId) + { + return AppRegex.Match(appFileId).Groups[2].Value; + } + + public static IThirdPartyApp GetAppByFileId(string fileId) + { + if (string.IsNullOrEmpty(fileId)) return null; + var match = AppRegex.Match(fileId); + return match.Success + ? GetApp(match.Groups[1].Value) + : null; + } + + public static IThirdPartyApp GetApp(string app) + { + switch (app) + { + case GoogleDriveApp.AppAttr: + return new GoogleDriveApp(); + case BoxApp.AppAttr: + return new BoxApp(); + } + + return new GoogleDriveApp(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/ThirdPartyApp/Token.cs b/products/ASC.Files/Server/ThirdPartyApp/Token.cs new file mode 100644 index 0000000000..a98ed94cdb --- /dev/null +++ b/products/ASC.Files/Server/ThirdPartyApp/Token.cs @@ -0,0 +1,178 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Diagnostics; +using System.Linq; + +using ASC.Common; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Common.EF; +using ASC.FederatedLogin; +using ASC.FederatedLogin.Helpers; +using ASC.Files.Core; +using ASC.Files.Core.EF; +using ASC.Security.Cryptography; + +using Microsoft.Extensions.Options; + +namespace ASC.Web.Files.ThirdPartyApp +{ + [DebuggerDisplay("{App} - {AccessToken}")] + public class Token : OAuth20Token + { + public string App { get; private set; } + + public Token(OAuth20Token oAuth20Token, string app) + : base(oAuth20Token) + { + App = app; + } + + public string GetRefreshedToken(TokenHelper tokenHelper) + { + if (IsExpired) + { + var app = ThirdPartySelector.GetApp(App); + try + { + tokenHelper.Logger.Debug("Refresh token for app: " + App); + + var refreshUrl = app.GetRefreshUrl(); + + var refreshed = OAuth20TokenHelper.RefreshToken(refreshUrl, this); + + if (refreshed != null) + { + AccessToken = refreshed.AccessToken; + RefreshToken = refreshed.RefreshToken; + ExpiresIn = refreshed.ExpiresIn; + Timestamp = DateTime.UtcNow; + + tokenHelper.SaveToken(this); + } + } + catch (Exception ex) + { + tokenHelper.Logger.Error("Refresh token for app: " + app, ex); + } + } + return AccessToken; + } + } + + public class TokenHelper + { + public ILog Logger { get; } + public FilesDbContext FilesDbContext { get; } + public InstanceCrypto InstanceCrypto { get; } + public AuthContext AuthContext { get; } + public TenantManager TenantManager { get; } + + public TokenHelper( + DbContextManager dbContextManager, + IOptionsMonitor option, + InstanceCrypto instanceCrypto, + AuthContext authContext, + TenantManager tenantManager) + { + Logger = option.CurrentValue; + FilesDbContext = dbContextManager.Get(FileConstant.DatabaseId); + InstanceCrypto = instanceCrypto; + AuthContext = authContext; + TenantManager = tenantManager; + } + + public void SaveToken(Token token) + { + var dbFilesThirdpartyApp = new DbFilesThirdpartyApp + { + App = token.App, + Token = EncryptToken(token), + UserId = AuthContext.CurrentAccount.ID, + TenantId = TenantManager.GetCurrentTenant().TenantId + }; + + FilesDbContext.AddOrUpdate(r => r.ThirdpartyApp, dbFilesThirdpartyApp); + FilesDbContext.SaveChanges(); + } + + public Token GetToken(string app) + { + return GetToken(app, AuthContext.CurrentAccount.ID); + } + + public Token GetToken(string app, Guid userId) + { + var oAuth20Token = FilesDbContext.ThirdpartyApp + .Where(r => r.TenantId == TenantManager.GetCurrentTenant().TenantId) + .Where(r => r.UserId == userId) + .Where(r => r.App == app) + .Select(r => r.Token) + .FirstOrDefault(); + + if (oAuth20Token == null) return null; + + return new Token(DecryptToken(oAuth20Token), app); + } + + public void DeleteToken(string app, Guid? userId = null) + { + var apps = FilesDbContext.ThirdpartyApp + .Where(r => r.TenantId == TenantManager.GetCurrentTenant().TenantId) + .Where(r => r.UserId == (userId ?? AuthContext.CurrentAccount.ID)) + .Where(r => r.App == app); + + FilesDbContext.RemoveRange(apps); + FilesDbContext.SaveChanges(); + } + + private string EncryptToken(OAuth20Token token) + { + var t = token.ToJson(); + return string.IsNullOrEmpty(t) ? string.Empty : InstanceCrypto.Encrypt(t); + } + + private OAuth20Token DecryptToken(string token) + { + return string.IsNullOrEmpty(token) ? null : Token.FromJson(InstanceCrypto.Decrypt(token)); + } + } + + public static class TokenHelperExtension + { + public static DIHelper AddTokenHelperService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddFilesDbContextService() + .AddInstanceCryptoService() + .AddAuthContextService() + .AddTenantManagerService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Utils/ChunkedUploadSessionHolder.cs b/products/ASC.Files/Server/Utils/ChunkedUploadSessionHolder.cs new file mode 100644 index 0000000000..dd699ca827 --- /dev/null +++ b/products/ASC.Files/Server/Utils/ChunkedUploadSessionHolder.cs @@ -0,0 +1,131 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.IO; + +using ASC.Common; +using ASC.Common.Logging; +using ASC.Core.ChunkedUploader; +using ASC.Files.Core; +using ASC.Web.Files.Classes; +using ASC.Web.Studio.Core; + +using Microsoft.Extensions.Options; + +using File = ASC.Files.Core.File; + +namespace ASC.Web.Files.Utils +{ + public class ChunkedUploadSessionHolder + { + public static readonly TimeSpan SlidingExpiration = TimeSpan.FromHours(12); + + public IOptionsMonitor Options { get; } + public GlobalStore GlobalStore { get; } + public SetupInfo SetupInfo { get; } + + public ChunkedUploadSessionHolder(IOptionsMonitor options, GlobalStore globalStore, SetupInfo setupInfo) + { + Options = options; + GlobalStore = globalStore; + SetupInfo = setupInfo; + + // clear old sessions + try + { + CommonSessionHolder(false).DeleteExpired(); + } + catch (Exception err) + { + options.CurrentValue.Error(err); + } + } + + public void StoreSession(ChunkedUploadSession s) + { + CommonSessionHolder(false).Store(s); + } + + public void RemoveSession(ChunkedUploadSession s) + { + CommonSessionHolder(false).Remove(s); + } + + public ChunkedUploadSession GetSession(string sessionId) + { + return (ChunkedUploadSession)CommonSessionHolder(false).Get(sessionId); + } + + public ChunkedUploadSession CreateUploadSession(File file, long contentLength) + { + var result = new ChunkedUploadSession(file, contentLength); + CommonSessionHolder().Init(result); + return result; + } + + public void UploadChunk(ChunkedUploadSession uploadSession, Stream stream, long length) + { + CommonSessionHolder().UploadChunk(uploadSession, stream, length); + } + + public void FinalizeUploadSession(ChunkedUploadSession uploadSession) + { + CommonSessionHolder().Finalize(uploadSession); + } + + public void Move(ChunkedUploadSession chunkedUploadSession, string newPath) + { + CommonSessionHolder().Move(chunkedUploadSession, newPath); + } + + public void AbortUploadSession(ChunkedUploadSession uploadSession) + { + CommonSessionHolder().Abort(uploadSession); + } + + public Stream UploadSingleChunk(ChunkedUploadSession uploadSession, Stream stream, long chunkLength) + { + return CommonSessionHolder().UploadSingleChunk(uploadSession, stream, chunkLength); + } + + private CommonChunkedUploadSessionHolder CommonSessionHolder(bool currentTenant = true) + { + return new CommonChunkedUploadSessionHolder(Options, GlobalStore.GetStore(currentTenant), FileConstant.StorageDomainTmp, SetupInfo.ChunkUploadSize); + } + } + + public static class ChunkedUploadSessionHolderExtention + { + public static DIHelper AddChunkedUploadSessionHolderService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddGlobalStoreService() + .AddSetupInfo(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Utils/EntryManager.cs b/products/ASC.Files/Server/Utils/EntryManager.cs new file mode 100644 index 0000000000..5a33c2dd70 --- /dev/null +++ b/products/ASC.Files/Server/Utils/EntryManager.cs @@ -0,0 +1,1112 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Security; +using System.Text; +using System.Threading; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Users; +using ASC.Files.Core; +using ASC.Files.Core.Data; +using ASC.Files.Core.Security; +using ASC.Files.Resources; +using ASC.Web.Core.Files; +using ASC.Web.Files.Api; +using ASC.Web.Files.Classes; +using ASC.Web.Files.Core; +using ASC.Web.Files.Helpers; +using ASC.Web.Files.Services.DocumentService; +using ASC.Web.Files.ThirdPartyApp; + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +using Newtonsoft.Json.Linq; + +using File = ASC.Files.Core.File; +using FileShare = ASC.Files.Core.Security.FileShare; + +namespace ASC.Web.Files.Utils +{ + public class LockerManager + { + public AuthContext AuthContext { get; } + public IDaoFactory DaoFactory { get; } + + public LockerManager(AuthContext authContext, IDaoFactory daoFactory) + { + AuthContext = authContext; + DaoFactory = daoFactory; + } + + public bool FileLockedForMe(object fileId, Guid userId = default) + { + var app = ThirdPartySelector.GetAppByFileId(fileId.ToString()); + if (app != null) + { + return false; + } + + userId = userId == default ? AuthContext.CurrentAccount.ID : userId; + var tagDao = DaoFactory.TagDao; + var lockedBy = FileLockedBy(fileId, tagDao); + return lockedBy != Guid.Empty && lockedBy != userId; + } + + public Guid FileLockedBy(object fileId, ITagDao tagDao) + { + var tagLock = tagDao.GetTags(fileId, FileEntryType.File, TagType.Locked).FirstOrDefault(); + return tagLock != null ? tagLock.Owner : Guid.Empty; + } + } + + public class BreadCrumbsManager + { + public IDaoFactory DaoFactory { get; } + public FileSecurity FileSecurity { get; } + public GlobalFolderHelper GlobalFolderHelper { get; } + public AuthContext AuthContext { get; } + + public BreadCrumbsManager( + IDaoFactory daoFactory, + FileSecurity fileSecurity, + GlobalFolderHelper globalFolderHelper, + AuthContext authContext) + { + DaoFactory = daoFactory; + FileSecurity = fileSecurity; + GlobalFolderHelper = globalFolderHelper; + AuthContext = authContext; + } + + public List GetBreadCrumbs(object folderId) + { + var folderDao = DaoFactory.FolderDao; + return GetBreadCrumbs(folderId, folderDao); + } + + public List GetBreadCrumbs(object folderId, IFolderDao folderDao) + { + if (folderId == null) return new List(); + var breadCrumbs = FileSecurity.FilterRead(folderDao.GetParentFolders(folderId)).ToList(); + + var firstVisible = breadCrumbs.ElementAtOrDefault(0); + + object rootId = null; + if (firstVisible == null) + { + rootId = GlobalFolderHelper.FolderShare; + } + else + { + switch (firstVisible.FolderType) + { + case FolderType.DEFAULT: + if (!firstVisible.ProviderEntry) + { + rootId = GlobalFolderHelper.FolderShare; + } + else + { + switch (firstVisible.RootFolderType) + { + case FolderType.USER: + rootId = AuthContext.CurrentAccount.ID == firstVisible.RootFolderCreator + ? GlobalFolderHelper.FolderMy + : GlobalFolderHelper.FolderShare; + break; + case FolderType.COMMON: + rootId = GlobalFolderHelper.FolderCommon; + break; + } + } + break; + + case FolderType.BUNCH: + rootId = GlobalFolderHelper.FolderProjects; + break; + } + } + + if (rootId != null) + { + breadCrumbs.Insert(0, folderDao.GetFolder(rootId)); + } + + return breadCrumbs; + } + } + + public class EntryManager + { + private const string UPDATE_LIST = "filesUpdateList"; + private readonly ICache cache; + + public IDaoFactory DaoFactory { get; } + public FileSecurity FileSecurity { get; } + public GlobalFolderHelper GlobalFolderHelper { get; } + public PathProvider PathProvider { get; } + public AuthContext AuthContext { get; } + public FilesIntegration FilesIntegration { get; } + public FileMarker FileMarker { get; } + public FileUtility FileUtility { get; } + public Global Global { get; } + public GlobalStore GlobalStore { get; } + public CoreBaseSettings CoreBaseSettings { get; } + public FilesSettingsHelper FilesSettingsHelper { get; } + public UserManager UserManager { get; } + public FileShareLink FileShareLink { get; } + public DocumentServiceHelper DocumentServiceHelper { get; } + public ThirdpartyConfiguration ThirdpartyConfiguration { get; } + public DocumentServiceConnector DocumentServiceConnector { get; } + public LockerManager LockerManager { get; } + public BreadCrumbsManager BreadCrumbsManager { get; } + public IServiceProvider ServiceProvider { get; } + public ILog Logger { get; } + + public EntryManager( + IDaoFactory daoFactory, + FileSecurity fileSecurity, + GlobalFolderHelper globalFolderHelper, + PathProvider pathProvider, + AuthContext authContext, + FilesIntegration filesIntegration, + FileMarker fileMarker, + FileUtility fileUtility, + Global global, + GlobalStore globalStore, + CoreBaseSettings coreBaseSettings, + FilesSettingsHelper filesSettingsHelper, + UserManager userManager, + IOptionsMonitor optionsMonitor, + FileShareLink fileShareLink, + DocumentServiceHelper documentServiceHelper, + ThirdpartyConfiguration thirdpartyConfiguration, + DocumentServiceConnector documentServiceConnector, + LockerManager lockerManager, + BreadCrumbsManager breadCrumbsManager, + IServiceProvider serviceProvider) + { + DaoFactory = daoFactory; + FileSecurity = fileSecurity; + GlobalFolderHelper = globalFolderHelper; + PathProvider = pathProvider; + AuthContext = authContext; + FilesIntegration = filesIntegration; + FileMarker = fileMarker; + FileUtility = fileUtility; + Global = global; + GlobalStore = globalStore; + CoreBaseSettings = coreBaseSettings; + FilesSettingsHelper = filesSettingsHelper; + UserManager = userManager; + FileShareLink = fileShareLink; + DocumentServiceHelper = documentServiceHelper; + ThirdpartyConfiguration = thirdpartyConfiguration; + DocumentServiceConnector = documentServiceConnector; + LockerManager = lockerManager; + BreadCrumbsManager = breadCrumbsManager; + ServiceProvider = serviceProvider; + Logger = optionsMonitor.CurrentValue; + cache = AscCache.Memory; + } + + public IEnumerable GetEntries(Folder parent, int from, int count, FilterType filter, bool subjectGroup, Guid subjectId, string searchText, bool searchInContent, bool withSubfolders, OrderBy orderBy, out int total) + { + total = 0; + + if (parent == null) throw new ArgumentNullException("parent", FilesCommonResource.ErrorMassage_FolderNotFound); + + var fileSecurity = FileSecurity; + var entries = Enumerable.Empty(); + + searchInContent = searchInContent && filter != FilterType.ByExtension && !Equals(parent.ID, GlobalFolderHelper.FolderTrash); + + if (parent.FolderType == FolderType.Projects && parent.ID.Equals(GlobalFolderHelper.FolderProjects)) + { + //TODO + //var apiServer = new ASC.Api.ApiServer(); + //var apiUrl = string.Format("{0}project/maxlastmodified.json", SetupInfo.WebApiBaseUrl); + + string responseBody = null;// apiServer.GetApiResponse(apiUrl, "GET"); + if (responseBody != null) + { + var responseApi = JObject.Parse(Encoding.UTF8.GetString(Convert.FromBase64String(responseBody))); + + var projectLastModified = responseApi["response"].Value(); + const string projectLastModifiedCacheKey = "documents/projectFolders/projectLastModified"; + var projectListCacheKey = string.Format("documents/projectFolders/{0}", AuthContext.CurrentAccount.ID); + Dictionary> folderIDProjectTitle = null; + + if (folderIDProjectTitle == null) + { + //apiUrl = string.Format("{0}project/filter.json?sortBy=title&sortOrder=ascending&status=open&fields=id,title,security,projectFolder", SetupInfo.WebApiBaseUrl); + + responseApi = JObject.Parse(""); //Encoding.UTF8.GetString(Convert.FromBase64String(apiServer.GetApiResponse(apiUrl, "GET")))); + + var responseData = responseApi["response"]; + + if (!(responseData is JArray)) return entries.ToList(); + + folderIDProjectTitle = new Dictionary>(); + foreach (JObject projectInfo in responseData.Children()) + { + var projectID = projectInfo["id"].Value(); + var projectTitle = Global.ReplaceInvalidCharsAndTruncate(projectInfo["title"].Value()); + + if (projectInfo.TryGetValue("security", out var projectSecurityJToken)) + { + var projectSecurity = projectInfo["security"].Value(); + if (projectSecurity.TryGetValue("canReadFiles", out var projectCanFileReadJToken)) + { + if (!projectSecurity["canReadFiles"].Value()) + { + continue; + } + } + } + + int projectFolderID; + if (projectInfo.TryGetValue("projectFolder", out var projectFolderIDjToken)) + projectFolderID = projectFolderIDjToken.Value(); + else + projectFolderID = (int)FilesIntegration.RegisterBunch("projects", "project", projectID.ToString()); + + if (!folderIDProjectTitle.ContainsKey(projectFolderID)) + folderIDProjectTitle.Add(projectFolderID, new KeyValuePair(projectID, projectTitle)); + + AscCache.Memory.Remove("documents/folders/" + projectFolderID); + AscCache.Memory.Insert("documents/folders/" + projectFolderID, projectTitle, TimeSpan.FromMinutes(30)); + } + } + + var rootKeys = folderIDProjectTitle.Keys.ToArray(); + if (filter == FilterType.None || filter == FilterType.FoldersOnly) + { + var folders = DaoFactory.FolderDao.GetFolders(rootKeys, filter, subjectGroup, subjectId, searchText, withSubfolders, false); + + var emptyFilter = string.IsNullOrEmpty(searchText) && filter == FilterType.None && subjectId == Guid.Empty; + if (!emptyFilter) + { + var projectFolderIds = + folderIDProjectTitle + .Where(projectFolder => string.IsNullOrEmpty(searchText) + || (projectFolder.Value.Value ?? "").ToLower().Trim().Contains(searchText.ToLower().Trim())) + .Select(projectFolder => projectFolder.Key); + + folders.RemoveAll(folder => rootKeys.Contains(folder.ID)); + + var projectFolders = DaoFactory.FolderDao.GetFolders(projectFolderIds.ToArray(), filter, subjectGroup, subjectId, null, false, false); + folders.AddRange(projectFolders); + } + + folders.ForEach(x => + { + x.Title = folderIDProjectTitle.ContainsKey(x.ID) ? folderIDProjectTitle[x.ID].Value : x.Title; + x.FolderUrl = folderIDProjectTitle.ContainsKey(x.ID) ? PathProvider.GetFolderUrl(x, folderIDProjectTitle[x.ID].Key) : string.Empty; + }); + + if (withSubfolders) + { + folders = fileSecurity.FilterRead(folders).ToList(); + } + + entries = entries.Concat(folders); + } + + if (filter != FilterType.FoldersOnly && withSubfolders) + { + var files = DaoFactory.FileDao.GetFiles(rootKeys, filter, subjectGroup, subjectId, searchText, searchInContent).ToList(); + files = fileSecurity.FilterRead(files).ToList(); + entries = entries.Concat(files); + } + } + + parent.TotalFiles = entries.Aggregate(0, (a, f) => a + (f.FileEntryType == FileEntryType.Folder ? ((Folder)f).TotalFiles : 1)); + parent.TotalSubFolders = entries.Aggregate(0, (a, f) => a + (f.FileEntryType == FileEntryType.Folder ? ((Folder)f).TotalSubFolders + 1 : 0)); + } + else if (parent.FolderType == FolderType.SHARE) + { + //share + var shared = (IEnumerable)fileSecurity.GetSharesForMe(filter, subjectGroup, subjectId, searchText, searchInContent, withSubfolders); + + entries = entries.Concat(shared); + + parent.TotalFiles = entries.Aggregate(0, (a, f) => a + (f.FileEntryType == FileEntryType.Folder ? ((Folder)f).TotalFiles : 1)); + parent.TotalSubFolders = entries.Aggregate(0, (a, f) => a + (f.FileEntryType == FileEntryType.Folder ? ((Folder)f).TotalSubFolders + 1 : 0)); + } + else + { + if (parent.FolderType == FolderType.TRASH) + withSubfolders = false; + + var folders = DaoFactory.FolderDao.GetFolders(parent.ID, orderBy, filter, subjectGroup, subjectId, searchText, withSubfolders).Cast(); + folders = fileSecurity.FilterRead(folders); + entries = entries.Concat(folders); + + var files = DaoFactory.FileDao.GetFiles(parent.ID, orderBy, filter, subjectGroup, subjectId, searchText, searchInContent, withSubfolders).Cast(); + files = fileSecurity.FilterRead(files); + entries = entries.Concat(files); + + if (filter == FilterType.None || filter == FilterType.FoldersOnly) + { + var folderList = GetThirpartyFolders(parent, searchText); + + var thirdPartyFolder = FilterEntries(folderList, filter, subjectGroup, subjectId, searchText, searchInContent); + entries = entries.Concat(thirdPartyFolder); + } + } + + if (orderBy.SortedBy != SortedByType.New) + { + entries = SortEntries(entries, orderBy); + + total = entries.Count(); + if (0 < from) entries = entries.Skip(from); + if (0 < count) entries = entries.Take(count); + } + + entries = FileMarker.SetTagsNew(parent, entries); + + //sorting after marking + if (orderBy.SortedBy == SortedByType.New) + { + entries = SortEntries(entries, orderBy); + + total = entries.Count(); + if (0 < from) entries = entries.Skip(from); + if (0 < count) entries = entries.Take(count); + } + + SetFileStatus(entries.Where(r => r != null && r.ID != null && r.FileEntryType == FileEntryType.File).Select(r => r as File).ToList()); + + return entries; + } + + public IEnumerable GetThirpartyFolders(Folder parent, string searchText = null) + { + var folderList = new List(); + + if ((parent.ID.Equals(GlobalFolderHelper.FolderMy) || parent.ID.Equals(GlobalFolderHelper.FolderCommon)) + && ThirdpartyConfiguration.SupportInclusion + && (Global.IsAdministrator + || CoreBaseSettings.Personal + || FilesSettingsHelper.EnableThirdParty)) + { + var providerDao = DaoFactory.ProviderDao; + if (providerDao == null) return folderList; + + var fileSecurity = FileSecurity; + + var providers = providerDao.GetProvidersInfo(parent.RootFolderType, searchText); + folderList = providers + .Select(providerInfo => GetFakeThirdpartyFolder(providerInfo, parent.ID)) + .Where(fileSecurity.CanRead).ToList(); + + if (folderList.Any()) + { + var securityDao = DaoFactory.SecurityDao; + securityDao.GetPureShareRecords(folderList.Cast().ToArray()) + //.Where(x => x.Owner == SecurityContext.CurrentAccount.ID) + .Select(x => x.EntryId).Distinct().ToList() + .ForEach(id => + { + folderList.First(y => y.ID.Equals(id)).Shared = true; + }); + } + } + + return folderList; + } + + public IEnumerable FilterEntries(IEnumerable entries, FilterType filter, bool subjectGroup, Guid subjectId, string searchText, bool searchInContent) + { + if (entries == null || !entries.Any()) return entries; + + if (subjectId != Guid.Empty) + { + entries = entries.Where(f => + subjectGroup + ? UserManager.GetUsersByGroup(subjectId).Any(s => s.ID == f.CreateBy) + : f.CreateBy == subjectId + ) + .ToList(); + } + + Func where = null; + + switch (filter) + { + case FilterType.SpreadsheetsOnly: + case FilterType.PresentationsOnly: + case FilterType.ImagesOnly: + case FilterType.DocumentsOnly: + case FilterType.ArchiveOnly: + case FilterType.FilesOnly: + case FilterType.MediaOnly: + where = f => f.FileEntryType == FileEntryType.File && (((File)f).FilterType == filter || filter == FilterType.FilesOnly); + break; + case FilterType.FoldersOnly: + where = f => f.FileEntryType == FileEntryType.Folder; + break; + case FilterType.ByExtension: + var filterExt = (searchText ?? string.Empty).ToLower().Trim(); + where = f => !string.IsNullOrEmpty(filterExt) && f.FileEntryType == FileEntryType.File && FileUtility.GetFileExtension(f.Title).Contains(filterExt); + break; + } + + if (where != null) + { + entries = entries.Where(where).ToList(); + } + + if ((!searchInContent || filter == FilterType.ByExtension) && !string.IsNullOrEmpty(searchText = (searchText ?? string.Empty).ToLower().Trim())) + { + entries = entries.Where(f => f.Title.ToLower().Contains(searchText)).ToList(); + } + return entries; + } + + public IEnumerable SortEntries(IEnumerable entries, OrderBy orderBy) + { + if (entries == null || !entries.Any()) return entries; + + Comparison sorter; + + if (orderBy == null) + { + orderBy = FilesSettingsHelper.DefaultOrder; + } + + var c = orderBy.IsAsc ? 1 : -1; + switch (orderBy.SortedBy) + { + case SortedByType.Type: + sorter = (x, y) => + { + var cmp = 0; + if (x.FileEntryType == FileEntryType.File && y.FileEntryType == FileEntryType.File) + cmp = c * (FileUtility.GetFileExtension((x.Title)).CompareTo(FileUtility.GetFileExtension(y.Title))); + return cmp == 0 ? x.Title.EnumerableComparer(y.Title) : cmp; + }; + break; + case SortedByType.Author: + sorter = (x, y) => + { + var cmp = c * string.Compare(x.ModifiedByString, y.ModifiedByString); + return cmp == 0 ? x.Title.EnumerableComparer(y.Title) : cmp; + }; + break; + case SortedByType.Size: + sorter = (x, y) => + { + var cmp = 0; + if (x.FileEntryType == FileEntryType.File && y.FileEntryType == FileEntryType.File) + cmp = c * ((File)x).ContentLength.CompareTo(((File)y).ContentLength); + return cmp == 0 ? x.Title.EnumerableComparer(y.Title) : cmp; + }; + break; + case SortedByType.AZ: + sorter = (x, y) => c * x.Title.EnumerableComparer(y.Title); + break; + case SortedByType.DateAndTime: + sorter = (x, y) => + { + var cmp = c * DateTime.Compare(x.ModifiedOn, y.ModifiedOn); + return cmp == 0 ? x.Title.EnumerableComparer(y.Title) : cmp; + }; + break; + case SortedByType.DateAndTimeCreation: + sorter = (x, y) => + { + var cmp = c * DateTime.Compare(x.CreateOn, y.CreateOn); + return cmp == 0 ? x.Title.EnumerableComparer(y.Title) : cmp; + }; + break; + case SortedByType.New: + sorter = (x, y) => + { + var isNewSortResult = x.IsNew.CompareTo(y.IsNew); + return c * (isNewSortResult == 0 ? DateTime.Compare(x.ModifiedOn, y.ModifiedOn) : isNewSortResult); + }; + break; + default: + sorter = (x, y) => c * x.Title.EnumerableComparer(y.Title); + break; + } + + if (orderBy.SortedBy != SortedByType.New) + { + // folders on top + var folders = entries.Where(r => r.FileEntryType == FileEntryType.Folder).ToList(); + var files = entries.Where(r => r.FileEntryType == FileEntryType.File).ToList(); + folders.Sort(sorter); + files.Sort(sorter); + + return folders.Concat(files); + } + + var result = entries.ToList(); + + result.Sort(sorter); + + return result; + } + + public Folder GetFakeThirdpartyFolder(IProviderInfo providerInfo, object parentFolderId = null) + { + //Fake folder. Don't send request to third party + var folder = ServiceProvider.GetService(); + + folder.ParentFolderID = parentFolderId; + + folder.ID = providerInfo.RootFolderId; + folder.CreateBy = providerInfo.Owner; + folder.CreateOn = providerInfo.CreateOn; + folder.FolderType = FolderType.DEFAULT; + folder.ModifiedBy = providerInfo.Owner; + folder.ModifiedOn = providerInfo.CreateOn; + folder.ProviderId = providerInfo.ID; + folder.ProviderKey = providerInfo.ProviderKey; + folder.RootFolderCreator = providerInfo.Owner; + folder.RootFolderId = providerInfo.RootFolderId; + folder.RootFolderType = providerInfo.RootFolderType; + folder.Shareable = false; + folder.Title = providerInfo.CustomerTitle; + folder.TotalFiles = 0; + folder.TotalSubFolders = 0; + + return folder; + } + + + public List GetBreadCrumbs(object folderId) + { + return BreadCrumbsManager.GetBreadCrumbs(folderId); + } + + public List GetBreadCrumbs(object folderId, IFolderDao folderDao) + { + return BreadCrumbsManager.GetBreadCrumbs(folderId, folderDao); + } + + + public void SetFileStatus(File file) + { + if (file == null || file.ID == null) return; + + SetFileStatus(new List(1) { file }); + } + + public void SetFileStatus(IEnumerable files) + { + var tagDao = DaoFactory.TagDao; + var tagsNew = tagDao.GetNewTags(AuthContext.CurrentAccount.ID, files); + + var tagsLocked = tagDao.GetTags(TagType.Locked, files.ToArray()); + + foreach (var file in files) + { + if (tagsNew.Any(r => r.EntryId.Equals(file.ID))) + { + file.IsNew = true; + } + + var tagLocked = tagsLocked.FirstOrDefault(t => t.EntryId.Equals(file.ID)); + + var lockedBy = tagLocked != null ? tagLocked.Owner : Guid.Empty; + file.Locked = lockedBy != Guid.Empty; + file.LockedBy = lockedBy != Guid.Empty && lockedBy != AuthContext.CurrentAccount.ID + ? Global.GetUserName(lockedBy) + : null; + } + } + + public bool FileLockedForMe(object fileId, Guid userId = default) + { + return LockerManager.FileLockedForMe(fileId, userId); + } + + public Guid FileLockedBy(object fileId, ITagDao tagDao) + { + return LockerManager.FileLockedBy(fileId, tagDao); + } + + + public File SaveEditing(string fileId, string fileExtension, string downloadUri, Stream stream, string doc, string comment = null, bool checkRight = true, bool encrypted = false, ForcesaveType? forcesave = null) + { + var newExtension = string.IsNullOrEmpty(fileExtension) + ? FileUtility.GetFileExtension(downloadUri) + : fileExtension; + + var app = ThirdPartySelector.GetAppByFileId(fileId); + if (app != null) + { + app.SaveFile(fileId, newExtension, downloadUri, stream); + return null; + } + + var fileDao = DaoFactory.FileDao; + var editLink = FileShareLink.Check(doc, false, fileDao, out var file); + if (file == null) + { + file = fileDao.GetFile(fileId); + } + + if (file == null) throw new FileNotFoundException(FilesCommonResource.ErrorMassage_FileNotFound); + var fileSecurity = FileSecurity; + if (checkRight && !editLink && (!(fileSecurity.CanEdit(file) || fileSecurity.CanReview(file)) || UserManager.GetUsers(AuthContext.CurrentAccount.ID).IsVisitor(UserManager))) throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException_EditFile); + if (checkRight && FileLockedForMe(file.ID)) throw new Exception(FilesCommonResource.ErrorMassage_LockedFile); + if (checkRight && FileTracker.IsEditing(file.ID)) throw new Exception(FilesCommonResource.ErrorMassage_SecurityException_UpdateEditingFile); + if (file.RootFolderType == FolderType.TRASH) throw new Exception(FilesCommonResource.ErrorMassage_ViewTrashItem); + + var currentExt = file.ConvertedExtension; + if (string.IsNullOrEmpty(newExtension)) newExtension = FileUtility.GetInternalExtension(file.Title); + + var replaceVersion = false; + if (file.Forcesave != ForcesaveType.None) + { + if (file.Forcesave == ForcesaveType.User && FilesSettingsHelper.StoreForcesave) + { + file.Version++; + } + else + { + replaceVersion = true; + } + } + else + { + if (file.Version != 1) + { + file.VersionGroup++; + } + else + { + var storeTemplate = GlobalStore.GetStoreTemplate(); + + var path = FileConstant.NewDocPath + Thread.CurrentThread.CurrentCulture + "/"; + if (!storeTemplate.IsDirectory(path)) + { + path = FileConstant.NewDocPath + "default/"; + } + path += "new" + FileUtility.GetInternalExtension(file.Title); + + //todo: think about the criteria for saving after creation + if (file.ContentLength != storeTemplate.GetFileSize("", path)) + { + file.VersionGroup++; + } + } + file.Version++; + } + file.Forcesave = forcesave ?? ForcesaveType.None; + + if (string.IsNullOrEmpty(comment)) + comment = FilesCommonResource.CommentEdit; + + file.Encrypted = encrypted; + + file.ConvertedType = FileUtility.GetFileExtension(file.Title) != newExtension ? newExtension : null; + + if (file.ProviderEntry && !newExtension.Equals(currentExt)) + { + if (FileUtility.ExtsConvertible.Keys.Contains(newExtension) + && FileUtility.ExtsConvertible[newExtension].Contains(currentExt)) + { + if (stream != null) + { + downloadUri = PathProvider.GetTempUrl(stream, newExtension); + downloadUri = DocumentServiceConnector.ReplaceCommunityAdress(downloadUri); + } + + var key = DocumentServiceConnector.GenerateRevisionId(downloadUri); + DocumentServiceConnector.GetConvertedUri(downloadUri, newExtension, currentExt, key, null, false, out downloadUri); + + stream = null; + } + else + { + file.ID = null; + file.Title = FileUtility.ReplaceFileExtension(file.Title, newExtension); + } + + file.ConvertedType = null; + } + + using (var tmpStream = new MemoryStream()) + { + if (stream != null) + { + stream.CopyTo(tmpStream); + } + else + { + // hack. http://ubuntuforums.org/showthread.php?t=1841740 + if (WorkContext.IsMono) + { + ServicePointManager.ServerCertificateValidationCallback += (s, ce, ca, p) => true; + } + + var req = (HttpWebRequest)WebRequest.Create(downloadUri); + using (var editedFileStream = new ResponseStream(req.GetResponse())) + { + editedFileStream.CopyTo(tmpStream); + } + } + tmpStream.Position = 0; + + file.ContentLength = tmpStream.Length; + file.Comment = string.IsNullOrEmpty(comment) ? null : comment; + if (replaceVersion) + { + file = fileDao.ReplaceFileVersion(file, tmpStream); + } + else + { + file = fileDao.SaveFile(file, tmpStream); + } + } + + FileMarker.MarkAsNew(file); + FileMarker.RemoveMarkAsNew(file); + return file; + } + + public void TrackEditing(string fileId, Guid tabId, Guid userId, string doc, bool editingAlone = false) + { + bool checkRight; + if (FileTracker.GetEditingBy(fileId).Contains(userId)) + { + checkRight = FileTracker.ProlongEditing(fileId, tabId, userId, editingAlone); + if (!checkRight) return; + } + + bool editLink; + var fileDao = DaoFactory.FileDao; + editLink = FileShareLink.Check(doc, false, fileDao, out var file); + if (file == null) + file = fileDao.GetFile(fileId); + + if (file == null) throw new FileNotFoundException(FilesCommonResource.ErrorMassage_FileNotFound); + var fileSecurity = FileSecurity; + if (!editLink && (!fileSecurity.CanEdit(file, userId) && !fileSecurity.CanReview(file, userId) && !fileSecurity.CanFillForms(file, userId) && !fileSecurity.CanComment(file, userId) || UserManager.GetUsers(userId).IsVisitor(UserManager))) throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException_EditFile); + if (FileLockedForMe(file.ID, userId)) throw new Exception(FilesCommonResource.ErrorMassage_LockedFile); + if (file.RootFolderType == FolderType.TRASH) throw new Exception(FilesCommonResource.ErrorMassage_ViewTrashItem); + + checkRight = FileTracker.ProlongEditing(fileId, tabId, userId, editingAlone); + if (checkRight) + { + FileTracker.ChangeRight(fileId, userId, false); + } + } + + + public File UpdateToVersionFile(object fileId, int version, string doc = null, bool checkRight = true) + { + var fileDao = DaoFactory.FileDao; + if (version < 1) throw new ArgumentNullException("version"); + + var editLink = FileShareLink.Check(doc, false, fileDao, out var fromFile); + + if (fromFile == null) + fromFile = fileDao.GetFile(fileId); + + if (fromFile == null) throw new FileNotFoundException(FilesCommonResource.ErrorMassage_FileNotFound); + + if (fromFile.Version != version) + fromFile = fileDao.GetFile(fromFile.ID, Math.Min(fromFile.Version, version)); + + if (fromFile == null) throw new FileNotFoundException(FilesCommonResource.ErrorMassage_FileNotFound); + if (checkRight && !editLink && (!FileSecurity.CanEdit(fromFile) || UserManager.GetUsers(AuthContext.CurrentAccount.ID).IsVisitor(UserManager))) throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException_EditFile); + if (FileLockedForMe(fromFile.ID)) throw new Exception(FilesCommonResource.ErrorMassage_LockedFile); + if (checkRight && FileTracker.IsEditing(fromFile.ID)) throw new Exception(FilesCommonResource.ErrorMassage_SecurityException_UpdateEditingFile); + if (fromFile.RootFolderType == FolderType.TRASH) throw new Exception(FilesCommonResource.ErrorMassage_ViewTrashItem); + if (fromFile.ProviderEntry) throw new Exception(FilesCommonResource.ErrorMassage_BadRequest); + + var exists = cache.Get(UPDATE_LIST + fileId.ToString()) != null; + if (exists) + { + throw new Exception(FilesCommonResource.ErrorMassage_UpdateEditingFile); + } + else + { + cache.Insert(UPDATE_LIST + fileId.ToString(), fileId.ToString(), TimeSpan.FromMinutes(2)); + } + + try + { + var currFile = fileDao.GetFile(fileId); + var newFile = ServiceProvider.GetService(); + + newFile.ID = fromFile.ID; + newFile.Version = currFile.Version + 1; + newFile.VersionGroup = currFile.VersionGroup; + newFile.Title = FileUtility.ReplaceFileExtension(currFile.Title, FileUtility.GetFileExtension(fromFile.Title)); + newFile.FileStatus = currFile.FileStatus; + newFile.FolderID = currFile.FolderID; + newFile.CreateBy = currFile.CreateBy; + newFile.CreateOn = currFile.CreateOn; + newFile.ModifiedBy = fromFile.ModifiedBy; + newFile.ModifiedOn = fromFile.ModifiedOn; + newFile.ConvertedType = fromFile.ConvertedType; + newFile.Comment = string.Format(FilesCommonResource.CommentRevert, fromFile.ModifiedOnString); + newFile.Encrypted = fromFile.Encrypted; + + using (var stream = fileDao.GetFileStream(fromFile)) + { + newFile.ContentLength = stream.CanSeek ? stream.Length : fromFile.ContentLength; + newFile = fileDao.SaveFile(newFile, stream); + } + + FileMarker.MarkAsNew(newFile); + + SetFileStatus(newFile); + + return newFile; + } + catch (Exception e) + { + Logger.Error(string.Format("Error on update {0} to version {1}", fileId, version), e); + throw new Exception(e.Message, e); + } + finally + { + cache.Remove(UPDATE_LIST + fromFile.ID); + } + } + + public File CompleteVersionFile(object fileId, int version, bool continueVersion, bool checkRight = true) + { + var fileDao = DaoFactory.FileDao; + var fileVersion = version > 0 +? fileDao.GetFile(fileId, version) +: fileDao.GetFile(fileId); + if (fileVersion == null) throw new FileNotFoundException(FilesCommonResource.ErrorMassage_FileNotFound); + if (checkRight && (!FileSecurity.CanEdit(fileVersion) || UserManager.GetUsers(AuthContext.CurrentAccount.ID).IsVisitor(UserManager))) throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException_EditFile); + if (FileLockedForMe(fileVersion.ID)) throw new Exception(FilesCommonResource.ErrorMassage_LockedFile); + if (fileVersion.RootFolderType == FolderType.TRASH) throw new Exception(FilesCommonResource.ErrorMassage_ViewTrashItem); + if (fileVersion.ProviderEntry) throw new Exception(FilesCommonResource.ErrorMassage_BadRequest); + + var lastVersionFile = fileDao.GetFile(fileVersion.ID); + + if (continueVersion) + { + if (lastVersionFile.VersionGroup > 1) + { + fileDao.ContinueVersion(fileVersion.ID, fileVersion.Version); + lastVersionFile.VersionGroup--; + } + } + else + { + if (!FileTracker.IsEditing(lastVersionFile.ID)) + { + if (fileVersion.Version == lastVersionFile.Version) + { + lastVersionFile = UpdateToVersionFile(fileVersion.ID, fileVersion.Version, null, checkRight); + } + + fileDao.CompleteVersion(fileVersion.ID, fileVersion.Version); + lastVersionFile.VersionGroup++; + } + } + + SetFileStatus(lastVersionFile); + + return lastVersionFile; + } + + public bool FileRename(object fileId, string title, out File file) + { + var fileDao = DaoFactory.FileDao; + file = fileDao.GetFile(fileId); + if (file == null) throw new FileNotFoundException(FilesCommonResource.ErrorMassage_FileNotFound); + if (!FileSecurity.CanEdit(file)) throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException_RenameFile); + if (!FileSecurity.CanDelete(file) && UserManager.GetUsers(AuthContext.CurrentAccount.ID).IsVisitor(UserManager)) throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException_RenameFile); + if (FileLockedForMe(file.ID)) throw new Exception(FilesCommonResource.ErrorMassage_LockedFile); + if (file.ProviderEntry && FileTracker.IsEditing(file.ID)) throw new Exception(FilesCommonResource.ErrorMassage_UpdateEditingFile); + if (file.RootFolderType == FolderType.TRASH) throw new Exception(FilesCommonResource.ErrorMassage_ViewTrashItem); + + title = Global.ReplaceInvalidCharsAndTruncate(title); + + var ext = FileUtility.GetFileExtension(file.Title); + if (string.Compare(ext, FileUtility.GetFileExtension(title), true) != 0) + { + title += ext; + } + + var fileAccess = file.Access; + + var renamed = false; + if (string.Compare(file.Title, title, false) != 0) + { + var newFileID = fileDao.FileRename(file, title); + + file = fileDao.GetFile(newFileID); + file.Access = fileAccess; + + DocumentServiceHelper.RenameFile(file, fileDao); + + renamed = true; + } + + SetFileStatus(file); + + return renamed; + } + + + //Long operation + public void DeleteSubitems(object parentId, IFolderDao folderDao, IFileDao fileDao) + { + var folders = folderDao.GetFolders(parentId); + foreach (var folder in folders) + { + DeleteSubitems(folder.ID, folderDao, fileDao); + + Logger.InfoFormat("Delete folder {0} in {1}", folder.ID, parentId); + folderDao.DeleteFolder(folder.ID); + } + + var files = fileDao.GetFiles(parentId, null, FilterType.None, false, Guid.Empty, string.Empty, true); + foreach (var file in files) + { + Logger.InfoFormat("Delete file {0} in {1}", file.ID, parentId); + fileDao.DeleteFile(file.ID); + } + } + + public void MoveSharedItems(object parentId, object toId, IFolderDao folderDao, IFileDao fileDao) + { + var fileSecurity = FileSecurity; + + var folders = folderDao.GetFolders(parentId); + foreach (var folder in folders) + { + var shared = folder.Shared + && fileSecurity.GetShares(folder).Any(record => record.Share != FileShare.Restrict); + if (shared) + { + Logger.InfoFormat("Move shared folder {0} from {1} to {2}", folder.ID, parentId, toId); + folderDao.MoveFolder(folder.ID, toId, null); + } + else + { + MoveSharedItems(folder.ID, toId, folderDao, fileDao); + } + } + + var files = fileDao.GetFiles(parentId, null, FilterType.None, false, Guid.Empty, string.Empty, true); + foreach (var file + in files.Where(file => + file.Shared + && fileSecurity.GetShares(file) + .Any(record => + record.Subject != FileConstant.ShareLinkId + && record.Share != FileShare.Restrict))) + { + Logger.InfoFormat("Move shared file {0} from {1} to {2}", file.ID, parentId, toId); + fileDao.MoveFile(file.ID, toId); + } + } + + public static void ReassignItems(object parentId, Guid fromUserId, Guid toUserId, IFolderDao folderDao, IFileDao fileDao) + { + var fileIds = fileDao.GetFiles(parentId, new OrderBy(SortedByType.AZ, true), FilterType.ByUser, false, fromUserId, null, true, true) + .Where(file => file.CreateBy == fromUserId).Select(file => file.ID); + + fileDao.ReassignFiles(fileIds.ToArray(), toUserId); + + var folderIds = folderDao.GetFolders(parentId, new OrderBy(SortedByType.AZ, true), FilterType.ByUser, false, fromUserId, null, true) + .Where(folder => folder.CreateBy == fromUserId).Select(folder => folder.ID); + + folderDao.ReassignFolders(folderIds.ToArray(), toUserId); + } + } + + public static class EntryManagerExtension + { + public static DIHelper AddEntryManagerService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddDaoFactoryService() + .AddFileSecurityService() + .AddGlobalFolderHelperService() + .AddPathProviderService() + .AddAuthContextService() + .AddFileMarkerService() + .AddFileUtilityService() + .AddGlobalService() + .AddGlobalStoreService() + .AddCoreBaseSettingsService() + .AddFilesSettingsHelperService() + .AddUserManagerService() + .AddFileShareLinkService() + .AddDocumentServiceConnectorService() + .AddDocumentServiceHelperService() + .AddFilesIntegrationService() + .AddThirdpartyConfigurationService() + .AddLockerManagerService() + .AddBreadCrumbsManagerService() + ; + } + } + + public static class LockerManagerExtension + { + public static DIHelper AddLockerManagerService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddAuthContextService() + .AddDaoFactoryService(); + } + } + + public static class BreadCrumbsManagerExtension + { + public static DIHelper AddBreadCrumbsManagerService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddDaoFactoryService() + .AddFileSecurityService() + .AddGlobalFolderHelperService() + .AddAuthContextService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Utils/FileConverter.cs b/products/ASC.Files/Server/Utils/FileConverter.cs new file mode 100644 index 0000000000..df9ad230b6 --- /dev/null +++ b/products/ASC.Files/Server/Utils/FileConverter.cs @@ -0,0 +1,754 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net; +using System.Runtime.Serialization; +using System.Security; +using System.Threading; +using System.Web; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Common.Security.Authentication; +using ASC.Core; +using ASC.Files.Core; +using ASC.Files.Core.Data; +using ASC.Files.Core.Security; +using ASC.Files.Resources; +using ASC.MessagingSystem; +using ASC.Web.Core.Files; +using ASC.Web.Files.Classes; +using ASC.Web.Files.Core; +using ASC.Web.Files.Helpers; +using ASC.Web.Files.Services.DocumentService; +using ASC.Web.Files.Services.WCFService.FileOperations; +using ASC.Web.Studio.Core; + +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +using File = ASC.Files.Core.File; +using SecurityContext = ASC.Core.SecurityContext; + +namespace ASC.Web.Files.Utils +{ + public class FileConverter + { + private static readonly object locker = new object(); + private static readonly IDictionary conversionQueue = new Dictionary(new FileComparer()); + private readonly ICache cache; + + private static Timer timer; + private static readonly object singleThread = new object(); + private const int TIMER_PERIOD = 500; + + public FileUtility FileUtility { get; } + public FilesLinkUtility FilesLinkUtility { get; } + public IDaoFactory DaoFactory { get; } + public SetupInfo SetupInfo { get; } + public PathProvider PathProvider { get; } + public FileSecurity FileSecurity { get; } + public FileMarker FileMarker { get; } + public TenantManager TenantManager { get; } + public AuthContext AuthContext { get; } + public EntryManager EntryManager { get; } + public FilesSettingsHelper FilesSettingsHelper { get; } + public GlobalFolderHelper GlobalFolderHelper { get; } + public FilesMessageService FilesMessageService { get; } + public FileShareLink FileShareLink { get; } + public DocumentServiceHelper DocumentServiceHelper { get; } + public DocumentServiceConnector DocumentServiceConnector { get; } + public IServiceProvider ServiceProvider { get; } + public IHttpContextAccessor HttpContextAccesor { get; } + public ILog Logger { get; } + + public FileConverter( + FileUtility fileUtility, + FilesLinkUtility filesLinkUtility, + IDaoFactory daoFactory, + SetupInfo setupInfo, + PathProvider pathProvider, + FileSecurity fileSecurity, + FileMarker fileMarker, + TenantManager tenantManager, + AuthContext authContext, + EntryManager entryManager, + IOptionsMonitor options, + FilesSettingsHelper filesSettingsHelper, + GlobalFolderHelper globalFolderHelper, + FilesMessageService filesMessageService, + FileShareLink fileShareLink, + DocumentServiceHelper documentServiceHelper, + DocumentServiceConnector documentServiceConnector, + IServiceProvider serviceProvider) + { + FileUtility = fileUtility; + FilesLinkUtility = filesLinkUtility; + DaoFactory = daoFactory; + SetupInfo = setupInfo; + PathProvider = pathProvider; + FileSecurity = fileSecurity; + FileMarker = fileMarker; + TenantManager = tenantManager; + AuthContext = authContext; + EntryManager = entryManager; + FilesSettingsHelper = filesSettingsHelper; + GlobalFolderHelper = globalFolderHelper; + FilesMessageService = filesMessageService; + FileShareLink = fileShareLink; + DocumentServiceHelper = documentServiceHelper; + DocumentServiceConnector = documentServiceConnector; + ServiceProvider = serviceProvider; + Logger = options.CurrentValue; + cache = AscCache.Memory; + } + public FileConverter( + FileUtility fileUtility, + FilesLinkUtility filesLinkUtility, + IDaoFactory daoFactory, + SetupInfo setupInfo, + PathProvider pathProvider, + FileSecurity fileSecurity, + FileMarker fileMarker, + TenantManager tenantManager, + AuthContext authContext, + EntryManager entryManager, + IOptionsMonitor options, + FilesSettingsHelper filesSettingsHelper, + GlobalFolderHelper globalFolderHelper, + FilesMessageService filesMessageService, + FileShareLink fileShareLink, + DocumentServiceHelper documentServiceHelper, + DocumentServiceConnector documentServiceConnector, + IServiceProvider serviceProvider, + IHttpContextAccessor httpContextAccesor) + : this(fileUtility, filesLinkUtility, daoFactory, setupInfo, pathProvider, fileSecurity, + fileMarker, tenantManager, authContext, entryManager, options, filesSettingsHelper, + globalFolderHelper, filesMessageService, fileShareLink, documentServiceHelper, documentServiceConnector, + serviceProvider) + { + HttpContextAccesor = httpContextAccesor; + } + + public bool EnableAsUploaded + { + get { return FileUtility.ExtsMustConvert.Any() && !string.IsNullOrEmpty(FilesLinkUtility.DocServiceConverterUrl); } + } + + public bool MustConvert(File file) + { + if (file == null) return false; + + var ext = FileUtility.GetFileExtension(file.Title); + return FileUtility.ExtsMustConvert.Contains(ext); + } + + public bool EnableConvert(File file, string toExtension) + { + if (file == null || string.IsNullOrEmpty(toExtension)) + { + return false; + } + + if (file.Encrypted) + { + return false; + } + + var fileExtension = file.ConvertedExtension; + if (fileExtension.Trim('.').Equals(toExtension.Trim('.'), StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + fileExtension = FileUtility.GetFileExtension(file.Title); + if (FileUtility.InternalExtension.Values.Contains(toExtension)) + { + return true; + } + + return FileUtility.ExtsConvertible.Keys.Contains(fileExtension) && FileUtility.ExtsConvertible[fileExtension].Contains(toExtension); + } + + public Stream Exec(File file) + { + return Exec(file, FileUtility.GetInternalExtension(file.Title)); + } + + public Stream Exec(File file, string toExtension) + { + if (!EnableConvert(file, toExtension)) + { + var fileDao = DaoFactory.FileDao; + return fileDao.GetFileStream(file); + } + + if (file.ContentLength > SetupInfo.AvailableFileSize) + { + throw new Exception(string.Format(FilesCommonResource.ErrorMassage_FileSizeConvert, FileSizeComment.FilesSizeToString(SetupInfo.AvailableFileSize))); + } + + var fileUri = PathProvider.GetFileStreamUrl(file); + var docKey = DocumentServiceHelper.GetDocKey(file); + fileUri = DocumentServiceConnector.ReplaceCommunityAdress(fileUri); + DocumentServiceConnector.GetConvertedUri(fileUri, file.ConvertedExtension, toExtension, docKey, null, false, out var convertUri); + + if (WorkContext.IsMono && ServicePointManager.ServerCertificateValidationCallback == null) + { + ServicePointManager.ServerCertificateValidationCallback += (s, c, n, p) => true; //HACK: http://ubuntuforums.org/showthread.php?t=1841740 + } + return new ResponseStream(((HttpWebRequest)WebRequest.Create(convertUri)).GetResponse()); + } + + public File ExecSync(File file, string doc) + { + var fileDao = DaoFactory.FileDao; + var fileSecurity = FileSecurity; + if (!fileSecurity.CanRead(file)) + { + var readLink = FileShareLink.Check(doc, true, fileDao, out file); + if (file == null) + { + throw new ArgumentNullException("file", FilesCommonResource.ErrorMassage_FileNotFound); + } + if (!readLink) + { + throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException_ReadFile); + } + } + + var fileUri = PathProvider.GetFileStreamUrl(file); + var fileExtension = file.ConvertedExtension; + var toExtension = FileUtility.GetInternalExtension(file.Title); + var docKey = DocumentServiceHelper.GetDocKey(file); + + fileUri = DocumentServiceConnector.ReplaceCommunityAdress(fileUri); + DocumentServiceConnector.GetConvertedUri(fileUri, fileExtension, toExtension, docKey, null, false, out var convertUri); + + return SaveConvertedFile(file, convertUri); + } + + public void ExecAsync(File file, bool deleteAfter, string password = null) + { + if (!MustConvert(file)) + { + throw new ArgumentException(FilesCommonResource.ErrorMassage_NotSupportedFormat); + } + if (!string.IsNullOrEmpty(file.ConvertedType) || FileUtility.InternalExtension.Values.Contains(FileUtility.GetFileExtension(file.Title))) + { + return; + } + + FileMarker.RemoveMarkAsNew(file); + + lock (locker) + { + if (conversionQueue.ContainsKey(file)) + { + return; + } + + var queueResult = new ConvertFileOperationResult + { + Source = string.Format("{{\"id\":\"{0}\", \"version\":\"{1}\"}}", file.ID, file.Version), + OperationType = FileOperationType.Convert, + Error = string.Empty, + Progress = 0, + Result = string.Empty, + Processed = "", + Id = string.Empty, + TenantId = TenantManager.GetCurrentTenant().TenantId, + Account = AuthContext.CurrentAccount, + Delete = deleteAfter, + StartDateTime = DateTime.Now, + Url = HttpContextAccesor?.HttpContext != null ? HttpContextAccesor.HttpContext.Request.GetUrlRewriter().ToString() : null, + Password = password + }; + conversionQueue.Add(file, queueResult); + cache.Insert(GetKey(file), queueResult, TimeSpan.FromMinutes(10)); + + if (timer == null) + { + timer = new Timer(CheckConvertFilesStatus, null, 0, Timeout.Infinite); + } + else + { + timer.Change(0, Timeout.Infinite); + } + } + } + + public bool IsConverting(File file) + { + if (!MustConvert(file) || !string.IsNullOrEmpty(file.ConvertedType)) + { + return false; + } + var result = cache.Get(GetKey(file)); + return result != null && result.Progress != 100 && string.IsNullOrEmpty(result.Error); + } + + public IEnumerable GetStatus(IEnumerable> filesPair) + { + var fileSecurity = FileSecurity; + var result = new List(); + foreach (var pair in filesPair) + { + var file = pair.Key; + var key = GetKey(file); + var operation = cache.Get(key); + if (operation != null && (pair.Value || fileSecurity.CanRead(file))) + { + result.Add(operation); + lock (locker) + { + if (operation.Progress == 100) + { + conversionQueue.Remove(file); + cache.Remove(key); + } + } + } + } + return result; + } + + private string FileJsonSerializer(File file, string folderTitle) + { + if (file == null) return string.Empty; + + EntryManager.SetFileStatus(file); + return + string.Format("{{ \"id\": \"{0}\"," + + " \"title\": \"{1}\"," + + " \"version\": \"{2}\"," + + " \"folderId\": \"{3}\"," + + " \"folderTitle\": \"{4}\"," + + " \"fileXml\": \"{5}\" }}", + file.ID, + file.Title, + file.Version, + file.FolderID, + folderTitle ?? "", + File.Serialize(file).Replace('"', '\'')); + } + + private void CheckConvertFilesStatus(object _) + { + if (Monitor.TryEnter(singleThread)) + { + using var scope = ServiceProvider.CreateScope(); + var logger = scope.ServiceProvider.GetService>().CurrentValue; + var tenantManager = scope.ServiceProvider.GetService(); + var userManager = scope.ServiceProvider.GetService(); + var securityContext = scope.ServiceProvider.GetService(); + var daoFactory = scope.ServiceProvider.GetService(); + + try + { + List filesIsConverting; + lock (locker) + { + timer.Change(Timeout.Infinite, Timeout.Infinite); + + conversionQueue.Where(x => !string.IsNullOrEmpty(x.Value.Processed) + && (x.Value.Progress == 100 && DateTime.UtcNow - x.Value.StopDateTime > TimeSpan.FromMinutes(1) || + DateTime.UtcNow - x.Value.StopDateTime > TimeSpan.FromMinutes(10))) + .ToList() + .ForEach(x => + { + conversionQueue.Remove(x); + cache.Remove(GetKey(x.Key)); + }); + + logger.DebugFormat("Run CheckConvertFilesStatus: count {0}", conversionQueue.Count); + + if (conversionQueue.Count == 0) + { + return; + } + + filesIsConverting = conversionQueue + .Where(x => string.IsNullOrEmpty(x.Value.Processed)) + .Select(x => x.Key) + .ToList(); + } + + var fileSecurity = FileSecurity; + foreach (var file in filesIsConverting) + { + var fileUri = file.ID.ToString(); + string convertedFileUrl; + int operationResultProgress; + + try + { + int tenantId; + IAccount account; + string password; + + lock (locker) + { + if (!conversionQueue.Keys.Contains(file)) continue; + + var operationResult = conversionQueue[file]; + if (!string.IsNullOrEmpty(operationResult.Processed)) continue; + + operationResult.Processed = "1"; + tenantId = operationResult.TenantId; + account = operationResult.Account; + password = operationResult.Password; + + //if (HttpContext.Current == null && !WorkContext.IsMono) + //{ + // HttpContext.Current = new HttpContext( + // new HttpRequest("hack", operationResult.Url, string.Empty), + // new HttpResponse(new StringWriter())); + //} + + cache.Insert(GetKey(file), operationResult, TimeSpan.FromMinutes(10)); + } + + tenantManager.SetCurrentTenant(tenantId); + securityContext.AuthenticateMe(account); + + var user = userManager.GetUsers(account.ID); + var culture = string.IsNullOrEmpty(user.CultureName) ? TenantManager.GetCurrentTenant().GetCulture() : CultureInfo.GetCultureInfo(user.CultureName); + Thread.CurrentThread.CurrentCulture = culture; + Thread.CurrentThread.CurrentUICulture = culture; + + if (!fileSecurity.CanRead(file) && file.RootFolderType != FolderType.BUNCH) + { + //No rights in CRM after upload before attach + throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException_ReadFile); + } + if (file.ContentLength > SetupInfo.AvailableFileSize) + { + throw new Exception(string.Format(FilesCommonResource.ErrorMassage_FileSizeConvert, FileSizeComment.FilesSizeToString(SetupInfo.AvailableFileSize))); + } + + fileUri = PathProvider.GetFileStreamUrl(file); + + var toExtension = FileUtility.GetInternalExtension(file.Title); + var fileExtension = file.ConvertedExtension; + var docKey = DocumentServiceHelper.GetDocKey(file); + + fileUri = DocumentServiceConnector.ReplaceCommunityAdress(fileUri); + operationResultProgress = DocumentServiceConnector.GetConvertedUri(fileUri, fileExtension, toExtension, docKey, password, true, out convertedFileUrl); + } + catch (Exception exception) + { + var password = exception.InnerException != null + && (exception.InnerException is DocumentService.DocumentServiceException documentServiceException) + && documentServiceException.Code == DocumentService.DocumentServiceException.ErrorCode.ConvertPassword; + + logger.Error(string.Format("Error convert {0} with url {1}", file.ID, fileUri), exception); + lock (locker) + { + if (conversionQueue.Keys.Contains(file)) + { + var operationResult = conversionQueue[file]; + if (operationResult.Delete) + { + conversionQueue.Remove(file); + cache.Remove(GetKey(file)); + } + else + { + operationResult.Progress = 100; + operationResult.StopDateTime = DateTime.UtcNow; + operationResult.Error = exception.Message; + if (password) operationResult.Result = "password"; + cache.Insert(GetKey(file), operationResult, TimeSpan.FromMinutes(10)); + } + } + } + continue; + } + + operationResultProgress = Math.Min(operationResultProgress, 100); + if (operationResultProgress < 100) + { + lock (locker) + { + if (conversionQueue.Keys.Contains(file)) + { + var operationResult = conversionQueue[file]; + + if (DateTime.Now - operationResult.StartDateTime > TimeSpan.FromMinutes(10)) + { + operationResult.StopDateTime = DateTime.UtcNow; + operationResult.Error = FilesCommonResource.ErrorMassage_ConvertTimeout; + logger.ErrorFormat("CheckConvertFilesStatus timeout: {0} ({1})", file.ID, file.ContentLengthString); + } + else + { + operationResult.Processed = ""; + } + operationResult.Progress = operationResultProgress; + cache.Insert(GetKey(file), operationResult, TimeSpan.FromMinutes(10)); + } + } + + logger.Debug("CheckConvertFilesStatus iteration continue"); + continue; + } + + File newFile = null; + var operationResultError = string.Empty; + + try + { + newFile = SaveConvertedFile(file, convertedFileUrl); + } + catch (Exception e) + { + operationResultError = e.Message; + + logger.ErrorFormat("{0} ConvertUrl: {1} fromUrl: {2}: {3}", operationResultError, convertedFileUrl, fileUri, e); + continue; + } + finally + { + lock (locker) + { + if (conversionQueue.Keys.Contains(file)) + { + var operationResult = conversionQueue[file]; + if (operationResult.Delete) + { + conversionQueue.Remove(file); + cache.Remove(GetKey(file)); + } + else + { + if (newFile != null) + { + var folderDao = daoFactory.FolderDao; + var folder = folderDao.GetFolder(newFile.FolderID); + var folderTitle = fileSecurity.CanRead(folder) ? folder.Title : null; + operationResult.Result = FileJsonSerializer(newFile, folderTitle); + } + + operationResult.Progress = 100; + operationResult.StopDateTime = DateTime.UtcNow; + operationResult.Processed = "1"; + if (!string.IsNullOrEmpty(operationResultError)) + { + operationResult.Error = operationResultError; + } + cache.Insert(GetKey(file), operationResult, TimeSpan.FromMinutes(10)); + } + } + } + } + + logger.Debug("CheckConvertFilesStatus iteration end"); + } + + lock (locker) + { + timer.Change(TIMER_PERIOD, TIMER_PERIOD); + } + } + catch (Exception exception) + { + logger.Error(exception.Message, exception); + lock (locker) + { + timer.Change(Timeout.Infinite, Timeout.Infinite); + } + } + finally + { + Monitor.Exit(singleThread); + } + } + } + + private File SaveConvertedFile(File file, string convertedFileUrl) + { + var fileSecurity = FileSecurity; + var fileDao = DaoFactory.FileDao; + var folderDao = DaoFactory.FolderDao; + File newFile = null; + var newFileTitle = FileUtility.ReplaceFileExtension(file.Title, FileUtility.GetInternalExtension(file.Title)); + + if (!FilesSettingsHelper.StoreOriginalFiles && fileSecurity.CanEdit(file)) + { + newFile = (File)file.Clone(); + newFile.Version++; + } + else + { + var folderId = GlobalFolderHelper.FolderMy; + + var parent = folderDao.GetFolder(file.FolderID); + if (parent != null + && fileSecurity.CanCreate(parent)) + { + folderId = parent.ID; + } + + if (Equals(folderId, 0)) throw new SecurityException(FilesCommonResource.ErrorMassage_FolderNotFound); + + if (FilesSettingsHelper.UpdateIfExist && (parent != null && folderId != parent.ID || !file.ProviderEntry)) + { + newFile = fileDao.GetFile(folderId, newFileTitle); + if (newFile != null && fileSecurity.CanEdit(newFile) && !EntryManager.FileLockedForMe(newFile.ID) && !FileTracker.IsEditing(newFile.ID)) + { + newFile.Version++; + } + else + { + newFile = null; + } + } + + if (newFile == null) + { + newFile = ServiceProvider.GetService(); + newFile.FolderID = folderId; + } + } + + newFile.Title = newFileTitle; + newFile.ConvertedType = null; + newFile.Comment = string.Format(FilesCommonResource.CommentConvert, file.Title); + + var req = (HttpWebRequest)WebRequest.Create(convertedFileUrl); + + if (WorkContext.IsMono && ServicePointManager.ServerCertificateValidationCallback == null) + { + ServicePointManager.ServerCertificateValidationCallback += (s, c, n, p) => true; //HACK: http://ubuntuforums.org/showthread.php?t=1841740 + } + + try + { + using (var convertedFileStream = new ResponseStream(req.GetResponse())) + { + newFile.ContentLength = convertedFileStream.Length; + newFile = fileDao.SaveFile(newFile, convertedFileStream); + } + } + catch (WebException e) + { + using var response = e.Response; + var httpResponse = (HttpWebResponse)response; + var errorString = string.Format("WebException: {0}", httpResponse.StatusCode); + + if (httpResponse.StatusCode != HttpStatusCode.NotFound) + { + using var responseStream = response.GetResponseStream(); + if (responseStream != null) + { + using var readStream = new StreamReader(responseStream); + var text = readStream.ReadToEnd(); + errorString += string.Format(" Error message: {0}", text); + } + } + + throw new Exception(errorString); + } + + FilesMessageService.Send(newFile, MessageInitiator.DocsService, MessageAction.FileConverted, newFile.Title); + FileMarker.MarkAsNew(newFile); + + var tagDao = DaoFactory.TagDao; + var tags = tagDao.GetTags(file.ID, FileEntryType.File, TagType.System).ToList(); + if (tags.Any()) + { + tags.ForEach(r => r.EntryId = newFile.ID); + tagDao.SaveTags(tags); + } + + return newFile; + } + + private static string GetKey(File f) + { + return string.Format("fileConvertation-{0}", f.ID); + } + + + private class FileComparer : IEqualityComparer + { + public bool Equals(File x, File y) + { + return x != null && y != null && Equals(x.ID, y.ID) && x.Version == y.Version; + } + + public int GetHashCode(File obj) + { + return obj.ID.GetHashCode() + obj.Version.GetHashCode(); + } + } + + [DataContract(Name = "operation_result", Namespace = "")] + private class ConvertFileOperationResult : FileOperationResult + { + public DateTime StartDateTime { get; set; } + public DateTime StopDateTime { get; set; } + public int TenantId { get; set; } + public IAccount Account { get; set; } + public bool Delete { get; set; } + public string Url { get; set; } + public string Password { get; set; } + } + } + + public static class FileConverterExtension + { + public static DIHelper AddFileConverterService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddFilesLinkUtilityService() + .AddFileUtilityService() + .AddDaoFactoryService() + .AddSetupInfo() + .AddPathProviderService() + .AddFileSecurityService() + .AddFileMarkerService() + .AddTenantManagerService() + .AddAuthContextService() + .AddEntryManagerService() + .AddFilesSettingsHelperService() + .AddGlobalFolderHelperService() + .AddFilesMessageService() + .AddFileShareLinkService() + .AddDocumentServiceHelperService() + .AddDocumentServiceConnectorService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Utils/FileMarker.cs b/products/ASC.Files/Server/Utils/FileMarker.cs new file mode 100644 index 0000000000..8e9c9fa9be --- /dev/null +++ b/products/ASC.Files/Server/Utils/FileMarker.cs @@ -0,0 +1,760 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Common.Threading.Workers; +using ASC.Core; +using ASC.Core.Users; +using ASC.Files.Core; +using ASC.Files.Core.Data; +using ASC.Files.Core.Security; +using ASC.Files.Resources; +using ASC.Web.Files.Classes; + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +using static ASC.Web.Files.Utils.FileMarker; + +using File = ASC.Files.Core.File; + +namespace ASC.Web.Files.Utils +{ + public class FileMarkerHelper + { + private IServiceProvider ServiceProvider { get; } + public ILog Log { get; } + + public FileMarkerHelper(IServiceProvider serviceProvider, IOptionsMonitor optionsMonitor) + { + ServiceProvider = serviceProvider; + Log = optionsMonitor.CurrentValue; + } + + internal void ExecMarkFileAsNew(AsyncTaskData obj) + { + try + { + using var scope = ServiceProvider.CreateScope(); + var fileMarker = scope.ServiceProvider.GetService(); + fileMarker.ExecMarkFileAsNew(obj); + } + catch (Exception e) + { + Log.Error(e); + } + } + + } + + public class FileMarker + { + private static readonly object locker = new object(); + private readonly WorkerQueue tasks; + private readonly ICache cache; + + private const string CacheKeyFormat = "MarkedAsNew/{0}/folder_{1}"; + + public TenantManager TenantManager { get; } + public UserManager UserManager { get; } + public IDaoFactory DaoFactory { get; } + public GlobalFolder GlobalFolder { get; } + public FileSecurity FileSecurity { get; } + public CoreBaseSettings CoreBaseSettings { get; } + public AuthContext AuthContext { get; } + public IServiceProvider ServiceProvider { get; } + public FileMarkerHelper FileMarkerHelper { get; } + + public FileMarker( + TenantManager tenantManager, + UserManager userManager, + IDaoFactory daoFactory, + GlobalFolder globalFolder, + FileSecurity fileSecurity, + CoreBaseSettings coreBaseSettings, + AuthContext authContext, + IServiceProvider serviceProvider, + WorkerQueueOptionsManager workerQueueOptionsManager, + FileMarkerHelper fileMarkerHelper) + { + TenantManager = tenantManager; + UserManager = userManager; + DaoFactory = daoFactory; + GlobalFolder = globalFolder; + FileSecurity = fileSecurity; + CoreBaseSettings = coreBaseSettings; + AuthContext = authContext; + ServiceProvider = serviceProvider; + FileMarkerHelper = fileMarkerHelper; + cache = AscCache.Memory; + tasks = workerQueueOptionsManager.Value; + } + + internal void ExecMarkFileAsNew(AsyncTaskData obj) + { + TenantManager.SetCurrentTenant(Convert.ToInt32(obj.TenantID)); + + var folderDao = DaoFactory.FolderDao; + object parentFolderId; + + if (obj.FileEntry.FileEntryType == FileEntryType.File) + parentFolderId = ((File)obj.FileEntry).FolderID; + else + parentFolderId = obj.FileEntry.ID; + var parentFolders = folderDao.GetParentFolders(parentFolderId); + parentFolders.Reverse(); + + var userIDs = obj.UserIDs; + + var userEntriesData = new Dictionary>(); + + if (obj.FileEntry.RootFolderType == FolderType.BUNCH) + { + if (!userIDs.Any()) return; + + parentFolders.Add(folderDao.GetFolder(GlobalFolder.GetFolderProjects(DaoFactory))); + + var entries = new List { obj.FileEntry }; + entries = entries.Concat(parentFolders).ToList(); + + userIDs.ForEach(userID => + { + if (userEntriesData.ContainsKey(userID)) + userEntriesData[userID].AddRange(entries); + else + userEntriesData.Add(userID, entries); + + RemoveFromCahce(GlobalFolder.GetFolderProjects(DaoFactory), userID); + }); + } + else + { + var filesSecurity = FileSecurity; + + if (!userIDs.Any()) + { + userIDs = filesSecurity.WhoCanRead(obj.FileEntry).Where(x => x != obj.CurrentAccountId).ToList(); + } + if (obj.FileEntry.ProviderEntry) + { + userIDs = userIDs.Where(u => !UserManager.GetUsers(u).IsVisitor(UserManager)).ToList(); + } + + parentFolders.ForEach(parentFolder => + filesSecurity + .WhoCanRead(parentFolder) + .Where(userID => userIDs.Contains(userID) && userID != obj.CurrentAccountId) + .ToList() + .ForEach(userID => + { + if (userEntriesData.ContainsKey(userID)) + userEntriesData[userID].Add(parentFolder); + else + userEntriesData.Add(userID, new List { parentFolder }); + }) + ); + + + + if (obj.FileEntry.RootFolderType == FolderType.USER) + { + var folderShare = folderDao.GetFolder(GlobalFolder.GetFolderShare(DaoFactory.FolderDao)); + + foreach (var userID in userIDs) + { + var userFolderId = folderDao.GetFolderIDUser(false, userID); + if (Equals(userFolderId, 0)) continue; + + Folder rootFolder = null; + if (obj.FileEntry.ProviderEntry) + { + rootFolder = obj.FileEntry.RootFolderCreator == userID + ? folderDao.GetFolder(userFolderId) + : folderShare; + } + else if (!Equals(obj.FileEntry.RootFolderId, userFolderId)) + { + rootFolder = folderShare; + } + else + { + RemoveFromCahce(userFolderId, userID); + } + + if (rootFolder == null) continue; + + if (userEntriesData.ContainsKey(userID)) + userEntriesData[userID].Add(rootFolder); + else + userEntriesData.Add(userID, new List { rootFolder }); + + RemoveFromCahce(rootFolder.ID, userID); + } + } + + if (obj.FileEntry.RootFolderType == FolderType.COMMON) + { + userIDs.ForEach(userID => RemoveFromCahce(GlobalFolder.GetFolderCommon(this, DaoFactory), userID)); + + if (obj.FileEntry.ProviderEntry) + { + var commonFolder = folderDao.GetFolder(GlobalFolder.GetFolderCommon(this, DaoFactory)); + userIDs.ForEach(userID => + { + if (userEntriesData.ContainsKey(userID)) + userEntriesData[userID].Add(commonFolder); + else + userEntriesData.Add(userID, new List { commonFolder }); + + RemoveFromCahce(GlobalFolder.GetFolderCommon(this, DaoFactory), userID); + }); + } + } + + userIDs.ForEach(userID => + { + if (userEntriesData.ContainsKey(userID)) + userEntriesData[userID].Add(obj.FileEntry); + else + userEntriesData.Add(userID, new List { obj.FileEntry }); + }); + } + + var tagDao = DaoFactory.TagDao; + var newTags = new List(); + var updateTags = new List(); + + foreach (var userID in userEntriesData.Keys) + { + if (tagDao.GetNewTags(userID, obj.FileEntry).Any()) + continue; + + var entries = userEntriesData[userID].Distinct().ToList(); + + var exist = tagDao.GetNewTags(userID, entries).ToList(); + var update = exist.Where(t => t.EntryType == FileEntryType.Folder).ToList(); + update.ForEach(t => t.Count++); + updateTags.AddRange(update); + + entries.ForEach(entry => + { + if (entry != null && exist.All(tag => tag != null && !tag.EntryId.Equals(entry.ID))) + { + newTags.Add(Tag.New(userID, entry)); + } + }); + } + + if (updateTags.Any()) + tagDao.UpdateNewTags(updateTags); + if (newTags.Any()) + tagDao.SaveTags(newTags); + } + + public void MarkAsNew(FileEntry fileEntry, List userIDs = null) + { + if (CoreBaseSettings.Personal) return; + + if (fileEntry == null) return; + userIDs ??= new List(); + + var taskData = ServiceProvider.GetService(); + taskData.FileEntry = (FileEntry)fileEntry.Clone(); + taskData.UserIDs = userIDs; + + if (fileEntry.RootFolderType == FolderType.BUNCH && !userIDs.Any()) + { + var folderDao = DaoFactory.FolderDao; + var path = folderDao.GetBunchObjectID(fileEntry.RootFolderId); + + var projectID = path.Split('/').Last(); + if (string.IsNullOrEmpty(projectID)) return; + + var projectTeam = FileSecurity.WhoCanRead(fileEntry) + .Where(x => x != AuthContext.CurrentAccount.ID).ToList(); + + if (!projectTeam.Any()) return; + + taskData.UserIDs = projectTeam; + } + + lock (locker) + { + tasks.Add(taskData); + + if (!tasks.IsStarted) + tasks.Start(FileMarkerHelper.ExecMarkFileAsNew); + } + } + + public void RemoveMarkAsNew(FileEntry fileEntry, Guid userID = default) + { + if (CoreBaseSettings.Personal) return; + + userID = userID.Equals(default) ? AuthContext.CurrentAccount.ID : userID; + + if (fileEntry == null) return; + + var tagDao = DaoFactory.TagDao; + var folderDao = DaoFactory.FolderDao; + if (!tagDao.GetNewTags(userID, fileEntry).Any()) return; + + object folderID; + int valueNew; + var userFolderId = folderDao.GetFolderIDUser(false, userID); + + var removeTags = new List(); + + if (fileEntry.FileEntryType == FileEntryType.File) + { + folderID = ((File)fileEntry).FolderID; + + removeTags.Add(Tag.New(userID, fileEntry)); + valueNew = 1; + } + else + { + folderID = fileEntry.ID; + + var listTags = tagDao.GetNewTags(userID, (Folder)fileEntry, true).ToList(); + valueNew = listTags.FirstOrDefault(tag => tag.EntryId.Equals(fileEntry.ID)).Count; + + if (Equals(fileEntry.ID, userFolderId) || Equals(fileEntry.ID, GlobalFolder.GetFolderCommon(this, DaoFactory)) || Equals(fileEntry.ID, GlobalFolder.GetFolderShare(DaoFactory.FolderDao))) + { + var folderTags = listTags.Where(tag => tag.EntryType == FileEntryType.Folder); + + var providerFolderTags = folderTags.Select(tag => new KeyValuePair(tag, folderDao.GetFolder(tag.EntryId))) + .Where(pair => pair.Value != null && pair.Value.ProviderEntry).ToList(); + + foreach (var providerFolderTag in providerFolderTags) + { + listTags.Remove(providerFolderTag.Key); + listTags.AddRange(tagDao.GetNewTags(userID, providerFolderTag.Value, true)); + } + } + + removeTags.AddRange(listTags); + } + + var parentFolders = folderDao.GetParentFolders(folderID); + parentFolders.Reverse(); + + var rootFolder = parentFolders.LastOrDefault(); + object rootFolderId = null; + object cacheFolderId = null; + if (rootFolder == null) + { + } + else if (rootFolder.RootFolderType == FolderType.BUNCH) + { + cacheFolderId = rootFolderId = GlobalFolder.GetFolderProjects(DaoFactory); + } + else if (rootFolder.RootFolderType == FolderType.COMMON) + { + if (rootFolder.ProviderEntry) + cacheFolderId = rootFolderId = GlobalFolder.GetFolderCommon(this, DaoFactory); + else + cacheFolderId = GlobalFolder.GetFolderCommon(this, DaoFactory); + } + else if (rootFolder.RootFolderType == FolderType.USER) + { + if (rootFolder.ProviderEntry && rootFolder.RootFolderCreator == userID) + cacheFolderId = rootFolderId = userFolderId; + else if (!rootFolder.ProviderEntry && !Equals(rootFolder.RootFolderId, userFolderId) + || rootFolder.ProviderEntry && rootFolder.RootFolderCreator != userID) + cacheFolderId = rootFolderId = GlobalFolder.GetFolderShare(DaoFactory.FolderDao); + else + cacheFolderId = userFolderId; + } + else if (rootFolder.RootFolderType == FolderType.SHARE) + { + cacheFolderId = GlobalFolder.GetFolderShare(DaoFactory.FolderDao); + } + + if (rootFolderId != null) + { + parentFolders.Add(folderDao.GetFolder(rootFolderId)); + } + if (cacheFolderId != null) + { + RemoveFromCahce(cacheFolderId, userID); + } + + var updateTags = new List(); + foreach (var parentFolder in parentFolders) + { + var parentTag = tagDao.GetNewTags(userID, parentFolder).FirstOrDefault(); + + if (parentTag != null) + { + parentTag.Count -= valueNew; + + if (parentTag.Count > 0) + { + updateTags.Add(parentTag); + } + else + { + removeTags.Add(parentTag); + } + } + } + + if (updateTags.Any()) + tagDao.UpdateNewTags(updateTags); + if (removeTags.Any()) + tagDao.RemoveTags(removeTags); + } + + public void RemoveMarkAsNewForAll(FileEntry fileEntry) + { + List userIDs; + + var tagDao = DaoFactory.TagDao; + var tags = tagDao.GetTags(fileEntry.ID, fileEntry.FileEntryType == FileEntryType.File ? FileEntryType.File : FileEntryType.Folder, TagType.New); + userIDs = tags.Select(tag => tag.Owner).Distinct().ToList(); + + foreach (var userID in userIDs) + { + RemoveMarkAsNew(fileEntry, userID); + } + } + + public Dictionary GetRootFoldersIdMarkedAsNew() + { + var rootIds = new List + { + GlobalFolder.GetFolderMy(this, DaoFactory), + GlobalFolder.GetFolderCommon(this, DaoFactory), + GlobalFolder.GetFolderShare(DaoFactory.FolderDao), + GlobalFolder.GetFolderProjects(DaoFactory) + }; + + var requestIds = new List(); + var news = new Dictionary(); + + rootIds.ForEach(rootId => + { + var fromCache = GetCountFromCahce(rootId); + if (fromCache == -1) + { + requestIds.Add(rootId); + } + else if ((fromCache) > 0) + { + news.Add(rootId, (int)fromCache); + } + }); + + if (requestIds.Any()) + { + IEnumerable requestTags; + var tagDao = DaoFactory.TagDao; + var folderDao = DaoFactory.FolderDao; + requestTags = tagDao.GetNewTags(AuthContext.CurrentAccount.ID, folderDao.GetFolders(requestIds.ToArray())); + + requestIds.ForEach(requestId => + { + var requestTag = requestTags.FirstOrDefault(tag => tag.EntryId.Equals(requestId)); + InsertToCahce(requestId, requestTag == null ? 0 : requestTag.Count); + }); + + news = news.Concat(requestTags.ToDictionary(x => x.EntryId, x => x.Count)).ToDictionary(x => x.Key, x => x.Value); + } + + return news; + } + + public List MarkedItems(Folder folder) + { + if (folder == null) throw new ArgumentNullException("folder", FilesCommonResource.ErrorMassage_FolderNotFound); + if (!FileSecurity.CanRead(folder)) throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException_ViewFolder); + if (folder.RootFolderType == FolderType.TRASH && !Equals(folder.ID, GlobalFolder.GetFolderTrash(DaoFactory.FolderDao))) throw new SecurityException(FilesCommonResource.ErrorMassage_ViewTrashItem); + + var entryTags = new Dictionary(); + + var tagDao = DaoFactory.TagDao; + var fileDao = DaoFactory.FileDao; + var folderDao = DaoFactory.FolderDao; + var tags = (tagDao.GetNewTags(AuthContext.CurrentAccount.ID, folder, true) ?? new List()).ToList(); + + if (!tags.Any()) return new List(); + + if (Equals(folder.ID, GlobalFolder.GetFolderMy(this, DaoFactory)) || Equals(folder.ID, GlobalFolder.GetFolderCommon(this, DaoFactory)) || Equals(folder.ID, GlobalFolder.GetFolderShare(DaoFactory.FolderDao))) + { + var folderTags = tags.Where(tag => tag.EntryType == FileEntryType.Folder); + + var providerFolderTags = folderTags.Select(tag => new KeyValuePair(tag, folderDao.GetFolder(tag.EntryId))) + .Where(pair => pair.Value != null && pair.Value.ProviderEntry).ToList(); + providerFolderTags.Reverse(); + + foreach (var providerFolderTag in providerFolderTags) + { + tags.AddRange(tagDao.GetNewTags(AuthContext.CurrentAccount.ID, providerFolderTag.Value, true)); + } + } + + tags = tags.Distinct().ToList(); + tags.RemoveAll(tag => Equals(tag.EntryId, folder.ID)); + tags = tags.Where(t => t.EntryType == FileEntryType.Folder) + .Concat(tags.Where(t => t.EntryType == FileEntryType.File)).ToList(); + + foreach (var tag in tags) + { + var entry = tag.EntryType == FileEntryType.File + ? (FileEntry)fileDao.GetFile(tag.EntryId) + : (FileEntry)folderDao.GetFolder(tag.EntryId); + if (entry != null) + { + entryTags.Add(entry, tag); + } + else + { + //todo: RemoveMarkAsNew(tag); + } + } + + foreach (var entryTag in entryTags) + { + var entry = entryTag.Key; + var parentId = + entry.FileEntryType == FileEntryType.File + ? ((File)entry).FolderID + : ((Folder)entry).ParentFolderID; + + var parentEntry = entryTags.Keys.FirstOrDefault(entryCountTag => Equals(entryCountTag.ID, parentId)); + if (parentEntry != null) + entryTags[parentEntry].Count -= entryTag.Value.Count; + } + + var result = new List(); + + foreach (var entryTag in entryTags) + { + if (!string.IsNullOrEmpty(entryTag.Key.Error)) + { + RemoveMarkAsNew(entryTag.Key); + continue; + } + + if (entryTag.Value.Count > 0) + { + result.Add(entryTag.Key); + } + } + return result; + } + + public IEnumerable SetTagsNew(Folder parent, IEnumerable entries) + { + var tagDao = DaoFactory.TagDao; + var folderDao = DaoFactory.FolderDao; + var totalTags = tagDao.GetNewTags(AuthContext.CurrentAccount.ID, parent, false).ToList(); + + if (totalTags.Any()) + { + var parentFolderTag = Equals(GlobalFolder.GetFolderShare(DaoFactory.FolderDao), parent.ID) + ? tagDao.GetNewTags(AuthContext.CurrentAccount.ID, folderDao.GetFolder(GlobalFolder.GetFolderShare(DaoFactory.FolderDao))).FirstOrDefault() + : totalTags.FirstOrDefault(tag => tag.EntryType == FileEntryType.Folder && Equals(tag.EntryId, parent.ID)); + + totalTags.Remove(parentFolderTag); + var countSubNew = 0; + totalTags.ForEach(tag => countSubNew += tag.Count); + + if (parentFolderTag == null) + { + parentFolderTag = Tag.New(AuthContext.CurrentAccount.ID, parent, 0); + parentFolderTag.Id = -1; + } + + if (parentFolderTag.Count != countSubNew) + { + if (countSubNew > 0) + { + var diff = parentFolderTag.Count - countSubNew; + + parentFolderTag.Count -= diff; + if (parentFolderTag.Id == -1) + { + tagDao.SaveTags(parentFolderTag); + } + else + { + tagDao.UpdateNewTags(parentFolderTag); + } + + var cacheFolderId = parent.ID; + var parentsList = DaoFactory.FolderDao.GetParentFolders(parent.ID); + parentsList.Reverse(); + parentsList.Remove(parent); + + if (parentsList.Any()) + { + var rootFolder = parentsList.Last(); + object rootFolderId = null; + cacheFolderId = rootFolder.ID; + if (rootFolder.RootFolderType == FolderType.BUNCH) + cacheFolderId = rootFolderId = GlobalFolder.GetFolderProjects(DaoFactory); + else if (rootFolder.RootFolderType == FolderType.USER && !Equals(rootFolder.RootFolderId, GlobalFolder.GetFolderMy(this, DaoFactory))) + cacheFolderId = rootFolderId = GlobalFolder.GetFolderShare(DaoFactory.FolderDao); + + if (rootFolderId != null) + { + parentsList.Add(DaoFactory.FolderDao.GetFolder(rootFolderId)); + } + + var fileSecurity = FileSecurity; + + foreach (var folderFromList in parentsList) + { + var parentTreeTag = tagDao.GetNewTags(AuthContext.CurrentAccount.ID, folderFromList).FirstOrDefault(); + + if (parentTreeTag == null) + { + if (fileSecurity.CanRead(folderFromList)) + { + tagDao.SaveTags(Tag.New(AuthContext.CurrentAccount.ID, folderFromList, -diff)); + } + } + else + { + parentTreeTag.Count -= diff; + tagDao.UpdateNewTags(parentTreeTag); + } + } + } + + if (cacheFolderId != null) + { + RemoveFromCahce(cacheFolderId); + } + } + else + { + RemoveMarkAsNew(parent); + } + } + + entries.ToList().ForEach( + entry => + { + var curTag = totalTags.FirstOrDefault(tag => tag.EntryType == entry.FileEntryType && tag.EntryId.Equals(entry.ID)); + + if (entry.FileEntryType == FileEntryType.Folder) + { + ((Folder)entry).NewForMe = curTag != null ? curTag.Count : 0; + } + else if (curTag != null) + { + entry.IsNew = true; + } + }); + } + + + return entries; + } + + private void InsertToCahce(object folderId, int count) + { + var key = string.Format(CacheKeyFormat, AuthContext.CurrentAccount.ID, folderId); + cache.Insert(key, count.ToString(), TimeSpan.FromMinutes(10)); + } + + private int GetCountFromCahce(object folderId) + { + var key = string.Format(CacheKeyFormat, AuthContext.CurrentAccount.ID, folderId); + var count = cache.Get(key); + return count == null ? -1 : int.Parse(count); + } + + private void RemoveFromCahce(object folderId) + { + RemoveFromCahce(folderId, AuthContext.CurrentAccount.ID); + } + + private void RemoveFromCahce(object folderId, Guid userId) + { + var key = string.Format(CacheKeyFormat, userId, folderId); + cache.Remove(key); + } + + + public class AsyncTaskData + { + public AsyncTaskData(TenantManager tenantManager, AuthContext authContext) + { + TenantID = tenantManager.GetCurrentTenant().TenantId; + CurrentAccountId = authContext.CurrentAccount.ID; + } + + public int TenantID { get; private set; } + + public FileEntry FileEntry { get; set; } + + public List UserIDs { get; set; } + + public Guid CurrentAccountId { get; set; } + } + } + public static class FileMarkerExtention + { + public static DIHelper AddFileMarkerService(this DIHelper services) + { + _ = services + .TryAddTransient() + .TryAddScoped() + .TryAddSingleton() + .TryAddSingleton>() + .TryAddSingleton>() + .AddSingleton>, ConfigureWorkerQueue>(); + + _ = services.Configure>(r => + { + r.workerCount = 1; + r.waitInterval = (int)TimeSpan.FromSeconds(60).TotalMilliseconds; + r.errorCount = 1; + r.stopAfterFinsih = false; + }); + + return services + .AddTenantManagerService() + .AddUserManagerService() + .AddDaoFactoryService() + .AddGlobalFolderService() + .AddFileSecurityService() + .AddCoreBaseSettingsService() + .AddAuthContextService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Utils/FileShareLink.cs b/products/ASC.Files/Server/Utils/FileShareLink.cs new file mode 100644 index 0000000000..f3984b6d67 --- /dev/null +++ b/products/ASC.Files/Server/Utils/FileShareLink.cs @@ -0,0 +1,127 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System.Web; + +using ASC.Common; +using ASC.Common.Utils; +using ASC.Core.Common; +using ASC.Files.Core; +using ASC.Files.Core.Security; +using ASC.Web.Core.Files; +using ASC.Web.Files.Classes; + +using File = ASC.Files.Core.File; +using FileShare = ASC.Files.Core.Security.FileShare; + +namespace ASC.Web.Files.Utils +{ + public class FileShareLink + { + public FileUtility FileUtility { get; } + public FilesLinkUtility FilesLinkUtility { get; } + public BaseCommonLinkUtility BaseCommonLinkUtility { get; } + public Global Global { get; } + public FileSecurity FileSecurity { get; } + + public FileShareLink( + FileUtility fileUtility, + FilesLinkUtility filesLinkUtility, + BaseCommonLinkUtility baseCommonLinkUtility, + Global global, + FileSecurity fileSecurity) + { + FileUtility = fileUtility; + FilesLinkUtility = filesLinkUtility; + BaseCommonLinkUtility = baseCommonLinkUtility; + Global = global; + FileSecurity = fileSecurity; + } + + public string GetLink(File file, bool withHash = true) + { + var url = file.DownloadUrl; + + if (FileUtility.CanWebView(file.Title)) + url = FilesLinkUtility.GetFileWebPreviewUrl(FileUtility, file.Title, file.ID); + + if (withHash) + { + var linkParams = CreateKey(file.ID.ToString()); + url += "&" + FilesLinkUtility.DocShareKey + "=" + HttpUtility.UrlEncode(linkParams); + } + + return BaseCommonLinkUtility.GetFullAbsolutePath(url); + } + + public string CreateKey(string fileId) + { + return Signature.Create(fileId, Global.GetDocDbKey()); + } + + public string Parse(string doc) + { + return Signature.Read(doc ?? string.Empty, Global.GetDocDbKey()); + } + + public bool Check(string doc, bool checkRead, IFileDao fileDao, out File file) + { + var fileShare = Check(doc, fileDao, out file); + return (!checkRead && (fileShare == FileShare.ReadWrite || fileShare == FileShare.Review || fileShare == FileShare.FillForms || fileShare == FileShare.Comment)) + || (checkRead && fileShare != FileShare.Restrict); + } + + public FileShare Check(string doc, IFileDao fileDao, out File file) + { + file = null; + if (string.IsNullOrEmpty(doc)) return FileShare.Restrict; + var fileId = Parse(doc); + file = fileDao.GetFile(fileId); + if (file == null) return FileShare.Restrict; + + var filesSecurity = FileSecurity; + if (filesSecurity.CanEdit(file, FileConstant.ShareLinkId)) return FileShare.ReadWrite; + if (filesSecurity.CanReview(file, FileConstant.ShareLinkId)) return FileShare.Review; + if (filesSecurity.CanFillForms(file, FileConstant.ShareLinkId)) return FileShare.FillForms; + if (filesSecurity.CanComment(file, FileConstant.ShareLinkId)) return FileShare.Comment; + if (filesSecurity.CanRead(file, FileConstant.ShareLinkId)) return FileShare.Read; + return FileShare.Restrict; + } + } + public static class FileShareLinkExtension + { + public static DIHelper AddFileShareLinkService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddFilesLinkUtilityService() + .AddFileUtilityService() + .AddBaseCommonLinkUtilityService() + .AddGlobalService() + .AddFileSecurityService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Utils/FileSharing.cs b/products/ASC.Files/Server/Utils/FileSharing.cs new file mode 100644 index 0000000000..a125ac3d14 --- /dev/null +++ b/products/ASC.Files/Server/Utils/FileSharing.cs @@ -0,0 +1,588 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security; + +using ASC.Common; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Users; +using ASC.Files.Core; +using ASC.Files.Core.Data; +using ASC.Files.Core.Security; +using ASC.Files.Resources; +using ASC.Web.Core.Files; +using ASC.Web.Core.Users; +using ASC.Web.Files.Classes; +using ASC.Web.Files.Services.DocumentService; +using ASC.Web.Files.Services.NotifyService; +using ASC.Web.Files.Services.WCFService; + +using Microsoft.Extensions.Options; + +namespace ASC.Web.Files.Utils +{ + public class FileSharingAceHelper + { + public FileSecurity FileSecurity { get; } + public CoreBaseSettings CoreBaseSettings { get; } + public FileUtility FileUtility { get; } + public UserManager UserManager { get; } + public AuthContext AuthContext { get; } + public DocumentServiceHelper DocumentServiceHelper { get; } + public FileMarker FileMarker { get; } + public NotifyClient NotifyClient { get; } + public GlobalFolderHelper GlobalFolderHelper { get; } + public FileSharingHelper FileSharingHelper { get; } + + public FileSharingAceHelper( + FileSecurity fileSecurity, + CoreBaseSettings coreBaseSettings, + FileUtility fileUtility, + UserManager userManager, + AuthContext authContext, + DocumentServiceHelper documentServiceHelper, + FileMarker fileMarker, + NotifyClient notifyClient, + GlobalFolderHelper globalFolderHelper, + FileSharingHelper fileSharingHelper) + { + FileSecurity = fileSecurity; + CoreBaseSettings = coreBaseSettings; + FileUtility = fileUtility; + UserManager = userManager; + AuthContext = authContext; + DocumentServiceHelper = documentServiceHelper; + FileMarker = fileMarker; + NotifyClient = notifyClient; + GlobalFolderHelper = globalFolderHelper; + FileSharingHelper = fileSharingHelper; + } + + public bool SetAceObject(List aceWrappers, FileEntry entry, bool notify, string message) + { + if (entry == null) throw new ArgumentNullException(FilesCommonResource.ErrorMassage_BadRequest); + if (!FileSharingHelper.CanSetAccess(entry)) throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException); + + var fileSecurity = FileSecurity; + + var entryType = entry.FileEntryType; + var recipients = new Dictionary(); + var usersWithoutRight = new List(); + var changed = false; + + foreach (var w in aceWrappers.OrderByDescending(ace => ace.SubjectGroup)) + { + var subjects = fileSecurity.GetUserSubjects(w.SubjectId); + + var ownerId = entry.RootFolderType == FolderType.USER ? entry.RootFolderCreator : entry.CreateBy; + if (entry.RootFolderType == FolderType.COMMON && subjects.Contains(Constants.GroupAdmin.ID) + || ownerId == w.SubjectId) + continue; + + var share = w.Share; + + if (w.SubjectId == FileConstant.ShareLinkId) + { + if (w.Share == FileShare.ReadWrite && UserManager.GetUsers(AuthContext.CurrentAccount.ID).IsVisitor(UserManager)) throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException); + if (CoreBaseSettings.Personal && !FileUtility.CanWebView(entry.Title) && w.Share != FileShare.Restrict) throw new SecurityException(FilesCommonResource.ErrorMassage_BadRequest); + share = w.Share == FileShare.Restrict ? FileShare.None : w.Share; + } + + fileSecurity.Share(entry.ID, entryType, w.SubjectId, share); + changed = true; + + if (w.SubjectId == FileConstant.ShareLinkId) + continue; + + entry.Access = share; + + var listUsersId = new List(); + + if (w.SubjectGroup) + listUsersId = UserManager.GetUsersByGroup(w.SubjectId).Select(ui => ui.ID).ToList(); + else + listUsersId.Add(w.SubjectId); + listUsersId.Remove(AuthContext.CurrentAccount.ID); + + if (entryType == FileEntryType.File) + { + listUsersId.ForEach(uid => FileTracker.ChangeRight(entry.ID, uid, true)); + } + + var addRecipient = share == FileShare.Read + || share == FileShare.ReadWrite + || share == FileShare.Review + || share == FileShare.FillForms + || share == FileShare.Comment + || share == FileShare.None && entry.RootFolderType == FolderType.COMMON; + var removeNew = share == FileShare.None && entry.RootFolderType == FolderType.USER + || share == FileShare.Restrict; + listUsersId.ForEach(id => + { + recipients.Remove(id); + if (addRecipient) + { + recipients.Add(id, share); + } + else if (removeNew) + { + usersWithoutRight.Add(id); + } + }); + } + + if (entryType == FileEntryType.File) + { + DocumentServiceHelper.CheckUsersForDrop((File)entry); + } + + if (recipients.Any()) + { + if (entryType == FileEntryType.File + || ((Folder)entry).TotalSubFolders + ((Folder)entry).TotalFiles > 0 + || entry.ProviderEntry) + { + FileMarker.MarkAsNew(entry, recipients.Keys.ToList()); + } + + if (entry.RootFolderType == FolderType.USER + && notify) + { + NotifyClient.SendShareNotice(entry, recipients, message); + } + } + + usersWithoutRight.ForEach(userId => FileMarker.RemoveMarkAsNew(entry, userId)); + + return changed; + } + + public void RemoveAce(List entries) + { + var fileSecurity = FileSecurity; + + entries.ForEach( + entry => + { + if (entry.RootFolderType != FolderType.USER || Equals(entry.RootFolderId, GlobalFolderHelper.FolderMy)) + return; + + var entryType = entry.FileEntryType; + fileSecurity.Share(entry.ID, entryType, AuthContext.CurrentAccount.ID, fileSecurity.DefaultMyShare); + + if (entryType == FileEntryType.File) + { + DocumentServiceHelper.CheckUsersForDrop((File)entry); + } + + FileMarker.RemoveMarkAsNew(entry); + }); + } + } + + public class FileSharingHelper + { + public FileSharingHelper( + Global global, + GlobalFolderHelper globalFolderHelper, + FileSecurity fileSecurity, + AuthContext authContext, + UserManager userManager) + { + Global = global; + GlobalFolderHelper = globalFolderHelper; + FileSecurity = fileSecurity; + AuthContext = authContext; + UserManager = userManager; + } + + public Global Global { get; } + public GlobalFolderHelper GlobalFolderHelper { get; } + public FileSecurity FileSecurity { get; } + public AuthContext AuthContext { get; } + public UserManager UserManager { get; } + + public bool CanSetAccess(FileEntry entry) + { + return + entry != null + && (entry.RootFolderType == FolderType.COMMON && Global.IsAdministrator + || entry.RootFolderType == FolderType.USER + && (Equals(entry.RootFolderId, GlobalFolderHelper.FolderMy) || FileSecurity.CanEdit(entry)) + && !UserManager.GetUsers(AuthContext.CurrentAccount.ID).IsVisitor(UserManager)); + } + } + + public class FileSharing + { + public Global Global { get; } + public FileSecurity FileSecurity { get; } + public AuthContext AuthContext { get; } + public UserManager UserManager { get; } + public DisplayUserSettingsHelper DisplayUserSettingsHelper { get; } + public FileShareLink FileShareLink { get; } + public IDaoFactory DaoFactory { get; } + public FileSharingHelper FileSharingHelper { get; } + public ILog Logger { get; } + + public FileSharing( + Global global, + FileSecurity fileSecurity, + AuthContext authContext, + UserManager userManager, + IOptionsMonitor optionsMonitor, + DisplayUserSettingsHelper displayUserSettingsHelper, + FileShareLink fileShareLink, + IDaoFactory daoFactory, + FileSharingHelper fileSharingHelper) + { + Global = global; + FileSecurity = fileSecurity; + AuthContext = authContext; + UserManager = userManager; + DisplayUserSettingsHelper = displayUserSettingsHelper; + FileShareLink = fileShareLink; + DaoFactory = daoFactory; + FileSharingHelper = fileSharingHelper; + Logger = optionsMonitor.CurrentValue; + } + + public bool CanSetAccess(FileEntry entry) + { + return FileSharingHelper.CanSetAccess(entry); + } + + public List GetSharedInfo(FileEntry entry) + { + if (entry == null) throw new ArgumentNullException(FilesCommonResource.ErrorMassage_BadRequest); + if (!CanSetAccess(entry)) + { + Logger.ErrorFormat("User {0} can't get shared info for {1} {2}", AuthContext.CurrentAccount.ID, (entry.FileEntryType == FileEntryType.File ? "file" : "folder"), entry.ID); + throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException); + } + + var linkAccess = FileShare.Restrict; + var result = new List(); + + var fileSecurity = FileSecurity; + + var records = fileSecurity + .GetShares(entry) + .GroupBy(r => r.Subject) + .Select(g => g.OrderBy(r => r.Level) + .ThenBy(r => r.Level) + .ThenByDescending(r => r.Share, new FileShareRecord.ShareComparer()).FirstOrDefault()); + + foreach (var r in records) + { + if (r.Subject == FileConstant.ShareLinkId) + { + linkAccess = r.Share; + continue; + } + + var u = UserManager.GetUsers(r.Subject); + var isgroup = false; + var title = u.DisplayUserName(false, DisplayUserSettingsHelper); + + if (u.ID == Constants.LostUser.ID) + { + var g = UserManager.GetGroupInfo(r.Subject); + isgroup = true; + title = g.Name; + + if (g.ID == Constants.GroupAdmin.ID) + title = FilesCommonResource.Admin; + if (g.ID == Constants.GroupEveryone.ID) + title = FilesCommonResource.Everyone; + + if (g.ID == Constants.LostGroupInfo.ID) + { + fileSecurity.RemoveSubject(r.Subject); + continue; + } + } + + var w = new AceWrapper + { + SubjectId = r.Subject, + SubjectName = title, + SubjectGroup = isgroup, + Share = r.Share, + Owner = + entry.RootFolderType == FolderType.USER + ? entry.RootFolderCreator == r.Subject + : entry.CreateBy == r.Subject, + LockedRights = r.Subject == AuthContext.CurrentAccount.ID + }; + result.Add(w); + } + + if (entry.FileEntryType == FileEntryType.File && result.All(w => w.SubjectId != FileConstant.ShareLinkId) + && entry.FileEntryType == FileEntryType.File + && !((File)entry).Encrypted) + { + var w = new AceWrapper + { + SubjectId = FileConstant.ShareLinkId, + Link = FileShareLink.GetLink((File)entry), + SubjectGroup = true, + Share = linkAccess, + Owner = false + }; + result.Add(w); + } + + if (!result.Any(w => w.Owner)) + { + var ownerId = entry.RootFolderType == FolderType.USER ? entry.RootFolderCreator : entry.CreateBy; + var w = new AceWrapper + { + SubjectId = ownerId, + SubjectName = Global.GetUserName(ownerId), + SubjectGroup = false, + Share = FileShare.ReadWrite, + Owner = true + }; + result.Add(w); + } + + if (result.Any(w => w.SubjectId == AuthContext.CurrentAccount.ID)) + { + result.Single(w => w.SubjectId == AuthContext.CurrentAccount.ID).LockedRights = true; + } + + if (entry.RootFolderType == FolderType.COMMON) + { + if (result.All(w => w.SubjectId != Constants.GroupAdmin.ID)) + { + var w = new AceWrapper + { + SubjectId = Constants.GroupAdmin.ID, + SubjectName = FilesCommonResource.Admin, + SubjectGroup = true, + Share = FileShare.ReadWrite, + Owner = false, + LockedRights = true, + }; + result.Add(w); + } + if (result.All(w => w.SubjectId != Constants.GroupEveryone.ID)) + { + var w = new AceWrapper + { + SubjectId = Constants.GroupEveryone.ID, + SubjectName = FilesCommonResource.Everyone, + SubjectGroup = true, + Share = fileSecurity.DefaultCommonShare, + Owner = false, + DisableRemove = true + }; + result.Add(w); + } + } + + return result; + } + + public ItemList GetSharedInfo(ItemList objectIds) + { + if (!AuthContext.IsAuthenticated) + { + throw new InvalidOperationException(FilesCommonResource.ErrorMassage_SecurityException); + } + + var result = new List(); + + var folderDao = DaoFactory.FolderDao; + var fileDao = DaoFactory.FileDao; + + foreach (var objectId in objectIds) + { + if (string.IsNullOrEmpty(objectId)) + { + throw new InvalidOperationException(FilesCommonResource.ErrorMassage_BadRequest); + } + + var entryType = objectId.StartsWith("file_") ? FileEntryType.File : FileEntryType.Folder; + var entryId = objectId.Substring((entryType == FileEntryType.File ? "file_" : "folder_").Length); + + var entry = entryType == FileEntryType.File + ? (FileEntry)fileDao.GetFile(entryId) + : (FileEntry)folderDao.GetFolder(entryId); + + IEnumerable acesForObject; + try + { + acesForObject = GetSharedInfo(entry); + } + catch (Exception e) + { + Logger.Error(e); + throw new InvalidOperationException(e.Message, e); + } + + foreach (var aceForObject in acesForObject) + { + var duplicate = result.FirstOrDefault(ace => ace.SubjectId == aceForObject.SubjectId); + if (duplicate == null) + { + if (result.Any()) + { + aceForObject.Owner = false; + aceForObject.Share = FileShare.Varies; + } + continue; + } + + if (duplicate.Share != aceForObject.Share) + { + aceForObject.Share = FileShare.Varies; + } + if (duplicate.Owner != aceForObject.Owner) + { + aceForObject.Owner = false; + aceForObject.Share = FileShare.Varies; + } + result.Remove(duplicate); + } + + var withoutAce = result.Where(ace => + acesForObject.FirstOrDefault(aceForObject => + aceForObject.SubjectId == ace.SubjectId) == null); + foreach (var ace in withoutAce) + { + ace.Share = FileShare.Varies; + } + + var notOwner = result.Where(ace => + ace.Owner && + acesForObject.FirstOrDefault(aceForObject => + aceForObject.Owner + && aceForObject.SubjectId == ace.SubjectId) == null); + foreach (var ace in notOwner) + { + ace.Owner = false; + ace.Share = FileShare.Varies; + } + + result.AddRange(acesForObject); + } + + + var ownerAce = result.FirstOrDefault(ace => ace.Owner); + result.Remove(ownerAce); + + var meAce = result.FirstOrDefault(ace => ace.SubjectId == AuthContext.CurrentAccount.ID); + result.Remove(meAce); + + AceWrapper linkAce = null; + if (objectIds.Count > 1) + { + result.RemoveAll(ace => ace.SubjectId == FileConstant.ShareLinkId); + } + else + { + linkAce = result.FirstOrDefault(ace => ace.SubjectId == FileConstant.ShareLinkId); + } + + result.Sort((x, y) => string.Compare(x.SubjectName, y.SubjectName)); + + if (ownerAce != null) + { + result = new List { ownerAce }.Concat(result).ToList(); + } + if (meAce != null) + { + result = new List { meAce }.Concat(result).ToList(); + } + if (linkAce != null) + { + result.Remove(linkAce); + result = new List { linkAce }.Concat(result).ToList(); + } + + return new ItemList(result); + } + + public ItemList GetSharedInfoShort(string objectId) + { + var aces = GetSharedInfo(new ItemList { objectId }); + + return new ItemList( + aces.Where(aceWrapper => !aceWrapper.SubjectId.Equals(FileConstant.ShareLinkId) || aceWrapper.Share != FileShare.Restrict) + .Select(aceWrapper => new AceShortWrapper(aceWrapper))); + } + } + + public static class FileSharingExtension + { + public static DIHelper AddFileSharingService(this DIHelper services) + { + services.TryAddScoped(); + + return services + .AddGlobalService() + .AddFileSecurityService() + .AddAuthContextService() + .AddUserManagerService() + .AddDisplayUserSettingsService() + .AddFileShareLinkService() + .AddDaoFactoryService() + .AddFileSharingHelperService(); + } + public static DIHelper AddFileSharingHelperService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddGlobalService() + .AddGlobalFolderHelperService() + .AddFileSecurityService() + .AddAuthContextService() + .AddUserManagerService(); + } + public static DIHelper AddFileSharingAceHelperService(this DIHelper services) + { + services.TryAddScoped(); + + return services + .AddFileSecurityService() + .AddCoreBaseSettingsService() + .AddFileUtilityService() + .AddUserManagerService() + .AddAuthContextService() + .AddDocumentServiceHelperService() + .AddFileMarkerService() + .AddNotifyClientService() + .AddGlobalFolderHelperService() + .AddFileSharingHelperService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Utils/FileTracker.cs b/products/ASC.Files/Server/Utils/FileTracker.cs new file mode 100644 index 0000000000..8dbaa84efc --- /dev/null +++ b/products/ASC.Files/Server/Utils/FileTracker.cs @@ -0,0 +1,254 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; + +using ASC.Common.Caching; + +namespace ASC.Web.Files.Utils +{ + [DataContract] + public class FileTracker + { + private const string TRACKER = "filesTracker"; + private static readonly ICache cache = AscCache.Memory; + + public static readonly TimeSpan TrackTimeout = TimeSpan.FromSeconds(12); + public static readonly TimeSpan CacheTimeout = TimeSpan.FromSeconds(60); + public static readonly TimeSpan CheckRightTimeout = TimeSpan.FromMinutes(1); + + [DataMember] private readonly Dictionary _editingBy; + + + private FileTracker() + { + } + + private FileTracker(Guid tabId, Guid userId, bool newScheme, bool editingAlone) + { + _editingBy = new Dictionary { { tabId, new TrackInfo(userId, newScheme, editingAlone) } }; + } + + + public static Guid Add(Guid userId, object fileId) + { + var tabId = Guid.NewGuid(); + ProlongEditing(fileId, tabId, userId); + return tabId; + } + + public static bool ProlongEditing(object fileId, Guid tabId, Guid userId, bool editingAlone = false) + { + var checkRight = true; + var tracker = GetTracker(fileId); + if (tracker != null && IsEditing(fileId)) + { + if (tracker._editingBy.Keys.Contains(tabId)) + { + tracker._editingBy[tabId].TrackTime = DateTime.UtcNow; + checkRight = (DateTime.UtcNow - tracker._editingBy[tabId].CheckRightTime > CheckRightTimeout); + } + else + { + tracker._editingBy.Add(tabId, new TrackInfo(userId, tabId == userId, editingAlone)); + } + } + else + { + tracker = new FileTracker(tabId, userId, tabId == userId, editingAlone); + } + + SetTracker(fileId, tracker); + + return checkRight; + } + + public static void Remove(object fileId, Guid tabId = default, Guid userId = default) + { + var tracker = GetTracker(fileId); + if (tracker != null) + { + if (tabId != default) + { + tracker._editingBy.Remove(tabId); + SetTracker(fileId, tracker); + return; + } + if (userId != default) + { + var listForRemove = tracker._editingBy + .Where(b => tracker._editingBy[b.Key].UserId == userId) + .ToList(); + foreach (var editTab in listForRemove) + { + tracker._editingBy.Remove(editTab.Key); + } + SetTracker(fileId, tracker); + return; + } + } + + SetTracker(fileId, null); + } + + public static void RemoveAllOther(Guid userId, object fileId) + { + var tracker = GetTracker(fileId); + if (tracker != null) + { + var listForRemove = tracker._editingBy + .Where(b => b.Value.UserId != userId) + .ToList(); + if (listForRemove.Count() != tracker._editingBy.Count) + { + foreach (var forRemove in listForRemove) + { + tracker._editingBy.Remove(forRemove.Key); + } + SetTracker(fileId, tracker); + return; + } + } + SetTracker(fileId, null); + } + + public static bool IsEditing(object fileId) + { + var tracker = GetTracker(fileId); + if (tracker != null) + { + var listForRemove = tracker._editingBy + .Where(e => !e.Value.NewScheme && (DateTime.UtcNow - e.Value.TrackTime).Duration() > TrackTimeout) + .ToList(); + foreach (var editTab in listForRemove) + { + tracker._editingBy.Remove(editTab.Key); + } + + if (tracker._editingBy.Count == 0) + { + SetTracker(fileId, null); + return false; + } + + SetTracker(fileId, tracker); + return true; + } + SetTracker(fileId, null); + return false; + } + + public static bool IsEditingAlone(object fileId) + { + var tracker = GetTracker(fileId); + return tracker != null && tracker._editingBy.Count == 1 && tracker._editingBy.FirstOrDefault().Value.EditingAlone; + } + + public static void ChangeRight(object fileId, Guid userId, bool check) + { + var tracker = GetTracker(fileId); + if (tracker != null) + { + + tracker._editingBy.Values + .ToList() + .ForEach(i => + { + if (i.UserId == userId || userId == Guid.Empty) + { + i.CheckRightTime = check ? DateTime.MinValue : DateTime.UtcNow; + } + }); + SetTracker(fileId, tracker); + } + else + { + SetTracker(fileId, null); + } + } + + public static List GetEditingBy(object fileId) + { + var tracker = GetTracker(fileId); + return tracker != null && IsEditing(fileId) ? tracker._editingBy.Values.Select(i => i.UserId).Distinct().ToList() : new List(); + } + + private static FileTracker GetTracker(object fileId) + { + if (fileId != null) + { + return cache.Get(TRACKER + fileId); + } + return null; + } + + private static void SetTracker(object fileId, FileTracker tracker) + { + if (fileId != null) + { + if (tracker != null) + { + cache.Insert(TRACKER + fileId, tracker, CacheTimeout); + } + else + { + cache.Remove(TRACKER + fileId); + } + } + } + + + [DataContract] + internal class TrackInfo + { + [DataMember] public DateTime CheckRightTime; + + [DataMember] public DateTime TrackTime; + + [DataMember] public Guid UserId; + + [DataMember] public bool NewScheme; + + [DataMember] public bool EditingAlone; + + public TrackInfo() + { + } + + public TrackInfo(Guid userId, bool newScheme, bool editingAlone) + { + CheckRightTime = DateTime.UtcNow; + TrackTime = DateTime.UtcNow; + NewScheme = newScheme; + UserId = userId; + EditingAlone = editingAlone; + } + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Utils/FileUploader.cs b/products/ASC.Files/Server/Utils/FileUploader.cs new file mode 100644 index 0000000000..e15f88f015 --- /dev/null +++ b/products/ASC.Files/Server/Utils/FileUploader.cs @@ -0,0 +1,368 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security; +using System.Threading; + +using ASC.Common; +using ASC.Core; +using ASC.Core.Users; +using ASC.Files.Core; +using ASC.Files.Core.Data; +using ASC.Files.Core.Security; +using ASC.Files.Resources; +using ASC.MessagingSystem; +using ASC.Web.Core.Files; +using ASC.Web.Files.Classes; +using ASC.Web.Files.Helpers; +using ASC.Web.Studio.Core; +using ASC.Web.Studio.UserControls.Statistics; +using ASC.Web.Studio.Utility; + +using Microsoft.Extensions.DependencyInjection; + +using File = ASC.Files.Core.File; + +namespace ASC.Web.Files.Utils +{ + public class FileUploader + { + public FilesSettingsHelper FilesSettingsHelper { get; } + public FileUtility FileUtility { get; } + public UserManager UserManager { get; } + public TenantManager TenantManager { get; } + public AuthContext AuthContext { get; } + public SetupInfo SetupInfo { get; } + public TenantExtra TenantExtra { get; } + public TenantStatisticsProvider TenantStatisticsProvider { get; } + public FileMarker FileMarker { get; } + public FileConverter FileConverter { get; } + public IDaoFactory DaoFactory { get; } + public Global Global { get; } + public FilesLinkUtility FilesLinkUtility { get; } + public FilesMessageService FilesMessageService { get; } + public FileSecurity FileSecurity { get; } + public EntryManager EntryManager { get; } + public IServiceProvider ServiceProvider { get; } + public ChunkedUploadSessionHolder ChunkedUploadSessionHolder { get; } + + public FileUploader( + FilesSettingsHelper filesSettingsHelper, + FileUtility fileUtility, + UserManager userManager, + TenantManager tenantManager, + AuthContext authContext, + SetupInfo setupInfo, + TenantExtra tenantExtra, + TenantStatisticsProvider tenantStatisticsProvider, + FileMarker fileMarker, + FileConverter fileConverter, + IDaoFactory daoFactory, + Global global, + FilesLinkUtility filesLinkUtility, + FilesMessageService filesMessageService, + FileSecurity fileSecurity, + EntryManager entryManager, + IServiceProvider serviceProvider, + ChunkedUploadSessionHolder chunkedUploadSessionHolder) + { + FilesSettingsHelper = filesSettingsHelper; + FileUtility = fileUtility; + UserManager = userManager; + TenantManager = tenantManager; + AuthContext = authContext; + SetupInfo = setupInfo; + TenantExtra = tenantExtra; + TenantStatisticsProvider = tenantStatisticsProvider; + FileMarker = fileMarker; + FileConverter = fileConverter; + DaoFactory = daoFactory; + Global = global; + FilesLinkUtility = filesLinkUtility; + FilesMessageService = filesMessageService; + FileSecurity = fileSecurity; + EntryManager = entryManager; + ServiceProvider = serviceProvider; + ChunkedUploadSessionHolder = chunkedUploadSessionHolder; + } + + public File Exec(string folderId, string title, long contentLength, Stream data) + { + return Exec(folderId, title, contentLength, data, !FilesSettingsHelper.UpdateIfExist); + } + + public File Exec(string folderId, string title, long contentLength, Stream data, bool createNewIfExist, bool deleteConvertStatus = true) + { + if (contentLength <= 0) + throw new Exception(FilesCommonResource.ErrorMassage_EmptyFile); + + var file = VerifyFileUpload(folderId, title, contentLength, !createNewIfExist); + + var dao = DaoFactory.FileDao; + file = dao.SaveFile(file, data); + + FileMarker.MarkAsNew(file); + + if (FileConverter.EnableAsUploaded && FileConverter.MustConvert(file)) + FileConverter.ExecAsync(file, deleteConvertStatus); + + return file; + } + + public File VerifyFileUpload(string folderId, string fileName, bool updateIfExists, string relativePath = null) + { + fileName = Global.ReplaceInvalidCharsAndTruncate(fileName); + + if (Global.EnableUploadFilter && !FileUtility.ExtsUploadable.Contains(FileUtility.GetFileExtension(fileName))) + throw new NotSupportedException(FilesCommonResource.ErrorMassage_NotSupportedFormat); + + folderId = GetFolderId(folderId, string.IsNullOrEmpty(relativePath) ? null : relativePath.Split('/').ToList()); + + var fileDao = DaoFactory.FileDao; + var file = fileDao.GetFile(folderId, fileName); + + if (updateIfExists && CanEdit(file)) + { + file.Title = fileName; + file.ConvertedType = null; + file.Comment = FilesCommonResource.CommentUpload; + file.Version++; + file.VersionGroup++; + file.Encrypted = false; + + return file; + } + + var newFile = ServiceProvider.GetService(); + newFile.FolderID = folderId; + newFile.Title = fileName; + return newFile; + } + + public File VerifyFileUpload(string folderId, string fileName, long fileSize, bool updateIfExists) + { + if (fileSize <= 0) + throw new Exception(FilesCommonResource.ErrorMassage_EmptyFile); + + var maxUploadSize = GetMaxFileSize(folderId); + + if (fileSize > maxUploadSize) + throw FileSizeComment.GetFileSizeException(maxUploadSize); + + var file = VerifyFileUpload(folderId, fileName, updateIfExists); + file.ContentLength = fileSize; + return file; + } + + private bool CanEdit(File file) + { + return file != null + && FileSecurity.CanEdit(file) + && !UserManager.GetUsers(AuthContext.CurrentAccount.ID).IsVisitor(UserManager) + && !EntryManager.FileLockedForMe(file.ID) + && !FileTracker.IsEditing(file.ID) + && file.RootFolderType != FolderType.TRASH + && !file.Encrypted; + } + + private string GetFolderId(object folderId, IList relativePath) + { + var folderDao = DaoFactory.FolderDao; + var folder = folderDao.GetFolder(folderId); + + if (folder == null) + throw new DirectoryNotFoundException(FilesCommonResource.ErrorMassage_FolderNotFound); + + if (!FileSecurity.CanCreate(folder)) + throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException_Create); + + if (relativePath != null && relativePath.Any()) + { + var subFolderTitle = Global.ReplaceInvalidCharsAndTruncate(relativePath.FirstOrDefault()); + + if (!string.IsNullOrEmpty(subFolderTitle)) + { + folder = folderDao.GetFolder(subFolderTitle, folder.ID); + + if (folder == null) + { + var newFolder = ServiceProvider.GetService(); + newFolder.Title = subFolderTitle; + newFolder.ParentFolderID = folderId; + + folderId = folderDao.SaveFolder(newFolder); + + folder = folderDao.GetFolder(folderId); + FilesMessageService.Send(folder, MessageAction.FolderCreated, folder.Title); + } + + folderId = folder.ID; + + relativePath.RemoveAt(0); + folderId = GetFolderId(folderId, relativePath); + } + } + + return folderId.ToString(); + } + + #region chunked upload + + public File VerifyChunkedUpload(string folderId, string fileName, long fileSize, bool updateIfExists, string relativePath = null) + { + var maxUploadSize = GetMaxFileSize(folderId, true); + + if (fileSize > maxUploadSize) + throw FileSizeComment.GetFileSizeException(maxUploadSize); + + var file = VerifyFileUpload(folderId, fileName, updateIfExists, relativePath); + file.ContentLength = fileSize; + + return file; + } + + public ChunkedUploadSession InitiateUpload(string folderId, string fileId, string fileName, long contentLength, bool encrypted) + { + if (string.IsNullOrEmpty(folderId)) + folderId = null; + + if (string.IsNullOrEmpty(fileId)) + fileId = null; + + var file = ServiceProvider.GetService(); + file.ID = fileId; + file.FolderID = folderId; + file.Title = fileName; + file.ContentLength = contentLength; + + var dao = DaoFactory.FileDao; + var uploadSession = dao.CreateUploadSession(file, contentLength); + + uploadSession.Expired = uploadSession.Created + ChunkedUploadSessionHolder.SlidingExpiration; + uploadSession.Location = FilesLinkUtility.GetUploadChunkLocationUrl(uploadSession.Id); + uploadSession.TenantId = TenantManager.GetCurrentTenant().TenantId; + uploadSession.UserId = AuthContext.CurrentAccount.ID; + uploadSession.FolderId = folderId; + uploadSession.CultureName = Thread.CurrentThread.CurrentUICulture.Name; + uploadSession.Encrypted = encrypted; + + ChunkedUploadSessionHolder.StoreSession(uploadSession); + + return uploadSession; + } + + public ChunkedUploadSession UploadChunk(string uploadId, Stream stream, long chunkLength) + { + var uploadSession = ChunkedUploadSessionHolder.GetSession(uploadId); + uploadSession.Expired = DateTime.UtcNow + ChunkedUploadSessionHolder.SlidingExpiration; + + if (chunkLength <= 0) + { + throw new Exception(FilesCommonResource.ErrorMassage_EmptyFile); + } + + if (chunkLength > SetupInfo.ChunkUploadSize) + { + throw FileSizeComment.GetFileSizeException(SetupInfo.MaxUploadSize(TenantExtra, TenantStatisticsProvider)); + } + + var maxUploadSize = GetMaxFileSize(uploadSession.FolderId, uploadSession.BytesTotal > 0); + + if (uploadSession.BytesUploaded + chunkLength > maxUploadSize) + { + AbortUpload(uploadSession); + throw FileSizeComment.GetFileSizeException(maxUploadSize); + } + + var dao = DaoFactory.FileDao; + dao.UploadChunk(uploadSession, stream, chunkLength); + + if (uploadSession.BytesUploaded == uploadSession.BytesTotal) + { + FileMarker.MarkAsNew(uploadSession.File); + ChunkedUploadSessionHolder.RemoveSession(uploadSession); + } + else + { + ChunkedUploadSessionHolder.StoreSession(uploadSession); + } + + return uploadSession; + } + + public void AbortUpload(string uploadId) + { + AbortUpload(ChunkedUploadSessionHolder.GetSession(uploadId)); + } + + private void AbortUpload(ChunkedUploadSession uploadSession) + { + DaoFactory.FileDao.AbortUploadSession(uploadSession); + + ChunkedUploadSessionHolder.RemoveSession(uploadSession); + } + + private long GetMaxFileSize(object folderId, bool chunkedUpload = false) + { + var folderDao = DaoFactory.FolderDao; + return folderDao.GetMaxUploadSize(folderId, chunkedUpload); + } + + #endregion + } + + public static class FileUploaderExtention + { + public static DIHelper AddFileUploaderService(this DIHelper services) + { + services.TryAddScoped(); + + return services + .AddChunkedUploadSessionHolderService() + .AddEntryManagerService() + .AddFileSecurityService() + .AddFilesLinkUtilityService() + .AddFilesMessageService() + .AddGlobalService() + .AddDaoFactoryService() + .AddFileConverterService() + .AddFileMarkerService() + .AddTenantStatisticsProviderService() + .AddTenantExtraService() + .AddUserManagerService() + .AddTenantManagerService() + .AddAuthContextService() + .AddSetupInfo() + .AddFileUtilityService() + .AddFilesSettingsHelperService() + ; + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Utils/MailMergeTask.cs b/products/ASC.Files/Server/Utils/MailMergeTask.cs new file mode 100644 index 0000000000..5752656a90 --- /dev/null +++ b/products/ASC.Files/Server/Utils/MailMergeTask.cs @@ -0,0 +1,222 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.IO; +using System.Net; +using System.Web; + +using ASC.Common; +using ASC.Common.Web; +using ASC.Core; +using ASC.Core.Common; +using ASC.Web.Studio.Core; + +using Newtonsoft.Json.Linq; + +namespace ASC.Web.Files.Utils +{ + public class MailMergeTask : IDisposable + { + internal const string MessageBodyFormat = "id={0}&from={1}&subject={2}&to%5B%5D={3}&body={4}&mimeReplyToId="; + + public string From; + public string Subject; + public string To; + public string Message; + public string AttachTitle; + public Stream Attach; + public int MessageId; + public string StreamId; + + public MailMergeTask() + { + MessageId = 0; + } + + public void Dispose() + { + if (Attach != null) + Attach.Dispose(); + } + } + + public class MailMergeTaskRunner + { + public SetupInfo SetupInfo { get; } + public SecurityContext SecurityContext { get; } + public BaseCommonLinkUtility BaseCommonLinkUtility { get; } + + //private ApiServer _apiServer; + + //protected ApiServer Api + //{ + // get { return _apiServer ?? (_apiServer = new ApiServer()); } + //} + + public MailMergeTaskRunner(SetupInfo setupInfo, SecurityContext securityContext, BaseCommonLinkUtility baseCommonLinkUtility) + { + SetupInfo = setupInfo; + SecurityContext = securityContext; + BaseCommonLinkUtility = baseCommonLinkUtility; + } + + public string Run(MailMergeTask mailMergeTask) + { + if (string.IsNullOrEmpty(mailMergeTask.From)) throw new ArgumentException("From is null"); + if (string.IsNullOrEmpty(mailMergeTask.To)) throw new ArgumentException("To is null"); + + CreateDraftMail(mailMergeTask); + + var bodySendAttach = AttachToMail(mailMergeTask); + + return SendMail(mailMergeTask, bodySendAttach); + } + + private void CreateDraftMail(MailMergeTask mailMergeTask) + { + var apiUrlCreate = string.Format("{0}mail/drafts/save.json", SetupInfo.WebApiBaseUrl); + var bodyCreate = + string.Format( + MailMergeTask.MessageBodyFormat, + mailMergeTask.MessageId, + HttpUtility.UrlEncode(mailMergeTask.From), + HttpUtility.UrlEncode(mailMergeTask.Subject), + HttpUtility.UrlEncode(mailMergeTask.To), + HttpUtility.UrlEncode(mailMergeTask.Message)); + + string responseCreateString = null; //TODO: Encoding.UTF8.GetString(Convert.FromBase64String(Api.GetApiResponse(apiUrlCreate, "PUT", bodyCreate))); + var responseCreate = JObject.Parse(responseCreateString); + + if (responseCreate["statusCode"].Value() != (int)HttpStatusCode.OK) + { + throw new Exception("Create draft failed: " + responseCreate["error"]["message"].Value()); + } + + mailMergeTask.MessageId = responseCreate["response"]["id"].Value(); + mailMergeTask.StreamId = responseCreate["response"]["streamId"].Value(); + } + + private string AttachToMail(MailMergeTask mailMergeTask) + { + if (mailMergeTask.Attach == null) return string.Empty; + + if (string.IsNullOrEmpty(mailMergeTask.AttachTitle)) mailMergeTask.AttachTitle = "attach.pdf"; + + var apiUrlAttach = string.Format("{0}mail/messages/attachment/add?id_message={1}&name={2}", + SetupInfo.WebApiBaseUrl, + mailMergeTask.MessageId, + mailMergeTask.AttachTitle); + + var request = (HttpWebRequest)WebRequest.Create(BaseCommonLinkUtility.GetFullAbsolutePath(apiUrlAttach)); + request.Method = "POST"; + request.ContentType = MimeMapping.GetMimeMapping(mailMergeTask.AttachTitle); + request.ContentLength = mailMergeTask.Attach.Length; + request.Headers.Add("Authorization", SecurityContext.AuthenticateMe(SecurityContext.CurrentAccount.ID)); + + const int bufferSize = 2048; + var buffer = new byte[bufferSize]; + int readed; + while ((readed = mailMergeTask.Attach.Read(buffer, 0, bufferSize)) > 0) + { + request.GetRequestStream().Write(buffer, 0, readed); + } + + // hack. http://ubuntuforums.org/showthread.php?t=1841740 + if (WorkContext.IsMono) + { + ServicePointManager.ServerCertificateValidationCallback += (s, ce, ca, p) => true; + } + + string responseAttachString; + using (var response = request.GetResponse()) + using (var stream = response.GetResponseStream()) + { + if (stream == null) throw new WebException("Could not get an answer"); + using (var reader = new StreamReader(stream)) + { + responseAttachString = reader.ReadToEnd(); + } + } + + var responseAttach = JObject.Parse(responseAttachString); + + if (responseAttach["statusCode"].Value() != (int)HttpStatusCode.Created) + { + throw new Exception("Attach failed: " + responseAttach["error"]["message"].Value()); + } + + var bodySendAttach = + "&attachments%5B0%5D%5BfileId%5D=" + HttpUtility.UrlEncode(responseAttach["response"]["fileId"].Value()) + + "&attachments%5B0%5D%5BfileName%5D=" + HttpUtility.UrlEncode(responseAttach["response"]["fileName"].Value()) + + "&attachments%5B0%5D%5Bsize%5D=" + HttpUtility.UrlEncode(responseAttach["response"]["size"].Value()) + + "&attachments%5B0%5D%5BcontentType%5D=" + HttpUtility.UrlEncode(responseAttach["response"]["contentType"].Value()) + + "&attachments%5B0%5D%5BfileNumber%5D=" + HttpUtility.UrlEncode(responseAttach["response"]["fileNumber"].Value()) + + "&attachments%5B0%5D%5BstoredName%5D=" + HttpUtility.UrlEncode(responseAttach["response"]["storedName"].Value()) + + "&attachments%5B0%5D%5BstreamId%5D=" + HttpUtility.UrlEncode(responseAttach["response"]["streamId"].Value()) + ; + + return bodySendAttach; + } + + private string SendMail(MailMergeTask mailMergeTask, string bodySendAttach) + { + var apiUrlSend = string.Format("{0}mail/messages/send.json", SetupInfo.WebApiBaseUrl); + + var bodySend = + string.Format( + MailMergeTask.MessageBodyFormat, + mailMergeTask.MessageId, + HttpUtility.UrlEncode(mailMergeTask.From), + HttpUtility.UrlEncode(mailMergeTask.Subject), + HttpUtility.UrlEncode(mailMergeTask.To), + HttpUtility.UrlEncode(mailMergeTask.Message)); + + bodySend += bodySendAttach; + + string responseSendString = null;//TODO: Encoding.UTF8.GetString(Convert.FromBase64String(Api.GetApiResponse(apiUrlSend, "PUT", bodySend))); + var responseSend = JObject.Parse(responseSendString); + + if (responseSend["statusCode"].Value() != (int)HttpStatusCode.OK) + { + throw new Exception("Create draft failed: " + responseSend["error"]["message"].Value()); + } + return responseSend["response"].Value(); + } + } + + public static class MailMergeTaskRunnerExtension + { + public static DIHelper AddMailMergeTaskRunnerService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddSetupInfo() + .AddSecurityContextService() + .AddBaseCommonLinkUtilityService(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/Utils/SocketManager.cs b/products/ASC.Files/Server/Utils/SocketManager.cs new file mode 100644 index 0000000000..d9a951689f --- /dev/null +++ b/products/ASC.Files/Server/Utils/SocketManager.cs @@ -0,0 +1,62 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using ASC.Common; +using ASC.Core; +using ASC.Core.Notify.Signalr; + +using Microsoft.Extensions.Options; + +namespace ASC.Web.Files.Utils +{ + public class SocketManager + { + private readonly SignalrServiceClient _signalrServiceClient; + + public SocketManager(IOptionsSnapshot optionsSnapshot, TenantManager tenantManager) + { + _signalrServiceClient = optionsSnapshot.Get("files"); + TenantManager = tenantManager; + } + + public TenantManager TenantManager { get; } + + public void FilesChangeEditors(object fileId, bool finish = false) + { + _signalrServiceClient.FilesChangeEditors(TenantManager.GetCurrentTenant().TenantId, fileId.ToString(), finish); + } + } + public static class SocketManagerExtension + { + public static DIHelper AddSocketManagerService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddTenantManagerService() + .AddSignalrServiceClient(); + } + } +} \ No newline at end of file diff --git a/products/ASC.Files/Server/appsettings.json b/products/ASC.Files/Server/appsettings.json new file mode 100644 index 0000000000..9bf702e310 --- /dev/null +++ b/products/ASC.Files/Server/appsettings.json @@ -0,0 +1,3 @@ +{ + "pathToConf": "..\\..\\..\\config" +} diff --git a/products/ASC.People/Server/Controllers/GroupController.cs b/products/ASC.People/Server/Controllers/GroupController.cs index 0adfa2a13b..c1719863e4 100644 --- a/products/ASC.People/Server/Controllers/GroupController.cs +++ b/products/ASC.People/Server/Controllers/GroupController.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; + using ASC.Api.Core; using ASC.Api.Utils; +using ASC.Common; using ASC.Common.Web; using ASC.Core; using ASC.Core.Users; @@ -12,8 +14,8 @@ using ASC.Web.Api.Models; using ASC.Web.Api.Routing; using ASC.Web.Core.Users; using ASC.Web.Studio.Utility; + using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.DependencyInjection; namespace ASC.Employee.Core.Controllers { @@ -220,7 +222,7 @@ namespace ASC.Employee.Core.Controllers public static class GroupControllerExtention { - public static IServiceCollection AddGroupController(this IServiceCollection services) + public static DIHelper AddGroupController(this DIHelper services) { return services .AddGroupWraperFull() diff --git a/products/ASC.People/Server/Controllers/PeopleController.cs b/products/ASC.People/Server/Controllers/PeopleController.cs index 93052330c9..95b3645e4b 100644 --- a/products/ASC.People/Server/Controllers/PeopleController.cs +++ b/products/ASC.People/Server/Controllers/PeopleController.cs @@ -10,6 +10,7 @@ using System.Net.Mail; using System.Security; using ASC.Api.Core; +using ASC.Common; using ASC.Common.Logging; using ASC.Common.Utils; using ASC.Common.Web; @@ -39,7 +40,6 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using SecurityContext = ASC.Core.SecurityContext; @@ -1461,7 +1461,7 @@ namespace ASC.Employee.Core.Controllers public static class PeopleControllerExtention { - public static IServiceCollection AddPeopleController(this IServiceCollection services) + public static DIHelper AddPeopleController(this DIHelper services) { return services .AddAccountLinker() diff --git a/products/ASC.People/Server/Models/GroupWrapperFull.cs b/products/ASC.People/Server/Models/GroupWrapperFull.cs index ac9ec12ed3..46b56642f3 100644 --- a/products/ASC.People/Server/Models/GroupWrapperFull.cs +++ b/products/ASC.People/Server/Models/GroupWrapperFull.cs @@ -28,10 +28,10 @@ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; + +using ASC.Common; using ASC.Core; using ASC.Core.Users; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; namespace ASC.Web.Api.Models { @@ -107,7 +107,7 @@ namespace ASC.Web.Api.Models public static class GroupWraperFullExtension { - public static IServiceCollection AddGroupWraperFull(this IServiceCollection services) + public static DIHelper AddGroupWraperFull(this DIHelper services) { services.TryAddScoped(); diff --git a/products/ASC.People/Server/Program.cs b/products/ASC.People/Server/Program.cs index 96fb8eab3a..5732fd8d83 100644 --- a/products/ASC.People/Server/Program.cs +++ b/products/ASC.People/Server/Program.cs @@ -16,10 +16,10 @@ namespace ASC.People public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }) .ConfigureAppConfiguration((hostingContext, config) => { var buided = config.Build(); diff --git a/products/ASC.People/Server/Startup.cs b/products/ASC.People/Server/Startup.cs index 83cb54ab54..d1b5cde26b 100644 --- a/products/ASC.People/Server/Startup.cs +++ b/products/ASC.People/Server/Startup.cs @@ -4,6 +4,7 @@ using System; using ASC.Api.Core.Auth; using ASC.Api.Core.Core; using ASC.Api.Core.Middleware; +using ASC.Common; using ASC.Common.DependencyInjection; using ASC.Common.Logging; using ASC.Common.Threading.Progress; @@ -43,8 +44,8 @@ namespace ASC.People services.AddHttpContextAccessor(); services.AddControllers() - .AddNewtonsoftJson() - .AddXmlSerializerFormatters(); + .AddNewtonsoftJson() + .AddXmlSerializerFormatters(); services.AddTransient, CustomJsonOptionsWrapper>(); @@ -68,8 +69,9 @@ namespace ASC.People config.OutputFormatters.Add(new XmlOutputFormatter()); }); + var diHelper = new DIHelper(services); - services + diHelper .AddConfirmAuthHandler() .AddCookieAuthHandler() .AddCultureMiddleware() @@ -78,7 +80,7 @@ namespace ASC.People .AddProductSecurityFilter() .AddTenantStatusFilter(); - services.Configure>(r => + diHelper.Configure>(r => { r.workerCount = 2; r.waitInterval = (int)TimeSpan.FromSeconds(30).TotalMilliseconds; @@ -86,7 +88,7 @@ namespace ASC.People r.stopAfterFinsih = true; }); - services.Configure>(r => + diHelper.Configure>(r => { r.workerCount = 1; r.waitInterval = (int)TimeSpan.FromMinutes(5).TotalMilliseconds; @@ -95,7 +97,7 @@ namespace ASC.People r.errorCount = 0; }); - services.Configure>(r => + diHelper.Configure>(r => { r.workerCount = 1; r.waitInterval = (int)TimeSpan.FromMinutes(5).TotalMilliseconds; @@ -104,9 +106,9 @@ namespace ASC.People r.errorCount = 0; }); - services.AddNLogManager("ASC.Api", "ASC.Web"); + diHelper.AddNLogManager("ASC.Api", "ASC.Web"); - services + diHelper .AddPeopleController() .AddGroupController(); diff --git a/web/ASC.Web.Api/Controllers/AuthenticationController.cs b/web/ASC.Web.Api/Controllers/AuthenticationController.cs index c896ddad1e..369356e532 100644 --- a/web/ASC.Web.Api/Controllers/AuthenticationController.cs +++ b/web/ASC.Web.Api/Controllers/AuthenticationController.cs @@ -1,4 +1,6 @@ using System; + +using ASC.Common; using ASC.Core; using ASC.Core.Tenants; using ASC.Core.Users; @@ -6,9 +8,10 @@ using ASC.Security.Cryptography; using ASC.Web.Api.Models; using ASC.Web.Api.Routing; using ASC.Web.Core; + using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.DependencyInjection; + using static ASC.Security.Cryptography.EmailValidationKeyProvider; namespace ASC.Web.Api.Controllers @@ -131,7 +134,7 @@ namespace ASC.Web.Api.Controllers public static class AuthenticationControllerExtension { - public static IServiceCollection AddAuthenticationController(this IServiceCollection services) + public static DIHelper AddAuthenticationController(this DIHelper services) { return services .AddUserManagerService() diff --git a/web/ASC.Web.Api/Controllers/ModulesController.cs b/web/ASC.Web.Api/Controllers/ModulesController.cs index 0e1a5a79ba..8706b51466 100644 --- a/web/ASC.Web.Api/Controllers/ModulesController.cs +++ b/web/ASC.Web.Api/Controllers/ModulesController.cs @@ -1,16 +1,18 @@ using System.Collections.Generic; + +using ASC.Common; using ASC.Core; -using ASC.Web.Api.Routing; +using ASC.Web.Api.Routing; using ASC.Web.Core; using ASC.Web.Core.WebZones; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.DependencyInjection; -namespace ASC.Web.Api.Controllers -{ - [DefaultRoute] - [ApiController] - public class ModulesController : ControllerBase +using Microsoft.AspNetCore.Mvc; + +namespace ASC.Web.Api.Controllers +{ + [DefaultRoute] + [ApiController] + public class ModulesController : ControllerBase { public UserManager UserManager { get; } public TenantManager TenantManager { get; } @@ -26,8 +28,8 @@ namespace ASC.Web.Api.Controllers WebItemManagerSecurity = webItemManagerSecurity; } - [Read] - public IEnumerable GetAll() + [Read] + public IEnumerable GetAll() { var result = new List(); @@ -35,19 +37,19 @@ namespace ASC.Web.Api.Controllers { result.Add(a.ApiURL); } - - return result; - } + + return result; + } } public static class ModulesControllerExtension { - public static IServiceCollection AddModulesController(this IServiceCollection services) + public static DIHelper AddModulesController(this DIHelper services) { return services .AddUserManagerService() .AddTenantManagerService() .AddWebItemManagerSecurity(); } - } -} + } +} diff --git a/web/ASC.Web.Api/Controllers/PortalController.cs b/web/ASC.Web.Api/Controllers/PortalController.cs index 58e49875bd..5ddc752d75 100644 --- a/web/ASC.Web.Api/Controllers/PortalController.cs +++ b/web/ASC.Web.Api/Controllers/PortalController.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Security; using ASC.Api.Core; +using ASC.Common; using ASC.Common.Logging; using ASC.Core; using ASC.Core.Billing; @@ -17,7 +18,6 @@ using ASC.Web.Studio.Core.Notify; using ASC.Web.Studio.Utility; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; namespace ASC.Web.Api.Controllers @@ -152,7 +152,7 @@ namespace ASC.Web.Api.Controllers public static class PortalControllerExtension { - public static IServiceCollection AddPortalController(this IServiceCollection services) + public static DIHelper AddPortalController(this DIHelper services) { return services .AddUrlShortener() diff --git a/web/ASC.Web.Api/Controllers/SettingsController.cs b/web/ASC.Web.Api/Controllers/SettingsController.cs index fb17f92553..bdefb04e78 100644 --- a/web/ASC.Web.Api/Controllers/SettingsController.cs +++ b/web/ASC.Web.Api/Controllers/SettingsController.cs @@ -36,6 +36,7 @@ using System.Web; using ASC.Api.Collections; using ASC.Api.Core; using ASC.Api.Utils; +using ASC.Common; using ASC.Common.Logging; using ASC.Common.Utils; using ASC.Core; @@ -74,7 +75,6 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; namespace ASC.Api.Settings @@ -1492,7 +1492,7 @@ namespace ASC.Api.Settings public static class SettingsControllerExtension { - public static IServiceCollection AddSettingsController(this IServiceCollection services) + public static DIHelper AddSettingsController(this DIHelper services) { return services .AddMessageTargetService() diff --git a/web/ASC.Web.Api/Controllers/SmtpSettingsController.cs b/web/ASC.Web.Api/Controllers/SmtpSettingsController.cs index 25be85c473..9b6fce958d 100644 --- a/web/ASC.Web.Api/Controllers/SmtpSettingsController.cs +++ b/web/ASC.Web.Api/Controllers/SmtpSettingsController.cs @@ -24,74 +24,76 @@ */ -using System; -using ASC.Api.Core; +using System; + +using ASC.Api.Core; using ASC.Api.Settings.Smtp; +using ASC.Common; using ASC.Core; using ASC.Core.Billing; using ASC.Core.Configuration; -using ASC.Core.Tenants; -using ASC.MessagingSystem; -using ASC.Web.Api.Routing; -using ASC.Web.Core.PublicResources; +using ASC.Core.Tenants; +using ASC.MessagingSystem; +using ASC.Web.Api.Routing; +using ASC.Web.Core.PublicResources; using ASC.Web.Studio.Core; -using ASC.Web.Studio.Core.Notify; -using ASC.Web.Studio.Utility; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; +using ASC.Web.Studio.Core.Notify; +using ASC.Web.Studio.Utility; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; + namespace ASC.Api.Settings { - [DefaultRoute] - [ApiController] + [DefaultRoute] + [ApiController] public class SmtpSettingsController : ControllerBase - { - //private static DistributedTaskQueue SMTPTasks { get; } = new DistributedTaskQueue("smtpOperations"); - - public Tenant Tenant { get { return ApiContext.Tenant; } } - - public ApiContext ApiContext { get; } - public UserManager UserManager { get; } - public SecurityContext SecurityContext { get; } - public PermissionContext PermissionContext { get; } - public TenantManager TenantManager { get; } - public CoreSettings CoreSettings { get; } - public CoreConfiguration CoreConfiguration { get; } - public CoreBaseSettings CoreBaseSettings { get; } - public IConfiguration Configuration { get; } - public MessageService MessageService { get; } - public StudioNotifyService StudioNotifyService { get; } - public IWebHostEnvironment WebHostEnvironment { get; } - - - public SmtpSettingsController( - MessageService messageService, - StudioNotifyService studioNotifyService, - ApiContext apiContext, - UserManager userManager, - SecurityContext securityContext, - PermissionContext permissionContext, - TenantManager tenantManager, - CoreSettings coreSettings, - CoreConfiguration coreConfiguration, - CoreBaseSettings coreBaseSettings, - IConfiguration configuration) - { - MessageService = messageService; - StudioNotifyService = studioNotifyService; - ApiContext = apiContext; - UserManager = userManager; - SecurityContext = securityContext; - PermissionContext = permissionContext; - TenantManager = tenantManager; - CoreSettings = coreSettings; - CoreConfiguration = coreConfiguration; - CoreBaseSettings = coreBaseSettings; - Configuration = configuration; - } - + { + //private static DistributedTaskQueue SMTPTasks { get; } = new DistributedTaskQueue("smtpOperations"); + + public Tenant Tenant { get { return ApiContext.Tenant; } } + + public ApiContext ApiContext { get; } + public UserManager UserManager { get; } + public SecurityContext SecurityContext { get; } + public PermissionContext PermissionContext { get; } + public TenantManager TenantManager { get; } + public CoreSettings CoreSettings { get; } + public CoreConfiguration CoreConfiguration { get; } + public CoreBaseSettings CoreBaseSettings { get; } + public IConfiguration Configuration { get; } + public MessageService MessageService { get; } + public StudioNotifyService StudioNotifyService { get; } + public IWebHostEnvironment WebHostEnvironment { get; } + + + public SmtpSettingsController( + MessageService messageService, + StudioNotifyService studioNotifyService, + ApiContext apiContext, + UserManager userManager, + SecurityContext securityContext, + PermissionContext permissionContext, + TenantManager tenantManager, + CoreSettings coreSettings, + CoreConfiguration coreConfiguration, + CoreBaseSettings coreBaseSettings, + IConfiguration configuration) + { + MessageService = messageService; + StudioNotifyService = studioNotifyService; + ApiContext = apiContext; + UserManager = userManager; + SecurityContext = securityContext; + PermissionContext = permissionContext; + TenantManager = tenantManager; + CoreSettings = coreSettings; + CoreConfiguration = coreConfiguration; + CoreBaseSettings = coreBaseSettings; + Configuration = configuration; + } + [Read("smtp")] public SmtpSettingsWrapper GetSmtpSettings() @@ -135,8 +137,8 @@ namespace ASC.Api.Settings CoreConfiguration.SmtpSettings = null; } - var current = CoreBaseSettings.Standalone ? CoreConfiguration.SmtpSettings : SmtpSettings.Empty; - + var current = CoreBaseSettings.Standalone ? CoreConfiguration.SmtpSettings : SmtpSettings.Empty; + return ToSmtpSettings(current, true); } @@ -146,7 +148,7 @@ namespace ASC.Api.Settings // CheckSmtpPermissions(); // var settings = ToSmtpSettings(CoreConfiguration.SmtpSettings); - + // //add resolve // var smtpTestOp = new SmtpOperation(settings, Tenant.TenantId, SecurityContext.CurrentAccount.ID, UserManager, SecurityContext, TenantManager, Configuration); @@ -247,24 +249,24 @@ namespace ASC.Api.Settings throw new BillingException(Resource.ErrorNotAllowedOption, "Smtp"); } } - } - - public static class SmtpSettingsControllerExtension - { - public static IServiceCollection AddSmtpSettingsController(this IServiceCollection services) - { - return services - .AddMessageServiceService() - .AddStudioNotifyServiceService() - .AddApiContextService() - .AddUserManagerService() - .AddSecurityContextService() - .AddPermissionContextService() - .AddTenantManagerService() - .AddCoreSettingsService() - .AddCoreConfigurationService() - .AddCoreBaseSettingsService() - ; - } - } + } + + public static class SmtpSettingsControllerExtension + { + public static DIHelper AddSmtpSettingsController(this DIHelper services) + { + return services + .AddMessageServiceService() + .AddStudioNotifyServiceService() + .AddApiContextService() + .AddUserManagerService() + .AddSecurityContextService() + .AddPermissionContextService() + .AddTenantManagerService() + .AddCoreSettingsService() + .AddCoreConfigurationService() + .AddCoreBaseSettingsService() + ; + } + } } diff --git a/web/ASC.Web.Api/Models/BuildVersion.cs b/web/ASC.Web.Api/Models/BuildVersion.cs index 4b6c48de92..2b9bf76d7b 100644 --- a/web/ASC.Web.Api/Models/BuildVersion.cs +++ b/web/ASC.Web.Api/Models/BuildVersion.cs @@ -24,11 +24,12 @@ */ -using System.Runtime.Serialization; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; +using System.Runtime.Serialization; +using ASC.Common; + +using Microsoft.Extensions.Configuration; + namespace ASC.Api.Settings { [DataContract(Name = "buildversion", Namespace = "")] @@ -41,19 +42,19 @@ namespace ASC.Api.Settings public string DocumentServer { get; set; } [DataMember(EmitDefaultValue = false)] - public string MailServer { get; set; } - public IConfiguration Configuration { get; } - - public BuildVersion(IConfiguration configuration) - { - Configuration = configuration; - } + public string MailServer { get; set; } + public IConfiguration Configuration { get; } + + public BuildVersion(IConfiguration configuration) + { + Configuration = configuration; + } public BuildVersion GetCurrentBuildVersion() - { - CommunityServer = GetCommunityVersion(); - DocumentServer = GetDocumentVersion(); - MailServer = GetMailServerVersion(); + { + CommunityServer = GetCommunityVersion(); + DocumentServer = GetDocumentVersion(); + MailServer = GetMailServerVersion(); return this; } @@ -63,9 +64,9 @@ namespace ASC.Api.Settings } private static string GetDocumentVersion() - { - return ""; - //TODO + { + return ""; + //TODO /* if (string.IsNullOrEmpty(FilesLinkUtility.DocServiceApiUrl)) return null; @@ -74,12 +75,12 @@ namespace ASC.Api.Settings } private static string GetMailServerVersion() - { - //TODO - return ""; - /* + { + //TODO + return ""; + /* try - { + { var engineFactory = new EngineFactory( CoreContext.TenantManager.GetCurrentTenant().TenantId, @@ -95,14 +96,14 @@ namespace ASC.Api.Settings return null;*/ } - } - + } + public static class BuildVersionExtension { - public static IServiceCollection AddBuildVersionService(this IServiceCollection services) - { - services.TryAddSingleton(); - + public static DIHelper AddBuildVersionService(this DIHelper services) + { + services.TryAddSingleton(); + return services; } } diff --git a/web/ASC.Web.Api/Startup.cs b/web/ASC.Web.Api/Startup.cs index 839c992d62..95b6306f6c 100644 --- a/web/ASC.Web.Api/Startup.cs +++ b/web/ASC.Web.Api/Startup.cs @@ -2,6 +2,7 @@ using ASC.Api.Core.Auth; using ASC.Api.Core.Core; using ASC.Api.Core.Middleware; using ASC.Api.Settings; +using ASC.Common; using ASC.Common.DependencyInjection; using ASC.Common.Logging; using ASC.Web.Api.Controllers; @@ -62,7 +63,9 @@ namespace ASC.Web.Api config.OutputFormatters.Add(new XmlOutputFormatter()); }); - services + var diHelper = new DIHelper(services); + + diHelper .AddConfirmAuthHandler() .AddCookieAuthHandler() .AddCultureMiddleware() @@ -71,9 +74,9 @@ namespace ASC.Web.Api .AddProductSecurityFilter() .AddTenantStatusFilter(); - services.AddNLogManager("ASC.Api", "ASC.Web"); + diHelper.AddNLogManager("ASC.Api", "ASC.Web"); - services + diHelper .AddAuthenticationController() .AddModulesController() .AddPortalController() diff --git a/web/ASC.Web.Common/src/api/modules/index.js b/web/ASC.Web.Common/src/api/modules/index.js index cec9b6771e..29f9e12a84 100644 --- a/web/ASC.Web.Common/src/api/modules/index.js +++ b/web/ASC.Web.Common/src/api/modules/index.js @@ -28,17 +28,7 @@ export function getModulesList() { iconUrl: m.link + "images/icon.svg", imageUrl: m.link + m.imageUrl, }; - }) - .concat({ - id: "77777777-7777-7777-7777-777777777777", - title: "Documents", - link: "/coming-soon/", - imageUrl: "/images/documents.svg", - iconUrl: "/images/documentsIcon.svg", - description: "Create, edit and share documents. Collaborate on them in real-time. 100% compatibility with MS Office formats guaranteed.", - isPrimary: true, - iconName: "DocumentsIcon" - }); + }); return newModules; }) diff --git a/web/ASC.Web.Core/CookiesManager.cs b/web/ASC.Web.Core/CookiesManager.cs index 979a47e3f6..5feb0d9961 100644 --- a/web/ASC.Web.Core/CookiesManager.cs +++ b/web/ASC.Web.Core/CookiesManager.cs @@ -29,13 +29,13 @@ using System.Linq; using System.Security; using System.Web; +using ASC.Common; using ASC.Core; using ASC.Core.Tenants; using ASC.Core.Users; using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; + using SecurityContext = ASC.Core.SecurityContext; @@ -238,7 +238,7 @@ namespace ASC.Web.Core public static class CookiesManagerExtension { - public static IServiceCollection AddCookiesManagerService(this IServiceCollection services) + public static DIHelper AddCookiesManagerService(this DIHelper services) { services.TryAddScoped(); diff --git a/web/ASC.Web.Core/FileSizeComment.cs b/web/ASC.Web.Core/FileSizeComment.cs index 5acf232e67..123a764d33 100644 --- a/web/ASC.Web.Core/FileSizeComment.cs +++ b/web/ASC.Web.Core/FileSizeComment.cs @@ -25,11 +25,11 @@ using System; + +using ASC.Common; using ASC.Core.Tenants; using ASC.Web.Core.PublicResources; using ASC.Web.Studio.Utility; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; namespace ASC.Web.Studio.Core { @@ -162,7 +162,7 @@ namespace ASC.Web.Studio.Core public static class FileSizeCommentExtension { - public static IServiceCollection AddFileSizeCommentService(this IServiceCollection services) + public static DIHelper AddFileSizeCommentService(this DIHelper services) { services.TryAddScoped(); diff --git a/web/ASC.Web.Core/Files/FileUtility.cs b/web/ASC.Web.Core/Files/FileUtility.cs index bbee9e5408..e966a5b54f 100644 --- a/web/ASC.Web.Core/Files/FileUtility.cs +++ b/web/ASC.Web.Core/Files/FileUtility.cs @@ -30,6 +30,7 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; +using ASC.Common; using ASC.Core.Common.EF; using ASC.Core.Common.EF.Context; @@ -40,7 +41,10 @@ namespace ASC.Web.Core.Files public class FileUtility { public DbContextManager FilesDbContext { get; set; } - public FileUtility(IConfiguration configuration, FilesLinkUtility filesLinkUtility, DbContextManager dbContextManager) + public FileUtility( + IConfiguration configuration, + FilesLinkUtility filesLinkUtility, + DbContextManager dbContextManager) { Configuration = configuration; FilesLinkUtility = filesLinkUtility; @@ -374,4 +378,15 @@ namespace ASC.Web.Core.Files #endregion } + + public static class FileUtilityExtention + { + public static DIHelper AddFileUtilityService(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddFilesLinkUtilityService() + .AddFilesDbContextService(); + } + } } \ No newline at end of file diff --git a/web/ASC.Web.Core/Files/FilesLinkUtility.cs b/web/ASC.Web.Core/Files/FilesLinkUtility.cs index 4b07c704cc..dc3befd7f4 100644 --- a/web/ASC.Web.Core/Files/FilesLinkUtility.cs +++ b/web/ASC.Web.Core/Files/FilesLinkUtility.cs @@ -29,7 +29,9 @@ using System.Text.RegularExpressions; using System.Threading; using System.Web; +using ASC.Common; using ASC.Core; +using ASC.Core.Common; using ASC.Security.Cryptography; using ASC.Web.Studio.Utility; @@ -43,14 +45,22 @@ namespace ASC.Web.Core.Files public const string EditorPage = "doceditor.aspx"; private readonly string FilesUploaderURL; public CommonLinkUtility CommonLinkUtility { get; set; } + public BaseCommonLinkUtility BaseCommonLinkUtility { get; } public CoreBaseSettings CoreBaseSettings { get; set; } public CoreSettings CoreSettings { get; set; } public IConfiguration Configuration { get; } public InstanceCrypto InstanceCrypto { get; } - public FilesLinkUtility(CommonLinkUtility commonLinkUtility, CoreBaseSettings coreBaseSettings, CoreSettings coreSettings, IConfiguration configuration, InstanceCrypto instanceCrypto) + public FilesLinkUtility( + CommonLinkUtility commonLinkUtility, + BaseCommonLinkUtility baseCommonLinkUtility, + CoreBaseSettings coreBaseSettings, + CoreSettings coreSettings, + IConfiguration configuration, + InstanceCrypto instanceCrypto) { CommonLinkUtility = commonLinkUtility; + BaseCommonLinkUtility = baseCommonLinkUtility; CoreBaseSettings = coreBaseSettings; CoreSettings = coreSettings; Configuration = configuration; @@ -60,7 +70,7 @@ namespace ASC.Web.Core.Files public string FilesBaseAbsolutePath { - get { return CommonLinkUtility.ToAbsolute(FilesBaseVirtualPath); } + get { return BaseCommonLinkUtility.ToAbsolute(FilesBaseVirtualPath); } } public const string FileId = "fileid"; @@ -74,6 +84,7 @@ namespace ASC.Web.Core.Files public const string FolderUrl = "folderurl"; public const string OutType = "outputtype"; public const string AuthKey = "stream_auth"; + public const string Anchor = "anchor"; public string FileHandlerPath { @@ -420,4 +431,18 @@ namespace ASC.Web.Core.Files return "DocKey_" + key; } } + public static class FilesLinkUtilityExtention + { + public static DIHelper AddFilesLinkUtilityService(this DIHelper services) + { + services.TryAddScoped(); + + return services + .AddCommonLinkUtilityService() + .AddBaseCommonLinkUtilityService() + .AddCoreBaseSettingsService() + .AddCoreSettingsService() + .AddInstanceCryptoService(); + } + } } \ No newline at end of file diff --git a/web/ASC.Web.Core/Helpers/ApiSystemHelper.cs b/web/ASC.Web.Core/Helpers/ApiSystemHelper.cs index c509efd4e8..f797b606f9 100644 --- a/web/ASC.Web.Core/Helpers/ApiSystemHelper.cs +++ b/web/ASC.Web.Core/Helpers/ApiSystemHelper.cs @@ -31,13 +31,15 @@ using System.Linq; using System.Net; using System.Security.Cryptography; using System.Text; -using System.Web; +using System.Web; + +using ASC.Common; using ASC.Core.Tenants; -using ASC.Web.Studio.Utility; -using Microsoft.AspNetCore.WebUtilities; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; +using ASC.Web.Studio.Utility; + +using Microsoft.AspNetCore.WebUtilities; +using Microsoft.Extensions.Configuration; + using Newtonsoft.Json.Linq; namespace ASC.Web.Core.Helpers @@ -48,23 +50,23 @@ namespace ASC.Web.Core.Helpers public string ApiCacheUrl { get; private set; } - private string Skey { get; set; } - public CommonLinkUtility CommonLinkUtility { get; } - + private string Skey { get; set; } + public CommonLinkUtility CommonLinkUtility { get; } + public ApiSystemHelper(IConfiguration configuration, CommonLinkUtility commonLinkUtility) { ApiSystemUrl = configuration["web:api-system"]; ApiCacheUrl = configuration["web:api-cache"]; - Skey = configuration["core:machinekey"]; - CommonLinkUtility = commonLinkUtility; + Skey = configuration["core:machinekey"]; + CommonLinkUtility = commonLinkUtility; } public string CreateAuthToken(string pkey) { - using var hasher = new HMACSHA1(Encoding.UTF8.GetBytes(Skey)); - var now = DateTime.UtcNow.ToString("yyyyMMddHHmmss"); - var hash = WebEncoders.Base64UrlEncode(hasher.ComputeHash(Encoding.UTF8.GetBytes(string.Join("\n", now, pkey)))); + using var hasher = new HMACSHA1(Encoding.UTF8.GetBytes(Skey)); + var now = DateTime.UtcNow.ToString("yyyyMMddHHmmss"); + var hash = WebEncoders.Base64UrlEncode(hasher.ComputeHash(Encoding.UTF8.GetBytes(string.Join("\n", now, pkey)))); return string.Format("ASC {0}:{1}:{2}", pkey, now, hash); } @@ -84,20 +86,20 @@ namespace ASC.Web.Core.Helpers var response = exception.Response; try { - using var stream = response.GetResponseStream(); - using var reader = new StreamReader(stream, Encoding.UTF8); - var result = reader.ReadToEnd(); - - var resObj = JObject.Parse(result); - if (resObj["error"] != null) - { - if (resObj["error"].ToString() == "portalNameExist") - { - var varians = resObj.Value("variants").Select(jv => jv.Value()); - throw new TenantAlreadyExistsException("Address busy.", varians); - } - - throw new Exception(resObj["error"].ToString()); + using var stream = response.GetResponseStream(); + using var reader = new StreamReader(stream, Encoding.UTF8); + var result = reader.ReadToEnd(); + + var resObj = JObject.Parse(result); + if (resObj["error"] != null) + { + if (resObj["error"].ToString() == "portalNameExist") + { + var varians = resObj.Value("variants").Select(jv => jv.Value()); + throw new TenantAlreadyExistsException("Address busy.", varians); + } + + throw new Exception(resObj["error"].ToString()); } } finally @@ -157,23 +159,23 @@ namespace ASC.Web.Core.Helpers { webRequest.ContentLength = data.Length; - using var writer = new StreamWriter(webRequest.GetRequestStream()); + using var writer = new StreamWriter(webRequest.GetRequestStream()); writer.Write(data); } - using var response = webRequest.GetResponse(); - using var stream = response.GetResponseStream(); - using var reader = new StreamReader(stream, Encoding.UTF8); + using var response = webRequest.GetResponse(); + using var stream = response.GetResponseStream(); + using var reader = new StreamReader(stream, Encoding.UTF8); return reader.ReadToEnd(); } - } - - public static class ApiSystemHelperExtension - { - public static IServiceCollection AddApiSystemHelper(this IServiceCollection services) - { - services.TryAddScoped(); - return services; - } - } + } + + public static class ApiSystemHelperExtension + { + public static DIHelper AddApiSystemHelper(this DIHelper services) + { + services.TryAddScoped(); + return services; + } + } } \ No newline at end of file diff --git a/web/ASC.Web.Core/LoginProfileExtension.cs b/web/ASC.Web.Core/LoginProfileExtension.cs new file mode 100644 index 0000000000..fa0d7ee4cc --- /dev/null +++ b/web/ASC.Web.Core/LoginProfileExtension.cs @@ -0,0 +1,40 @@ +using System; +using System.Threading; + +using ASC.Core; +using ASC.Core.Users; +using ASC.FederatedLogin.Profile; +using ASC.Web.Core.PublicResources; + +namespace ASC.Web.Core +{ + public static class LoginProfileExtension + { + public static UserInfo ProfileToUserInfo(this LoginProfile loginProfile, CoreBaseSettings coreBaseSettings) + { + if (string.IsNullOrEmpty(loginProfile.EMail)) throw new Exception(Resource.ErrorNotCorrectEmail); + + var firstName = loginProfile.FirstName; + if (string.IsNullOrEmpty(firstName)) firstName = loginProfile.DisplayName; + + var userInfo = new UserInfo + { + FirstName = string.IsNullOrEmpty(firstName) ? UserControlsCommonResource.UnknownFirstName : firstName, + LastName = string.IsNullOrEmpty(loginProfile.LastName) ? UserControlsCommonResource.UnknownLastName : loginProfile.LastName, + Email = loginProfile.EMail, + Title = string.Empty, + Location = string.Empty, + CultureName = coreBaseSettings.CustomMode ? "ru-RU" : Thread.CurrentThread.CurrentUICulture.Name, + ActivationStatus = EmployeeActivationStatus.Activated, + }; + + var gender = loginProfile.Gender; + if (!string.IsNullOrEmpty(gender)) + { + userInfo.Sex = gender == "male"; + } + + return userInfo; + } + } +} diff --git a/web/ASC.Web.Core/LogoUploader.cs b/web/ASC.Web.Core/LogoUploader.cs index db0abe13e3..7fa34c0dc6 100644 --- a/web/ASC.Web.Core/LogoUploader.cs +++ b/web/ASC.Web.Core/LogoUploader.cs @@ -27,13 +27,13 @@ using System; using System.Globalization; using System.IO; - + +using ASC.Common; using ASC.Common.Logging; using ASC.Core; using ASC.Data.Storage; -using ASC.Web.Core.Users; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; +using ASC.Web.Core.Users; + using Microsoft.Extensions.Options; namespace ASC.Web.Studio.UserControls.CustomNavigation @@ -188,7 +188,7 @@ namespace ASC.Web.Studio.UserControls.CustomNavigation public static class StorageHelperExtension { - public static IServiceCollection AddStorageHelperService(this IServiceCollection services) + public static DIHelper AddStorageHelperService(this DIHelper services) { services.TryAddScoped(); return services diff --git a/web/ASC.Web.Core/Notify/NotifyConfiguration.cs b/web/ASC.Web.Core/Notify/NotifyConfiguration.cs index fc0b141519..dd2bb7ac2c 100644 --- a/web/ASC.Web.Core/Notify/NotifyConfiguration.cs +++ b/web/ASC.Web.Core/Notify/NotifyConfiguration.cs @@ -31,6 +31,7 @@ using System.Linq; using System.Text.RegularExpressions; using System.Threading; +using ASC.Common; using ASC.Common.Logging; using ASC.Common.Notify.Engine; using ASC.Core; @@ -371,7 +372,7 @@ namespace ASC.Web.Studio.Core.Notify public static class NotifyConfigurationExtension { - public static IServiceCollection AddNotifyConfiguration(this IServiceCollection services) + public static DIHelper AddNotifyConfiguration(this DIHelper services) { return services .AddJabberStylerService() diff --git a/web/ASC.Web.Core/Notify/StudioNotifyHelper.cs b/web/ASC.Web.Core/Notify/StudioNotifyHelper.cs index 5d99ffdd90..2d5b0aefdf 100644 --- a/web/ASC.Web.Core/Notify/StudioNotifyHelper.cs +++ b/web/ASC.Web.Core/Notify/StudioNotifyHelper.cs @@ -26,23 +26,22 @@ using System; using System.Collections.Generic; -using System.Linq; +using System.Linq; -using ASC.Common.Logging; +using ASC.Common; +using ASC.Common.Logging; using ASC.Core; -using ASC.Core.Common.Settings; +using ASC.Core.Common.Settings; using ASC.Core.Users; using ASC.Notify.Model; -using ASC.Notify.Recipients; -using ASC.Web.Core.Utility.Skins; -using ASC.Web.Core.WhiteLabel; +using ASC.Notify.Recipients; +using ASC.Web.Core.Utility.Skins; +using ASC.Web.Core.WhiteLabel; using ASC.Web.Studio.Utility; - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Options; - + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Options; + namespace ASC.Web.Studio.Core.Notify { public class StudioNotifyHelper @@ -53,51 +52,51 @@ namespace ASC.Web.Studio.Core.Notify public readonly ISubscriptionProvider SubscriptionProvider; - public readonly IRecipientProvider RecipientsProvider; - - private readonly int CountMailsToNotActivated; - - private readonly string NotificationImagePath; + public readonly IRecipientProvider RecipientsProvider; - private UserManager UserManager { get; } - public SettingsManager SettingsManager { get; } - public CommonLinkUtility CommonLinkUtility { get; } - private SetupInfo SetupInfo { get; } - private TenantManager TenantManager { get; } - public TenantExtra TenantExtra { get; } - public CoreBaseSettings CoreBaseSettings { get; } - public WebImageSupplier WebImageSupplier { get; } - private ILog Log { get; } - - public StudioNotifyHelper( - StudioNotifySource studioNotifySource, - UserManager userManager, - SettingsManager settingsManager, - AdditionalWhiteLabelSettingsHelper additionalWhiteLabelSettingsHelper, - CommonLinkUtility commonLinkUtility, - SetupInfo setupInfo, - TenantManager tenantManager, - TenantExtra tenantExtra, - CoreBaseSettings coreBaseSettings, - WebImageSupplier webImageSupplier, - IConfiguration configuration, + private readonly int CountMailsToNotActivated; + + private readonly string NotificationImagePath; + + private UserManager UserManager { get; } + public SettingsManager SettingsManager { get; } + public CommonLinkUtility CommonLinkUtility { get; } + private SetupInfo SetupInfo { get; } + private TenantManager TenantManager { get; } + public TenantExtra TenantExtra { get; } + public CoreBaseSettings CoreBaseSettings { get; } + public WebImageSupplier WebImageSupplier { get; } + private ILog Log { get; } + + public StudioNotifyHelper( + StudioNotifySource studioNotifySource, + UserManager userManager, + SettingsManager settingsManager, + AdditionalWhiteLabelSettingsHelper additionalWhiteLabelSettingsHelper, + CommonLinkUtility commonLinkUtility, + SetupInfo setupInfo, + TenantManager tenantManager, + TenantExtra tenantExtra, + CoreBaseSettings coreBaseSettings, + WebImageSupplier webImageSupplier, + IConfiguration configuration, IOptionsMonitor option) - { - Helplink = commonLinkUtility.GetHelpLink(settingsManager, additionalWhiteLabelSettingsHelper, false); - NotifySource = studioNotifySource; - UserManager = userManager; - SettingsManager = settingsManager; - CommonLinkUtility = commonLinkUtility; - SetupInfo = setupInfo; - TenantManager = tenantManager; - TenantExtra = tenantExtra; - CoreBaseSettings = coreBaseSettings; - WebImageSupplier = webImageSupplier; - SubscriptionProvider = NotifySource.GetSubscriptionProvider(); - RecipientsProvider = NotifySource.GetRecipientsProvider(); - Log = option.CurrentValue; - - int.TryParse(configuration["core:notify:countspam"], out CountMailsToNotActivated); + { + Helplink = commonLinkUtility.GetHelpLink(settingsManager, additionalWhiteLabelSettingsHelper, false); + NotifySource = studioNotifySource; + UserManager = userManager; + SettingsManager = settingsManager; + CommonLinkUtility = commonLinkUtility; + SetupInfo = setupInfo; + TenantManager = tenantManager; + TenantExtra = tenantExtra; + CoreBaseSettings = coreBaseSettings; + WebImageSupplier = webImageSupplier; + SubscriptionProvider = NotifySource.GetSubscriptionProvider(); + RecipientsProvider = NotifySource.GetRecipientsProvider(); + Log = option.CurrentValue; + + int.TryParse(configuration["core:notify:countspam"], out CountMailsToNotActivated); NotificationImagePath = configuration["web:notification:image:path"]; } @@ -141,51 +140,51 @@ namespace ASC.Web.Studio.Core.Notify public IRecipient ToRecipient(Guid userId) { return RecipientsProvider.GetRecipient(userId.ToString()); - } - - public IRecipient[] RecipientFromEmail(string email, bool checkActivation) - { - return RecipientFromEmail(new List { email }, checkActivation); - } - - public IRecipient[] RecipientFromEmail(List emails, bool checkActivation) - { - var res = new List(); - - if (emails == null) return res.ToArray(); - - res.AddRange(emails. - Select(email => email.ToLower()). - Select(e => new DirectRecipient(e, null, new[] { e }, checkActivation))); - - if (!checkActivation - && CountMailsToNotActivated > 0 - && TenantExtra.Saas && !CoreBaseSettings.Personal) - { - var tenant = TenantManager.GetCurrentTenant(); - var tariff = TenantManager.GetTenantQuota(tenant.TenantId); - if (tariff.Free || tariff.Trial) - { - var spamEmailSettings = SettingsManager.Load(); - var sended = spamEmailSettings.MailsSended; - - var mayTake = Math.Max(0, CountMailsToNotActivated - sended); - var tryCount = res.Count(); - if (mayTake < tryCount) - { - res = res.Take(mayTake).ToList(); - - Log.Warn(string.Format("Free tenant {0} for today is trying to send {1} more letters without checking activation. Sent {2}", tenant.TenantId, tryCount, mayTake)); - } - spamEmailSettings.MailsSended = sended + tryCount; - SettingsManager.Save(spamEmailSettings); - } - } - - return res.ToArray(); - } - - + } + + public IRecipient[] RecipientFromEmail(string email, bool checkActivation) + { + return RecipientFromEmail(new List { email }, checkActivation); + } + + public IRecipient[] RecipientFromEmail(List emails, bool checkActivation) + { + var res = new List(); + + if (emails == null) return res.ToArray(); + + res.AddRange(emails. + Select(email => email.ToLower()). + Select(e => new DirectRecipient(e, null, new[] { e }, checkActivation))); + + if (!checkActivation + && CountMailsToNotActivated > 0 + && TenantExtra.Saas && !CoreBaseSettings.Personal) + { + var tenant = TenantManager.GetCurrentTenant(); + var tariff = TenantManager.GetTenantQuota(tenant.TenantId); + if (tariff.Free || tariff.Trial) + { + var spamEmailSettings = SettingsManager.Load(); + var sended = spamEmailSettings.MailsSended; + + var mayTake = Math.Max(0, CountMailsToNotActivated - sended); + var tryCount = res.Count(); + if (mayTake < tryCount) + { + res = res.Take(mayTake).ToList(); + + Log.Warn(string.Format("Free tenant {0} for today is trying to send {1} more letters without checking activation. Sent {2}", tenant.TenantId, tryCount, mayTake)); + } + spamEmailSettings.MailsSended = sended + tryCount; + SettingsManager.Save(spamEmailSettings); + } + } + + return res.ToArray(); + } + + public string GetNotifyAnalytics(INotifyAction action, bool toowner, bool toadmins, bool tousers, bool toguests) { @@ -204,21 +203,21 @@ namespace ASC.Web.Studio.Core.Notify TenantManager.GetCurrentTenant().TenantId, target, action.ID)); - } - - public string GetNotificationImageUrl(string imageFileName) - { - if (string.IsNullOrEmpty(NotificationImagePath)) - { - return - CommonLinkUtility.GetFullAbsolutePath( - WebImageSupplier.GetAbsoluteWebPath("notification/" + imageFileName)); - } - - return NotificationImagePath.TrimEnd('/') + "/" + imageFileName; - } - - + } + + public string GetNotificationImageUrl(string imageFileName) + { + if (string.IsNullOrEmpty(NotificationImagePath)) + { + return + CommonLinkUtility.GetFullAbsolutePath( + WebImageSupplier.GetAbsoluteWebPath("notification/" + imageFileName)); + } + + return NotificationImagePath.TrimEnd('/') + "/" + imageFileName; + } + + public bool IsSubscribedToNotify(Guid userId, INotifyAction notifyAction) { return IsSubscribedToNotify(ToRecipient(userId), notifyAction); @@ -251,7 +250,7 @@ namespace ASC.Web.Studio.Core.Notify public static class StudioNotifyHelperExtension { - public static IServiceCollection AddStudioNotifyHelperService(this IServiceCollection services) + public static DIHelper AddStudioNotifyHelperService(this DIHelper services) { services.TryAddScoped(); @@ -261,9 +260,9 @@ namespace ASC.Web.Studio.Core.Notify .AddAdditionalWhiteLabelSettingsService() .AddCommonLinkUtilityService() .AddTenantManagerService() - .AddSetupInfo() - .AddTenantExtraService() - .AddCoreBaseSettingsService() + .AddSetupInfo() + .AddTenantExtraService() + .AddCoreBaseSettingsService() .AddWebImageSupplierService(); } } diff --git a/web/ASC.Web.Core/Notify/StudioNotifyService.cs b/web/ASC.Web.Core/Notify/StudioNotifyService.cs index a44b41f323..78cc8fa97a 100644 --- a/web/ASC.Web.Core/Notify/StudioNotifyService.cs +++ b/web/ASC.Web.Core/Notify/StudioNotifyService.cs @@ -31,6 +31,7 @@ using System.Linq; using System.Threading; using System.Web; +using ASC.Common; using ASC.Common.Logging; using ASC.Core; using ASC.Core.Common.Settings; @@ -49,7 +50,6 @@ using ASC.Web.Studio.Utility; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; namespace ASC.Web.Studio.Core.Notify @@ -962,7 +962,7 @@ namespace ASC.Web.Studio.Core.Notify public static class StudioNotifyServiceExtension { - public static IServiceCollection AddStudioNotifyServiceService(this IServiceCollection services) + public static DIHelper AddStudioNotifyServiceService(this DIHelper services) { services.TryAddScoped(); diff --git a/web/ASC.Web.Core/Notify/StudioNotifyServiceHelper.cs b/web/ASC.Web.Core/Notify/StudioNotifyServiceHelper.cs index 45c1a985ad..ed89b82469 100644 --- a/web/ASC.Web.Core/Notify/StudioNotifyServiceHelper.cs +++ b/web/ASC.Web.Core/Notify/StudioNotifyServiceHelper.cs @@ -1,13 +1,12 @@ using System.Linq; +using ASC.Common; using ASC.Common.Caching; using ASC.Core; using ASC.Notify.Model; using ASC.Notify.Patterns; using ASC.Notify.Recipients; using ASC.Web.Studio.Core.Notify; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; namespace ASC.Web.Core.Notify { @@ -30,38 +29,38 @@ namespace ASC.Web.Core.Notify { SendNoticeToAsync(action, null, recipients, senderNames, false, args); } - - public void SendNoticeToAsync(INotifyAction action, string objectID, IRecipient[] recipients, string[] senderNames, params ITagValue[] args) - { - SendNoticeToAsync(action, objectID, recipients, senderNames, false, args); - } - - public void SendNoticeToAsync(INotifyAction action, string objectID, IRecipient[] recipients, params ITagValue[] args) - { - SendNoticeToAsync(action, objectID, recipients, null, false, args); - } - - public void SendNoticeToAsync(INotifyAction action, string objectID, IRecipient[] recipients, bool checkSubscription, params ITagValue[] args) - { - SendNoticeToAsync(action, objectID, recipients, null, checkSubscription, args); - } - - public void SendNoticeAsync(INotifyAction action, string objectID, IRecipient recipient, params ITagValue[] args) - { - SendNoticeToAsync(action, objectID, new[] { recipient }, null, false, args); - } - - public void SendNoticeAsync(INotifyAction action, string objectID, params ITagValue[] args) + + public void SendNoticeToAsync(INotifyAction action, string objectID, IRecipient[] recipients, string[] senderNames, params ITagValue[] args) + { + SendNoticeToAsync(action, objectID, recipients, senderNames, false, args); + } + + public void SendNoticeToAsync(INotifyAction action, string objectID, IRecipient[] recipients, params ITagValue[] args) + { + SendNoticeToAsync(action, objectID, recipients, null, false, args); + } + + public void SendNoticeToAsync(INotifyAction action, string objectID, IRecipient[] recipients, bool checkSubscription, params ITagValue[] args) + { + SendNoticeToAsync(action, objectID, recipients, null, checkSubscription, args); + } + + public void SendNoticeAsync(INotifyAction action, string objectID, IRecipient recipient, params ITagValue[] args) + { + SendNoticeToAsync(action, objectID, new[] { recipient }, null, false, args); + } + + public void SendNoticeAsync(INotifyAction action, string objectID, params ITagValue[] args) { var subscriptionSource = StudioNotifyHelper.NotifySource.GetSubscriptionProvider(); var recipients = subscriptionSource.GetRecipients(action, objectID); - SendNoticeToAsync(action, objectID, recipients, null, false, args); - } - - public void SendNoticeAsync(INotifyAction action, string objectID, IRecipient recipient, bool checkSubscription, params ITagValue[] args) - { - SendNoticeToAsync(action, objectID, new[] { recipient }, null, checkSubscription, args); + SendNoticeToAsync(action, objectID, recipients, null, false, args); + } + + public void SendNoticeAsync(INotifyAction action, string objectID, IRecipient recipient, bool checkSubscription, params ITagValue[] args) + { + SendNoticeToAsync(action, objectID, new[] { recipient }, null, checkSubscription, args); } public void SendNoticeToAsync(INotifyAction action, string objectID, IRecipient[] recipients, string[] senderNames, bool checkSubsciption, params ITagValue[] args) @@ -112,7 +111,7 @@ namespace ASC.Web.Core.Notify public static class StudioNotifyServiceHelperExtension { - public static IServiceCollection AddStudioNotifyServiceHelper(this IServiceCollection services) + public static DIHelper AddStudioNotifyServiceHelper(this DIHelper services) { services.TryAddScoped(); services.TryAddSingleton(typeof(ICacheNotify<>), typeof(KafkaCache<>)); diff --git a/web/ASC.Web.Core/Notify/StudioNotifyServiceSender.cs b/web/ASC.Web.Core/Notify/StudioNotifyServiceSender.cs index aaaf75f900..96dda2d4e2 100644 --- a/web/ASC.Web.Core/Notify/StudioNotifyServiceSender.cs +++ b/web/ASC.Web.Core/Notify/StudioNotifyServiceSender.cs @@ -28,6 +28,8 @@ using System; using System.Globalization; using System.Linq; using System.Threading; + +using ASC.Common; using ASC.Common.Caching; using ASC.Core; using ASC.Core.Configuration; @@ -36,9 +38,9 @@ using ASC.Notify.Patterns; using ASC.Notify.Recipients; using ASC.Web.Core.Users; using ASC.Web.Studio.Utility; + using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; namespace ASC.Web.Studio.Core.Notify { @@ -177,7 +179,7 @@ namespace ASC.Web.Studio.Core.Notify public static class ServiceLauncherExtension { - public static IServiceCollection AddStudioNotifyServiceSender(this IServiceCollection services) + public static DIHelper AddStudioNotifyServiceSender(this DIHelper services) { services.TryAddSingleton(); diff --git a/web/ASC.Web.Core/Notify/StudioNotifySource.cs b/web/ASC.Web.Core/Notify/StudioNotifySource.cs index e273b8f30f..a18fc96031 100644 --- a/web/ASC.Web.Core/Notify/StudioNotifySource.cs +++ b/web/ASC.Web.Core/Notify/StudioNotifySource.cs @@ -24,14 +24,13 @@ */ +using ASC.Common; using ASC.Core; using ASC.Core.Notify; using ASC.Notify.Model; using ASC.Notify.Patterns; using ASC.Notify.Recipients; using ASC.Web.Core.PublicResources; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; namespace ASC.Web.Studio.Core.Notify { @@ -261,7 +260,7 @@ namespace ASC.Web.Studio.Core.Notify public static class StudioNotifySourceExtension { - public static IServiceCollection AddStudioNotifySourceService(this IServiceCollection services) + public static DIHelper AddStudioNotifySourceService(this DIHelper services) { services.TryAddScoped(); diff --git a/web/ASC.Web.Core/Notify/StudioPeriodicNotify.cs b/web/ASC.Web.Core/Notify/StudioPeriodicNotify.cs index 73bbf1fa80..ae61eaa227 100644 --- a/web/ASC.Web.Core/Notify/StudioPeriodicNotify.cs +++ b/web/ASC.Web.Core/Notify/StudioPeriodicNotify.cs @@ -28,15 +28,16 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; -using System.Threading; +using System.Threading; +using ASC.Common; using ASC.Common.Logging; using ASC.Core; using ASC.Core.Billing; using ASC.Core.Common.Billing; -using ASC.Core.Common.EF; -using ASC.Core.Common.EF.Context; -using ASC.Core.Common.Settings; +using ASC.Core.Common.EF; +using ASC.Core.Common.EF.Context; +using ASC.Core.Common.Settings; using ASC.Core.Tenants; using ASC.Core.Users; using ASC.Notify.Model; @@ -45,11 +46,10 @@ using ASC.Web.Core.Helpers; using ASC.Web.Core.PublicResources; using ASC.Web.Core.Users; using ASC.Web.Core.WhiteLabel; -using ASC.Web.Studio.Utility; - -using Microsoft.Extensions.Configuration; +using ASC.Web.Studio.Utility; + +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; namespace ASC.Web.Studio.Core.Notify @@ -105,19 +105,19 @@ namespace ASC.Web.Studio.Core.Notify var authContext = scope.ServiceProvider.GetService(); var commonLinkUtility = scope.ServiceProvider.GetService(); var apiSystemHelper = scope.ServiceProvider.GetService(); - var setupInfo = scope.ServiceProvider.GetService(); + var setupInfo = scope.ServiceProvider.GetService(); var context = scope.ServiceProvider.GetService>(); var couponManager = scope.ServiceProvider.GetService(); var client = WorkContext.NotifyContext.NotifyService.RegisterClient(studioNotifyHelper.NotifySource, scope); var tariff = paymentManager.GetTariff(tenant.TenantId); - var quota = tenantManager.GetTenantQuota(tenant.TenantId); - var createdDate = tenant.CreatedDateTime.Date; - - var dueDateIsNotMax = tariff.DueDate != DateTime.MaxValue; - var dueDate = tariff.DueDate.Date; - - var delayDueDateIsNotMax = tariff.DelayDueDate != DateTime.MaxValue; + var quota = tenantManager.GetTenantQuota(tenant.TenantId); + var createdDate = tenant.CreatedDateTime.Date; + + var dueDateIsNotMax = tariff.DueDate != DateTime.MaxValue; + var dueDate = tariff.DueDate.Date; + + var delayDueDateIsNotMax = tariff.DelayDueDate != DateTime.MaxValue; var delayDueDate = tariff.DelayDueDate.Date; INotifyAction action = null; @@ -206,12 +206,12 @@ namespace ASC.Web.Studio.Core.Notify { List datesWithActivity; - datesWithActivity = - context.Get(dbid).FeedAggregates - .Where(r => r.Tenant == tenantManager.GetCurrentTenant().TenantId) - .Where(r => r.CreatedDate <= nowDate.AddDays(-1)) - .GroupBy(r => r.CreatedDate.Date) - .Select(r => r.Key) + datesWithActivity = + context.Get(dbid).FeedAggregates + .Where(r => r.Tenant == tenantManager.GetCurrentTenant().TenantId) + .Where(r => r.CreatedDate <= nowDate.AddDays(-1)) + .GroupBy(r => r.CreatedDate.Date) + .Select(r => r.Key) .ToList(); if (datesWithActivity.Count < 5) @@ -500,7 +500,7 @@ namespace ASC.Web.Studio.Core.Notify new TagValue(Tags.ActiveUsers, userManager.GetUsers().Count()), new TagValue(Tags.Price, rquota.Price), new TagValue(Tags.PricePeriod, rquota.Year3 ? UserControlsCommonResource.TariffPerYear3 : rquota.Year ? UserControlsCommonResource.TariffPerYear : UserControlsCommonResource.TariffPerMonth), - new TagValue(Tags.DueDate, dueDate.ToLongDateString()), + new TagValue(Tags.DueDate, dueDate.ToLongDateString()), new TagValue(Tags.DelayDueDate, (delayDueDateIsNotMax ? delayDueDate : dueDate).ToLongDateString()), TagValues.BlueButton(blueButtonText, "http://www.onlyoffice.com/call-back-form.aspx"), TagValues.GreenButton(greenButtonText, greenButtonUrl), @@ -555,8 +555,8 @@ namespace ASC.Web.Studio.Core.Notify { using var scope = ServiceProvider.CreateScope(); var tenantManager = scope.ServiceProvider.GetService(); - var configuration = scope.ServiceProvider.GetService(); - var settingsManager = scope.ServiceProvider.GetService(); + var configuration = scope.ServiceProvider.GetService(); + var settingsManager = scope.ServiceProvider.GetService(); var defaultRebranding = MailWhiteLabelSettings.IsDefault(settingsManager, configuration); tenantManager.SetCurrentTenant(tenant.TenantId); @@ -565,18 +565,18 @@ namespace ASC.Web.Studio.Core.Notify var paymentManager = scope.ServiceProvider.GetService(); var tenantExtra = scope.ServiceProvider.GetService(); var coreBaseSettings = scope.ServiceProvider.GetService(); - var commonLinkUtility = scope.ServiceProvider.GetService(); + var commonLinkUtility = scope.ServiceProvider.GetService(); var context = scope.ServiceProvider.GetService>(); var client = WorkContext.NotifyContext.NotifyService.RegisterClient(studioNotifyHelper.NotifySource, scope); var tariff = paymentManager.GetTariff(tenant.TenantId); var quota = tenantManager.GetTenantQuota(tenant.TenantId); - var createdDate = tenant.CreatedDateTime.Date; - - var dueDateIsNotMax = tariff.DueDate != DateTime.MaxValue; - var dueDate = tariff.DueDate.Date; - - var delayDueDateIsNotMax = tariff.DelayDueDate != DateTime.MaxValue; + var createdDate = tenant.CreatedDateTime.Date; + + var dueDateIsNotMax = tariff.DueDate != DateTime.MaxValue; + var dueDate = tariff.DueDate.Date; + + var delayDueDateIsNotMax = tariff.DelayDueDate != DateTime.MaxValue; var delayDueDate = tariff.DelayDueDate.Date; INotifyAction action = null; @@ -695,14 +695,14 @@ namespace ASC.Web.Studio.Core.Notify else if (createdDate.AddDays(5) == nowDate) { List datesWithActivity; - - datesWithActivity = - context.Get(dbid).FeedAggregates - .Where(r => r.Tenant == tenantManager.GetCurrentTenant().TenantId) - .Where(r => r.CreatedDate <= nowDate.AddDays(-1)) - .GroupBy(r => r.CreatedDate.Date) - .Select(r => r.Key) - .ToList(); + + datesWithActivity = + context.Get(dbid).FeedAggregates + .Where(r => r.Tenant == tenantManager.GetCurrentTenant().TenantId) + .Where(r => r.CreatedDate <= nowDate.AddDays(-1)) + .GroupBy(r => r.CreatedDate.Date) + .Select(r => r.Key) + .ToList(); if (datesWithActivity.Count < 5) { @@ -900,7 +900,7 @@ namespace ASC.Web.Studio.Core.Notify new TagValue(Tags.ActiveUsers, userManager.GetUsers().Count()), new TagValue(Tags.Price, rquota.Price), new TagValue(Tags.PricePeriod, rquota.Year3 ? UserControlsCommonResource.TariffPerYear3 : rquota.Year ? UserControlsCommonResource.TariffPerYear : UserControlsCommonResource.TariffPerMonth), - new TagValue(Tags.DueDate, dueDate.ToLongDateString()), + new TagValue(Tags.DueDate, dueDate.ToLongDateString()), new TagValue(Tags.DelayDueDate, (delayDueDateIsNotMax ? delayDueDate : dueDate).ToLongDateString()), TagValues.BlueButton(blueButtonText, "http://www.onlyoffice.com/call-back-form.aspx"), TagValues.GreenButton(greenButtonText, greenButtonUrl), @@ -957,9 +957,9 @@ namespace ASC.Web.Studio.Core.Notify var userManager = scope.ServiceProvider.GetService(); var displayUserSettingsHelper = scope.ServiceProvider.GetService(); var studioNotifyHelper = scope.ServiceProvider.GetService(); - var client = WorkContext.NotifyContext.NotifyService.RegisterClient(studioNotifyHelper.NotifySource, scope); - - var createdDate = tenant.CreatedDateTime.Date; + var client = WorkContext.NotifyContext.NotifyService.RegisterClient(studioNotifyHelper.NotifySource, scope); + + var createdDate = tenant.CreatedDateTime.Date; INotifyAction action = null; @@ -1254,7 +1254,7 @@ namespace ASC.Web.Studio.Core.Notify } public static class StudioPeriodicNotifyExtension { - public static IServiceCollection AddStudioPeriodicNotify(this IServiceCollection services) + public static DIHelper AddStudioPeriodicNotify(this DIHelper services) { services.TryAddSingleton(); services.TryAddSingleton(); @@ -1268,7 +1268,7 @@ namespace ASC.Web.Studio.Core.Notify .AddTenantExtraService() .AddAuthContextService() .AddCommonLinkUtilityService() - .AddSetupInfo() + .AddSetupInfo() .AddFeedDbService() .AddCoreBaseSettingsService() .AddDisplayUserSettingsService() diff --git a/web/ASC.Web.Core/Notify/StudioWhatsNewNotify.cs b/web/ASC.Web.Core/Notify/StudioWhatsNewNotify.cs index f00aa023a9..f4c2c537bb 100644 --- a/web/ASC.Web.Core/Notify/StudioWhatsNewNotify.cs +++ b/web/ASC.Web.Core/Notify/StudioWhatsNewNotify.cs @@ -28,7 +28,9 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; -using System.Threading; +using System.Threading; + +using ASC.Common; using ASC.Common.Logging; using ASC.Common.Utils; using ASC.Core; @@ -41,10 +43,10 @@ using ASC.Notify.Patterns; using ASC.Web.Core; using ASC.Web.Core.PublicResources; using ASC.Web.Core.Users; -using ASC.Web.Studio.Utility; +using ASC.Web.Studio.Utility; + using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; namespace ASC.Web.Studio.Core.Notify @@ -81,7 +83,7 @@ namespace ASC.Web.Studio.Core.Notify { using var scope = ServiceProvider.CreateScope(); var tenantManager = scope.ServiceProvider.GetService(); - var paymentManager = scope.ServiceProvider.GetService(); + var paymentManager = scope.ServiceProvider.GetService(); var tenantUtil = scope.ServiceProvider.GetService(); var tenant = tenantManager.GetTenant(tenantid); @@ -305,7 +307,7 @@ namespace ASC.Web.Studio.Core.Notify public static class StudioWhatsNewNotifyExtension { - public static IServiceCollection AddStudioWhatsNewNotify(this IServiceCollection services) + public static DIHelper AddStudioWhatsNewNotify(this DIHelper services) { services.TryAddSingleton(); diff --git a/web/ASC.Web.Core/PersonalSettings.cs b/web/ASC.Web.Core/PersonalSettings.cs new file mode 100644 index 0000000000..a925ecbdf4 --- /dev/null +++ b/web/ASC.Web.Core/PersonalSettings.cs @@ -0,0 +1,99 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Runtime.Serialization; + +using ASC.Common; +using ASC.Core.Common.Settings; + +namespace ASC.Web.Studio.Core +{ + [Serializable] + [DataContract] + public class PersonalSettings : ISettings + { + [DataMember(Name = "IsNewUser")] + public bool IsNewUserSetting { get; set; } + + [DataMember(Name = "IsNotActivated")] + public bool IsNotActivatedSetting { get; set; } + + public Guid ID + { + get { return new Guid("{B3427865-8E32-4E66-B6F3-91C61922239F}"); } + } + + public ISettings GetDefault(IServiceProvider serviceProvider) + { + return new PersonalSettings + { + IsNewUserSetting = false, + IsNotActivatedSetting = false, + }; + } + } + public class PersonalSettingsHelper + { + public PersonalSettingsHelper(SettingsManager settingsManager) + { + SettingsManager = settingsManager; + } + + public bool IsNewUser + { + get { return SettingsManager.LoadForCurrentUser().IsNewUserSetting; } + set + { + var settings = SettingsManager.LoadForCurrentUser(); + settings.IsNewUserSetting = value; + SettingsManager.SaveForCurrentUser(settings); + } + } + + public bool IsNotActivated + { + get { return SettingsManager.LoadForCurrentUser().IsNotActivatedSetting; } + set + { + var settings = SettingsManager.LoadForCurrentUser(); + settings.IsNotActivatedSetting = value; + SettingsManager.SaveForCurrentUser(settings); + } + } + + public SettingsManager SettingsManager { get; } + } + + public static class PersonalSettingsExtention + { + public static DIHelper AddTenantCookieSettingsService(this DIHelper services) + { + services.TryAddScoped(); + return services.AddSettingsManagerService(); + } + } +} \ No newline at end of file diff --git a/web/ASC.Web.Core/PromotionsSettings.cs b/web/ASC.Web.Core/PromotionsSettings.cs new file mode 100644 index 0000000000..b326b72919 --- /dev/null +++ b/web/ASC.Web.Core/PromotionsSettings.cs @@ -0,0 +1,51 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using System; +using System.Runtime.Serialization; + +using ASC.Core.Common.Settings; + +namespace ASC.Web.Studio.Core +{ + [Serializable] + [DataContract] + public class PromotionsSettings : ISettings + { + [DataMember(Name = "Show")] + public bool Show { get; set; } + + public Guid ID + { + get { return new Guid("{D291A4C1-179D-4ced-895A-E094E809C859}"); } + } + + public ISettings GetDefault(IServiceProvider serviceProvider) + { + return new PromotionsSettings { Show = true }; + } + } +} \ No newline at end of file diff --git a/web/ASC.Web.Core/PublicResources/CustomModeResource.Designer.cs b/web/ASC.Web.Core/PublicResources/CustomModeResource.Designer.cs index e022606f1d..4d78c8a151 100644 --- a/web/ASC.Web.Core/PublicResources/CustomModeResource.Designer.cs +++ b/web/ASC.Web.Core/PublicResources/CustomModeResource.Designer.cs @@ -200,6 +200,24 @@ namespace ASC.Web.Core.PublicResources { } } + /// + /// Looks up a localized string similar to Do the same as a user|Link Dropbox, Box and other accounts in the 'Common Documents' section|Set up access rights to the documents and folders in the 'Common Documents' section. + /// + public static string ProductAdminOpportunitiesCustomMode { + get { + return ResourceManager.GetString("ProductAdminOpportunitiesCustomMode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create and edit own documents as well as the shared documents with the proper access rights|Give reading/editing access to other users to the documents and folders|Link Dropbox, Box and other accounts in the 'My documents' section. + /// + public static string ProductUserOpportunitiesCustomMode { + get { + return ResourceManager.GetString("ProductUserOpportunitiesCustomMode", resourceCulture); + } + } + /// /// Looks up a localized string similar to Welcome to ONLYOFFICE Personal. /// diff --git a/web/ASC.Web.Core/PublicResources/CustomModeResource.es.resx b/web/ASC.Web.Core/PublicResources/CustomModeResource.es.resx index ce24e5aa34..6305f069fe 100644 --- a/web/ASC.Web.Core/PublicResources/CustomModeResource.es.resx +++ b/web/ASC.Web.Core/PublicResources/CustomModeResource.es.resx @@ -201,6 +201,12 @@ La eliminación de datos personales permitió liberar: ^Ha recibido este mensaje de email porque Usted es un usuario registrado del portal "${__VirtualRootPath}":"${__VirtualRootPath}".^ + + Hacer lo mismo que el usuario|Enlazar Dropbox, Box y otras cuentas en la sección 'Documentos comunes'|Dar derechos de acceso a los documentos y carpetas en la sección 'Documentos comunes' + + + Crear y editar sus propios documentos y documentos compartidos con los derechos de acceso propios|Dar derechos de acceso a otros usuarios para leer/editar los documentos y carpetas|Enlazar Dropbox, Box y otras cuentas en la sección de 'Mis documentos' + Bienvenido al ONLYOFFICE Personal diff --git a/web/ASC.Web.Core/PublicResources/CustomModeResource.fr.resx b/web/ASC.Web.Core/PublicResources/CustomModeResource.fr.resx index 5578b0462f..1f142e2658 100644 --- a/web/ASC.Web.Core/PublicResources/CustomModeResource.fr.resx +++ b/web/ASC.Web.Core/PublicResources/CustomModeResource.fr.resx @@ -58,9 +58,30 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Faire la même chose qu'un utilisateur|Lier Dropbox, Box et d'autres comptes dans la section 'Documents communs'|Définir les droits d'accès aux documents et aux dossiers dans la section 'Documents communs' + + + Créez et travaillez sur vos propres documents ainsi que les documents partagés avec les droits d'accès appropriés|Déléguez les autorisations d'accès Lecture /Modification pour les autres utilisateurs aux documents et dossiers|Connectez Dropbox, Box et d'autres comptes dans la section 'Mes documents' + + + Bienvenue dans ONLYOFFICE Personal + Connectez votre stockage cloud préféré à ONLYOFFICE + + ONLYOFFICE Personal. Demande de changement d'adresse électronique + + + Assistance du mot de passe ONLYOFFICE Personal + + + ONLYOFFICE Personal. Veuillez activer votre adresse mail + + + La Fermeture de compte ONLYOFFICE Personal + ${LetterLogoText}. Supprimer les données utilisateur est terminé diff --git a/web/ASC.Web.Core/PublicResources/CustomModeResource.it.resx b/web/ASC.Web.Core/PublicResources/CustomModeResource.it.resx index 95fe1d421c..9aba1102ae 100644 --- a/web/ASC.Web.Core/PublicResources/CustomModeResource.it.resx +++ b/web/ASC.Web.Core/PublicResources/CustomModeResource.it.resx @@ -58,6 +58,32 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Gentile $UserName, + +Il tuo profilo utente è stato aggiunto correttamente al portale in "${__VirtualRootPath}":"${__VirtualRootPath}". Inizia oggi stesso! + +h3.Inizia a lavorare con i documenti: + +# Crea e modifica documenti di testo, fogli di calcolo e presentazioni. +# Connetti gli archivi cloud che usi per creare un unico spazio di lavoro per tutti i tuoi documenti. +# Condividi documenti con i membri del tuo team. +# Esplora diversi modi di lavorare sui documenti insieme: due modalità di co-editing in tempo reale, revisione, commenti, chat. + +h3.Prova altri strumenti ${LetterLogoText}: + +# Aggiungi il tuo account e-mail a "$ {LetterLogoText} Mail": "${__VirtualRootPath} / addons / mail /". +# Crea "Progetti":"${__VirtualRootPath} / prodotti / progetti /" e gestisci il tuo flusso di lavoro aggiungendo milestone e attività. +# Gestisci le tue relazioni con i clienti usando "CRM":"${__VirtualRootPath} / prodotti / crm /". +# Usa "Community":"${__VirtualRootPath} / prodotti / community /" per iniziare blog, forum, aggiungere eventi, condividere segnalibri. +# Organizza il tuo programma di lavoro con il "Calendario" incorporato:"${__VirtualRootPath} / addons / calendario /". +# Avvia "Talk": "${__VirtualRootPath} / addons / talk /" per chattare online con i tuoi compagni di squadra. + +$ GreenButton + +Cordialmente, +${LetterLogoText} Team + h1.Benvenuti su ONLYOFFICE Personal @@ -111,6 +137,36 @@ Interessato? Ulteriori informazioni su "ONLYOFFICE for business": "http://www.on Cordiali saluti, il team ONLYOFFICE + + + Hai richiesto di cambiare il tuo indirizzo email utilizzato per accedere al portale $ {LetterLogoText}. + +Segui il link qui sotto per cambiare il tuo indirizzo email: + +$GreenButton + +* Nota *: questo collegamento è valido solo per 7 giorni. Si prega di completare la procedura di modifica dell'email entro tale periodo. + +Se non desideri modificare la tua e-mail o non hai ricevuto questa e-mail per errore, ignorala. + +Cordiali saluti, +Squadra ONLYOFFICE + + + Salve, + +È stata richiesta la modifica della password utilizzata per accedere al portale "${__VirtualRootPath}":"${__VirtualRootPath}" + +Fare click sul link qui di seguito per modificare la password: + +$GreenButton + +*Nota*: Questo link è valido solamente per 7 giorni. Si prega di completare la procedura di cambio password entro il periodo indicato. + +Se non si desidera modificare la password o questo messaggio è stato ricevuto per errore, si prega di ignorarlo. + +Cordialmente, +Il team ONLYOFFICE Salve, @@ -119,6 +175,20 @@ Hai appena registrato un account presso la soluzione ONLYOFFICE per uso personal Se non riesci ad aprire il link, copia il seguente "$InviteLink":"$InviteLink" e incollalo nella barra degli indirizzi del browser. +Cordiali saluti, +il team ONLYOFFICE + + + Hai richiesto la chiusura del tuo account su personal.onlyoffice.com. Segui il collegamento per completare la richiesta (il collegamento è attivo per un periodo di 7 giorni): + +$GreenButton + +* Nota *: dopo la cancellazione, il tuo account e tutti i dati ad esso associati verranno cancellati in modo permanente in conformità con la nostra "Dichiarazione sulla privacy":"https://help.onlyoffice.com/products/files/doceditor.aspx?fileid=5048502&amp;doc=SXhWMEVzSEYxNlVVaXJJeUVtS0kyYk14YWdXTEFUQmRWL250NllHNUFGbz0_IjUwNDg1MDIi0". + +Ignora questa e-mail se non vuoi procedere. + +In caso di domande, contattaci all'indirizzo "support@onlyoffice.com":"mailto: support@onlyoffice.com" + Cordiali saluti, il team ONLYOFFICE @@ -133,6 +203,12 @@ La procedura di rimozione dei dati dall'utente "$FromUserName":"$FromUserLink" ^ Ricevi questa email perché sei un utente registrato del portale "${__VirtualRootPath}":"$ {__VirtualRootPath}".^ + + Fare lo stesso che un utente|Collegare Dropbox, Box e altri account nella section 'Documenti comuni'|Impostare i diritti di accesso ai documenti e alle cartelle nella sezione 'Documenti comuni' + + + Creare e modificare i propri documenti e anche i documenti condivisi con i diritti di accesso adatti|Concedere l'accesso agli altri utenti per leggere/modificare i documenti e le cartelle|Collegare Dropbox, Box e altri account nella sezione 'I miei documenti' + Benvenuto su ONLYOFFICE Personal diff --git a/web/ASC.Web.Core/PublicResources/CustomModeResource.resx b/web/ASC.Web.Core/PublicResources/CustomModeResource.resx index c9fd8ef014..cd71a9546e 100644 --- a/web/ASC.Web.Core/PublicResources/CustomModeResource.resx +++ b/web/ASC.Web.Core/PublicResources/CustomModeResource.resx @@ -201,6 +201,12 @@ The deletion of personal data allowed to free: ^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal.^ + + Do the same as a user|Link Dropbox, Box and other accounts in the 'Common Documents' section|Set up access rights to the documents and folders in the 'Common Documents' section + + + Create and edit own documents as well as the shared documents with the proper access rights|Give reading/editing access to other users to the documents and folders|Link Dropbox, Box and other accounts in the 'My documents' section + Welcome to ONLYOFFICE Personal diff --git a/web/ASC.Web.Core/PublicResources/CustomModeResource.ru.resx b/web/ASC.Web.Core/PublicResources/CustomModeResource.ru.resx index 7acc553718..81cdb7bf3f 100644 --- a/web/ASC.Web.Core/PublicResources/CustomModeResource.ru.resx +++ b/web/ASC.Web.Core/PublicResources/CustomModeResource.ru.resx @@ -194,6 +194,12 @@ $GreenButton ^Вы получили это сообщение, так как зарегистрированы на портале "${__VirtualRootPath}":"${__VirtualRootPath}".^ + + Делать то же самое, что и пользователь|Настраивать права доступа к документам и папкам в разделе 'Общие документы' + + + Создавать и редактировать свои документы, а также те, к которым предоставлен доступ с соответствующими правами|Предоставлять другим пользователям доступ к документам и папкам с правами на чтение/редактирование + Добро пожаловать в ONLYOFFICE Personal diff --git a/web/ASC.Web.Core/SetupInfo.cs b/web/ASC.Web.Core/SetupInfo.cs index 04d616b13f..d7e532cde4 100644 --- a/web/ASC.Web.Core/SetupInfo.cs +++ b/web/ASC.Web.Core/SetupInfo.cs @@ -31,11 +31,12 @@ using System.Globalization; using System.Linq; using System.Text.RegularExpressions; +using ASC.Common; using ASC.Common.Web; +using ASC.Web.Studio.UserControls.Statistics; +using ASC.Web.Studio.Utility; using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; namespace ASC.Web.Studio.Core { @@ -54,7 +55,21 @@ namespace ASC.Web.Studio.Core public List EnabledCulturesPersonal { get; private set; } public decimal ExchangeRateRuble { get; private set; } public long MaxImageUploadSize { get; private set; } - public static long AvailableFileSize { get; private set; } + + /// + /// Max possible file size for not chunked upload. Less or equal than 100 mb. + /// + public long MaxUploadSize(TenantExtra tenantExtra, TenantStatisticsProvider tenantStatisticsProvider) + { + return Math.Min(AvailableFileSize, MaxChunkedUploadSize(tenantExtra, tenantStatisticsProvider)); + } + + public long AvailableFileSize + { + get; + private set; + } + public string TeamlabSiteRedirect { get; private set; } public long ChunkUploadSize { get; private set; } public bool ThirdPartyAuthEnabled { get; private set; } @@ -132,7 +147,8 @@ namespace ASC.Web.Studio.Core .ToList(); ExchangeRateRuble = GetAppSettings("exchange-rate.ruble", 65); - MaxImageUploadSize = GetAppSettings("web.max-upload-size", 1024 * 1024); + MaxImageUploadSize = GetAppSettings("web:max-upload-size", 1024 * 1024); + AvailableFileSize = GetAppSettings("web:available-file-size", 100L * 1024L * 1024L); AvailableFileSize = GetAppSettings("web.available-file-size", 100L * 1024L * 1024L); TeamlabSiteRedirect = GetAppSettings("web.teamlab-site", string.Empty); @@ -195,6 +211,17 @@ namespace ASC.Web.Studio.Core return hideSettings == null || !hideSettings.Contains(settings, StringComparer.CurrentCultureIgnoreCase); } + public long MaxChunkedUploadSize(TenantExtra tenantExtra, TenantStatisticsProvider tenantStatisticsProvider) + { + var diskQuota = tenantExtra.GetTenantQuota(); + if (diskQuota != null) + { + var usedSize = tenantStatisticsProvider.GetUsedSize(); + var freeSize = Math.Max(diskQuota.MaxTotalSize - usedSize, 0); + return Math.Min(freeSize, diskQuota.MaxFileSize); + } + return ChunkUploadSize; + } private string GetAppSettings(string key, string defaultValue) { @@ -225,7 +252,7 @@ namespace ASC.Web.Studio.Core public static class SetupInfoExtension { - public static IServiceCollection AddSetupInfo(this IServiceCollection services) + public static DIHelper AddSetupInfo(this DIHelper services) { services.TryAddSingleton(); diff --git a/web/ASC.Web.Core/Sms/SmsProvider.cs b/web/ASC.Web.Core/Sms/SmsProvider.cs index ad9a4bf2e2..25980a25fc 100644 --- a/web/ASC.Web.Core/Sms/SmsProvider.cs +++ b/web/ASC.Web.Core/Sms/SmsProvider.cs @@ -32,6 +32,7 @@ using System.Net; using System.Text.RegularExpressions; using System.Web; +using ASC.Common; using ASC.Common.Caching; using ASC.Common.Logging; using ASC.Core; @@ -44,8 +45,6 @@ using ASC.FederatedLogin.LoginProviders; using ASC.VoipService.Dao; using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; using Twilio.Clients; @@ -119,7 +118,7 @@ namespace ASC.Web.Core.Sms } public static class SmsProviderManagerExtension { - public static IServiceCollection AddSmsProviderManagerService(this IServiceCollection services) + public static DIHelper AddSmsProviderManagerService(this DIHelper services) { services.TryAddScoped(); return services.AddConsumerFactoryService(); diff --git a/web/ASC.Web.Core/Sms/StudioSmsNotificationSettings.cs b/web/ASC.Web.Core/Sms/StudioSmsNotificationSettings.cs index 2604277c5d..ed335c0107 100644 --- a/web/ASC.Web.Core/Sms/StudioSmsNotificationSettings.cs +++ b/web/ASC.Web.Core/Sms/StudioSmsNotificationSettings.cs @@ -27,14 +27,12 @@ using System; using System.Runtime.Serialization; +using ASC.Common; using ASC.Core; using ASC.Core.Common.Settings; using ASC.Web.Core.Sms; using ASC.Web.Studio.Utility; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; - namespace ASC.Web.Studio.Core.SMS { [Serializable] @@ -101,7 +99,7 @@ namespace ASC.Web.Studio.Core.SMS public static class StudioSmsNotificationSettingsExtension { - public static IServiceCollection AddStudioSmsNotificationSettingsService(this IServiceCollection services) + public static DIHelper AddStudioSmsNotificationSettingsService(this DIHelper services) { services.TryAddScoped(); diff --git a/web/ASC.Web.Core/Statistic/StatisticManager.cs b/web/ASC.Web.Core/Statistic/StatisticManager.cs index 71116ad9ee..29ba68c60d 100644 --- a/web/ASC.Web.Core/Statistic/StatisticManager.cs +++ b/web/ASC.Web.Core/Statistic/StatisticManager.cs @@ -29,13 +29,12 @@ using System.Collections.Generic; using System.Data; using System.Linq; +using ASC.Common; using ASC.Core.Common.EF; using ASC.Core.Common.EF.Context; using ASC.Core.Common.EF.Model; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; namespace ASC.Web.Studio.Core.Statistic { @@ -166,7 +165,7 @@ namespace ASC.Web.Studio.Core.Statistic public static class StatisticManagerExtension { - public static IServiceCollection AddStatisticManagerService(this IServiceCollection services) + public static DIHelper AddStatisticManagerService(this DIHelper services) { services.TryAddScoped(); diff --git a/web/ASC.Web.Core/Tfa/TfaManager.cs b/web/ASC.Web.Core/Tfa/TfaManager.cs index 4a6b55add8..df9660ec8e 100644 --- a/web/ASC.Web.Core/Tfa/TfaManager.cs +++ b/web/ASC.Web.Core/Tfa/TfaManager.cs @@ -21,31 +21,29 @@ * in every copy of the program you distribute. * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. * -*/ - - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Runtime.Serialization; -using System.Security.Cryptography; -using System.Text; - -using ASC.Common.Caching; -using ASC.Common.Utils; -using ASC.Core; -using ASC.Core.Common.Security; -using ASC.Core.Common.Settings; -using ASC.Core.Users; -using ASC.Web.Core; -using ASC.Web.Core.PublicResources; - -using Google.Authenticator; - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; +*/ + + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Runtime.Serialization; +using System.Security.Cryptography; +using System.Text; + +using ASC.Common; +using ASC.Common.Caching; +using ASC.Common.Utils; +using ASC.Core; +using ASC.Core.Common.Security; +using ASC.Core.Common.Settings; +using ASC.Core.Users; +using ASC.Web.Core; +using ASC.Web.Core.PublicResources; +using Google.Authenticator; + namespace ASC.Web.Studio.Core.TFA { [Serializable] @@ -53,10 +51,10 @@ namespace ASC.Web.Studio.Core.TFA public class BackupCode { [DataMember(Name = "Code")] - private string code; - - public Signature Signature { get; } - + private string code; + + public Signature Signature { get; } + public string Code { get { return Signature.Read(code); } @@ -67,43 +65,43 @@ namespace ASC.Web.Studio.Core.TFA public bool IsUsed { get; set; } public BackupCode(Signature signature, string code) - { - Signature = signature; + { + Signature = signature; Code = code; IsUsed = false; } } public class TfaManager - { - private static readonly TwoFactorAuthenticator Tfa = new TwoFactorAuthenticator(); - private static readonly ICache Cache = AscCache.Memory; - - public SettingsManager SettingsManager { get; } - public SecurityContext SecurityContext { get; } - public CookiesManager CookiesManager { get; } - public SetupInfo SetupInfo { get; } - public Signature Signature { get; } - - public TfaManager( - SettingsManager settingsManager, - SecurityContext securityContext, - CookiesManager cookiesManager, - SetupInfo setupInfo, - Signature signature) - { - SettingsManager = settingsManager; - SecurityContext = securityContext; - CookiesManager = cookiesManager; - SetupInfo = setupInfo; - Signature = signature; - } - + { + private static readonly TwoFactorAuthenticator Tfa = new TwoFactorAuthenticator(); + private static readonly ICache Cache = AscCache.Memory; + + public SettingsManager SettingsManager { get; } + public SecurityContext SecurityContext { get; } + public CookiesManager CookiesManager { get; } + public SetupInfo SetupInfo { get; } + public Signature Signature { get; } + + public TfaManager( + SettingsManager settingsManager, + SecurityContext securityContext, + CookiesManager cookiesManager, + SetupInfo setupInfo, + Signature signature) + { + SettingsManager = settingsManager; + SecurityContext = securityContext; + CookiesManager = cookiesManager; + SetupInfo = setupInfo; + Signature = signature; + } + public SetupCode GenerateSetupCode(UserInfo user, int size) { return Tfa.GenerateSetupCode(SetupInfo.TfaAppSender, user.Email, Encoding.UTF8.GetBytes(GenerateAccessToken(user)), size, true); - } - + } + public bool ValidateAuthCode(UserInfo user, int tenantId, string code, bool checkBackup = true) { if (!TfaAppAuthSettings.IsVisibleSettings @@ -116,13 +114,13 @@ namespace ASC.Web.Studio.Core.TFA code = (code ?? "").Trim(); - if (string.IsNullOrEmpty(code)) throw new Exception(Resource.ActivateTfaAppEmptyCode); - - int.TryParse(Cache.Get("tfa/" + user.ID), out var counter); - if (++counter > SetupInfo.LoginThreshold) - { - throw new BruteForceCredentialException(Resource.TfaTooMuchError); - } + if (string.IsNullOrEmpty(code)) throw new Exception(Resource.ActivateTfaAppEmptyCode); + + int.TryParse(Cache.Get("tfa/" + user.ID), out var counter); + if (++counter > SetupInfo.LoginThreshold) + { + throw new BruteForceCredentialException(Resource.TfaTooMuchError); + } Cache.Insert("tfa/" + user.ID, counter.ToString(CultureInfo.InvariantCulture), DateTime.UtcNow.Add(TimeSpan.FromMinutes(1))); if (!Tfa.ValidateTwoFactorPIN(GenerateAccessToken(user), code)) @@ -135,23 +133,23 @@ namespace ASC.Web.Studio.Core.TFA { throw new ArgumentException(Resource.TfaAppAuthMessageError); } - } - + } + Cache.Insert("tfa/" + user.ID, (--counter).ToString(CultureInfo.InvariantCulture), DateTime.UtcNow.Add(TimeSpan.FromMinutes(1))); if (!SecurityContext.IsAuthenticated) { var cookiesKey = SecurityContext.AuthenticateMe(user.ID); CookiesManager.SetCookies(CookiesType.AuthKey, cookiesKey); - } - - if (!TfaAppUserSettings.EnableForUser(SettingsManager, user.ID)) - { - GenerateBackupCodes(user); - return true; - } - - return false; + } + + if (!TfaAppUserSettings.EnableForUser(SettingsManager, user.ID)) + { + GenerateBackupCodes(user); + return true; + } + + return false; } public IEnumerable GenerateBackupCodes(UserInfo user) @@ -159,7 +157,7 @@ namespace ASC.Web.Studio.Core.TFA var count = SetupInfo.TfaAppBackupCodeCount; var length = SetupInfo.TfaAppBackupCodeLength; - const string alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-_"; + const string alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-_"; var data = new byte[length]; @@ -181,30 +179,30 @@ namespace ASC.Web.Studio.Core.TFA } } var settings = SettingsManager.LoadForCurrentUser(); - settings.CodesSetting = list; - SettingsManager.SaveForCurrentUser(settings); + settings.CodesSetting = list; + SettingsManager.SaveForCurrentUser(settings); return list; } private string GenerateAccessToken(UserInfo user) { - return Signature.Create(TfaAppUserSettings.GetSalt(SettingsManager, user.ID)).Substring(0, 10); + return Signature.Create(TfaAppUserSettings.GetSalt(SettingsManager, user.ID)).Substring(0, 10); + } + } + + public static class TfaManagerExtension + { + public static DIHelper AddTfaManagerService(this DIHelper services) + { + services.TryAddScoped(); + + return services + .AddSettingsManagerService() + .AddSetupInfo() + .AddSignatureService() + .AddCookiesManagerService() + .AddSecurityContextService(); } - } - - public static class TfaManagerExtension - { - public static IServiceCollection AddTfaManagerService(this IServiceCollection services) - { - services.TryAddScoped(); - - return services - .AddSettingsManagerService() - .AddSetupInfo() - .AddSignatureService() - .AddCookiesManagerService() - .AddSecurityContextService(); - } } } \ No newline at end of file diff --git a/web/ASC.Web.Core/Users/CustomNamingPeople.cs b/web/ASC.Web.Core/Users/CustomNamingPeople.cs index 4719f5b50b..90fc0850e4 100644 --- a/web/ASC.Web.Core/Users/CustomNamingPeople.cs +++ b/web/ASC.Web.Core/Users/CustomNamingPeople.cs @@ -30,10 +30,10 @@ using System.Linq; using System.Reflection; using System.Runtime.Serialization; using System.Xml; + +using ASC.Common; using ASC.Core.Common.Settings; using ASC.Web.Core.PublicResources; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; namespace ASC.Web.Core.Users { @@ -392,7 +392,7 @@ namespace ASC.Web.Core.Users public static class CustomNamingPeopleExtension { - public static IServiceCollection AddCustomNamingPeopleService(this IServiceCollection services) + public static DIHelper AddCustomNamingPeopleService(this DIHelper services) { services.TryAddScoped(); diff --git a/web/ASC.Web.Core/Users/UserManagerWrapper.cs b/web/ASC.Web.Core/Users/UserManagerWrapper.cs index 32fe500217..a640d5ebcd 100644 --- a/web/ASC.Web.Core/Users/UserManagerWrapper.cs +++ b/web/ASC.Web.Core/Users/UserManagerWrapper.cs @@ -30,6 +30,7 @@ using System.Net.Mail; using System.Text; using System.Text.RegularExpressions; +using ASC.Common; using ASC.Core; using ASC.Core.Common.Settings; using ASC.Core.Tenants; @@ -40,9 +41,6 @@ using ASC.Web.Core.PublicResources; using ASC.Web.Core.Utility; using ASC.Web.Studio.Core.Notify; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; - namespace ASC.Web.Core.Users { /// @@ -367,7 +365,7 @@ namespace ASC.Web.Core.Users } public static class UserManagerWrapperExtension { - public static IServiceCollection AddUserManagerWrapperService(this IServiceCollection services) + public static DIHelper AddUserManagerWrapperService(this DIHelper services) { services.TryAddScoped(); diff --git a/web/ASC.Web.Core/Users/UserPhotoManager.cs b/web/ASC.Web.Core/Users/UserPhotoManager.cs index 5800473f52..1f94da5b10 100644 --- a/web/ASC.Web.Core/Users/UserPhotoManager.cs +++ b/web/ASC.Web.Core/Users/UserPhotoManager.cs @@ -34,6 +34,7 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; +using ASC.Common; using ASC.Common.Caching; using ASC.Common.Logging; using ASC.Common.Threading.Workers; @@ -44,7 +45,6 @@ using ASC.Data.Storage; using ASC.Web.Core.Utility.Skins; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; namespace ASC.Web.Core.Users @@ -1005,7 +1005,7 @@ namespace ASC.Web.Core.Users public static class ResizeWorkerItemExtension { - public static IServiceCollection AddResizeWorkerItemService(this IServiceCollection services) + public static DIHelper AddResizeWorkerItemService(this DIHelper services) { services.TryAddSingleton>(); services.TryAddSingleton>(); @@ -1025,7 +1025,7 @@ namespace ASC.Web.Core.Users public static class UserPhotoManagerExtension { - public static IServiceCollection AddUserPhotoManagerService(this IServiceCollection services) + public static DIHelper AddUserPhotoManagerService(this DIHelper services) { services.TryAddScoped(); services.TryAddSingleton(); diff --git a/web/ASC.Web.Core/Utility/ColorThemesSettings.cs b/web/ASC.Web.Core/Utility/ColorThemesSettings.cs index 6134962fd8..aaa9635409 100644 --- a/web/ASC.Web.Core/Utility/ColorThemesSettings.cs +++ b/web/ASC.Web.Core/Utility/ColorThemesSettings.cs @@ -27,10 +27,11 @@ using System; using System.IO; using System.Runtime.Serialization; + +using ASC.Common; using ASC.Core.Common.Settings; + using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; namespace ASC.Web.Core.Utility @@ -145,7 +146,7 @@ namespace ASC.Web.Core.Utility public static class ColorThemesSettingsHelperExtension { - public static IServiceCollection AddColorThemesSettingsHelperService(this IServiceCollection services) + public static DIHelper AddColorThemesSettingsHelperService(this DIHelper services) { services.TryAddScoped(); return services.AddSettingsManagerService(); diff --git a/web/ASC.Web.Core/Utility/CommonLinkUtility.cs b/web/ASC.Web.Core/Utility/CommonLinkUtility.cs index 60816c79ec..6c12ad9f7f 100644 --- a/web/ASC.Web.Core/Utility/CommonLinkUtility.cs +++ b/web/ASC.Web.Core/Utility/CommonLinkUtility.cs @@ -30,6 +30,7 @@ using System.Linq; using System.Text.RegularExpressions; using System.Web; +using ASC.Common; using ASC.Common.Logging; using ASC.Core; using ASC.Core.Common; @@ -40,8 +41,6 @@ using ASC.Web.Core; using ASC.Web.Core.WhiteLabel; using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; namespace ASC.Web.Studio.Utility @@ -551,7 +550,7 @@ namespace ASC.Web.Studio.Utility public static class CommonLinkUtilityExtension { - public static IServiceCollection AddCommonLinkUtilityService(this IServiceCollection services) + public static DIHelper AddCommonLinkUtilityService(this DIHelper services) { services.TryAddScoped(); diff --git a/web/ASC.Web.Core/Utility/Skins/WebImageSupplier.cs b/web/ASC.Web.Core/Utility/Skins/WebImageSupplier.cs index 183b5f0fdd..dfd2d6799b 100644 --- a/web/ASC.Web.Core/Utility/Skins/WebImageSupplier.cs +++ b/web/ASC.Web.Core/Utility/Skins/WebImageSupplier.cs @@ -26,12 +26,11 @@ using System; +using ASC.Common; using ASC.Data.Storage; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; namespace ASC.Web.Core.Utility.Skins { @@ -104,7 +103,7 @@ namespace ASC.Web.Core.Utility.Skins return folderName.TrimStart('~'); } - private string GetAppThemeVirtualPath(IWebItem webitem) + private static string GetAppThemeVirtualPath(IWebItem webitem) { if (webitem == null || string.IsNullOrEmpty(webitem.StartURL)) { @@ -120,7 +119,7 @@ namespace ASC.Web.Core.Utility.Skins public static class WebImageSupplierExtension { - public static IServiceCollection AddWebImageSupplierService(this IServiceCollection services) + public static DIHelper AddWebImageSupplierService(this DIHelper services) { services.TryAddScoped(); diff --git a/web/ASC.Web.Core/Utility/TenantExtra.cs b/web/ASC.Web.Core/Utility/TenantExtra.cs index 0e5e600660..c9849a2afb 100644 --- a/web/ASC.Web.Core/Utility/TenantExtra.cs +++ b/web/ASC.Web.Core/Utility/TenantExtra.cs @@ -28,6 +28,7 @@ using System; using System.Collections.Generic; using System.Linq; +using ASC.Common; using ASC.Common.Web; using ASC.Core; using ASC.Core.Billing; @@ -39,9 +40,6 @@ using ASC.Web.Studio.Core; using ASC.Web.Studio.UserControls.Management; using ASC.Web.Studio.UserControls.Statistics; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; - namespace ASC.Web.Studio.Utility { public class TenantExtra @@ -249,7 +247,7 @@ namespace ASC.Web.Studio.Utility public static class TenantExtraExtension { - public static IServiceCollection AddTenantExtraService(this IServiceCollection services) + public static DIHelper AddTenantExtraService(this DIHelper services) { services.TryAddScoped(); diff --git a/web/ASC.Web.Core/Utility/TenantLogoHelper.cs b/web/ASC.Web.Core/Utility/TenantLogoHelper.cs new file mode 100644 index 0000000000..3d6d6aeea9 --- /dev/null +++ b/web/ASC.Web.Core/Utility/TenantLogoHelper.cs @@ -0,0 +1,99 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2018 + * + * This program is freeware. You can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html). + * In accordance with Section 7(a) of the GNU GPL 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 more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html + * + * You can contact Ascensio System SIA by email at sales@onlyoffice.com + * + * The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display + * Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3. + * + * Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains + * relevant author attributions when distributing the software. If the display of the logo in its graphic + * form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE" + * in every copy of the program you distribute. + * Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks. + * +*/ + + +using ASC.Common; +using ASC.Core.Common.Settings; +using ASC.Web.Core.WhiteLabel; + +namespace ASC.Web.Studio.Utility +{ + public class TenantLogoHelper + { + public TenantLogoManager TenantLogoManager { get; } + public SettingsManager SettingsManager { get; } + public TenantWhiteLabelSettingsHelper TenantWhiteLabelSettingsHelper { get; } + public TenantInfoSettingsHelper TenantInfoSettingsHelper { get; } + + public TenantLogoHelper( + TenantLogoManager tenantLogoManager, + SettingsManager settingsManager, + TenantWhiteLabelSettingsHelper tenantWhiteLabelSettingsHelper, + TenantInfoSettingsHelper tenantInfoSettingsHelper) + { + TenantLogoManager = tenantLogoManager; + SettingsManager = settingsManager; + TenantWhiteLabelSettingsHelper = tenantWhiteLabelSettingsHelper; + TenantInfoSettingsHelper = tenantInfoSettingsHelper; + } + + public string GetLogo(WhiteLabelLogoTypeEnum type, bool general = true, bool isDefIfNoWhiteLabel = false) + { + string imgUrl; + if (TenantLogoManager.WhiteLabelEnabled) + { + var _tenantWhiteLabelSettings = SettingsManager.Load(); + return TenantWhiteLabelSettingsHelper.GetAbsoluteLogoPath(_tenantWhiteLabelSettings, type, general); + } + else + { + if (isDefIfNoWhiteLabel) + { + imgUrl = TenantWhiteLabelSettingsHelper.GetAbsoluteDefaultLogoPath(type, general); + } + else + { + if (type == WhiteLabelLogoTypeEnum.Dark) + { + /*** simple scheme ***/ + var _tenantInfoSettings = SettingsManager.Load(); + imgUrl = TenantInfoSettingsHelper.GetAbsoluteCompanyLogoPath(_tenantInfoSettings); + /***/ + } + else + { + imgUrl = TenantWhiteLabelSettingsHelper.GetAbsoluteDefaultLogoPath(type, general); + } + } + } + + return imgUrl; + + } + } + public static class TenantLogoHelperExtention + { + public static DIHelper AddTenantLogoHelperService(this DIHelper services) + { + services.TryAddScoped(); + + return services + .AddTenantLogoManagerService() + .AddSettingsManagerService() + .AddTenantWhiteLabelSettingsService() + .AddTenantInfoSettingsService(); + } + } +} diff --git a/web/ASC.Web.Core/Utility/TenantStatisticsProvider.cs b/web/ASC.Web.Core/Utility/TenantStatisticsProvider.cs index 1bc8a3a2b5..14888a369f 100644 --- a/web/ASC.Web.Core/Utility/TenantStatisticsProvider.cs +++ b/web/ASC.Web.Core/Utility/TenantStatisticsProvider.cs @@ -27,11 +27,11 @@ using System; using System.Collections.Generic; using System.Linq; + +using ASC.Common; using ASC.Core; using ASC.Core.Tenants; using ASC.Core.Users; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; namespace ASC.Web.Studio.UserControls.Statistics { @@ -75,7 +75,7 @@ namespace ASC.Web.Studio.UserControls.Statistics public static class TenantStatisticsProviderExtension { - public static IServiceCollection AddTenantStatisticsProviderService(this IServiceCollection services) + public static DIHelper AddTenantStatisticsProviderService(this DIHelper services) { services.TryAddScoped(); diff --git a/web/ASC.Web.Core/Utility/UrlShortener.cs b/web/ASC.Web.Core/Utility/UrlShortener.cs index 908832cfe5..1392bdda63 100644 --- a/web/ASC.Web.Core/Utility/UrlShortener.cs +++ b/web/ASC.Web.Core/Utility/UrlShortener.cs @@ -2,16 +2,15 @@ using System.Net; using System.Security.Cryptography; using System.Text; -using System.Web; +using System.Web; -using ASC.Core.Common.Configuration; +using ASC.Common; +using ASC.Core.Common.Configuration; using ASC.FederatedLogin.LoginProviders; -using ASC.Web.Studio.Utility; - -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; - +using ASC.Web.Studio.Utility; + +using Microsoft.Extensions.Configuration; + namespace ASC.Web.Core.Utility { public interface IUrlShortener @@ -46,29 +45,29 @@ namespace ASC.Web.Core.Utility return _instance; } - } - - public IConfiguration Configuration { get; } - public ConsumerFactory ConsumerFactory { get; } - public CommonLinkUtility CommonLinkUtility { get; } - - public UrlShortener(IConfiguration configuration, ConsumerFactory consumerFactory, CommonLinkUtility commonLinkUtility) - { - Configuration = configuration; - ConsumerFactory = consumerFactory; - CommonLinkUtility = commonLinkUtility; + } + + public IConfiguration Configuration { get; } + public ConsumerFactory ConsumerFactory { get; } + public CommonLinkUtility CommonLinkUtility { get; } + + public UrlShortener(IConfiguration configuration, ConsumerFactory consumerFactory, CommonLinkUtility commonLinkUtility) + { + Configuration = configuration; + ConsumerFactory = consumerFactory; + CommonLinkUtility = commonLinkUtility; } } public class BitLyShortener : IUrlShortener - { - public BitLyShortener(ConsumerFactory consumerFactory) - { - ConsumerFactory = consumerFactory; - } - - public ConsumerFactory ConsumerFactory { get; } - + { + public BitLyShortener(ConsumerFactory consumerFactory) + { + ConsumerFactory = consumerFactory; + } + + public ConsumerFactory ConsumerFactory { get; } + public string GetShortenLink(string shareLink) { return ConsumerFactory.Get().GetShortenLink(shareLink); @@ -88,24 +87,24 @@ namespace ASC.Web.Core.Utility sKey = configuration["core.machinekey"]; if (!url.EndsWith("/")) - url += '/'; - CommonLinkUtility = commonLinkUtility; - } - - public CommonLinkUtility CommonLinkUtility { get; } - + url += '/'; + CommonLinkUtility = commonLinkUtility; + } + + public CommonLinkUtility CommonLinkUtility { get; } + public string GetShortenLink(string shareLink) { - using var client = new WebClient { Encoding = Encoding.UTF8 }; - client.Headers.Add("Authorization", CreateAuthToken()); + using var client = new WebClient { Encoding = Encoding.UTF8 }; + client.Headers.Add("Authorization", CreateAuthToken()); return CommonLinkUtility.GetFullAbsolutePath(url + client.DownloadString(new Uri(internalUrl + "?url=" + HttpUtility.UrlEncode(shareLink)))); } private string CreateAuthToken(string pkey = "urlShortener") { - using var hasher = new HMACSHA1(Encoding.UTF8.GetBytes(sKey)); - var now = DateTime.UtcNow.ToString("yyyyMMddHHmmss"); - var hash = Convert.ToBase64String(hasher.ComputeHash(Encoding.UTF8.GetBytes(string.Join("\n", now, pkey)))); + using var hasher = new HMACSHA1(Encoding.UTF8.GetBytes(sKey)); + var now = DateTime.UtcNow.ToString("yyyyMMddHHmmss"); + var hash = Convert.ToBase64String(hasher.ComputeHash(Encoding.UTF8.GetBytes(string.Join("\n", now, pkey)))); return string.Format("ASC {0}:{1}:{2}", pkey, now, hash); } } @@ -116,16 +115,16 @@ namespace ASC.Web.Core.Utility { return null; } - } - - public static class UrlShortenerExtension - { - public static IServiceCollection AddUrlShortener(this IServiceCollection services) - { - services.TryAddScoped(); - return services - .AddConsumerFactoryService() - .AddCommonLinkUtilityService(); - } - } + } + + public static class UrlShortenerExtension + { + public static DIHelper AddUrlShortener(this DIHelper services) + { + services.TryAddScoped(); + return services + .AddConsumerFactoryService() + .AddCommonLinkUtilityService(); + } + } } diff --git a/web/ASC.Web.Core/WebItemManager.cs b/web/ASC.Web.Core/WebItemManager.cs index 307fce5698..76cbd9b282 100644 --- a/web/ASC.Web.Core/WebItemManager.cs +++ b/web/ASC.Web.Core/WebItemManager.cs @@ -27,14 +27,15 @@ using System; using System.Collections.Generic; using System.Linq; + +using ASC.Common; using ASC.Common.Logging; using ASC.Core; using ASC.Web.Core.WebZones; using Autofac; + using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; namespace ASC.Web.Core @@ -256,12 +257,12 @@ namespace ASC.Web.Core public static class WebItemManagerExtension { - public static IServiceCollection AddWebItemManager(this IServiceCollection services) + public static DIHelper AddWebItemManager(this DIHelper services) { services.TryAddSingleton(); return services; } - public static IServiceCollection AddWebItemManagerSecurity(this IServiceCollection services) + public static DIHelper AddWebItemManagerSecurity(this DIHelper services) { services.TryAddScoped(); return services diff --git a/web/ASC.Web.Core/WebItemSecurity.cs b/web/ASC.Web.Core/WebItemSecurity.cs index 2911f09eff..b332736060 100644 --- a/web/ASC.Web.Core/WebItemSecurity.cs +++ b/web/ASC.Web.Core/WebItemSecurity.cs @@ -28,6 +28,8 @@ using System; using System.Collections.Generic; using System.Linq; using System.Security; + +using ASC.Common; using ASC.Common.Caching; using ASC.Common.Security; using ASC.Common.Security.Authorizing; @@ -35,8 +37,7 @@ using ASC.Core; using ASC.Core.Common.Settings; using ASC.Core.Users; using ASC.Web.Core.Utility.Settings; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; + using SecurityAction = ASC.Common.Security.Authorizing.Action; namespace ASC.Web.Core @@ -434,7 +435,7 @@ namespace ASC.Web.Core public static class WebItemSecurityExtension { - public static IServiceCollection AddWebItemSecurity(this IServiceCollection services) + public static DIHelper AddWebItemSecurity(this DIHelper services) { services.TryAddScoped(); @@ -450,7 +451,7 @@ namespace ASC.Web.Core .AddAuthManager() .AddSettingsManagerService(); } - public static IServiceCollection AddWebItemSecurityCache(this IServiceCollection services) + public static DIHelper AddWebItemSecurityCache(this DIHelper services) { services.TryAddSingleton(); return services; diff --git a/web/ASC.Web.Core/WhiteLabel/AdditionalWhiteLabelSettings.cs b/web/ASC.Web.Core/WhiteLabel/AdditionalWhiteLabelSettings.cs index fa11bfda68..5fdf6010c7 100644 --- a/web/ASC.Web.Core/WhiteLabel/AdditionalWhiteLabelSettings.cs +++ b/web/ASC.Web.Core/WhiteLabel/AdditionalWhiteLabelSettings.cs @@ -26,12 +26,14 @@ using System; using System.Globalization; -using System.Runtime.Serialization; -using ASC.Core.Common.Settings; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; +using System.Runtime.Serialization; +using ASC.Common; +using ASC.Core.Common.Settings; + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + namespace ASC.Web.Core.WhiteLabel { [Serializable] @@ -97,33 +99,33 @@ namespace ASC.Web.Core.WhiteLabel get { return new Guid("{0108422F-C05D-488E-B271-30C4032494DA}"); } } - public ISettings GetDefault() - { - return new AdditionalWhiteLabelSettings - { - StartDocsEnabled = true, - LicenseAgreementsEnabled = true, - LicenseAgreementsUrl = DefaultLicenseAgreements - }; - } - + public ISettings GetDefault() + { + return new AdditionalWhiteLabelSettings + { + StartDocsEnabled = true, + LicenseAgreementsEnabled = true, + LicenseAgreementsUrl = DefaultLicenseAgreements + }; + } + public ISettings GetDefault(IConfiguration configuration) - { + { var additionalWhiteLabelSettingsHelper = new AdditionalWhiteLabelSettingsHelper(configuration); - return new AdditionalWhiteLabelSettings - { - StartDocsEnabled = true, - HelpCenterEnabled = additionalWhiteLabelSettingsHelper.DefaultHelpCenterUrl != null, - FeedbackAndSupportEnabled = additionalWhiteLabelSettingsHelper.DefaultFeedbackAndSupportUrl != null, - FeedbackAndSupportUrl = additionalWhiteLabelSettingsHelper.DefaultFeedbackAndSupportUrl, - UserForumEnabled = additionalWhiteLabelSettingsHelper.DefaultUserForumUrl != null, - UserForumUrl = additionalWhiteLabelSettingsHelper.DefaultUserForumUrl, - VideoGuidesEnabled = additionalWhiteLabelSettingsHelper.DefaultVideoGuidesUrl != null, - VideoGuidesUrl = additionalWhiteLabelSettingsHelper.DefaultVideoGuidesUrl, - SalesEmail = additionalWhiteLabelSettingsHelper.DefaultMailSalesEmail, - BuyUrl = additionalWhiteLabelSettingsHelper.DefaultBuyUrl, - LicenseAgreementsEnabled = true, - LicenseAgreementsUrl = DefaultLicenseAgreements + return new AdditionalWhiteLabelSettings + { + StartDocsEnabled = true, + HelpCenterEnabled = additionalWhiteLabelSettingsHelper.DefaultHelpCenterUrl != null, + FeedbackAndSupportEnabled = additionalWhiteLabelSettingsHelper.DefaultFeedbackAndSupportUrl != null, + FeedbackAndSupportUrl = additionalWhiteLabelSettingsHelper.DefaultFeedbackAndSupportUrl, + UserForumEnabled = additionalWhiteLabelSettingsHelper.DefaultUserForumUrl != null, + UserForumUrl = additionalWhiteLabelSettingsHelper.DefaultUserForumUrl, + VideoGuidesEnabled = additionalWhiteLabelSettingsHelper.DefaultVideoGuidesUrl != null, + VideoGuidesUrl = additionalWhiteLabelSettingsHelper.DefaultVideoGuidesUrl, + SalesEmail = additionalWhiteLabelSettingsHelper.DefaultMailSalesEmail, + BuyUrl = additionalWhiteLabelSettingsHelper.DefaultBuyUrl, + LicenseAgreementsEnabled = true, + LicenseAgreementsUrl = DefaultLicenseAgreements }; } @@ -136,25 +138,25 @@ namespace ASC.Web.Core.WhiteLabel } public static AdditionalWhiteLabelSettings Instance(SettingsManager settingsManager) - { + { return settingsManager.LoadForDefaultTenant(); - } - - public ISettings GetDefault(IServiceProvider serviceProvider) - { - return GetDefault(serviceProvider.GetService()); - } - } - - public class AdditionalWhiteLabelSettingsHelper - { - public IConfiguration Configuration { get; } - - public AdditionalWhiteLabelSettingsHelper(IConfiguration configuration) - { - Configuration = configuration; - } - + } + + public ISettings GetDefault(IServiceProvider serviceProvider) + { + return GetDefault(serviceProvider.GetService()); + } + } + + public class AdditionalWhiteLabelSettingsHelper + { + public IConfiguration Configuration { get; } + + public AdditionalWhiteLabelSettingsHelper(IConfiguration configuration) + { + Configuration = configuration; + } + public string DefaultHelpCenterUrl { get @@ -207,13 +209,13 @@ namespace ASC.Web.Core.WhiteLabel var site = Configuration["web.teamlab-site"]; return !string.IsNullOrEmpty(site) ? site + "/post.ashx?type=buyenterprise" : ""; } - } - } - + } + } + public static class AdditionalWhiteLabelSettingsExtension { - public static IServiceCollection AddAdditionalWhiteLabelSettingsService(this IServiceCollection services) - { + public static DIHelper AddAdditionalWhiteLabelSettingsService(this DIHelper services) + { services.TryAddSingleton(); return services; } diff --git a/web/ASC.Web.Core/WhiteLabel/TenantInfoSettings.cs b/web/ASC.Web.Core/WhiteLabel/TenantInfoSettings.cs index 46fb10c57e..069461343e 100644 --- a/web/ASC.Web.Core/WhiteLabel/TenantInfoSettings.cs +++ b/web/ASC.Web.Core/WhiteLabel/TenantInfoSettings.cs @@ -28,15 +28,16 @@ using System; using System.Drawing; using System.Globalization; using System.IO; -using System.Runtime.Serialization; +using System.Runtime.Serialization; + +using ASC.Common; using ASC.Core; using ASC.Core.Common.Settings; using ASC.Data.Storage; -using ASC.Web.Core.Utility.Skins; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; +using ASC.Web.Core.Utility.Skins; +using Microsoft.Extensions.Configuration; + namespace ASC.Web.Core.WhiteLabel { [Serializable] @@ -53,9 +54,9 @@ namespace ASC.Web.Core.WhiteLabel public ISettings GetDefault(IServiceProvider serviceProvider) { - return new TenantInfoSettings() - { - _isDefault = true + return new TenantInfoSettings() + { + _isDefault = true }; } @@ -63,26 +64,26 @@ namespace ASC.Web.Core.WhiteLabel { get { return new Guid("{5116B892-CCDD-4406-98CD-4F18297C0C0A}"); } } - } - - public class TenantInfoSettingsHelper - { - public WebImageSupplier WebImageSupplier { get; } - public StorageFactory StorageFactory { get; } - public TenantManager TenantManager { get; } - public IConfiguration Configuration { get; } - - public TenantInfoSettingsHelper( - WebImageSupplier webImageSupplier, - StorageFactory storageFactory, - TenantManager tenantManager, - IConfiguration configuration) - { - WebImageSupplier = webImageSupplier; - StorageFactory = storageFactory; - TenantManager = tenantManager; - Configuration = configuration; - } + } + + public class TenantInfoSettingsHelper + { + public WebImageSupplier WebImageSupplier { get; } + public StorageFactory StorageFactory { get; } + public TenantManager TenantManager { get; } + public IConfiguration Configuration { get; } + + public TenantInfoSettingsHelper( + WebImageSupplier webImageSupplier, + StorageFactory storageFactory, + TenantManager tenantManager, + IConfiguration configuration) + { + WebImageSupplier = webImageSupplier; + StorageFactory = storageFactory; + TenantManager = tenantManager; + Configuration = configuration; + } public void RestoreDefault(TenantInfoSettings tenantInfoSettings, TenantLogoManager tenantLogoManager) { RestoreDefaultTenantName(); @@ -165,19 +166,19 @@ namespace ASC.Web.Core.WhiteLabel var fileName = tenantInfoSettings._companyLogoFileName ?? ""; return storage.IsFile(fileName) ? storage.GetReadStream(fileName) : null; - } - } - + } + } + public static class TenantInfoSettingsExtension { - public static IServiceCollection AddTenantInfoSettingsService(this IServiceCollection services) - { - services.TryAddScoped(); + public static DIHelper AddTenantInfoSettingsService(this DIHelper services) + { + services.TryAddScoped(); - return services - .AddWebImageSupplierService() - .AddStorageFactoryService() - .AddTenantManagerService() + return services + .AddWebImageSupplierService() + .AddStorageFactoryService() + .AddTenantManagerService() .AddSettingsManagerService(); } } diff --git a/web/ASC.Web.Core/WhiteLabel/TenantLogoManager.cs b/web/ASC.Web.Core/WhiteLabel/TenantLogoManager.cs index 7c76519ff6..7fd4263aaa 100644 --- a/web/ASC.Web.Core/WhiteLabel/TenantLogoManager.cs +++ b/web/ASC.Web.Core/WhiteLabel/TenantLogoManager.cs @@ -26,17 +26,16 @@ using System; using System.IO; -using System.Linq; +using System.Linq; +using ASC.Common; using ASC.Common.Caching; -using ASC.Core; -using ASC.Core.Common.Settings; - -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; - +using ASC.Core; +using ASC.Core.Common.Settings; + +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; + namespace ASC.Web.Core.WhiteLabel { public class TenantLogoManager @@ -50,40 +49,40 @@ namespace ASC.Web.Core.WhiteLabel { get; private set; - } - - public ICache Cache { get; } - public ICacheNotify CacheNotify { get; } - - public TenantLogoManager( - TenantWhiteLabelSettingsHelper tenantWhiteLabelSettingsHelper, - SettingsManager settingsManager, - TenantInfoSettingsHelper tenantInfoSettingsHelper, - TenantManager tenantManager, - AuthContext authContext, - IConfiguration configuration, - ICacheNotify cacheNotify) - { - TenantWhiteLabelSettingsHelper = tenantWhiteLabelSettingsHelper; - SettingsManager = settingsManager; - TenantInfoSettingsHelper = tenantInfoSettingsHelper; - TenantManager = tenantManager; - AuthContext = authContext; - Configuration = configuration; + } + + public ICache Cache { get; } + public ICacheNotify CacheNotify { get; } + + public TenantLogoManager( + TenantWhiteLabelSettingsHelper tenantWhiteLabelSettingsHelper, + SettingsManager settingsManager, + TenantInfoSettingsHelper tenantInfoSettingsHelper, + TenantManager tenantManager, + AuthContext authContext, + IConfiguration configuration, + ICacheNotify cacheNotify) + { + TenantWhiteLabelSettingsHelper = tenantWhiteLabelSettingsHelper; + SettingsManager = settingsManager; + TenantInfoSettingsHelper = tenantInfoSettingsHelper; + TenantManager = tenantManager; + AuthContext = authContext; + Configuration = configuration; var hideSettings = (Configuration["web:hide-settings"] ?? "").Split(new[] { ',', ';', ' ' }); - WhiteLabelEnabled = !hideSettings.Contains("WhiteLabel", StringComparer.CurrentCultureIgnoreCase); - Cache = AscCache.Memory; - CacheNotify = cacheNotify; + WhiteLabelEnabled = !hideSettings.Contains("WhiteLabel", StringComparer.CurrentCultureIgnoreCase); + Cache = AscCache.Memory; + CacheNotify = cacheNotify; } public string GetFavicon(bool general, bool timeParam) { - string faviconPath; + string faviconPath; var tenantWhiteLabelSettings = SettingsManager.Load(); if (WhiteLabelEnabled) { faviconPath = TenantWhiteLabelSettingsHelper.GetAbsoluteLogoPath(tenantWhiteLabelSettings, WhiteLabelLogoTypeEnum.Favicon, general); - if (timeParam) + if (timeParam) { var now = DateTime.Now; faviconPath = string.Format("{0}?t={1}", faviconPath, now.Ticks); @@ -92,14 +91,14 @@ namespace ASC.Web.Core.WhiteLabel else { faviconPath = TenantWhiteLabelSettingsHelper.GetAbsoluteDefaultLogoPath(WhiteLabelLogoTypeEnum.Favicon, general); - } - + } + return faviconPath; } public string GetTopLogo(bool general)//LogoLightSmall - { - var tenantWhiteLabelSettings = SettingsManager.Load(); + { + var tenantWhiteLabelSettings = SettingsManager.Load(); if (WhiteLabelEnabled) { @@ -108,7 +107,7 @@ namespace ASC.Web.Core.WhiteLabel return TenantWhiteLabelSettingsHelper.GetAbsoluteDefaultLogoPath(WhiteLabelLogoTypeEnum.LightSmall, general); } - public string GetLogoDark(bool general) + public string GetLogoDark(bool general) { if (WhiteLabelEnabled) { @@ -122,8 +121,8 @@ namespace ASC.Web.Core.WhiteLabel } public string GetLogoDocsEditor(bool general) - { - var tenantWhiteLabelSettings = SettingsManager.Load(); + { + var tenantWhiteLabelSettings = SettingsManager.Load(); if (WhiteLabelEnabled) { @@ -157,23 +156,23 @@ namespace ASC.Web.Core.WhiteLabel } } return !AuthContext.IsAuthenticated; - } - + } + public bool WhiteLabelPaid { get { return TenantManager.GetTenantQuota(TenantManager.GetCurrentTenant().TenantId).WhiteLabel; } - } - - public TenantWhiteLabelSettingsHelper TenantWhiteLabelSettingsHelper { get; } - public SettingsManager SettingsManager { get; } - public TenantInfoSettingsHelper TenantInfoSettingsHelper { get; } - public TenantManager TenantManager { get; } - public AuthContext AuthContext { get; } - public IConfiguration Configuration { get; } - + } + + public TenantWhiteLabelSettingsHelper TenantWhiteLabelSettingsHelper { get; } + public SettingsManager SettingsManager { get; } + public TenantInfoSettingsHelper TenantInfoSettingsHelper { get; } + public TenantManager TenantManager { get; } + public AuthContext AuthContext { get; } + public IConfiguration Configuration { get; } + /// /// Get logo stream or null in case of default logo /// @@ -202,20 +201,20 @@ namespace ASC.Web.Core.WhiteLabel } public void RemoveMailLogoDataFromCache() - { + { CacheNotify.Publish(new TenantLogoCacheItem() { Key = CacheKey }, CacheNotifyAction.Remove); } - } - + } + public static class TenantLogoManagerExtension { - public static IServiceCollection AddTenantLogoManagerService(this IServiceCollection services) - { - services.TryAddScoped(); - - return services - .AddTenantWhiteLabelSettingsService() - .AddTenantInfoSettingsService() + public static DIHelper AddTenantLogoManagerService(this DIHelper services) + { + services.TryAddScoped(); + + return services + .AddTenantWhiteLabelSettingsService() + .AddTenantInfoSettingsService() .AddTenantManagerService(); } } diff --git a/web/ASC.Web.Core/WhiteLabel/TenantWhiteLabelSettings.cs b/web/ASC.Web.Core/WhiteLabel/TenantWhiteLabelSettings.cs index 2227dda7bc..a7e9bb3da1 100644 --- a/web/ASC.Web.Core/WhiteLabel/TenantWhiteLabelSettings.cs +++ b/web/ASC.Web.Core/WhiteLabel/TenantWhiteLabelSettings.cs @@ -30,20 +30,19 @@ using System.Drawing; using System.Globalization; using System.IO; using System.Linq; -using System.Runtime.Serialization; +using System.Runtime.Serialization; +using ASC.Common; using ASC.Common.Logging; using ASC.Core; using ASC.Core.Common.Settings; using ASC.Core.Common.WhiteLabel; using ASC.Data.Storage; using ASC.Web.Core.Users; -using ASC.Web.Core.Utility.Skins; - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Options; +using ASC.Web.Core.Utility.Skins; +using Microsoft.Extensions.Options; + using TMResourceData; namespace ASC.Web.Core.WhiteLabel @@ -57,33 +56,33 @@ namespace ASC.Web.Core.WhiteLabel #region Logos information: extension, isDefault, text for img auto generating [DataMember(Name = "LogoLightSmallExt")] - internal string _logoLightSmallExt; + internal string _logoLightSmallExt; [DataMember(Name = "DefaultLogoLightSmall")] internal bool _isDefaultLogoLightSmall { get; set; } [DataMember(Name = "LogoDarkExt")] - internal string _logoDarkExt; + internal string _logoDarkExt; [DataMember(Name = "DefaultLogoDark")] internal bool _isDefaultLogoDark { get; set; } [DataMember(Name = "LogoFaviconExt")] - internal string _logoFaviconExt; + internal string _logoFaviconExt; [DataMember(Name = "DefaultLogoFavicon")] internal bool _isDefaultLogoFavicon { get; set; } [DataMember(Name = "LogoDocsEditorExt")] - internal string _logoDocsEditorExt; + internal string _logoDocsEditorExt; [DataMember(Name = "DefaultLogoDocsEditor")] internal bool _isDefaultLogoDocsEditor { get; set; } [DataMember(Name = "LogoText")] - private string _logoText { get; set; } - + private string _logoText { get; set; } + public string GetLogoText(SettingsManager settingsManager) { if (!string.IsNullOrEmpty(_logoText) && _logoText != DefaultLogoText) @@ -91,12 +90,12 @@ namespace ASC.Web.Core.WhiteLabel var partnerSettings = settingsManager.LoadForDefaultTenant(); return string.IsNullOrEmpty(partnerSettings._logoText) ? DefaultLogoText : partnerSettings._logoText; - } - + } + public void SetLogoText(string val) - { + { _logoText = val; - } + } #endregion @@ -133,10 +132,10 @@ namespace ASC.Web.Core.WhiteLabel public Guid ID { get { return new Guid("{05d35540-c80b-4b17-9277-abd9e543bf93}"); } - } - + } + #region Get/Set IsDefault and Extension - + internal bool GetIsDefault(WhiteLabelLogoTypeEnum type) { return type switch @@ -200,49 +199,49 @@ namespace ASC.Web.Core.WhiteLabel } #endregion - } - - public class TenantWhiteLabelSettingsHelper - { - private const string moduleName = "whitelabel"; - - public WebImageSupplier WebImageSupplier { get; } - public UserPhotoManager UserPhotoManager { get; } - public StorageFactory StorageFactory { get; } - public WhiteLabelHelper WhiteLabelHelper { get; } - public TenantManager TenantManager { get; } - public SettingsManager SettingsManager { get; } - public CoreBaseSettings CoreBaseSettings { get; } - public IOptionsMonitor Option { get; } - - public ILog Log { get; set; } - - public TenantWhiteLabelSettingsHelper( + } + + public class TenantWhiteLabelSettingsHelper + { + private const string moduleName = "whitelabel"; + + public WebImageSupplier WebImageSupplier { get; } + public UserPhotoManager UserPhotoManager { get; } + public StorageFactory StorageFactory { get; } + public WhiteLabelHelper WhiteLabelHelper { get; } + public TenantManager TenantManager { get; } + public SettingsManager SettingsManager { get; } + public CoreBaseSettings CoreBaseSettings { get; } + public IOptionsMonitor Option { get; } + + public ILog Log { get; set; } + + public TenantWhiteLabelSettingsHelper( WebImageSupplier webImageSupplier, UserPhotoManager userPhotoManager, StorageFactory storageFactory, - WhiteLabelHelper whiteLabelHelper, - TenantManager tenantManager, - SettingsManager settingsManager, + WhiteLabelHelper whiteLabelHelper, + TenantManager tenantManager, + SettingsManager settingsManager, CoreBaseSettings coreBaseSettings, - IOptionsMonitor option) - { - WebImageSupplier = webImageSupplier; - UserPhotoManager = userPhotoManager; - StorageFactory = storageFactory; - WhiteLabelHelper = whiteLabelHelper; - TenantManager = tenantManager; - SettingsManager = settingsManager; - CoreBaseSettings = coreBaseSettings; - Option = option; - Log = option.CurrentValue; - } - + IOptionsMonitor option) + { + WebImageSupplier = webImageSupplier; + UserPhotoManager = userPhotoManager; + StorageFactory = storageFactory; + WhiteLabelHelper = whiteLabelHelper; + TenantManager = tenantManager; + SettingsManager = settingsManager; + CoreBaseSettings = coreBaseSettings; + Option = option; + Log = option.CurrentValue; + } + #region Restore default - + public void RestoreDefault(TenantWhiteLabelSettings tenantWhiteLabelSettings, TenantLogoManager tenantLogoManager) { - tenantWhiteLabelSettings._logoLightSmallExt = null; + tenantWhiteLabelSettings._logoLightSmallExt = null; tenantWhiteLabelSettings._logoDarkExt = null; tenantWhiteLabelSettings._logoFaviconExt = null; tenantWhiteLabelSettings._logoDocsEditorExt = null; @@ -540,10 +539,10 @@ namespace ASC.Web.Core.WhiteLabel { throw new UnknownImageFormatException(error); } - } - + } + #region Save for Resource replacement - + private static readonly List AppliedTenants = new List(); public void Apply(TenantWhiteLabelSettings tenantWhiteLabelSettings, int tenantId) @@ -596,20 +595,20 @@ namespace ASC.Web.Core.WhiteLabel } } - #endregion + #endregion } public static class TenantWhiteLabelSettingsExtension { - public static IServiceCollection AddTenantWhiteLabelSettingsService(this IServiceCollection services) - { + public static DIHelper AddTenantWhiteLabelSettingsService(this DIHelper services) + { services.TryAddScoped(); return services .AddUserPhotoManagerService() .AddWebImageSupplierService() .AddStorageFactoryService() - .AddWhiteLabelHelperService() - .AddSettingsManagerService() + .AddWhiteLabelHelperService() + .AddSettingsManagerService() .AddCoreBaseSettingsService(); } } diff --git a/web/ASC.Web.Studio/Startup.cs b/web/ASC.Web.Studio/Startup.cs index 741217876f..49e2a6b821 100644 --- a/web/ASC.Web.Studio/Startup.cs +++ b/web/ASC.Web.Studio/Startup.cs @@ -1,4 +1,5 @@ using ASC.Api.Core.Auth; +using ASC.Common; using ASC.Common.DependencyInjection; using ASC.Common.Logging; using ASC.Data.Storage; @@ -35,9 +36,11 @@ namespace ASC.Web.Studio services.AddAuthentication("cookie").AddScheme("cookie", a => { }); - services.AddNLogManager("ASC.Api", "ASC.Web"); + var diHelper = new DIHelper(services); - services + diHelper.AddNLogManager("ASC.Api", "ASC.Web"); + + diHelper .AddCookieAuthHandler() .AddStorage() .AddPathUtilsService()