diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE new file mode 100644 index 0000000000..d200c54488 --- /dev/null +++ b/.github/ISSUE_TEMPLATE @@ -0,0 +1,24 @@ +### Documentation + +Please check the official documentation before asking questions: https://docs.abp.io + +### GitHub Issues + +GitHub issues are for bug reports, feature requests and other discussions about the framework. + +If you're creating a bug/problem report, please include followings: + +* Your **ABP Framework version**. +* Your **User Interface** type (Angular/MVC/React... etc.) if the issue is related to a specific UI +* Exception message and **stack trace** if available (check the logs). +* Steps needed to **reproduce** the problem. + +Please **write in English**. + +### Stack Overflow + +Please use Stack Overflow for your questions about using the framework, templates and samples: + +https://stackoverflow.com/questions/tagged/abp + +Use **abp** tag in your questions. \ No newline at end of file diff --git a/.gitignore b/.gitignore index fe565c7ad9..1f14485237 100644 --- a/.gitignore +++ b/.gitignore @@ -298,6 +298,9 @@ samples/MicroserviceDemo/microservices/TenantManagementService.Host/Logs/logs.tx /templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/package-lock.json /templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/package-lock.json /templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/package-lock.json + +modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/Logs/ /templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/package-lock.json /templates/app/angular/package-lock.json -/modules/client-simulation/demo/Volo.ClientSimulation.Demo/package-lock.json \ No newline at end of file +/modules/client-simulation/demo/Volo.ClientSimulation.Demo/package-lock.json + diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json index c6010b777b..0c33439726 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json @@ -35,6 +35,7 @@ "NameFilter": "Name", "CreationTime": "Creation time", "IsPro": "Is pro", + "ShowOnModuleList": "Show on module list", "EfCoreConfigureMethodName": "Configure method name", "IsProFilter": "Is pro", "ApplicationType": "Application type", diff --git a/build/common.ps1 b/build/common.ps1 index 6428ae51b3..70a432818b 100644 --- a/build/common.ps1 +++ b/build/common.ps1 @@ -19,6 +19,7 @@ $solutionPaths = ( "../modules/audit-logging", "../modules/background-jobs", "../modules/client-simulation", + "../modules/virtual-file-explorer", "../templates/module/aspnet-core", "../templates/app/aspnet-core", "../samples/MicroserviceDemo", diff --git a/common.props b/common.props index ab7b86e3b2..cc85429a5b 100644 --- a/common.props +++ b/common.props @@ -1,7 +1,7 @@ latest - 2.8.0 + 2.9.0 $(NoWarn);CS1591 https://abp.io/assets/abp_nupkg.png https://abp.io diff --git a/docs/cs/Contribution/Index.md b/docs/cs/Contribution/Index.md index fd7f65d14e..ac1aaf00c1 100644 --- a/docs/cs/Contribution/Index.md +++ b/docs/cs/Contribution/Index.md @@ -41,7 +41,9 @@ Nový jazyk je publikován jakmile jsou minimálně tyto překlady dokončeny. ABP framework má flexibilní [lokalizační systém](../Localization.md). Můžete tak vytvořit lokalizované uživatelské prostředí pro svou vlastní aplikaci. -K tomu mají framework a vestavěné moduly již lokalizované texty. Například [lokalizační texty pro Volo.Abp.UI balík](https://github.com/abpframework/abp/blob/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json). Můžete vytvořit nový soubor ve [stejné složce](https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi) k přidání překladu. +K tomu mají framework a vestavěné moduly již lokalizované texty. Například [lokalizační texty pro Volo.Abp.UI balík](https://github.com/abpframework/abp/blob/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json). + +Můžete vytvořit nový soubor ve [stejné složce](https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi) k přidání překladu. * Naklonujte [ABP repozitář](https://github.com/abpframework/abp/) z Githubu. * Vytvořte nový soubor pro cílový jazyk pro lokalizační text v (json) souboru (u souboru en.json). @@ -49,6 +51,8 @@ K tomu mají framework a vestavěné moduly již lokalizované texty. Napříkla * Přeložte texty. * Zašlete pull request na Githubu. +K překladu lokalizovaných textů můžete také použít příkaz `abp translate` of [ABP CLI](CLI.md). + ABP je modulářní framework, proto je zde mnoho zdrojů lokalizačních textů, jeden pro každý modul. K najití všech .json souborů, vyhledejte po naklonování repozitáře soubory "en.json". Můžete se taky podívat na [tento seznam](Localization-Text-Files.md) souborů lokalizačních textů. ### Příspevky do blogu a návody diff --git a/docs/cs/Contribution/Localization-Text-Files.md b/docs/cs/Contribution/Localization-Text-Files.md deleted file mode 100644 index fc9567dbb1..0000000000 --- a/docs/cs/Contribution/Localization-Text-Files.md +++ /dev/null @@ -1,40 +0,0 @@ -## Textové soubory pro lokalizaci - -Toto je seznam lokalizačních textových souborů pro každého kdo chce přispět k překladu textů ve frameworku. Tento seznam budeme udržovat aktuální: - -* https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/en.json -* https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/Resources/AbpValidation/en.json -* https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/en.json -* https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json -* https://github.com/abpframework/abp/tree/master/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/Resource/en.json -* https://github.com/abpframework/abp/tree/master/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/en.json -* https://github.com/abpframework/abp/tree/master/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/CountryNames/en.json -* https://github.com/abpframework/abp/tree/master/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/Validation/en.json -* https://github.com/abpframework/abp/tree/master/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/en.json -* https://github.com/abpframework/abp/tree/master/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/SourceExt/en.json -* https://github.com/abpframework/abp/tree/master/modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/en.json -* https://github.com/abpframework/abp/tree/master/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Localization/Resources/Blogging/ApplicationContracts/en.json -* https://github.com/abpframework/abp/tree/master/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/en.json -* https://github.com/abpframework/abp/tree/master/modules/docs/app/VoloDocs.Web/Localization/Resources/VoloDocs/Web/en.json -* https://github.com/abpframework/abp/tree/master/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/en.json -* https://github.com/abpframework/abp/tree/master/modules/docs/src/Volo.Docs.Admin.Web/Localization/Resources/Docs/Web/en.json -* https://github.com/abpframework/abp/tree/master/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/en.json -* https://github.com/abpframework/abp/tree/master/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/Localization/ApplicationContracts/en.json -* https://github.com/abpframework/abp/tree/master/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/Localization/Domain/en.json -* https://github.com/abpframework/abp/tree/master/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Localization/Resources/FeatureManagement/en.json -* https://github.com/abpframework/abp/tree/master/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/Localization/ApplicationContracts/en.json -* https://github.com/abpframework/abp/tree/master/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/Localization/Domain/en.json -* https://github.com/abpframework/abp/tree/master/modules/identity/src/Volo.Abp.Identity.Web/Localization/Resources/AbpIdentity/en.json -* https://github.com/abpframework/abp/tree/master/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Localization/Resources/AbpPermissionManagement/en.json -* https://github.com/abpframework/abp/tree/master/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Localization/Resources/AbpSettingManagement/en.json -* https://github.com/abpframework/abp/tree/master/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/Localization/ApplicationContracts/en.json -* https://github.com/abpframework/abp/tree/master/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Localization/Resources/AbpTenantManagement/Web/en.json -* https://github.com/abpframework/abp-samples/tree/master/BookStore/src/Acme.BookStore.Domain.Shared/Localization/BookStore/en.json -* https://github.com/abpframework/abp-samples/tree/master/DashboardDemo/src/DashboardDemo.Domain/Localization/DashboardDemo/en.json -* https://github.com/abpframework/abp/tree/master/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/Localization/ApplicationContracts/en.json -* https://github.com/abpframework/abp/tree/master/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain/ProductManagement/Localization/Domain/en.json -* https://github.com/abpframework/abp/tree/master/samples/MicroserviceDemo/modules/product/src/ProductManagement.Web/Localization/Resources/ProductManagement/en.json -* https://github.com/abpframework/abp/tree/master/templates/mvc-module/src/MyCompanyName.MyProjectName.Application.Contracts/Localization/MyProjectName/ApplicationContracts/en.json -* https://github.com/abpframework/abp/tree/master/templates/mvc-module/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/DomainShared/en.json -* https://github.com/abpframework/abp/tree/master/templates/mvc-module/src/MyCompanyName.MyProjectName.Web/Localization/MyProjectName/Web/en.json -* https://github.com/abpframework/abp/tree/master/templates/mvc/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/en.json diff --git a/docs/en/AspNet-Boilerplate-Migration-Guide.md b/docs/en/AspNet-Boilerplate-Migration-Guide.md index d47b266839..39dd01eaa4 100644 --- a/docs/en/AspNet-Boilerplate-Migration-Guide.md +++ b/docs/en/AspNet-Boilerplate-Migration-Guide.md @@ -216,9 +216,9 @@ However, it provides the following methods those can be used to query a single e * `FindAsync(id)` returns the entity or null if not found. * `GetAsync(id)` method returns the entity or throws an `EntityNotFoundException` (which causes HTTP 404 status code) if not found. -#### Sync vs Async +#### Sync vs Async -ABP Framework repository has no sync methods (like `Insert`). All the methods are async (like `InsertAsync`). So, if your application has sync repository method usages, convert them to async versions. +ABP Framework repository has no sync methods (like `Insert`). All the methods are async (like `InsertAsync`). So, if your application has sync repository method usages, convert them to async versions. In general, ABP Framework forces you to completely use async everywhere, because mixing async & sync methods is not a recommended approach. @@ -444,7 +444,7 @@ ASP.NET Boilerplate uses Castle Windsor's [logging facility](http://docs.castlep using Castle.Core.Logging; //1: Import Logging namespace public class TaskAppService : ITaskAppService -{ +{ //2: Getting a logger using property injection public ILogger Logger { get; set; } @@ -693,26 +693,22 @@ public class AbpTenantManagementWebMainMenuContributor : IMenuContributor var administrationMenu = context.Menu.GetAdministration(); //Resolve some needed services from the DI container - var authorizationService = context.ServiceProvider - .GetRequiredService(); - var l = context.ServiceProvider - .GetRequiredService>(); + var l = context.GetLocalizer(); var tenantManagementMenuItem = new ApplicationMenuItem( TenantManagementMenuNames.GroupName, l["Menu:TenantManagement"], icon: "fa fa-users"); - + administrationMenu.AddItem(tenantManagementMenuItem); //Conditionally add the "Tenants" menu item based on the permission - if (await authorizationService - .IsGrantedAsync(TenantManagementPermissions.Tenants.Default)) + if (await context.IsGrantedAsync(TenantManagementPermissions.Tenants.Default)) { tenantManagementMenuItem.AddItem( new ApplicationMenuItem( TenantManagementMenuNames.Tenants, - l["Tenants"], + l["Tenants"], url: "/TenantManagement/Tenants")); } } @@ -731,4 +727,4 @@ The following features are not present for the ABP Framework. Here, a list of so * [Real time notification system](https://aspnetboilerplate.com/Pages/Documents/Notification-System) ([#633](https://github.com/abpframework/abp/issues/633)) * [NHibernate Integration](https://aspnetboilerplate.com/Pages/Documents/NHibernate-Integration) ([#339](https://github.com/abpframework/abp/issues/339)) - We don't intent to work on this, but any community contribution welcome. -Some of these features will eventually be implemented. However, you can implement them yourself if they are important for you. If you want, you can [contribute](Contribution/Index.md) to the framework, it is appreciated. \ No newline at end of file +Some of these features will eventually be implemented. However, you can implement them yourself if they are important for you. If you want, you can [contribute](Contribution/Index.md) to the framework, it is appreciated. diff --git a/docs/en/Background-Jobs-Quartz.md b/docs/en/Background-Jobs-Quartz.md index 8039192708..aa087ace0c 100644 --- a/docs/en/Background-Jobs-Quartz.md +++ b/docs/en/Background-Jobs-Quartz.md @@ -40,7 +40,7 @@ public class YourModule : AbpModule ## Configuration -Quartz is a very configurable library,and the ABP framework provides `AbpQuartzPreOptions` for this. You can use the `PreConfigure` method in your module class to pre-configure this option. ABP will use it when initializing the Quartz module. For example: +Quartz is a very configurable library,and the ABP framework provides `AbpQuartzOptions` for this. You can use the `PreConfigure` method in your module class to pre-configure this option. ABP will use it when initializing the Quartz module. For example: ````csharp [DependsOn( @@ -53,7 +53,7 @@ public class YourModule : AbpModule { var configuration = context.Services.GetConfiguration(); - PreConfigure(options => + PreConfigure(options => { options.Properties = new NameValueCollection { @@ -70,4 +70,52 @@ public class YourModule : AbpModule } ```` -Quartz stores job and scheduling information **in memory by default**. In the example, we use the pre-configuration of [options pattern](Options.md) to change it to the database. For more configuration of Quartz, please refer to the Quartz's [documentation](https://www.quartz-scheduler.net/documentation/quartz-3.x/tutorial/index.html). \ No newline at end of file +Quartz stores job and scheduling information **in memory by default**. In the example, we use the pre-configuration of [options pattern](Options.md) to change it to the database. For more configuration of Quartz, please refer to the Quartz's [documentation](https://www.quartz-scheduler.net/documentation/quartz-3.x/tutorial/index.html). + +## Exception handling + +### Default exception handling strategy + +When an exception occurs in the background job,ABP provide the **default handling strategy** retrying once every 3 seconds, up to 3 times. You can change the retry count and retry interval via `AbpBackgroundJobQuartzOptions` options: + +```csharp +[DependsOn( + //...other dependencies + typeof(AbpBackgroundJobsQuartzModule) //Add the new module dependency + )] +public class YourModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.RetryCount = 1; + options.RetryIntervalMillisecond = 1000; + }); + } +} +``` + +### Customize exception handling strategy + +You can customize the exception handling strategy via `AbpBackgroundJobQuartzOptions` options: + +```csharp +[DependsOn( + //...other dependencies + typeof(AbpBackgroundJobsQuartzModule) //Add the new module dependency + )] +public class YourModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.RetryStrategy = async (retryIndex, executionContext, exception) => + { + // customize exception handling + }; + }); + } +} +``` \ No newline at end of file diff --git a/docs/en/Background-Workers-Quartz.md b/docs/en/Background-Workers-Quartz.md index 0e9c640ee2..f8495b27a4 100644 --- a/docs/en/Background-Workers-Quartz.md +++ b/docs/en/Background-Workers-Quartz.md @@ -36,11 +36,11 @@ public class YourModule : AbpModule } ```` -### Configuration +## Configuration See [Configuration](Background-Jobs-Quartz#Configuration). -### Create a Background Worker +## Create a Background Worker A background work is a class that derives from the `QuartzBackgroundWorkerBase` base class. for example. A simple worker class is shown below: @@ -49,8 +49,8 @@ public class MyLogWorker : QuartzBackgroundWorkerBase { public MyLogWorker() { - JobDetail = JobBuilder.Create().Build(); - Trigger = TriggerBuilder.Create().StartNow().Build(); + JobDetail = JobBuilder.Create().WithIdentity(nameof(MyLogWorker)).Build(); + Trigger = TriggerBuilder.Create().WithIdentity(nameof(MyLogWorker)).StartNow().Build(); } public override Task Execute(IJobExecutionContext context) @@ -63,6 +63,82 @@ public class MyLogWorker : QuartzBackgroundWorkerBase We simply implemented the Execute method to write a log. The background worker is a **singleton by default**. If you want, you can also implement a [dependency interface](Dependency-Injection#DependencyInterfaces) to register it as another life cycle. +> Tips: Add identity to background workers is a best practice,because quartz distinguishes different jobs based on identity. + +## Add to BackgroundWorkerManager + +Default background workers are **automatically** added to the BackgroundWorkerManager when the application is **initialized**. You can set `AutoRegister` property value to `false`,if you want to add it manually: + +```` csharp +public class MyLogWorker : QuartzBackgroundWorkerBase +{ + public MyLogWorker() + { + AutoRegister = false; + JobDetail = JobBuilder.Create().WithIdentity(nameof(MyLogWorker)).Build(); + Trigger = TriggerBuilder.Create().WithIdentity(nameof(MyLogWorker)).StartNow().Build(); + } + + public override Task Execute(IJobExecutionContext context) + { + Logger.LogInformation("Executed MyLogWorker..!"); + return Task.CompletedTask; + } +} +```` + +If you want to globally disable auto add worker, you can global disable via `AbpBackgroundWorkerQuartzOptions` options: + +```csharp +[DependsOn( + //...other dependencies + typeof(AbpBackgroundWorkersQuartzModule) //Add the new module dependency + )] +public class YourModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.IsAutoRegisterEnabled = false; + }); + } +} +``` + +## Advanced topics + +### Customize ScheduleJob + +Assume you have a worker executes every 10 minutes,but because server is unavailable for 30 minutes, 3 executions are missed. You want to execute all missed times after the server is available. You should define your background worker like this: + +```csharp +public class MyLogWorker : QuartzBackgroundWorkerBase +{ + public MyLogWorker() + { + JobDetail = JobBuilder.Create().WithIdentity(nameof(MyLogWorker)).Build(); + Trigger = TriggerBuilder.Create().WithIdentity(nameof(MyLogWorker)).WithSimpleSchedule(s=>s.WithIntervalInMinutes(1).RepeatForever().WithMisfireHandlingInstructionIgnoreMisfires()).Build(); + + ScheduleJob = async scheduler => + { + if (!await scheduler.CheckExists(JobDetail.Key)) + { + await scheduler.ScheduleJob(JobDetail, Trigger); + } + }; + } + + public override Task Execute(IJobExecutionContext context) + { + Logger.LogInformation("Executed MyLogWorker..!"); + return Task.CompletedTask; + } +} +``` + +In the example we defined the worker execution interval to be 10 minutes and set `WithMisfireHandlingInstructionIgnoreMisfires`. we customized `ScheduleJob` and add worker to quartz only when the background worker does not exist. + ### More Please see Quartz's [documentation](https://www.quartz-scheduler.net/documentation/index.html) for more information. diff --git a/docs/en/Blog-Posts/2020-05-22 v2_8_Release/Post.md b/docs/en/Blog-Posts/2020-05-22 v2_8_Release/Post.md new file mode 100644 index 0000000000..6df6d84405 --- /dev/null +++ b/docs/en/Blog-Posts/2020-05-22 v2_8_Release/Post.md @@ -0,0 +1,214 @@ +# ABP v2.8.0 Releases & Road Map + +The **ABP Framework** & and the **ABP Commercial** v2.8 have been released. This post will cover **what's new** with these releases and the **middle-term road maps** for the projects. + +## What's New in the ABP Framework 2.8? + +You can see all the changes on the [GitHub release notes](https://github.com/abpframework/abp/releases/tag/2.8.0). This post will only cover the important features/changes. + +### SignalR Integration Package + +We've published [a new package](https://www.nuget.org/packages/Volo.Abp.AspNetCore.SignalR) to integrate SignalR to ABP framework based applications. + +> It is already possible to follow [the standard Microsoft tutorial](https://docs.microsoft.com/en-us/aspnet/core/tutorials/signalr) to add [SignalR](https://docs.microsoft.com/en-us/aspnet/core/signalr/introduction) to your application. However, ABP provides a SignalR integration packages those simplify the integration and usage. + +See the [SignalR Integration document](https://docs.abp.io/en/abp/latest/SignalR-Integration) to start with the SignalR. + +#### SignalR Demo Application + +We've also created a simple chat application to demonstrate how to use it. + +![signalr-chat-demo](signalr-chat-demo.png) + +See [the source code of the application.](https://github.com/abpframework/abp-samples/tree/master/SignalRDemo) + +### Console Application Startup Template + +The new console application template can be used to create a new console application that has the ABP Framework integrated. + +Use ABP CLI to create a new console application, specifying the `console` as the `-t` (template) option: + +````bash +abp new MyApp -t console +```` + +Thanks to the contribution of [@liangshiw](https://github.com/liangshiw) for this template. + +### RTL Support for the MVC UI & Arabic Localization + +[@kgamalseif](https://github.com/kgamalseif) has contributed a RTL implementation for the MVC UI which looks pretty fine: + +![rtl-ui](rtl-ui.png) + +He also localized all the framework and module resources. Thanks to him for this great contribution. + +### Others + +Some other highlights from this release: + +* Converted HttpApi.Client packages of the modules to .netstandard 2.0 to be compatible with other kind of applications. +* Improved the object extensibility system to better handle UI, localization and validation. +* Implemented disabling background job execution for HangFire & Quartz intergrations. +* New JsTree integration package for the MVC UI. +* Moved all samples to the new [abp-samples](https://github.com/abpframework/abp-samples) repository and created an [index page](https://docs.abp.io/en/abp/latest/Samples/Index) to see all. + +### Deprecations + +* Deprecated the `app.UseMvcWithDefaultRouteAndArea()` and introduced the `app.UseConfiguredEndpoints()` (see [#3880](https://github.com/abpframework/abp/issues/3880)). +* Deprecated the `UsePostgreSql()` and introduced the `UseNpgsql()` for the [Volo.Abp.EntityFrameworkCore.PostgreSql](http://nuget.org/packages/Volo.Abp.EntityFrameworkCore.PostgreSql) package. Switch to `UseNpgsql()` if you are using PostgreSQL. + +Old methods are marked as `Obsolete` and will be removed in the next major versions. + +## What's New in the ABP Commercial 2.8? + +### The New Lepton Theme + +We've completely revised [the lepton theme](https://commercial.abp.io/themes). See with different styles: + +![lepton-themes](lepton-themes.gif) + +Example screenshots from the language management page of the ABP Commercial: + +![lepton-abp-default-theme](lepton-abp-default-theme.png) + +(Default style UI) + +![lepton-abp-material-theme](lepton-abp-material-theme.png) + +(Material style UI) + +[Create a demo](https://commercial.abp.io/demo) to test all the styles in live. You can change the style from the settings page. + +### The New Chat Module + +The first version of [the chat module](https://commercial.abp.io/modules/Volo.Chat) has been released with this version. It has only the MVC / Razor Pages UI. Angular UI is on the way. + +![abp-chat-module](abp-chat-module.png) + +It currently has a simple **real time text messaging** functionality. More features like group messaging, sending images/files are on the road map. + +### Others + +* Implemented [module entity extension](https://docs.abp.io/en/commercial/latest/guides/module-entity-extensions) system for the Angular UI. Also improved the system to better handle float/double/decimal, date, datetime, enum and boolean properties. +* Managing product groups on a tree view for the [EasyCRM sample application](https://docs.abp.io/en/commercial/latest/samples/easy-crm). + +## About the Next Versions + +We publish feature releases in **every 2 weeks**. So, the planned date of the next feature version is **June 04** and the version number is **2.9**. This (probably) will be the **last 2.x version** and the following version will be **3.0**. + +### ABP Framework 2.9 & 3.0 + +#### Organization Unit System + +Organization Unit system for the Identity module was intended to be released with 2.8, but unfortunately we couldn't be sure about the stability of the feature, so deferred it to the 2.9. + +#### gRPC + +We planned to work on a gRPC integrated example application. Then we will plan to create gRPC endpoints for all [pre-built modules](https://docs.abp.io/en/abp/latest/Modules/Index) and to [the startup templates](https://docs.abp.io/en/abp/latest/Startup-Templates/Index). We want to use these endpoints with the new planned [Blazor](https://docs.microsoft.com/en-us/aspnet/core/blazor/) UI option (there is a [huge demand](https://github.com/abpframework/abp/issues/394) on a Blazor UI, we know). It doesn't mean that we'll finish the whole work in 3.0, but we are starting and will continue in 3.0+ versions. + +#### Oracle with EF Core + +We see that the people using Oracle with EF Core has some pains, independent from the ABP Framework. Because there is no stable & free Oracle provider for EF Core 3.1 yet. We only see the [Devart](https://www.devart.com/) has created a [paid package](https://www.nuget.org/packages/Devart.Data.Oracle.EFCore). + +[@ebicoglu](https://github.com/ebicoglu) has [created a gist](https://gist.github.com/ebicoglu/9f364c7eff9d87315af0178866186401) to demonstrate how to use it. We [planned](https://github.com/abpframework/abp/issues/3983) to work on an integration package to make it even easier. + +#### API Documentation + +We are [working](https://github.com/abpframework/abp/issues/1184) to create an API documentation for the framework and build a CD pipeline to automatically publish it in every new release. This will make easier to explore the framework classes. + +#### Sample Application: Using SignalR on a Tiered/Distributed system + +Using SignalR on a distributed/microservice system can be tricky since the services are not connected to clients and can not directly call client functions from the server. One way to overcome this problem is using a distributed message bus (like RabbitMQ) that transfers the message from the service to the web application to deliver to the client. + +We will create an example application and document it to demonstrate such an architecture and how it is easy by using the ABP Framework. + +While this topic is not directly related to the ABP Framework and the problem is not unique to an ABP based application, we find useful to create such guides to developers. + +#### And... + +We will spend more time to write more documentation, implement performance improvements, make more tests, creating more extensibility points and so on. + +### ABP Commercial 2.9 & 3.0 + +#### Organization Unit Management UI + +In parallel to the OU system in the ABP Framework (mentioned above), we are creating a UI to manage the organization units, which will be released with the 2.9. + +#### Angular UI for the Chat Module + +The Chat Module (mentioned above) only has the ASP.NET Core MVC / Razor Pages UI now. We are working to create the Angular UI for this module. + +#### New Module Idea: File Management + +We are looking to create a File Management Module that is used to manage (upload/download) and share files between users. You may think as a very simple and lightweight Google Drive :). + +#### Easy CRM Angular UI + +[Easy CRM](https://docs.abp.io/en/commercial/latest/samples/easy-crm) is a sample application we've released with the previous version of the ABP Commercial. In this version, we've added more features to this application. In the next version, we will work on the Angular UI for it. + +We found this application very useful since it is very close to a real world application compared to the simple [BookStore](https://docs.abp.io/en/commercial/latest/samples/index#book-store) example. + +#### And... + +We are working to improve current [modules](https://commercial.abp.io/modules), [themes](https://commercial.abp.io/themes) and the [tooling](https://commercial.abp.io/tools) to provide a more comfortable developer experience with the version 3.0. + +## The Road Map + +We are frequently asked about the road map of the [ABP Framework](https://abp.io/) and the [ABP Commercial](https://commercial.abp.io/). While we've answered to it in various platforms, with this release, we've adding road map pages for these products to their documentation: + +* [ABP Framework Road Map](https://docs.abp.io/en/abp/latest/Road-Map) +* [ABP Commercial Road Map](https://docs.abp.io/en/commercial/latest/road-map) + +I am also writing the road map here, in the following sections; + +### ABP Framework Road Map + +You can always check the milestone planning and the prioritized backlog issues on [the GitHub repository](https://github.com/abpframework/abp/milestones). + +While we will **continue to add other exciting features**, we will work on the following major items in the middle term: + +* **gRPC integration** and implementation for all the pre-built modules. +* **Blazor UI** for the framework and all the pre-built modules. +* **.NET 5.0**! As Microsoft has announced that the .NET 5.0 will be released in November 2020, we will prepare for this change before and move to the .NET 5.0 just after Microsoft releases it. We hope a smooth transition. + +### ABP Commercial Road Map + +We will work on the same items in parallel to to ABP Framework to implement them in the ABP Commercial side: + +* gRPC integration +* Blazor UI +* .NET 5.0 + +In addition, we will be working on the following items in the middle term: + +* A startup template to create microservice solutions (that has Ocelot, Redis, RabbitMQ, ElasticSearch, IdentityServer... etc. pre-integrated and configured). +* More module extension points. +* Dynamic dashboard system. +* Real-time notification system. +* Subscription and payment system for the SaaS module. +* More authentication options. +* New application modules (we have tens of module ideas and will share by the time - the "file management" announced above was one of them). +* New themes & theme styles (including public/corporate web site themes). + +## BONUS: ABP.IO Platform Road Map + +While the ABP Framework and the ABP Commercial are the fundamental components of the ABP.IO Platform, we want to create a much bigger platform to bring the .NET community together to create reusable modules, share knowledge, help each other by taking the advantage of the ABP Framework's unified and standardized development model. + +So, we have new *.abp.io web site ideas I want to share with the community + +#### market.abp.io + +A platform that is used by developers/companies to publish their reusable application modules, themes, libraries and tools base don the ABP Framework. There will be free/open source and commercial products on this web site. + +#### jobs.abp.io + +We are getting too many emails from companies want to hire developers or other other companies to build their products based on the ABP.IO Platform. We, as [Volosoft](https://volosoft.com/), want to stay in the product side rather than customer basis projects. We generally lead them to experienced developers and companies. + +We have a plan to create a web site to meet each side, so you can find developers for your projects or you find short or long term works to do. + +## Follow the ABP! + +Follow the social media accounts to get informed about happenings on the ABP.IO Platform: + +* [@abpframework](https://twitter.com/abpframework): ABP Framework official Twitter account +* [@abpcommercial](https://twitter.com/abpcommercial): ABP Commercial official Twitter account diff --git a/docs/en/Blog-Posts/2020-05-22 v2_8_Release/abp-chat-module.png b/docs/en/Blog-Posts/2020-05-22 v2_8_Release/abp-chat-module.png new file mode 100644 index 0000000000..88509b6022 Binary files /dev/null and b/docs/en/Blog-Posts/2020-05-22 v2_8_Release/abp-chat-module.png differ diff --git a/docs/en/Blog-Posts/2020-05-22 v2_8_Release/lepton-abp-default-theme.png b/docs/en/Blog-Posts/2020-05-22 v2_8_Release/lepton-abp-default-theme.png new file mode 100644 index 0000000000..93e94e5a67 Binary files /dev/null and b/docs/en/Blog-Posts/2020-05-22 v2_8_Release/lepton-abp-default-theme.png differ diff --git a/docs/en/Blog-Posts/2020-05-22 v2_8_Release/lepton-abp-material-theme.png b/docs/en/Blog-Posts/2020-05-22 v2_8_Release/lepton-abp-material-theme.png new file mode 100644 index 0000000000..c78dc96245 Binary files /dev/null and b/docs/en/Blog-Posts/2020-05-22 v2_8_Release/lepton-abp-material-theme.png differ diff --git a/docs/en/Blog-Posts/2020-05-22 v2_8_Release/lepton-themes.gif b/docs/en/Blog-Posts/2020-05-22 v2_8_Release/lepton-themes.gif new file mode 100644 index 0000000000..7ce3935ef5 Binary files /dev/null and b/docs/en/Blog-Posts/2020-05-22 v2_8_Release/lepton-themes.gif differ diff --git a/docs/en/Blog-Posts/2020-05-22 v2_8_Release/rtl-ui.png b/docs/en/Blog-Posts/2020-05-22 v2_8_Release/rtl-ui.png new file mode 100644 index 0000000000..0e1a162eb1 Binary files /dev/null and b/docs/en/Blog-Posts/2020-05-22 v2_8_Release/rtl-ui.png differ diff --git a/docs/en/Blog-Posts/2020-05-22 v2_8_Release/signalr-chat-demo.png b/docs/en/Blog-Posts/2020-05-22 v2_8_Release/signalr-chat-demo.png new file mode 100644 index 0000000000..e5f647b79c Binary files /dev/null and b/docs/en/Blog-Posts/2020-05-22 v2_8_Release/signalr-chat-demo.png differ diff --git a/docs/en/CLI.md b/docs/en/CLI.md index d9b6832dd5..86877e9e25 100644 --- a/docs/en/CLI.md +++ b/docs/en/CLI.md @@ -18,6 +18,37 @@ dotnet tool update -g Volo.Abp.Cli ## Commands +Here, the list of all available commands before explaining their details: + +* **`help`**: Shows help on the usage of the ABP CLI. +* **`new`**: Generates a new solution based on the ABP [startup templates](Startup-Templates/Index.md). +* **`update`**: Automatically updates all ABP related NuGet and NPM packages in a solution. +* **`add-package`**: Adds an ABP package to a project. +* **`add-module`**: Adds a [multi-package application module](https://docs.abp.io/en/abp/latest/Modules/Index) to a solution. +* **`generate-proxy`**: Generates client side proxies to use HTTP API endpoints on the server. +* **`switch-to-preview`**: Switches to the latest [nightly builds](Nightly-Builds.md) of the ABP related packages on a solution. +* **`switch-to-stable`**: Switches to the latest stable versions of the ABP related packages on a solution. +* **`translate`**: Simplifies to translate localization files when you have multiple JSON [localization](Localization.md) files in a source control repository. +* **`login`**: Authenticates on your computer with your [abp.io](https://abp.io/) username and password. +* **`logout`**: Logouts from your computer if you've authenticated before. + +### help + +Shows basic usages of the ABP CLI. + +Usage: + +````bash +abp help [command-name] +```` + +Examples: + +````bash +abp help # Shows a general help. +abp help new # Shows help about the "new" command. +```` + ### new Generates a new solution based on the ABP [startup templates](Startup-Templates/Index.md). @@ -63,6 +94,27 @@ abp new Acme.BookStore * `--connection-string` or `-cs`: Overwrites the default connection strings in all `appsettings.json` files. The default connection string is `Server=localhost;Database=MyProjectName;Trusted_Connection=True;MultipleActiveResultSets=true`. You can set your own connection string if you don't want to use the default. Be aware that the default database provider is `SQL Server`, therefore you can only enter connection string for SQL Server! * `--local-framework-ref --abp-path`: keeps local references to projects instead of replacing with NuGet package references. +### update + +Updating all ABP related packages can be tedious since there are many packages of the framework and modules. This command automatically updates all ABP related NuGet and NPM packages in a solution or project to the latest versions. + +Usage: + +````bash +abp update [options] +```` + +* If you run in a directory with a .sln file, it updates all ABP related packages of the all projects of the solution to the latest versions. +* If you run in a directory with a .csproj file, it updates all ABP related packages of the project to the latest versions. + +#### Options + +* `--include-previews` or `-p`: Includes preview, beta and rc packages while checking the latest versions. +* `--npm`: Only updates NPM packages. +* `--nuget`: Only updates NuGet packages. +* `--solution-path` or `-sp`: Specify the solution path. Use the current directory by default +* `--solution-name` or `-sn`: Specify the solution name. Search `*.sln` files in the directory by default. + ### add-package Adds an ABP package to a project by, @@ -117,26 +169,28 @@ abp add-module Volo.Blogging * `-sp` or `--startup-project`: Relative path to the project folder of the startup project. Default value is the current folder. * `--with-source-code`: Add source code of the module instead of NuGet/NPM packages. -### update +### generate-proxy -Updating all ABP related packages can be tedious since there are many packages of the framework and modules. This command automatically updates all ABP related NuGet and NPM packages in a solution or project to the latest versions. +Generates client proxies for your HTTP APIs to make easy to consume your services from the client side. Before running `generate-proxy` command, your host must be up and running. Usage: ````bash -abp update [options] +abp generate-proxy [options] ```` -* If you run in a directory with a .sln file, it updates all ABP related packages of the all projects of the solution to the latest versions. -* If you run in a directory with a .csproj file, it updates all ABP related packages of the project to the latest versions. - #### Options -* `--include-previews` or `-p`: Includes preview, beta and rc packages while checking the latest versions. -* `--npm`: Only updates NPM packages. -* `--nuget`: Only updates NuGet packages. -* `--solution-path` or `-sp`: Specify the solution path. Use the current directory by default -* `--solution-name` or `-sn`: Specify the solution name. Search `*.sln` files in the directory by default. +* `--apiUrl` or `-a`: Specifies the root URL of the HTTP API. The default value is being retrieved from the `environment.ts` file for the Angular application. Make sure your host is up and running before running `abp generate-proxy`. +* `--ui` or `-u`: Specifies the UI framework. Default value is `angular` and it is the only UI framework supported for now. Creates TypeScript code. +* `--module` or `-m`: Specifies the module name. Default module name is `app`, which indicates your own application (you typically want this since every module is responsible to maintain its own client proxies). Set `all` for to generate proxies for all the modules. + +Example usage with the options: + +````bash +abp generate-proxy --apiUrl https://localhost:44305 --ui angular --module all +```` + ### switch-to-preview @@ -147,6 +201,7 @@ Usage: ````bash abp switch-to-preview [options] ```` + #### Options `--solution-directory` or `-sd`: Specifies the directory. The solution should be in that directory or in any of its sub directories. If not specified, default is the current directory. @@ -164,64 +219,71 @@ abp switch-to-stable [options] `--solution-directory` or `-sd`: Specifies the directory. The solution should be in that directory or in any of its sub directories. If not specified, default is the current directory. -### login +### translate -Some features of the CLI requires to be logged in to abp.io platform. To login with your username write: +Simplifies to translate [localization](Localization.md) files when you have multiple JSON [localization](Localization.md) files in a source control repository. -```bash -abp login -``` +* This command will create a unified json file based on the reference culture. +* It searches all the localization `JSON` files in the current directory and all subdirectories (recursively). Then creates a single file (named `abp-translation.json` by default) that includes all the entries need to be translated. +* Once you translate the entries in this file, you can then apply your changes to the original localization files using the `--apply` command. -```bash -abp login -p -``` +> The main purpose of this command is to translate ABP Framework localization files (since the [abp repository](https://github.com/abpframework/abp) has tens of localization files to be translated in different directories). -Notice that, a new login with an already active session, overwrites the previous session. +#### Creating the Translation File -### logout +First step is to create the unified translation file: -Logs you out by removing the session token from your computer. +````bash +abp translate -c [options] +```` -``` -abp logout -``` +Example: -### generate-proxy +````bash +abp translate -c de-DE +```` -Generates client proxies for your HTTP APIs to make easy to consume your services from the client side. Before running `generate-proxy` command, your host must be up and running. +This command created the unified translation file for the `de-DE` (German) culture. -Usage: +##### Additional Options + +* `--reference-culture` or `-r`: Default `en`. Specifies the reference culture. +* `--output` or `-o`: Output file name. Default `abp-translation.json`. +* `--all-values` or `-all`: Include all keys to translate. By default, the unified translation file only includes the missing texts for the target culture. Specify this parameter if you may need to revise the values already translated before. + +#### Applying Changes + +Once you translate the entries in the unified translation file, you can apply your changes to the original localization files using the `--apply` parameter: ````bash -abp generate-proxy [options] +abp translate --apply # apply all changes +abp translate -a # shortcut for --apply ```` -#### Options +Then review changes on your source control system to be sure that it has changed the proper files and send a Pull Request if you've translated ABP Framework resources. Thank you in advance for your contribution. -* `--apiUrl` or `-a`: Specifies the root URL of the HTTP API. The default value is being retrieved from the `environment.ts` file for the Angular application. Make sure your host is up and running before running `abp generate-proxy`. -* `--ui` or `-u`: Specifies the UI framework. Default value is `angular` and it is the only UI framework supported for now. Creates TypeScript code. -* `--module` or `-m`: Specifies the module name. Default module name is `app`, which indicates your own application (you typically want this since every module is responsible to maintain its own client proxies). Set `all` for to generate proxies for all the modules. +##### Additional Options -Example usage with the options: +* `--file` or `-f`: Default: `abp-translation.json`. The translation file (use only if you've used the `--output` option before). -````bash -abp generate-proxy --apiUrl https://localhost:44305 --ui angular --module all -```` +### login +Some features of the CLI requires to be logged in to abp.io platform. To login with your username write: -### help +```bash +abp login # Asks password separately +abp login -p # Specify the password as a parameter +``` -Writes basic usage information of the CLI. +> Using `-p` parameter might not be safe if someone is watching your screen :) It can be useful for automation purposes. -Usage: +A new login with an already active session overwrites the previous session. -````bash -abp help [command-name] -```` +### logout -Examples: +Logs you out by removing the session token from your computer. + +``` +abp logout +``` -````bash -abp help # Shows a general help. -abp help new # Shows help about the "new" command. -```` diff --git a/docs/en/Contribution/Index.md b/docs/en/Contribution/Index.md index ba755426de..d753dc6bc2 100644 --- a/docs/en/Contribution/Index.md +++ b/docs/en/Contribution/Index.md @@ -41,7 +41,9 @@ A new language is published after these minimum translations have been completed ABP framework has a flexible [localization system](../Localization.md). You can create localized user interfaces for your own application. -In addition to that, the framework and pre-build modules have already localized texts. As an example, see [the localization texts for the Volo.Abp.UI package](https://github.com/abpframework/abp/blob/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json). You can create a new file in the [same folder](https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi) to translate it. +In addition to that, the framework and pre-build modules have already localized texts. As an example, see [the localization texts for the Volo.Abp.UI package](https://github.com/abpframework/abp/blob/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json). + +You can create a new file in the [same folder](https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi) to translate it. * Clone the [ABP repository](https://github.com/abpframework/abp/) from Github. * Create a new file for the target language for a localization text (json) file (near to the en.json file). @@ -49,6 +51,8 @@ In addition to that, the framework and pre-build modules have already localized * Translate the texts. * Send pull request on Github. +You can also use the `abp translate` command of [ABP CLI](CLI.md) to translate localized texts. + ABP is a modular framework. So there are many localization text resource, one per module. To find all .json files, you can search for "en.json" after cloning the repository. You can also check [this list](Localization-Text-Files.md) for a list of localization text files. ### Blog Posts & Tutorials diff --git a/docs/en/Contribution/Localization-Text-Files.md b/docs/en/Contribution/Localization-Text-Files.md deleted file mode 100644 index fc4b116967..0000000000 --- a/docs/en/Contribution/Localization-Text-Files.md +++ /dev/null @@ -1,31 +0,0 @@ -## Localization Text Files - -Here, a list of localization text files for anyone wants to contribute to localization of the texts coming from the framework. We will keep this list up to date: - -* https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/en.json -* https://github.com/abpframework/abp/blob/master/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/en.json -* https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/en.json -* https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json -* https://github.com/abpframework/abp/tree/master/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/Resource/en.json -* https://github.com/abpframework/abp/tree/master/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/en.json -* https://github.com/abpframework/abp/tree/master/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/CountryNames/en.json -* https://github.com/abpframework/abp/tree/master/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/Validation/en.json -* https://github.com/abpframework/abp/tree/master/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/en.json -* https://github.com/abpframework/abp/tree/master/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/SourceExt/en.json -* https://github.com/abpframework/abp/blob/master/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en.json -* https://github.com/abpframework/abp/tree/master/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Localization/Resources/Blogging/ApplicationContracts/en.json -* https://github.com/abpframework/abp/tree/master/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Localization/Resources/en.json -* https://github.com/abpframework/abp/tree/master/modules/docs/app/VoloDocs.Web/Localization/Resources/VoloDocs/Web/en.json -* https://github.com/abpframework/abp/tree/master/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/en.json -* https://github.com/abpframework/abp/tree/master/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/en.json -* https://github.com/abpframework/abp/tree/master/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/Domain/en.json -* https://github.com/abpframework/abp/tree/master/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json -* https://github.com/abpframework/abp/tree/master/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/en.json -* https://github.com/abpframework/abp/tree/master/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/en.json -* https://github.com/abpframework/abp/tree/master/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/en.json -* https://github.com/abpframework/abp-samples/tree/master/BookStore/src/Acme.BookStore.Domain.Shared/Localization/BookStore/en.json -* https://github.com/abpframework/abp-samples/tree/master/DashboardDemo/src/DashboardDemo.Domain.Shared/Localization/DashboardDemo/en.json -* https://github.com/abpframework/abp/tree/master/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/Localization/ApplicationContracts/en.json -* https://github.com/abpframework/abp/tree/master/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain/ProductManagement/Localization/Domain/en.json -* https://github.com/abpframework/abp/tree/master/samples/MicroserviceDemo/modules/product/src/ProductManagement.Web/Localization/Resources/ProductManagement/en.json -* https://github.com/abpframework/abp/tree/master/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/en.json diff --git a/docs/en/Customizing-Application-Modules-Overriding-Services.md b/docs/en/Customizing-Application-Modules-Overriding-Services.md index b9890b3aca..ce3729c1ac 100644 --- a/docs/en/Customizing-Application-Modules-Overriding-Services.md +++ b/docs/en/Customizing-Application-Modules-Overriding-Services.md @@ -59,6 +59,7 @@ In most cases, you will want to change one or a few methods of the current imple ### Example: Overriding an Application Service ````csharp +//[RemoteService(IsEnabled = false)] // If you use dynamic controller feature you can disable remote service. Prevent creating duplicate controller for the application service. [Dependency(ReplaceServices = true)] [ExposeServices(typeof(IIdentityUserAppService), typeof(IdentityUserAppService))] public class MyIdentityUserAppService : IdentityUserAppService diff --git a/docs/en/Entity-Framework-Core-MySQL.md b/docs/en/Entity-Framework-Core-MySQL.md index d1fa66e1a4..a04d6073fc 100644 --- a/docs/en/Entity-Framework-Core-MySQL.md +++ b/docs/en/Entity-Framework-Core-MySQL.md @@ -25,23 +25,6 @@ MySQL connection strings are different than SQL Server connection strings. So, c You typically will change the `appsettings.json` inside the `.DbMigrator` and `.Web` projects, but it depends on your solution structure. -## Change the Migrations DbContext - -MySQL DBMS has some slight differences than the SQL Server. Some module database mapping configuration (especially the field lengths) causes problems with MySQL. For example, some of the the [IdentityServer module](Modules/IdentityServer.md) tables has such problems and it provides an option to configure the fields based on your DBMS. - -The startup template contains a *YourProjectName*MigrationsDbContext which is responsible to maintain and migrate the database schema. This DbContext basically calls extension methods of the depended modules to configure their database tables. - -Open the *YourProjectName*MigrationsDbContext and change the `builder.ConfigureIdentityServer();` line as shown below: - -````csharp -builder.ConfigureIdentityServer(options => -{ - options.DatabaseProvider = EfCoreDatabaseProvider.MySql; -}); -```` - -Then `ConfigureIdentityServer()` method will set the field lengths to not exceed the MySQL limits. Refer to related module documentation if you have any problem while creating or executing the database migrations. - ## Re-Generate the Migrations The startup template uses [Entity Framework Core's Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/). EF Core Migrations depend on the selected DBMS provider. So, changing the DBMS provider will cause the migration fails. diff --git a/docs/en/Entity-Framework-Core-Oracle.md b/docs/en/Entity-Framework-Core-Oracle.md new file mode 100644 index 0000000000..3c74cb6d8c --- /dev/null +++ b/docs/en/Entity-Framework-Core-Oracle.md @@ -0,0 +1,67 @@ +# Switch to EF Core Oracle Provider + +This document explains how to switch to the **Oracle** database provider for **[the application startup template](Startup-Templates/Application.md)** which comes with SQL Server provider pre-configured. + +> This document uses a paid library of [Devart](https://www.devart.com/dotconnect/oracle/) company, because it is the only library for Oracle that supports EF Core 3.x. + +## Replace the Volo.Abp.EntityFrameworkCore.SqlServer Package + +`.EntityFrameworkCore` project in the solution depends on the [Volo.Abp.EntityFrameworkCore.SqlServer](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore.SqlServer) NuGet package. Remove this package and add the same version of the [Volo.Abp.EntityFrameworkCore.Oracle.Devart](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore.Oracle.Devart) package. + +## Replace the Module Dependency + +Find ***YourProjectName*EntityFrameworkCoreModule** class inside the `.EntityFrameworkCore` project, remove `typeof(AbpEntityFrameworkCoreSqlServerModule)` from the `DependsOn` attribute, add `typeof(AbpEntityFrameworkCoreOracleDevartModule)` (also replace `using Volo.Abp.EntityFrameworkCore.SqlServer;` with `using Volo.Abp.EntityFrameworkCore.Oracle.Devart;`). + +## UseOracle() + +Find `UseSqlServer()` calls in your solution, replace with `UseOracle()`. Check the following files: + +* *YourProjectName*EntityFrameworkCoreModule.cs inside the `.EntityFrameworkCore` project. +* *YourProjectName*MigrationsDbContextFactory.cs inside the `.EntityFrameworkCore.DbMigrations` project. + + +In the `CreateDbContext()` method of the *YourProjectName*MigrationsDbContextFactory.cs, replace the following code block + +``` +var builder = new DbContextOptionsBuilder() + .UseSqlServer(configuration.GetConnectionString("Default")); +``` + +with this one +``` +var builder = (DbContextOptionsBuilder) + new DbContextOptionsBuilder().UseOracle + ( + configuration.GetConnectionString("Default") + ); +``` + +> Depending on your solution structure, you may find more code files need to be changed. + +## Change the Connection Strings + +Oracle connection strings are different than SQL Server connection strings. So, check all `appsettings.json` files in your solution and replace the connection strings inside them. See the [connectionstrings.com]( https://www.connectionstrings.com/oracle/ ) for details of Oracle connection string options. + +You typically will change the `appsettings.json` inside the `.DbMigrator` and `.Web` projects, but it depends on your solution structure. + +A sample connection string for Oracle: +``` +Data Source=localhost;User Id=myuser;Password=mypassword; +``` + +## Re-Generate the Migrations + +The startup template uses [Entity Framework Core's Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/) by default. +EF Core Migrations depend on the selected DBMS provider. Changing the DBMS provider, may not work with the existing migrations. + +* Delete the `Migrations` folder under the `.EntityFrameworkCore.DbMigrations` project and re-build the solution. +* Run `Add-Migration "Initial"` on the Package Manager Console window (select the `.DbMigrator` (or `.Web`) project as the startup project in the Solution Explorer and select the `.EntityFrameworkCore.DbMigrations` project as the default project in the Package Manager Console). + +This will scaffold a new migration for Oracle. + +Run the `.DbMigrator` project to create the database, apply the changes and seed the initial data. + +## Run the Application + +It is ready. Just run the application and enjoy coding. + diff --git a/docs/en/Entity-Framework-Core-Other-DBMS.md b/docs/en/Entity-Framework-Core-Other-DBMS.md index d902a8d93e..d9190df844 100644 --- a/docs/en/Entity-Framework-Core-Other-DBMS.md +++ b/docs/en/Entity-Framework-Core-Other-DBMS.md @@ -6,6 +6,7 @@ ABP framework provides **integration packages** for some common DBMSs to make th * [MySQL](Entity-Framework-Core-MySQL.md) * [PostgreSQL](Entity-Framework-Core-PostgreSQL.md) +* [Oracle](Entity-Framework-Core-Oracle.md) * [SQLite](Entity-Framework-Core-SQLite.md) However, you can configure your DBMS provider **without** these integration packages. While using the integration package is always recommended (it also makes standard for the depended version across different modules), you can do it manually if there is no integration package for your DBMS provider. diff --git a/docs/en/Getting-Started.md b/docs/en/Getting-Started.md index fa7f47f276..4011fd287c 100644 --- a/docs/en/Getting-Started.md +++ b/docs/en/Getting-Started.md @@ -398,7 +398,7 @@ Wait Expo CLI to start. Expo CLI opens the management interface on the `http://l In the above management interface, you can start the application with an Android emulator, an iOS simulator or a physical phone by the scan the QR code with the [Expo Client](https://expo.io/tools#client). -> See the [Android Studio Emulator](https://docs.expo.io/versions/v36.0.0/workflow/android-studio-emulator/), [iOS Simulator](https://docs.expo.io/versions/v36.0.0/workflow/ios-simulator/) documents on expo.io. +> See the [Android Studio Emulator](https://docs.expo.io/workflow/android-simulator/), [iOS Simulator](https://docs.expo.io/workflow/ios-simulator/) documents on expo.io. ![React Native login screen on iPhone 11](images/rn-login-iphone.png) diff --git a/docs/en/Index.md b/docs/en/Index.md index 48935d70c3..be1ba0c58e 100644 --- a/docs/en/Index.md +++ b/docs/en/Index.md @@ -8,8 +8,8 @@ Explore the left navigation menu to deep dive in the documentation. Easiest way to start a new project with ABP is to use the startup templates: -* [ASP.NET Core MVC (Razor Pages) UI Startup Template](Getting-Started-AspNetCore-MVC-Template.md) -* [Angular UI Startup Template](Getting-Started-Angular-Template.md) +* [ASP.NET Core MVC (Razor Pages) UI Startup Template](Getting-Started?UI=MVC&DB=EF&Tiered=No) +* [Angular UI Startup Template](Getting-Started?UI=NG&DB=EF&Tiered=No) If you want to start from scratch (with an empty project) then manually install the ABP Framework and use the following tutorials: diff --git a/docs/en/Modules/Index.md b/docs/en/Modules/Index.md index 2ffb509667..71bf58333c 100644 --- a/docs/en/Modules/Index.md +++ b/docs/en/Modules/Index.md @@ -23,6 +23,7 @@ There are some **free and open source** application modules developed and mainta * **[Setting Management](Setting-Management.md)**: Used to persist and manage the [settings](../Settings.md). * **Tenant Management**: Manages tenants for a [multi-tenant](../Multi-Tenancy.md) application. * **Users**: Abstract users, so other modules can depend on this module instead of the Identity module. +* [**Virtual File Explorer**](Virtual-File-Explorer.md): Provided a simple UI to view files in [virtual file system](../Virtual-File-System.md). See [the GitHub repository](https://github.com/abpframework/abp/tree/master/modules) for source code of all modules. diff --git a/docs/en/Modules/Virtual-File-Explorer.md b/docs/en/Modules/Virtual-File-Explorer.md new file mode 100644 index 0000000000..81059ff870 --- /dev/null +++ b/docs/en/Modules/Virtual-File-Explorer.md @@ -0,0 +1,80 @@ +# Virtual File Explorer Module + +## What is Virtual File Explorer Module? + +Virtual File Explorer Module provided a simple UI to view all files in [virtual file system](../Virtual-File-System.md). + +> Virtual File Explorer Module is not installed for [the startup templates](../Startup-Templates/Index.md). So, you need to manually add this module to your application. + +### Installation + +#### 1- Referencing Virtual File Explorer Module Packages + +It is recommended to use the ABP CLI to install the module, open the CMD window in the solution file (`.sln`) directory, and run the following command: + +`abp add-module Volo.VirtualFileExplorer` + +Or you can also manually install nuget package to `Acme.MyProject.Web` project: + +* Install [Volo.Abp.VirtualFileExplorer.Web](https://www.nuget.org/packages/Volo.Abp.VirtualFileExplorer.Web/) nuget package to `Acme.MyProject.Web` project. + + `Install-Package Volo.Abp.VirtualFileExplorer.Web` + +#### 2- Adding Module Dependencies + + * Open `MyProjectWebModule.cs`and add `typeof(AbpVirtualFileExplorerWebModule)` as shown below; + + ```csharp + [DependsOn( + typeof(AbpVirtualFileExplorerWebModule), + typeof(MyProjectApplicationModule), + typeof(MyProjectEntityFrameworkCoreModule), + typeof(AbpAutofacModule), + typeof(AbpIdentityWebModule), + typeof(AbpAccountWebModule), + typeof(AbpAspNetCoreMvcUiBasicThemeModule) + )] + public class MyProjectWebModule : AbpModule + { + //... + } + ``` + +#### 3- Adding NPM Package + + * Open `package.json` and add `@abp/virtual-file-explorer": "^2.9.0` as shown below: + + ```json + { + "version": "1.0.0", + "name": "my-app", + "private": true, + "dependencies": { + "@abp/aspnetcore.mvc.ui.theme.basic": "^2.9.0", + "@abp/virtual-file-explorer": "^2.9.0" + } + } + ``` + + Then open the command line terminal in the `Acme.MyProject.Web` project folder and run the following command: + + 1. `yarn` + 2. `gulp` + +That's all,Now run the application and Navigate to `/VirtualFileExplorer`. You will see virtual file explorer page: + +![Virtual-File-Explorer](../images/virtual-file-explorer.png) + +### Options + +You can disabled virtual file explorer module via `AbpVirtualFileExplorerOptions` options: + +```csharp +public override void PreConfigureServices(ServiceConfigurationContext context) +{ + PreConfigure(options => + { + options.IsEnabled = false; + }); +} +``` \ No newline at end of file diff --git a/docs/en/Multi-Tenancy.md b/docs/en/Multi-Tenancy.md index 6adef9b214..77ca49961b 100644 --- a/docs/en/Multi-Tenancy.md +++ b/docs/en/Multi-Tenancy.md @@ -303,10 +303,10 @@ TODO:... Volo.Abp.AspNetCore.MultiTenancy package adds following tenant resolvers to determine current tenant from current web request (ordered by priority). These resolvers are added and work out of the box: * **CurrentUserTenantResolveContributor**: Gets the tenant id from claims of the current user, if the current user has logged in. **This should always be the first contributor for security**. -* **QueryStringTenantResolver**: Tries to find current tenant id from query string parameter. Parameter name is "__tenant" by default. -* **RouteTenantResolver**: Tries to find current tenant id from route (URL path). Variable name is "__tenant" by default. So, if you defined a route with this variable, then it can determine the current tenant from the route. -* **HeaderTenantResolver**: Tries to find current tenant id from HTTP header. Header name is "__tenant" by default. -* **CookieTenantResolver**: Tries to find current tenant id from cookie values. Cookie name is "__tenant" by default. +* **QueryStringTenantResolveContributor**: Tries to find current tenant id from query string parameter. Parameter name is "__tenant" by default. +* **RouteTenantResolveContributor**: Tries to find current tenant id from route (URL path). Variable name is "__tenant" by default. So, if you defined a route with this variable, then it can determine the current tenant from the route. +* **HeaderTenantResolveContributor**: Tries to find current tenant id from HTTP header. Header name is "__tenant" by default. +* **CookieTenantResolveContributor**: Tries to find current tenant id from cookie values. Cookie name is "__tenant" by default. > If you use nginx as a reverse proxy server, please note that if `TenantKey` contains an underscore or other special characters, there may be a problem, please refer to: http://nginx.org/en/docs/http/ngx_http_core_module.html#ignore_invalid_headers @@ -346,7 +346,7 @@ namespace MyCompany.MyProject //Subdomain format: {0}.mydomain.com //Adding as the second highest priority resolver after 'CurrentUserTenantResolveContributor' to //ensure the user cannot impersonate a different tenant. - options.TenantResolvers.Insert(1, new DomainTenantResolver("{0}.mydomain.com")); + options.TenantResolvers.Insert(1, new DomainTenantResolveContributor("{0}.mydomain.com")); }); //... @@ -357,7 +357,7 @@ namespace MyCompany.MyProject {0} is the the placeholder to determine current tenant's unique name. -Instead of ``options.TenantResolvers.Insert(1, new DomainTenantResolver("{0}.mydomain.com"));`` you can use this shortcut: +Instead of ``options.TenantResolvers.Insert(1, new DomainTenantResolveContributor("{0}.mydomain.com"));`` you can use this shortcut: ````C# options.AddDomainTenantResolver("{0}.mydomain.com"); diff --git a/docs/en/Object-To-Object-Mapping.md b/docs/en/Object-To-Object-Mapping.md index b7463607e9..f5ca805611 100644 --- a/docs/en/Object-To-Object-Mapping.md +++ b/docs/en/Object-To-Object-Mapping.md @@ -162,6 +162,61 @@ public class MyProfile : Profile It is suggested to use the `MapExtraProperties()` method if both classes are extensible objects (implement the `IHasExtraProperties` interface). See the [object extension document](Object-Extensions.md) for more. +### Other Useful Extension Methods + +There are some more extension methods those can simplify your mapping code. + +#### Ignoring Audit Properties + +It is common to ignore audit properties when you map an object to another. + +Assume that you need to map a `ProductDto` ([DTO](Data-Transfer-Objects.md)) to a `Product` [entity](Entities.md) and the entity is inheriting from the `AuditedEntity` class (which provides properties like `CreationTime`, `CreatorId`, `IHasModificationTime`... etc). + +You probably want to ignore these base properties while mapping from the DTO. You can use `IgnoreAuditedObjectProperties()` method to ignore all audit properties (instead of manually ignoring them one by one): + +````csharp +public class MyProfile : Profile +{ + public MyProfile() + { + CreateMap() + .IgnoreAuditedObjectProperties(); + } +} +```` + +There are more extension methods like `IgnoreFullAuditedObjectProperties()` and `IgnoreCreationAuditedObjectProperties()` those can be used based on your entity type. + +> See the "*Base Classes & Interfaces for Audit Properties*" section in the [entities document](Entities.md) to know more about auditing properties. + +#### Ignoring Other Properties + +In AutoMapper, you typically write such a mapping code to ignore a property: + +````csharp +public class MyProfile : Profile +{ + public MyProfile() + { + CreateMap() + .ForMember(x => x.CreationTime, map => map.Ignore()); + } +} +```` + +We found it unnecessarily long and created the `Ignore()` extension method: + +````csharp +public class MyProfile : Profile +{ + public MyProfile() + { + CreateMap() + .Ignore(x => x.CreationTime); + } +} +```` + ## Advanced Topics ### IObjectMapper Interface diff --git a/docs/en/Road-Map.md b/docs/en/Road-Map.md new file mode 100644 index 0000000000..4ef8500e83 --- /dev/null +++ b/docs/en/Road-Map.md @@ -0,0 +1,11 @@ +# ABP Framework Road Map + +You can always check the milestone planning and the prioritized backlog issues on [the GitHub repository](https://github.com/abpframework/abp/milestones) for a detailed road map. + +While we will **continue to add other exciting features**, we will work on the following major items in the **middle term**: + +* **gRPC integration** and implementation for all the pre-built modules. +* **Blazor UI** for the framework and all the pre-built modules. +* **.NET 5.0**! As Microsoft has announced that the .NET 5.0 will be released in November 2020, we will prepare for this change before and move to the .NET 5.0 just after Microsoft releases it. We hope a smooth transition. + +Please create an issue on [the GitHub repository](https://github.com/abpframework/abp) for your feature requests, but first search in in the existing issues. \ No newline at end of file diff --git a/docs/en/Tutorials/Part-1.md b/docs/en/Tutorials/Part-1.md index be812cc162..bd08cbdcd5 100644 --- a/docs/en/Tutorials/Part-1.md +++ b/docs/en/Tutorials/Part-1.md @@ -607,7 +607,7 @@ namespace Acme.BookStore.Web.Menus { //<-- added the below code context.Menu.AddItem( - new ApplicationMenuItem("BooksStore", l["Menu:BookStore"]) + new ApplicationMenuItem("BooksStore", l["Menu:BookStore"], icon: "fa fa-book") .AddItem( new ApplicationMenuItem("BooksStore.Books", l["Menu:Books"], url: "/Books") ) @@ -646,7 +646,16 @@ Open the `en.json` (*English translations*) file and add the below localization "Type": "Type", "Price": "Price", "CreationTime": "Creation time", - "AreYouSureToDelete": "Are you sure you want to delete this item?" + "AreYouSureToDelete": "Are you sure you want to delete this item?", + "Enum:BookType:0": "Undefined", + "Enum:BookType:1": "Adventure", + "Enum:BookType:2": "Biography", + "Enum:BookType:3": "Dystopia", + "Enum:BookType:4": "Fantastic", + "Enum:BookType:5": "Horror", + "Enum:BookType:6": "Science", + "Enum:BookType:7": "ScienceFiction", + "Enum:BookType:8": "Poetry" } } ```` @@ -716,7 +725,11 @@ $(function () { ajax: abp.libs.datatables.createAjax(acme.bookStore.book.getList), columnDefs: [ { data: "name" }, - { data: "type" }, + { data: "type", + render: function(data){ + return l('Enum:BookType:' + data); + } + }, { data: "publishDate" }, { data: "price" }, { data: "creationTime" } diff --git a/docs/en/UI/Angular/Component-Replacement.md b/docs/en/UI/Angular/Component-Replacement.md index fb85aa476e..b11acbc6b5 100644 --- a/docs/en/UI/Angular/Component-Replacement.md +++ b/docs/en/UI/Angular/Component-Replacement.md @@ -1,10 +1,10 @@ -## Component Replacement +# Component Replacement You can replace some ABP components with your custom components. The reason that you **can replace** but **cannot customize** default ABP components is disabling or changing a part of that component can cause problems. So we named those components as _Replaceable Components_. -### How to Replace a Component +## How to Replace a Component Create a new component that you want to use instead of an ABP component. Add that component to `declarations` and `entryComponents` in the `AppModule`. @@ -15,16 +15,20 @@ import { ..., AddReplaceableComponent } from '@abp/ng.core'; // imported AddRepl import { eIdentityComponents } from '@abp/ng.identity'; // imported eIdentityComponents enum import { Store } from '@ngxs/store'; // imported Store //... -export class AppComponent { + +@Component(/* component metadata */) +export class AppComponent implements OnInit { constructor(..., private store: Store) {} // injected Store ngOnInit() { + // added dispatch this.store.dispatch( new AddReplaceableComponent({ component: YourNewRoleComponent, key: eIdentityComponents.Roles, }), ); + //... } } @@ -33,13 +37,13 @@ export class AppComponent { ![Example Usage](./images/component-replacement.gif) -### How to Replace a Layout +## How to Replace a Layout -Each ABP theme module has 3 layouts named `ApplicationLayoutComponent`, `AccountLayoutComponent`, `EmptyLayoutComponent`. These layouts can be replaced with the same way. +Each ABP theme module has 3 layouts named `ApplicationLayoutComponent`, `AccountLayoutComponent`, `EmptyLayoutComponent`. These layouts can be replaced the same way. > A layout component template should contain `` element. -The below example describes how to replace the `ApplicationLayoutComponent`: +The example below describes how to replace the `ApplicationLayoutComponent`: Run the following command to generate a layout in `angular` folder: @@ -55,7 +59,7 @@ Add the following code in your layout template (`my-layout.component.html`) wher ``` -Open the `app.component.ts` and add the below content: +Open `app.component.ts` in `src/app` folder and modify it as shown below: ```js import { ..., AddReplaceableComponent } from '@abp/ng.core'; // imported AddReplaceableComponent @@ -63,11 +67,13 @@ import { eThemeBasicComponents } from '@abp/ng.theme.basic'; // imported eThemeB import { MyApplicationLayoutComponent } from './shared/my-application-layout/my-application-layout.component'; // imported MyApplicationLayoutComponent import { Store } from '@ngxs/store'; // imported Store //... -export class AppComponent { + +@Component(/* component metadata */) +export class AppComponent implements OnInit { constructor(..., private store: Store) {} // injected Store ngOnInit() { - // added below content + // added dispatch this.store.dispatch( new AddReplaceableComponent({ component: MyApplicationLayoutComponent, @@ -80,6 +86,463 @@ export class AppComponent { } ``` +### Layout Components + +![Layout Components](./images/layout-components.png) + +#### How to Replace LogoComponent + +![LogoComponent](./images/logo-component.png) + +Run the following command in `angular` folder to create a new component called `LogoComponent`. + +```bash +yarn ng generate component logo --inlineTemplate --inlineStyle --entryComponent + +# You don't need the --entryComponent option in Angular 9 +``` + +Open the generated `logo.component.ts` in `src/app/logo` folder and replace its content with the following: + +```js +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-logo', + template: ` + + + logo + + `, +}) +export class LogoComponent {} +``` + +Open `app.component.ts` in `src/app` folder and modify it as shown below: + +```js +import { ..., AddReplaceableComponent } from '@abp/ng.core'; // imported AddReplaceableComponent +import { Store } from '@ngxs/store'; // imported Store +import { LogoComponent } from './logo/logo.component'; // imported NavItemsComponent +import { eThemeBasicComponents } from '@abp/ng.theme.basic'; // imported eThemeBasicComponents +//... + +@Component(/* component metadata */) +export class AppComponent implements OnInit { + constructor(..., private store: Store) {} // injected Store + + ngOnInit() { + //... + + // added dispatch + this.store.dispatch( + new AddReplaceableComponent({ + component: LogoComponent, + key: eThemeBasicComponents.Logo, + }), + ); + } +} +``` + +The final UI looks like below: + +![New logo](./images/replaced-logo-component.png) + +#### How to Replace RoutesComponent + +![RoutesComponent](./images/routes-component.png) + +Run the following command in `angular` folder to create a new component called `RoutesComponent`. + +```bash +yarn ng generate component routes --entryComponent + +# You don't need the --entryComponent option in Angular 9 +``` + +Open the generated `routes.component.ts` in `src/app/routes` folder and replace its content with the following: + +```js +import { ABP, ReplaceableComponents } from '@abp/ng.core'; +import { + Component, + HostBinding, + Inject, + Renderer2, + TrackByFunction, + AfterViewInit, +} from '@angular/core'; +import { fromEvent } from 'rxjs'; +import { debounceTime } from 'rxjs/operators'; + +@Component({ + selector: 'app-routes', + templateUrl: 'routes.component.html', +}) +export class RoutesComponent implements AfterViewInit { + @HostBinding('class.mx-auto') + marginAuto = true; + + smallScreen = window.innerWidth < 992; + + constructor(private renderer: Renderer2) {} + + ngAfterViewInit() { + fromEvent(window, 'resize') + .pipe(debounceTime(150)) + .subscribe(() => { + this.smallScreen = window.innerWidth < 992; + }); + } +} +``` + +Open the generated `routes.component.html` in `src/app/routes` folder and replace its content with the following: + +```html + +``` + +Open `app.component.ts` in `src/app` folder and modify it as shown below: + +```js +import { ..., AddReplaceableComponent } from '@abp/ng.core'; // imported AddReplaceableComponent +import { Store } from '@ngxs/store'; // imported Store +import { RoutesComponent } from './routes/routes.component'; // imported NavItemsComponent +import { eThemeBasicComponents } from '@abp/ng.theme.basic'; // imported eThemeBasicComponents +//... + +@Component(/* component metadata */) +export class AppComponent implements OnInit { + constructor(..., private store: Store) {} // injected Store + + ngOnInit() { + //... + + // added dispatch + this.store.dispatch( + new AddReplaceableComponent({ + component: RoutesComponent, + key: eThemeBasicComponents.Routes, + }), + ); + } +} +``` + +The final UI looks like below: + +![New routes](./images/replaced-routes-component.png) + +#### How to Replace NavItemsComponent + +![NavItemsComponent](./images/nav-items-component.png) + +Run the following command in `angular` folder to create a new component called `NavItemsComponent`. + +```bash +yarn ng generate component nav-items --entryComponent + +# You don't need the --entryComponent option in Angular 9 +``` + +Open the generated `nav-items.component.ts` in `src/app/nav-items` folder and replace the content with the following: + +```js +import { + ApplicationConfiguration, + AuthService, + ConfigState, + SessionState, + SetLanguage, +} from '@abp/ng.core'; +import { Component, AfterViewInit } from '@angular/core'; +import { Navigate, RouterState } from '@ngxs/router-plugin'; +import { Select, Store } from '@ngxs/store'; +import { Observable, fromEvent } from 'rxjs'; +import { map, debounceTime } from 'rxjs/operators'; +import snq from 'snq'; + +@Component({ + selector: 'app-nav-items', + templateUrl: 'nav-items.component.html', +}) +export class NavItemsComponent implements AfterViewInit { + @Select(ConfigState.getOne('currentUser')) + currentUser$: Observable; + + @Select(ConfigState.getDeep('localization.languages')) + languages$: Observable; + + smallScreen = window.innerWidth < 992; + + get defaultLanguage$(): Observable { + return this.languages$.pipe( + map( + languages => + snq( + () => languages.find(lang => lang.cultureName === this.selectedLangCulture).displayName, + ), + '', + ), + ); + } + + get dropdownLanguages$(): Observable { + return this.languages$.pipe( + map( + languages => + snq(() => languages.filter(lang => lang.cultureName !== this.selectedLangCulture)), + [], + ), + ); + } + + get selectedLangCulture(): string { + return this.store.selectSnapshot(SessionState.getLanguage); + } + + constructor(private store: Store, private authService: AuthService) {} + + ngAfterViewInit() { + fromEvent(window, 'resize') + .pipe(debounceTime(150)) + .subscribe(() => { + this.smallScreen = window.innerWidth < 992; + }); + } + + onChangeLang(cultureName: string) { + this.store.dispatch(new SetLanguage(cultureName)); + } + + logout() { + this.authService.logout().subscribe(() => { + this.store.dispatch( + new Navigate(['/'], null, { + state: { redirectUrl: this.store.selectSnapshot(RouterState).state.url }, + }), + ); + }); + } +} +``` + +Open the generated `nav-items.component.html` in `src/app/nav-items` folder and replace the content with the following: + +```html + +``` + +Open `app.component.ts` in `src/app` folder and modify it as shown below: + +```js +import { ..., AddReplaceableComponent } from '@abp/ng.core'; // imported AddReplaceableComponent +import { Store } from '@ngxs/store'; // imported Store +import { NavItemsComponent } from './nav-items/nav-items.component'; // imported NavItemsComponent +import { eThemeBasicComponents } from '@abp/ng.theme.basic'; // imported eThemeBasicComponents +//... + +@Component(/* component metadata */) +export class AppComponent implements OnInit { + constructor(..., private store: Store) {} // injected Store + + ngOnInit() { + //... + + // added dispatch + this.store.dispatch( + new AddReplaceableComponent({ + component: NavItemsComponent, + key: eThemeBasicComponents.NavItems, + }), + ); + } +} +``` + +The final UI looks like below: + +![New nav-items](./images/replaced-nav-items-component.png) + ## What's Next? - [Custom Setting Page](./Custom-Setting-Page.md) diff --git a/docs/en/UI/Angular/Localization.md b/docs/en/UI/Angular/Localization.md index d627f466ba..c8b73b08f3 100644 --- a/docs/en/UI/Angular/Localization.md +++ b/docs/en/UI/Angular/Localization.md @@ -130,6 +130,68 @@ this.store.selectSnapshot( Localization resources are stored in the `localization` property of `ConfigState`. +## RTL Support + +As of v2.9 ABP has RTL support. If you are generating a new project with v2.9 and above, everything is set, you do not need to do any changes. If you are migrating your project from an earlier version, please follow the 2 steps below: + +#### Step 1. Create Chunks for Bootstrap LTR and RTL + +Find [styles configuration in angular.json](https://angular.io/guide/workspace-config#style-script-config) and make sure the chunks in your project has `bootstrap-rtl.min` and `bootstrap-ltr.min` as shown below. + +```json +{ + "projects": { + "MyProjectName": { + "architect": { + "build": { + "options": { + "styles": [ + { + "input": "node_modules/@abp/ng.theme.shared/styles/bootstrap-rtl.min.css", + "inject": false, + "bundleName": "bootstrap-rtl.min" + }, + { + "input": "node_modules/bootstrap/dist/css/bootstrap.min.css", + "inject": true, + "bundleName": "bootstrap-ltr.min" + }, + { + "input": "node_modules/@fortawesome/fontawesome-free/css/all.min.css", + "inject": true, + "bundleName": "fontawesome-all.min" + }, + { + "input": "node_modules/@fortawesome/fontawesome-free/css/v4-shims.min.css", + "inject": true, + "bundleName": "fontawesome-v4-shims.min" + }, + "apps/dev-app/src/styles.scss" + ], + } + } + } + } + } +} + +#### Step 2. Clear Lazy Loaded Fontawesome in AppComponent + +If you have created and injected chunks for Fontawesome as seen above, you no longer need the lazy loading in the `AppComponent` which was implemented before v2.9. Simply remove them. The `AppComponent` in the template of the new version looks like this: + +```js +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-root', + template: ` + + + `, +}) +export class AppComponent {} +``` + ## See Also diff --git a/docs/en/UI/Angular/images/layout-components.png b/docs/en/UI/Angular/images/layout-components.png new file mode 100644 index 0000000000..3193b47bd1 Binary files /dev/null and b/docs/en/UI/Angular/images/layout-components.png differ diff --git a/docs/en/UI/Angular/images/logo-component.png b/docs/en/UI/Angular/images/logo-component.png new file mode 100644 index 0000000000..5c11fbf617 Binary files /dev/null and b/docs/en/UI/Angular/images/logo-component.png differ diff --git a/docs/en/UI/Angular/images/nav-items-component.png b/docs/en/UI/Angular/images/nav-items-component.png new file mode 100644 index 0000000000..d3e69916b6 Binary files /dev/null and b/docs/en/UI/Angular/images/nav-items-component.png differ diff --git a/docs/en/UI/Angular/images/replaced-logo-component.png b/docs/en/UI/Angular/images/replaced-logo-component.png new file mode 100644 index 0000000000..4661b390a8 Binary files /dev/null and b/docs/en/UI/Angular/images/replaced-logo-component.png differ diff --git a/docs/en/UI/Angular/images/replaced-nav-items-component.png b/docs/en/UI/Angular/images/replaced-nav-items-component.png new file mode 100644 index 0000000000..e4cc0339ae Binary files /dev/null and b/docs/en/UI/Angular/images/replaced-nav-items-component.png differ diff --git a/docs/en/UI/Angular/images/replaced-routes-component.png b/docs/en/UI/Angular/images/replaced-routes-component.png new file mode 100644 index 0000000000..3a6336afe0 Binary files /dev/null and b/docs/en/UI/Angular/images/replaced-routes-component.png differ diff --git a/docs/en/UI/Angular/images/routes-component.png b/docs/en/UI/Angular/images/routes-component.png new file mode 100644 index 0000000000..b708e86d36 Binary files /dev/null and b/docs/en/UI/Angular/images/routes-component.png differ diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index 47789023c4..9db7916f11 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -440,6 +440,10 @@ "text": "To PostgreSQL", "path": "Entity-Framework-Core-PostgreSQL.md" }, + { + "text": "To Oracle", + "path": "Entity-Framework-Core-Oracle.md" + }, { "text": "To SQLite", "path": "Entity-Framework-Core-SQLite.md" @@ -460,6 +464,15 @@ } ] }, + { + "text": "Real Time", + "items": [ + { + "text": "SignalR Integration", + "path": "SignalR-Integration.md" + } + ] + }, { "text": "Background", "items": [ @@ -531,13 +544,14 @@ "text": "Microservice Architecture", "path": "Microservice-Architecture.md" }, - { - "text": "Testing" - }, { "text": "Nightly Builds", "path": "Nightly-Builds.md" }, + { + "text": "Road Map", + "path": "Road-Map.md" + }, { "text": "Contribution Guide", "path": "Contribution/Index.md" diff --git a/docs/en/images/virtual-file-explorer.png b/docs/en/images/virtual-file-explorer.png new file mode 100644 index 0000000000..e49d0f2111 Binary files /dev/null and b/docs/en/images/virtual-file-explorer.png differ diff --git a/docs/zh-Hans/Background-Jobs-Quartz.md b/docs/zh-Hans/Background-Jobs-Quartz.md index 906d0b2d1f..61de4548f5 100644 --- a/docs/zh-Hans/Background-Jobs-Quartz.md +++ b/docs/zh-Hans/Background-Jobs-Quartz.md @@ -40,7 +40,7 @@ public class YourModule : AbpModule ## 配置 -Quartz是一个可配置的类库,对此ABP框架提供了 `AbpQuartzPreOptions`. 你可以在模块预配置此选项,ABP在初始化Quartz模块时将使用它. 例: +Quartz是一个可配置的类库,对此ABP框架提供了 `AbpQuartzOptions`. 你可以在模块预配置此选项,ABP在初始化Quartz模块时将使用它. 例: ````csharp [DependsOn( @@ -53,7 +53,7 @@ public class YourModule : AbpModule { var configuration = context.Services.GetConfiguration(); - PreConfigure(options => + PreConfigure(options => { options.Properties = new NameValueCollection { @@ -70,4 +70,52 @@ public class YourModule : AbpModule } ```` -Quartz**默认**将作业与调度信息存储在**内存**中,示例中我们使用[选项模式](Options.md)的预配置将其更改为存储到数据库中. 有关Quartz的更多配置请参阅[Quartz文档](https://www.quartz-scheduler.net/documentation/quartz-3.x/tutorial/index.html). \ No newline at end of file +Quartz**默认**将作业与调度信息存储在**内存**中,示例中我们使用[选项模式](Options.md)的预配置将其更改为存储到数据库中. 有关Quartz的更多配置请参阅[Quartz文档](https://www.quartz-scheduler.net/documentation/quartz-3.x/tutorial/index.html). + +## 异常处理 + +### 默认异常处理策略 + +当后台作业发生异常时ABP提供了**默认**异常处理策略,它会为你的作业重试3次,每次间隔3秒. 你可以通过 `AbpBackgroundJobQuartzOptions` 更改默认重试次数与间隔时间: + +```csharp +[DependsOn( + //...other dependencies + typeof(AbpBackgroundJobsQuartzModule) //Add the new module dependency + )] +public class YourModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.RetryCount = 1; + options.RetryIntervalMillisecond = 1000; + }); + } +} +``` + +### 自定义异常处理策略 + +你可以通过 `AbpBackgroundJobQuartzOptions` 选项自定义异常处理策略: + +```csharp +[DependsOn( + //...other dependencies + typeof(AbpBackgroundJobsQuartzModule) //Add the new module dependency + )] +public class YourModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.RetryStrategy = async (retryIndex, executionContext, exception) => + { + // customize exception handling + }; + }); + } +} +``` \ No newline at end of file diff --git a/docs/zh-Hans/Background-Workers-Quartz.md b/docs/zh-Hans/Background-Workers-Quartz.md index 4799633b07..196d0e1d07 100644 --- a/docs/zh-Hans/Background-Workers-Quartz.md +++ b/docs/zh-Hans/Background-Workers-Quartz.md @@ -36,11 +36,11 @@ public class YourModule : AbpModule } ```` -### 配置 +## 配置 参阅[配置](Background-Jobs-Quartz.md#配置). -### 创建后台工作者 +## 创建后台工作者 后台工作者是一个继承自 `QuartzBackgroundWorkerBase` 基类的类. 一个简单的工作者如下所示: @@ -49,8 +49,8 @@ public class MyLogWorker : QuartzBackgroundWorkerBase { public MyLogWorker() { - JobDetail = JobBuilder.Create().Build(); - Trigger = TriggerBuilder.Create().StartNow().Build(); + JobDetail = JobBuilder.Create().WithIdentity(nameof(MyLogWorker)).Build(); + Trigger = TriggerBuilder.Create().WithIdentity(nameof(MyLogWorker)).StartNow().Build(); } public override Task Execute(IJobExecutionContext context) @@ -63,6 +63,82 @@ public class MyLogWorker : QuartzBackgroundWorkerBase 示例中我们重写了 `Execute` 方法写入日志. 后台工作者默认是**单例**. 如果你需要,也可以实现[依赖接口](Dependency-Injection.md#依赖接口)将其注册为其他的生命周期. -### 更多 +> 提示: 为后台工作者添加标识是最佳实践,Quartz根据标识区分作业. 如果未指定标识会重复添加工作者到Quartz. + +## 添加到BackgroundWorkerManager + +默认后台工作者会在应用程序启动时**自动**添加到 `BackgroundWorkerManager`,如果你想要手动添加,可以将 `AutoRegister` 属性值设置为 `false`: + +```` csharp +public class MyLogWorker : QuartzBackgroundWorkerBase +{ + public MyLogWorker() + { + AutoRegister = false; + JobDetail = JobBuilder.Create().WithIdentity(nameof(MyLogWorker)).Build(); + Trigger = TriggerBuilder.Create().WithIdentity(nameof(MyLogWorker)).StartNow().Build(); + } + + public override Task Execute(IJobExecutionContext context) + { + Logger.LogInformation("Executed MyLogWorker..!"); + return Task.CompletedTask; + } +} +```` + +尽管你可以使用 `AutoRegister` 跳过自动添加,但如果你想要全局禁用这样会比较繁琐. 你可以通过 `AbpBackgroundWorkerQuartzOptions` 选项全局禁用: + +```csharp +[DependsOn( + //...other dependencies + typeof(AbpBackgroundWorkersQuartzModule) //Add the new module dependency + )] +public class YourModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.IsAutoRegisterEnabled = false; + }); + } +} +``` + +## 高级主题 + +### 自定义ScheduleJob + +例如你有一个每10分钟执行一次的工作者,但由于服务器不可用30分钟导致工作者错过了3次执行,你想要在服务器恢复正常后执行所有错过的执行. 你应该这样定义你的工作者: + +```csharp +public class MyLogWorker : QuartzBackgroundWorkerBase +{ + public MyLogWorker() + { + JobDetail = JobBuilder.Create().WithIdentity(nameof(MyLogWorker)).Build(); + Trigger = TriggerBuilder.Create().WithIdentity(nameof(MyLogWorker)).WithSimpleSchedule(s=>s.WithIntervalInMinutes(1).RepeatForever().WithMisfireHandlingInstructionIgnoreMisfires()).Build(); + + ScheduleJob = async scheduler => + { + if (!await scheduler.CheckExists(JobDetail.Key)) + { + await scheduler.ScheduleJob(JobDetail, Trigger); + } + }; + } + + public override Task Execute(IJobExecutionContext context) + { + Logger.LogInformation("Executed MyLogWorker..!"); + return Task.CompletedTask; + } +} +``` + +在示例中我们定义了工作者执行间隔为10分钟,并且设置 `WithMisfireHandlingInstructionIgnoreMisfires` ,另外自定义 `ScheduleJob` 仅当工作者不存在时向quartz添加调度作业. + +## 更多 参阅Quartz[文档](https://www.quartz-scheduler.net/documentation/index.html)了解更多信息. \ No newline at end of file diff --git a/docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/Post.md b/docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/Post.md new file mode 100644 index 0000000000..0f97391eb8 --- /dev/null +++ b/docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/Post.md @@ -0,0 +1,247 @@ +# ABP框架 v2.7.0已经发布! + +**ABP框架**和**ABP商业版** v2.7已经发布.我们没有为2.4,2.5和2.6发布博客文章,所以这篇文章也将涵盖这几个版本中**新增内容**和过去的2个月里**我们完成了什么**. + +## 关于发布周期与开发 + +之前说过我们已经开始**每两个星期**发布一个新的次要功能版本,一般在星期四.我们的目标是尽快提供新功能. + +在过去的7-8周里, 我们在**1,300+次提交**中完成和合并了数百个issue和pull request, 这只是ABP框架的库.每日提交的次数不断增加: + +![github-contribution-graph](github-contribution-graph.png) + +ABP.IO平台正在快速增长,我们从社区获取的贡献越来越多. + +## ABP框架有哪些新增内容? + +### 对象扩展系统(Object Extending System) + +在过去的几个版本中,我们主要集中在以NuGet/NPM包使用现有模块时, 提供扩展的方法, + +对象扩展系统允许模块开发者创建可扩展的模块并允许应用开发者更容易地定制和扩展. + +例如,你可以这样为Identity模块的User实体添加两个扩展属性: + +````csharp +ObjectExtensionManager.Instance + .AddOrUpdate(options => + { + options.AddOrUpdateProperty("SocialSecurityNumber"); + options.AddOrUpdateProperty("IsSuperUser"); + } + ); +```` + +也很容易为这些属性定义验证规则: + +````csharp +ObjectExtensionManager.Instance + .AddOrUpdateProperty( + "SocialSecurityNumber", + options => + { + options.Attributes.Add(new RequiredAttribute()); + options.Attributes.Add( + new StringLengthAttribute(32) { + MinimumLength = 6 + } + ); + }); +```` + +你甚至可以编写自定义代码来验证属性.它会自动适用于应用服务, 控制器或页面的参数对象. + +实体的扩展属性通常存储在数据库表的一个单独的JSON格式的字段中,但是你也可以轻松地使用EF Core映射配置该属性为一个表字段: + +````csharp +ObjectExtensionManager.Instance + .AddOrUpdateProperty( + "SocialSecurityNumber", + options => + { + options.MapEfCore(b => b.HasMaxLength(32)); + } + ); +```` + +请参见有关该系统的详细信息[对象扩展文档](https://docs.abp.io/en/abp/latest/Object-Extensions). + +也可参见[自定义现有模块](https://docs.abp.io/en/abp/latest/Customizing-Application-Modules-Guide)指南,以了解所有可能的自定义选项. + +### 文本模板包 + +[Volo.Abp.TextTemplating](https://www.nuget.org/packages/Volo.Abp.TextTemplating)是v2.7.0中新加入的包.此前,[Volo.Abp.Emailing](https://www.nuget.org/packages/Volo.Abp.Emailing)包也有类似的功能,但它的功能是有限的,实验性质的并且和发送邮件紧密耦合在一起. + +新文本模板包允许你定义基于文本的模板, 可以很容易地本地化和重用.你可以定义布局模板并且与其它模板共享. + +目前,我们正在使用它发送邮件.一个需要发送邮件的模块通常定义了一个模板.如: + +````xml +

{{L "PasswordReset"}}

+ +

{{L "PasswordResetInfoInEmail"}}

+ + +```` + +这是一个典型的密码重置邮件模板. + +* 模板系统是基于开源的[Scriban库](https://github.com/lunet-io/scriban).因此,它支持if条件,循环等等. +* `model`用于将数据传递到模板(就像ASP.NET Core MVC). +* `L`是一个特殊函数用于本地化给定的字符串. + +为所有邮件使用相同的布局是一个典型应用.所以,你可以定义一个布局模板.这是框架自带的标准的布局: + +````xml + + + + + + + {{content}} + + +```` + +布局中应该有一个`{{content}}`区域用来呈现子内容(就像MVC中的`RenderBody()`). + +最终应用程序可以很容易地通过覆盖模板内容来自定义它. + +每当你需要渲染模板时,提供模板名称和model来使用`ITemplateRenderer`服务.详情请参见[文本模板文档](https://docs.abp.io/en/abp/latest/Text-Templating).我们甚至还为ABP商业版创建了UI(请参见下面的相关章节). + +### 订阅异常 + +ABP框架的[异常处理系统](https://docs.abp.io/en/abp/latest/Exception-Handling)会自动处理异常并且为客户端返回相应的结果.在某些情况下,你可能希望每当异常发生时有一个回调.通过这种方式,例如,你可以发送邮件或采取基于异常的任何动作. + +只需要在你的应用程序中创建一个从`ExceptionSubscriber`派生的类: + +````csharp +public class MyExceptionSubscriber : ExceptionSubscriber +{ + public override async Task HandleAsync(ExceptionNotificationContext context) + { + //TODO... + } +} +```` + +更多信息请参见[异常处理](https://docs.abp.io/en/abp/latest/Exception-Handling). + +### 其他 + +在之前的发布中框架也有很多小的功能和改进.这里列举几个: + +* 新增了`AbpLocalizationOptions.DefaultResourceType`用来设置应用程序的默认资源类型.通过这种方式,当资源未指定时, 本地化系统使用默认的资源.最新的应用程序启动模板已经配置了,你也可以为你现有的应用程序设置它. +* 权限定义新增了`IsEnabled`.通过这种方式,你可以从应用程序中完全禁用权限和隐藏相关功能.这可在一些应用程序中做为功能开关的方式.用法请参见[#3486](https://github.com/abpframework/abp/issues/3486). +* 框架中定义的所有本地化资源新增了荷兰语和德语本地化.感谢贡献者们. + +## ABP商业版有哪些新增内容? + +[ABP商业版](https://commercial.abp.io/)的目标是基于ABP框架项目提供预构建的应用程序功能,代码生成工具,专业的主题,先进的示例和高级支持. + +我们正在并行工作于ABP商业版与ABP框架功能对齐,并提供更多的模块,主题选项和工具. + +本节将介绍ABP商业版这边有哪些进展. + +### 模块实体扩展系统 + +模块实体扩展系统是相对于对象扩展系统(上面介绍的)的一个更高级别的API,并提供了一种简单的方法来向现有实体中增加扩展属性.一个新的扩展属性可以很容易地自动成为HTTP API和用户界面的一部分. + +例如:向Identity模块的User实体中添加`SocialSecurityNumber` + +````csharp +ObjectExtensionManager.Instance.Modules() + .ConfigureIdentity(identity => + { + identity.ConfigureUser(user => + { + user.AddOrUpdateProperty( //属性类型: string + "SocialSecurityNumber", //属性名 + property => + { + //验证规则 + property.Attributes.Add(new RequiredAttribute()); + property.Attributes.Add( + new StringLengthAttribute(64) { + MinimumLength = 4 + } + ); + + //...该属性的其它配置 + } + ); + }); + }); +```` + +仅通过这样的配置,用户界面就将具有新的属性(在表中和在创建/编辑表单中): + +![module-entity-extended-ui](module-entity-extended-ui.png) + +新属性可以轻松地本地化和验证.目前,它支持原始类型,如字符串,数字和布尔型,但我们计划添加更多高级场景(如导航/查找属性). + +请参阅[模块实体扩展](https://docs.abp.io/en/commercial/latest/guides/module-entity-extensions)指南来了解如何使用和配置的详细信息. + +#### 其他扩展点 + +还有其他一些预先定义的点来定制和扩展依赖模块的用户界面: + +* 你可以为数据表中的实体添加新动作(下方左侧的图). +* 你可以向页面的工具栏中添加新的按钮(或其他控件)(下方右侧的图). +* 你可以向数据表中添加自定义列. + +![abp-commercial-ui-extensions](abp-commercial-ui-extensions.png) + +请参阅[自定义模块](https://docs.abp.io/en/commercial/latest/guides/customizing-modules)指南,以了解所有可能的方式来定制依赖模块. + +### 文本模板管理模块 + +我们在v2.7中推出一个新的模块:[文本模板管理](https://docs.abp.io/en/commercial/latest/modules/text-template-management).它基本上是用来在用户界面上编辑文本/邮件模板(在ABP框架2.7中加入的),并保存更改到数据库中. + +一个密码重置邮件模板的内容编辑截图: + +![text-template-content-ui](text-template-content-ui.png) + +当创建新工程时, 这个模块已经预装了. + +### 实体历史视图 + +审计日志UI模块现在显示所有应用程序中的实体变更, 带有属性修改的细节. + +![audit-log-entity-changes](audit-log-entity-changes.png) + +当点击实体的动作菜单时, 你还可以查看实体的历史信息: + +![tenant-entity-changes](tenant-entity-changes.png) + +### 更多示例 + +我们正在创建ABP商业版更多高级的示例应用程序.其中一个是简易CRM, 将在几天内提供给商业客户. + +这是一个简易CRM仪表盘的截图: + +![easy-crm](easy-crm.png) + +具有帐户,联系人,产品组,产品,订单等. + +### 新模块 + +我们将继续改善现有模块和创建新的模块.除了上面介绍的新的[文本模板管理](https://docs.abp.io/en/commercial/latest/modules/text-template-management), 还有: + +* 我们最近发布了[支付模块](https://commercial.abp.io/modules/Volo.Payment),目前对接了PayU和的2Checkout支付网关.更多网关陆续添加. +* 我们已经创建了一个简单的[Twilio短信集成](https://docs.abp.io/en/commercial/latest/modules/twilio-sms)模块,以通过Twilio发送短信. +* 我们正在开发一个**聊天模块**, 将在未来几周内可用. +* 我们正在致力于为Identity模块增加**组织单元管理**系统, 用于创建分层组织单元(Domain层将是开源和免费的). + +更多ABP商业版和ABP框架的模块,主题和工具选项正在开发中. + +## ABP框架 VS ABP商业版 + +我们([Volosoft](https://volosoft.com/) - ABP.IO平台背后的核心团队),在ABP框架和ABP商业版上花费几乎相同的精力,我们认为ABP.IO平台是一个整体. + +[ABP框架](https://abp.io/)提供了所有的基础设施和应用程序的独立框架功能,使你更具生产力,专注于自己的业务代码,并实现软件开发最佳实践.它为你提供不重复作业的一个明确和舒适的开发经验. + +[ABP商业版](https://commercial.abp.io/)提供内置功能,主题和工具, 如果你的需求涉及到这些, 就可以节省你的时间.除此之外还有框架的高级支持和预构建模块. diff --git a/docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/abp-commercial-ui-extensions.png b/docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/abp-commercial-ui-extensions.png new file mode 100644 index 0000000000..52fe37a712 Binary files /dev/null and b/docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/abp-commercial-ui-extensions.png differ diff --git a/docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/audit-log-entity-changes.png b/docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/audit-log-entity-changes.png new file mode 100644 index 0000000000..e06b3300ee Binary files /dev/null and b/docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/audit-log-entity-changes.png differ diff --git a/docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/easy-crm.png b/docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/easy-crm.png new file mode 100644 index 0000000000..e40f399525 Binary files /dev/null and b/docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/easy-crm.png differ diff --git a/docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/github-contribution-graph.png b/docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/github-contribution-graph.png new file mode 100644 index 0000000000..ff3db2ad22 Binary files /dev/null and b/docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/github-contribution-graph.png differ diff --git a/docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/module-entity-extended-ui.png b/docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/module-entity-extended-ui.png new file mode 100644 index 0000000000..25621aabb7 Binary files /dev/null and b/docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/module-entity-extended-ui.png differ diff --git a/docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/tenant-entity-changes.png b/docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/tenant-entity-changes.png new file mode 100644 index 0000000000..4cd573c588 Binary files /dev/null and b/docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/tenant-entity-changes.png differ diff --git a/docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/text-template-content-ui.png b/docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/text-template-content-ui.png new file mode 100644 index 0000000000..037e3e4d8b Binary files /dev/null and b/docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/text-template-content-ui.png differ diff --git a/docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/Post.md b/docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/Post.md new file mode 100644 index 0000000000..eb193b8e41 --- /dev/null +++ b/docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/Post.md @@ -0,0 +1,214 @@ +# ABP v2.8.0发布 & 路线图 + +**ABP框架**和**ABP商业版**v2.8已经发布.这篇文章将涵盖这些发布中的**新增内容**和项目的**中期路线图**. + +## ABP框架2.8有哪些新增内容? + +你可在[GitHub的发行说明](https://github.com/abpframework/abp/releases/tag/2.8.0)中看到所有的变更.这篇博客只包括重要的一些功能/变更. + +### SignalR集成包 + +我们已经发布了[一个新的包](https://www.nuget.org/packages/Volo.Abp.AspNetCore.SignalR)用来集成SignalR到基于ABP框架应用程序中. + +> 其实跟随[标准Microsoft教程](https://docs.microsoft.com/en-us/aspnet/core/tutorials/signalr)添加[SignalR](https://docs.microsoft.com/en-us/aspnet/core/signalr/introduction)到你的应用程序中已经可以实现.但是,ABP提供了SignalR集成包用来简化集成和使用. + +参见[SignalR集成文档](https://docs.abp.io/en/abp/latest/SignalR-Integration)开始使用SignalR. + +#### SignalR演示应用程序 + +我们也创建了一个简单的聊天应用程序来演示如何使用它. + +![signalr-chat-demo](signalr-chat-demo.png) + +参见[应用程序的源代码.](https://github.com/abpframework/abp-samples/tree/master/SignalRDemo) + +### 控制台应用程序启动模板 + +新的控制台应用程序模板,可用来创建整合了ABP框架的控制台应用程序. + +使用ABP CLI来创建新的控制台应用程序,并为`-t`(模板)选项指定`console`: + +````bash +abp new MyApp -t console +```` + +感谢[@liangshiw](https://github.com/liangshiw)为此模板的贡献. + +### 为MVC UI增加RTL支持和阿拉伯语本地化 + +[@kgamalseif](https://github.com/kgamalseif)为MVC UI贡献了RTL实现, 看起来很棒: + +![rtl-ui](rtl-ui.png) + +他还本地化了所有的框架和模块的资源.感谢他伟大的贡献. + +### 其它 + +本次发布的其它一些亮点: + +* 转换HttpApi.Client模块的包为.netstandard 2.0, 用来与其他类型的应用程序兼容. +* 改进的对象扩展系统,以更好地处理UI,本地化和验证. +* 为Hangfire和Quartz集成实现了禁用后台作业执行. +* 为MVC UI新增JsTree集成包. +* 移动所有示例到新的[ABP-示例](https://github.com/abpframework/abp-samples)库中并创建了[索引页](https://docs.abp.io/en/abp/latest/Samples/Index)来查看所有示例. + +### 弃用 + +* 弃用了`app.UseMvcWithDefaultRouteAndArea()`,并引入了`app.UseConfiguredEndpoints()`(见[#3880](https://github.com/abpframework/abp/issues/3880)). +* 弃用了`UsePostgreSql()`,并为[Volo.Abp.EntityFrameworkCore.PostgreSql](http://nuget.org/packages/Volo.Abp.EntityFrameworkCore.PostgreSql)包引入了`UseNpgsql()`.如果你正在使用PostgreSQL, 切换到`UseNpgsql()`. + +旧方法被标记为`Obsolete`,将在下一主要版本中删除. + +## ABP商业版2.8中有哪些新增内容? + +### 新的Lepton主题 + +我们已经彻底修改[lepton主题](https://commercial.abp.io/themes).来看下不同的风格: + +![lepton-themes](lepton-themes.gif) + +ABP商业版中语言管理页面的截图: + +![lepton-abp-default-theme](lepton-abp-default-theme.png) + +(默认风格UI) + +![lepton-abp-material-theme](lepton-abp-material-theme.png) + +(Material风格UI) + +[创建一个演示](https://commercial.abp.io/demo)来现场测试所有的风格.你可以设置页面更改风格. + +### 新增聊天模块 + +第一版的[聊天模块](https://commercial.abp.io/modules/Volo.Chat)已经在本版本中发布.当前只有MVC / Razor PagesUI.Angular UI正在开发中. + +![abp-chat-module](abp-chat-module.png) + +目前,它有一个简单的**实时文本消息**功能.更多的功能,比如群组消息,发送图像/文件在路线图中. + +### 其它 + +* 为Angular UI实现了[模块实体扩展](https://docs.abp.io/en/commercial/latest/guides/module-entity-extensions)系统.还提高该系统以更好地处理float/double/decimal,date,datetime,enum和bool属性. +* [EasyCRM示例应用程序](https://docs.abp.io/en/commercial/latest/samples/easy-crm)树形视图中管理产品组. + +## 关于下一个版本 + +我们**每2周**发布一次.因此,下一个功能版本的计划的日期为**6月4日**, 版本号为**2.9**.这(可能)会是**最后的2.x版本**, 下个版本将是**3.0**. + +### ABP框架 2.9 & 3.0 + +#### 组织单元系统 + +Identity模块的组织单元系统本来要在2.8中发布,但不幸的是我们不能肯定该功能的稳定性,所以推迟到了2.9. + +#### gRPC + +我们打算创建一个gPRC集成示例应用程序.然后,我们打算为所有[预构建模块](https://docs.abp.io/en/abp/latest/Modules/Index)和[启动模板](https://docs.abp.io/en/abp/latest/Startup-Templates/Index)创建gRPC端点.我们想为这些端点使用新计划的[Blazor](https://docs.microsoft.com/en-us/aspnet/core/blazor/) UI选项(我们知道Blazor UI有着[巨大的需求](https://github.com/abpframework/abp/issues/394).这并不意味着我们在3.0中会完成全部工作,但我们已经开始了, 并将在3.0+版本中继续. + +#### Oracle与EF Core + +我们看到,在ABP框架之外,人们使用Oracle与EF Core有一些痛点.这是因为EF Core 3.1目前还没有稳定并且免费的Oracle提供器.我们只看到了[Devart](https://www.devart.com/)创建了一个[付费的包](https://www.nuget.org/packages/Devart.Data.Oracle.EFCore). + +[@ebicoglu](https://github.com/ebicoglu)[创建了一个gist](https://gist.github.com/ebicoglu/9f364c7eff9d87315af0178866186401)用来演示如何使用它. 我们[打算](https://github.com/abpframework/abp/issues/3983)创建一个集成包来让它更简单. + +#### API文档 + +我们正[致力于](https://github.com/abpframework/abp/issues/1184)为框架创建一个API文档,并建立一个CD管线自动地在每次发布时公开它.这会更容易地探索框架中的类. + +#### 示例应用程序:在分层/分布式系统上使用SignalR + +在分布式/微服务系统上使用SignalR可能有一些麻烦,因为服务没有连接到客户端所以不能直接从服务器调用客户端函数.一个解决这个问题的方法是使用分布式消息总线(如RabbitMQ),用来向客户端转发从服务到Web应用程序的消息. + +我们将创建一个示例应用程序和文档来阐述这样一个架构,和使用ABP框架它会多么的容易. + +虽然这个话题与ABP框架没有直接关系, 而且这个问题不是ABP应用程序独有的,我们仍然觉得创建这样的指南对开发者是很有用的. + +#### 还有... + +我们会花更多的时间来写更多的文档,实现性能的提升,做更多的测试,创建更多的扩展点等等. + +### ABP商业版 2.9 & 3.0 + +#### 组织单元系统 + +并行于ABP框架的组织单元系统(如上所述),我们正在创建一个UI用来管理组织单元,将在2.9中发布. + +#### 聊天模块的Angular UI + +聊天模块(如上所述)目前只有ASP.NET Core MVC / Razor Pages UI.我们正努力为该模块创建Angular UI + +#### 新模块想法:文件管理 + +我们正在创建一个文件管理模块,用来管理(上传/下载)和在用户之间共享文件.你可将之视为一个轻量级的Google Drive :). + +#### Easy CRM Angular UI + +[Easy CRM](https://docs.abp.io/en/commercial/latest/samples/easy-crm)是我们之前发布的ABP商业版中的一个示例应用程序.在这个版本中,我们向这个应用程序中加入了更多的功能.在下一个版本中,我们会为它创建Angular UI. + +我们发现这个应用程序非常有用,因为与简单的[图书商城](https://docs.abp.io/en/commercial/latest/samples/index#book-store)相比, 它非常接近现实世界的应用程序. + +#### 还有.. + +我们正在致力于改进现有的[模块](https://commercial.abp.io/modules),[主题](https://commercial.abp.io/themes)和[工具](https://commercial.abp.io/tools), 旨在3.0版本中提供更舒适的开发者体验. + +## 路线图 + +我们经常被问到的[ABP框架](https://abp.io/)和[ABP商业版](https://commercial.abp.io/)的路线图.虽然我们已经在各种平台上回答过,但在这次发布中,我们在这些产品的文档里增加了路线图页面: + +* [ABP框架路线图](https://docs.abp.io/en/abp/latest/Road-Map) +* [ABP商业版路线图](https://docs.abp.io/en/commercial/latest/road-map) + +我也把路线图写在下面的章节中: + +### ABP框架路线图 + +你可随时在[GitHub的仓库](https://github.com/abpframework/abp/milestones)中检查里程碑规划和优先积压问题. + +虽然我们会**继续添加其它令人激动的功能**,但我们在中期将主要致力于下列内容的工作: + +* 为所有的预构建模块实现**gPRC集成**. +* 为框架和所有预构建的模块实现**Blazor UI**. +* **NET 5.0**!由于微软已经宣布了.NET 5.0将在2020年11月发布,在这之前我们会做好准备,并在微软发布后就迁移到.NET 5.0上, 我们希望能平稳过渡. + +### ABP商业版路线图 + +我们将与ABP框架同步进行, 为ABP商业版实现一些相同的内容: + +* gRPC集成 +* Blazor UI +* .NET 5.0 + +除此之外,我们将在中期致力于下列内容: + +* 一个用来创建微服务解决方案的启动模板(含有Ocelot,Redis,RabbitMQ,ElasticSearch,IdentityServer ..等等. 并且预先集成和配置好的). +* 更多的模块扩展点. +* 动态仪表板系统. +* 实时通知系统. +* SaaS的模块的认购及支付系统. +* 更多的身份认证选项. +* 新的应用模块(我们有几十个模块的想法, 将陆续与大家分享 - 上面宣布的"文件管理"就是其中之一). +* 新的主题与主题风格(包括公共/企业网站的主题). + +## 彩蛋:ABP.IO平台路线图 + +ABP框架和ABP商业版是ABP.IO平台的基本组成部分,我们还想创造一个更大的平台将.NET社区聚集在一起,利用ABP框架的统一性和标准开发模型来创建可重用模块,分享知识,彼此互相帮助. + +所以,我们有了新的*.abp.io网站的想法,我想分享给社区 + +#### market.abp.io + +一个平台用于让开发者/公司发布他们基于ABP框架的可重用的应用模块,主题, 库和工具.这个网站上会有免费/开源和商业产品. + +#### jobs.abp.io + +我们收到了太多来自其它公司的电子邮件, 想聘请其它公司的开发者来打造基于ABP.IO平台的自己的产品.我们作为[Volosoft](https://volosoft.com/),想留在产品端,而不是客户的基础工程.我们一般会把有经验的开发者和公司介绍给他们. + +我们计划创建一个网站来满足各方需要,这样你可以为你的项目找到开发者,或者找一个短期或长期的工作. + +## 跟随ABP! + +跟随社交媒体帐号,即可了解ABP.IO平台上发生的事情: + +* [@abpframework](https://twitter.com/abpframework): ABP框架官方推特帐号 +* [@abpcommercial](https://twitter.com/abpcommercial): ABP商业版官方推特帐号 diff --git a/docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/abp-chat-module.png b/docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/abp-chat-module.png new file mode 100644 index 0000000000..88509b6022 Binary files /dev/null and b/docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/abp-chat-module.png differ diff --git a/docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/lepton-abp-default-theme.png b/docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/lepton-abp-default-theme.png new file mode 100644 index 0000000000..93e94e5a67 Binary files /dev/null and b/docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/lepton-abp-default-theme.png differ diff --git a/docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/lepton-abp-material-theme.png b/docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/lepton-abp-material-theme.png new file mode 100644 index 0000000000..c78dc96245 Binary files /dev/null and b/docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/lepton-abp-material-theme.png differ diff --git a/docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/lepton-themes.gif b/docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/lepton-themes.gif new file mode 100644 index 0000000000..7ce3935ef5 Binary files /dev/null and b/docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/lepton-themes.gif differ diff --git a/docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/rtl-ui.png b/docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/rtl-ui.png new file mode 100644 index 0000000000..0e1a162eb1 Binary files /dev/null and b/docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/rtl-ui.png differ diff --git a/docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/signalr-chat-demo.png b/docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/signalr-chat-demo.png new file mode 100644 index 0000000000..e5f647b79c Binary files /dev/null and b/docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/signalr-chat-demo.png differ diff --git a/docs/zh-Hans/CLI.md b/docs/zh-Hans/CLI.md index 658545c4fd..c0147aa3a8 100644 --- a/docs/zh-Hans/CLI.md +++ b/docs/zh-Hans/CLI.md @@ -18,6 +18,37 @@ dotnet tool update -g Volo.Abp.Cli ## Commands +这里是所有可用的命令列表: + +* **`help`**: 展示ABP CLI的用法帮助信息. +* **`new`**:生成基于ABP的[启动模板](Startup-Templates/Index.md). +* **`update`**:自动更新的ABP解决方案ABP相关的NuGet和NPM包. +* **`add-package`**: 添加ABP包到项目. +* **`add-module`**: 添加[应用模块](https://docs.abp.io/en/abp/latest/Modules/Index)到解决方案. +* **`generate-proxy`**: 生成客户端代理以使用服务器上的HTTP API端点. +* **`switch-to-preview`**: 切换解决方案所有ABP相关包为[夜间构建](Nightly-Builds.md)版本. +* **`switch-to-stable`**: 切换解决方案所有ABP相关包为最新的稳定版本. +* **`translate`**: 当源代码控制存储库中有多个JSON[本地化](Localization.md文件时,可简化翻译本地化文件的过程. +* **`login`**: 使用你在[abp.io](https://abp.io/)的用户名和密码在你的计算机上认证. +* **`logout`**: 在你的计算机注销认证. + +### help + +展示ABP CLI的基本用法: + +用法: + +````bash +abp help [command-name] +```` + +示例: + +````bash +abp help # Shows a general help. +abp help new # Shows help about the "new" command. +```` + ### new 生成基于ABP[启动模板](Startup-Templates/Index.md)的新解决方案. diff --git a/docs/zh-Hans/Contribution/Index.md b/docs/zh-Hans/Contribution/Index.md index 9b645c34c3..058f195b0a 100644 --- a/docs/zh-Hans/Contribution/Index.md +++ b/docs/zh-Hans/Contribution/Index.md @@ -41,7 +41,9 @@ ABP是[开源](https://github.com/abpframework)和社区驱动项目. 本指南 ABP框架具有灵活的[本地化系统](../Localization.md). 你可以为自己的应用程序创建本地化用户界面. -除此之外,框架和预构建模块已经本地化了文本.请参阅[Volo.Abp.UI包的本地化文本](https://github.com/abpframework/abp/blob/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json).你可以在[相同文件夹](https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi)中创建一个新文件进行翻译. +除此之外,框架和预构建模块已经本地化了文本.请参阅[Volo.Abp.UI包的本地化文本](https://github.com/abpframework/abp/blob/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json). + +你可以在[相同文件夹](https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi)中创建一个新文件进行翻译. * 从Github克隆[ABP存储库](https://github.com/abpframework/abp/). * 为本地化文本(json)文件(en.json文件同目录下)创建目标语言的新文件. @@ -49,6 +51,9 @@ ABP框架具有灵活的[本地化系统](../Localization.md). 你可以为自 * 翻译文本. * 在Github上发送拉取请求(Pull request). + +你还可以使用[ABP CLI](CLI.md)的`abp translation`命令来翻译本地化文本. + ABP是一个模块化框架. 所以有很多本地化文本资源, 每个模块都有一个. 要查找所有.json文件,可以在克隆存储库后搜索"en.json". 你还可以检查[此列表](Localization-Text-Files.md)以获取本地化文本文件列表. ### 博客文章和教程 diff --git a/docs/zh-Hans/Contribution/Localization-Text-Files.md b/docs/zh-Hans/Contribution/Localization-Text-Files.md deleted file mode 100644 index 93132f516a..0000000000 --- a/docs/zh-Hans/Contribution/Localization-Text-Files.md +++ /dev/null @@ -1,31 +0,0 @@ -## 本地化文本文件 - -这是一个来自框架的本地化文本文件列表, 任何人都可以做出贡献. 我们会将此列表保持最新: - -* https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/en.json -* https://github.com/abpframework/abp/blob/master/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/en.json -* https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/en.json -* https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json -* https://github.com/abpframework/abp/tree/master/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/Resource/en.json -* https://github.com/abpframework/abp/tree/master/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/en.json -* https://github.com/abpframework/abp/tree/master/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/CountryNames/en.json -* https://github.com/abpframework/abp/tree/master/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/Validation/en.json -* https://github.com/abpframework/abp/tree/master/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/en.json -* https://github.com/abpframework/abp/tree/master/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/SourceExt/en.json -* https://github.com/abpframework/abp/blob/master/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en.json -* https://github.com/abpframework/abp/tree/master/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Localization/Resources/Blogging/ApplicationContracts/en.json -* https://github.com/abpframework/abp/tree/master/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Localization/Resources/en.json -* https://github.com/abpframework/abp/tree/master/modules/docs/app/VoloDocs.Web/Localization/Resources/VoloDocs/Web/en.json -* https://github.com/abpframework/abp/tree/master/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/en.json -* https://github.com/abpframework/abp/tree/master/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/en.json -* https://github.com/abpframework/abp/tree/master/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/Domain/en.json -* https://github.com/abpframework/abp/tree/master/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json -* https://github.com/abpframework/abp/tree/master/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/en.json -* https://github.com/abpframework/abp/tree/master/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/en.json -* https://github.com/abpframework/abp/tree/master/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/en.json -* https://github.com/abpframework/abp-samples/tree/master/BookStore/src/Acme.BookStore.Domain.Shared/Localization/BookStore/en.json -* https://github.com/abpframework/abp-samples/tree/master/DashboardDemo/src/DashboardDemo.Domain.Shared/Localization/DashboardDemo/en.json -* https://github.com/abpframework/abp/tree/master/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/Localization/ApplicationContracts/en.json -* https://github.com/abpframework/abp/tree/master/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain/ProductManagement/Localization/Domain/en.json -* https://github.com/abpframework/abp/tree/master/samples/MicroserviceDemo/modules/product/src/ProductManagement.Web/Localization/Resources/ProductManagement/en.json -* https://github.com/abpframework/abp/tree/master/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/en.json \ No newline at end of file diff --git a/docs/zh-Hans/Customizing-Application-Modules-Overriding-Services.md b/docs/zh-Hans/Customizing-Application-Modules-Overriding-Services.md index 94a2120059..cf40590d61 100644 --- a/docs/zh-Hans/Customizing-Application-Modules-Overriding-Services.md +++ b/docs/zh-Hans/Customizing-Application-Modules-Overriding-Services.md @@ -59,6 +59,7 @@ context.Services.Replace( ### 示例: 重写服务方法 ````csharp +//[RemoteService(IsEnabled = false)] // 如果你在使用动态控制器,为了避免为应用服务创建重复的控制器, 你可以禁用远程访问. [Dependency(ReplaceServices = true)] [ExposeServices(typeof(IIdentityUserAppService), typeof(IdentityUserAppService))] public class MyIdentityUserAppService : IdentityUserAppService diff --git a/docs/zh-Hans/Entity-Framework-Core-MySQL.md b/docs/zh-Hans/Entity-Framework-Core-MySQL.md index 15e2205c38..48297ea68d 100644 --- a/docs/zh-Hans/Entity-Framework-Core-MySQL.md +++ b/docs/zh-Hans/Entity-Framework-Core-MySQL.md @@ -25,23 +25,6 @@ MySQL连接字符串与SQL Server连接字符串不同. 所以检查你的解决 通常需要更改 `.DbMigrator` 和 `.Web` 项目里面的 `appsettings.json` ,但它取决于你的解决方案结构. -## 更改迁移DbContext - -MySQL DBMS与SQL Server有一些细微的差异. 某些模块数据库映射配置(尤其是字段长度)会导致MySQL出现问题. 例如某些[IdentityServer模块](Modules/IdentityServer.md)表就存在这样的问题,它提供了一个选项可以根据你的DBMS配置字段. - -启动模板包含*YourProjectName*MigrationsDbContext,它负责维护和迁移数据库架构. 此DbContext基本上调用依赖模块的扩展方法来配置其数据库表. - -打开 *YourProjectName*MigrationsDbContext 更改 `builder.ConfigureIdentityServer();` 行,如下所示: - -````csharp -builder.ConfigureIdentityServer(options => -{ - options.DatabaseProvider = EfCoreDatabaseProvider.MySql; -}); -```` - -然后 `ConfigureIdentityServer()` 方法会将字段长度设置为不超过MySQL的限制. 如果在创建或执行数据库迁移时遇到任何问题请参考相关的模块文档. - ## 重新生成迁移 启动模板使用[Entity Framework Core的Code First迁移](https://docs.microsoft.com/zh-cn/ef/core/managing-schemas/migrations/). EF Core迁移取决于所选的DBMS提供程序. 因此更改DBMS提供程序会导致迁移失败. diff --git a/docs/zh-Hans/Entity-Framework-Core-Oracle.md b/docs/zh-Hans/Entity-Framework-Core-Oracle.md new file mode 100644 index 0000000000..5156331f40 --- /dev/null +++ b/docs/zh-Hans/Entity-Framework-Core-Oracle.md @@ -0,0 +1,73 @@ +# 切换到EF Core Oracle提供程序 + +本文介绍如何将预配置为SqlServer提供程序的 **[应用程序启动模板](Startup-Templates/Application.md)** 切换到 **Oracle** 数据库提供程序 + +> 本文档使用[Devart](https://www.devart.com/dotconnect/oracle/)公司的付费库,因为它是oracle唯一支持EF Core 3.x的库 + +## 替换Volo.Abp.EntityFrameworkCore.SqlServer包 + +解决方案中的 `.EntityFrameworkCore` 项目依赖于 [Volo.Abp.EntityFrameworkCore.SqlServer](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore.SqlServer) NuGet包. 删除这个包并且添加相同版本的 [Volo.Abp.EntityFrameworkCore.Oracle.Devart](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore.Oracle.Devart) 包. + +## 替换模块依赖项 + +在 `.EntityFrameworkCore` 项目中找到 **YourProjectName*EntityFrameworkCoreModule** 类, 删除 `DependsOn` attribute 上的`typeof(AbpEntityFrameworkCoreSqlServerModule)`, 添加 `typeof(AbpEntityFrameworkCoreOracleDevartModule)` (或者替换 `using Volo.Abp.EntityFrameworkCore.SqlServer;` 为 `using Volo.Abp.EntityFrameworkCore.Oracle.Devart;`). + +## UseOracle() + +Find `UseSqlServer()` calls in your solution, replace with `UseOracle()`. Check the following files: + +* *YourProjectName*EntityFrameworkCoreModule.cs inside the `.EntityFrameworkCore` project. +* *YourProjectName*MigrationsDbContextFactory.cs inside the `.EntityFrameworkCore.DbMigrations` project. + +In the `CreateDbContext()` method of the *YourProjectName*MigrationsDbContextFactory.cs, replace the following code block + +查找你的解决方案中 `UseSqlServer()`调用,替换为 `UseOracle()`. 检查下列文件: + +* `.EntityFrameworkCore` 项目中的*YourProjectName*EntityFrameworkCoreModule.cs. +* `.EntityFrameworkCore` 项目中的*YourProjectName*MigrationsDbContextFactory.cs. + +使用以下代码替换*YourProjectName*MigrationsDbContextFactory.cs中的 `CreateDbContext()` 方法: + +``` +var builder = new DbContextOptionsBuilder() + .UseSqlServer(configuration.GetConnectionString("Default")); +``` + +与这个 + +``` +var builder = (DbContextOptionsBuilder) + new DbContextOptionsBuilder().UseOracle + ( + configuration.GetConnectionString("Default") + ); +``` + +> 根据你的解决方案的结构,你可能发现更多需要改变代码的文件. + +## 更改连接字符串 + +Oracle连接字符串与SQL Server连接字符串不同. 所以检查你的解决方案中所有的 `appsettings.json` 文件,更改其中的连接字符串. 有关oracle连接字符串选项的详细内容请参见[connectionstrings.com](https://www.connectionstrings.com/oracle/). + +通常需要更改 `.DbMigrator` 和 `.Web` 项目里面的 `appsettings.json` ,但它取决于你的解决方案结构. + +Oracle连接字符串示例: + +``` +Data Source=localhost;User Id=myuser;Password=mypassword; +``` + +## 重新生成迁移 + +启动模板使用[Entity Framework Core的Code First迁移](https://docs.microsoft.com/zh-cn/ef/core/managing-schemas/migrations/). EF Core迁移取决于所选的DBMS提供程序. 因此更改DBMS提供程序会导致迁移失败. + +* 删除 `.EntityFrameworkCore.DbMigrations` 项目下的Migrations文件夹,并重新生成解决方案. +* 在包管理控制台中运行 `Add-Migration "Initial"`(在解决方案资源管理器选择 `.DbMigrator` (或 `.Web`) 做为启动项目并且选择 `.EntityFrameworkCore.DbMigrations` 做为默认项目). + +这将创建一个配置所有数据库对象(表)的数据库迁移. + +运行 `.DbMigrator` 项目创建数据库和初始种子数据. + +## 运行应用程序 + +它已准备就绪, 只需要运行该应用程序与享受编码. \ No newline at end of file diff --git a/docs/zh-Hans/Entity-Framework-Core-Other-DBMS.md b/docs/zh-Hans/Entity-Framework-Core-Other-DBMS.md index ca70d8b5c6..fb8ad15787 100644 --- a/docs/zh-Hans/Entity-Framework-Core-Other-DBMS.md +++ b/docs/zh-Hans/Entity-Framework-Core-Other-DBMS.md @@ -6,6 +6,7 @@ ABP框架为一些常见的DMBS提供了简化配置的**集成包**,你可以 * [MySQL](Entity-Framework-Core-MySQL.md) * [PostgreSQL](Entity-Framework-Core-PostgreSQL.md) +* [Oracle](Entity-Framework-Core-Oracle.md) * [SQLite](Entity-Framework-Core-SQLite.md) 你也可以不使用集成包配置DBMS提供程序,虽然总是建议使用集成包(它也使不同模块之间的依赖版本成为标准版本),但是如果没有用于DBMS提供程序的集成包,也可以手动集成. diff --git a/docs/zh-Hans/Getting-Started.md b/docs/zh-Hans/Getting-Started.md index 1b156b070d..adf9517eec 100644 --- a/docs/zh-Hans/Getting-Started.md +++ b/docs/zh-Hans/Getting-Started.md @@ -403,7 +403,7 @@ yarn start 在上面的管理界面中,可以通过使用[Expo Client](https://expo.io/tools#client)扫描二维码,使用Android模拟器,iOS模拟器或真机来启动应用程序. -> 请参阅[expo.io](https://docs.expo.io/versions/v36.0.0/workflow/ios-simulator/)上的[Android Studio模拟器](https://docs.expo.io/versions/v36.0.0/workflow/android-studio-emulator/)和[iOS模拟器文档](https://docs.expo.io/versions/v36.0.0/workflow/android-studio-emulator/). +> 请参阅expo.io上的[Android Studio模拟器](https://docs.expo.io/workflow/android-simulator/)和[iOS模拟器文档](https://docs.expo.io/workflow/ios-simulator/). ![React Native login screen on iPhone 11](images/rn-login-iphone.png) diff --git a/docs/zh-Hans/Index.md b/docs/zh-Hans/Index.md index 71e5a520da..ce4fcfa3b4 100644 --- a/docs/zh-Hans/Index.md +++ b/docs/zh-Hans/Index.md @@ -10,7 +10,8 @@ ABP是一个**开源应用程序框架**,专注于基于ASP.NET Core的Web应用 使用ABP开发新项目的最简单方法是使用启动模板: -* [ASP.NET Core MVC 模板](Getting-Started-AspNetCore-MVC-Template.md) +* [ASP.NET Core MVC (Razor Pages) UI 启动模板](Getting-Started?UI=MVC&DB=EF&Tiered=No) +* [Angular UI 启动模板](Getting-Started?UI=NG&DB=EF&Tiered=No) 如果你想从头开始(使用空项目),请手动安装ABP框架并使用以下教程: diff --git a/docs/zh-Hans/Modules/Index.md b/docs/zh-Hans/Modules/Index.md index fa7b063004..a24a95c121 100644 --- a/docs/zh-Hans/Modules/Index.md +++ b/docs/zh-Hans/Modules/Index.md @@ -23,6 +23,7 @@ ABP是一个 **模块化的应用程序框架** 由十多个 **nuget packages** * **Setting Management**: 用于保存设置. * **Tenant Management**: 管理[多租户](../Multi-Tenancy.md)应用程序的租户. * **Users**: 抽象用户, 因此其他模块可以依赖此模块而不是Identity模块. +* [**Virtual File Explorer**](Virtual-File-Explorer.md): 提供简单的UI查看[虚拟文件系统](../Virtual-File-System.md)中的文件. 模块化文档正在编写中. 请参阅[这个仓库](https://github.com/abpframework/abp/tree/master/modules)获取所有模块的源代码. diff --git a/docs/zh-Hans/Modules/Virtual-File-Explorer.md b/docs/zh-Hans/Modules/Virtual-File-Explorer.md new file mode 100644 index 0000000000..375165d221 --- /dev/null +++ b/docs/zh-Hans/Modules/Virtual-File-Explorer.md @@ -0,0 +1,80 @@ +# 虚拟文件浏览器模块 + +## 什么是虚拟文件浏览器模块 + +虚拟文件浏览器模块提供了一个简单的UI来查看[虚拟文件系统](../Virtual-File-System.md)所有的文件. + +> [启动模板](../Startup-Templates/Index.md)默认并没有安装这个模块,所以你需要手动安装到应用程序. + +### 安装 + +#### 1- 引用虚拟文件浏览器模块包 + +建议使用ABP CLI安装模块,在解决方案文件 (`.sln`) 目录打开 `CMD` 窗口,运行以下命令: + +`abp add-module Volo.VirtualFileExplorer` + +或者你也可以手动安装nuget包到 `Acme.MyProject.Web` 项目: + +* 安装[Volo.Abp.VirtualFileExplorer.Web](https://www.nuget.org/packages/Volo.Abp.VirtualFileExplorer.Web/) nuget包到 `Acme.MyProject.Web` 项目. + + `Install-Package Volo.Abp.VirtualFileExplorer.Web` + +#### 2- 添加模块依赖 + +* 打开 `MyProjectWebModule.cs` 并且添加 `typeof(AbpVirtualFileExplorerWebModule)` 如下所示; + + ```csharp + [DependsOn( + typeof(AbpVirtualFileExplorerWebModule), + typeof(MyProjectApplicationModule), + typeof(MyProjectEntityFrameworkCoreModule), + typeof(AbpAutofacModule), + typeof(AbpIdentityWebModule), + typeof(AbpAccountWebModule), + typeof(AbpAspNetCoreMvcUiBasicThemeModule) + )] + public class MyProjectWebModule : AbpModule + { + //... + } + ``` + +#### 3- 添加NPM包 + + * 打开 `package.json` 添加 `@abp/virtual-file-explorer": "^2.9.0` 如下所示: + + ```json + { + "version": "1.0.0", + "name": "my-app", + "private": true, + "dependencies": { + "@abp/aspnetcore.mvc.ui.theme.basic": "^2.9.0", + "@abp/virtual-file-explorer": "^2.9.0" + } + } + ``` + + 然后在 `Acme.MyProject.Web` 项目目录打开命令行终端运行以下命令: + + 1. `yarn` + 2. `gulp` + +这就是全部,运行应用程序导航到 `/VirtualFileExplorer`. 你会看到虚拟文件浏览器页面: + +![Virtual-File-Explorer](../images/virtual-file-explorer.png) + +### 选项 + +你可以通过 `AbpVirtualFileExplorerOptions` 选项禁用虚拟文件浏览器模块: + +```csharp +public override void PreConfigureServices(ServiceConfigurationContext context) +{ + PreConfigure(options => + { + options.IsEnabled = false; + }); +} +``` \ No newline at end of file diff --git a/docs/zh-Hans/Multi-Tenancy.md b/docs/zh-Hans/Multi-Tenancy.md index 49cf358205..3e65a97cef 100644 --- a/docs/zh-Hans/Multi-Tenancy.md +++ b/docs/zh-Hans/Multi-Tenancy.md @@ -304,10 +304,10 @@ TODO:... Volo.Abp.AspNetCore.MultiTenancy 添加了下面这些租户解析器,从当前Web请求(按优先级排序)中确定当前租户. * **CurrentUserTenantResolveContributor**: 如果当前用户已登录,从当前用户的声明中获取租户Id. **出于安全考虑,应该始终将其做为第一个Contributor**. -* **QueryStringTenantResolver**: 尝试从query string参数中获取当前租户,默认参数名为"__tenant". -* **RouteTenantResolver**:尝试从当前路由中获取(URL路径),默认是变量名是"__tenant".所以,如果你的路由中定义了这个变量,就可以从路由中确定当前租户. -* **HeaderTenantResolver**: 尝试从HTTP header中获取当前租户,默认的header名称是"__tenant". -* **CookieTenantResolver**: 尝试从当前cookie中获取当前租户.默认的Cookie名称是"__tenant". +* **QueryStringTenantResolveContributor**: 尝试从query string参数中获取当前租户,默认参数名为"__tenant". +* **RouteTenantResolveContributor**:尝试从当前路由中获取(URL路径),默认是变量名是"__tenant".所以,如果你的路由中定义了这个变量,就可以从路由中确定当前租户. +* **HeaderTenantResolveContributor**: 尝试从HTTP header中获取当前租户,默认的header名称是"__tenant". +* **CookieTenantResolveContributor**: 尝试从当前cookie中获取当前租户.默认的Cookie名称是"__tenant". > 如果你使用nginx作为反向代理服务器,请注意如果`TenantKey`包含下划线或其他特殊字符可能存在问题, 请参考: http://nginx.org/en/docs/http/ngx_http_core_module.html#ignore_invalid_headers @@ -344,7 +344,7 @@ namespace MyCompany.MyProject Configure(options => { //子域名格式: {0}.mydomain.com (作为第二优先级解析器添加, 位于CurrentUserTenantResolveContributor之后) - options.TenantResolvers.Insert(1, new DomainTenantResolver("{0}.mydomain.com")); + options.TenantResolvers.Insert(1, new DomainTenantResolveContributor("{0}.mydomain.com")); }); //... @@ -355,7 +355,7 @@ namespace MyCompany.MyProject {0}是用来确定当前租户唯一名称的占位符. -你可以使用下面的方法,代替``options.TenantResolvers.Insert(1, new DomainTenantResolver("{0}.mydomain.com"));``: +你可以使用下面的方法,代替``options.TenantResolvers.Insert(1, new DomainTenantResolveContributor("{0}.mydomain.com"));``: ````C# options.AddDomainTenantResolver("{0}.mydomain.com"); diff --git a/docs/zh-Hans/Road-Map.md b/docs/zh-Hans/Road-Map.md new file mode 100644 index 0000000000..b764ce2e2e --- /dev/null +++ b/docs/zh-Hans/Road-Map.md @@ -0,0 +1,11 @@ +# ABP Framework 路线图 + +您可以随时在[GitHub仓库](https://github.com/abpframework/abp/milestones)上查看里程碑计划和优先的积压问题,获取详细的路线图. + +虽然我们将**继续添加其他令人兴奋的功能**,但我们将在`中期`中处理以下主要项目: + +* **gRPC 集成**和为所有预构建模块实现它. +* 为所有预构建模块实现 **Blazor UI**. +* **.NET 5.0**! Microsoft宣布.NET 5.0将在2020年11月发布,我们将为此更改做准备并在Microsoft发布它之后立即迁移到.NET 5.0. 我们希望顺利过渡. + +请在[GitHub仓库](https://github.com/abpframework/abpork/abp/milestones)为你的功能请求创建issue,但在创建前请先搜索是否已存在类似的issues. \ No newline at end of file diff --git a/docs/zh-Hans/SignalR-Integration.md b/docs/zh-Hans/SignalR-Integration.md index 8045bbd394..3dea373f5a 100644 --- a/docs/zh-Hans/SignalR-Integration.md +++ b/docs/zh-Hans/SignalR-Integration.md @@ -224,4 +224,14 @@ ABP实现 `SignalR` 的 `IUserIdProvider` 接口,从ABP框架的 `ICurrentUser` 参阅 [SignalR集成Demo](https://github.com/abpframework/abp-samples/tree/master/SignalRDemo),它有一个简单的聊天页面,可以在(经过身份验证的)用户之间发送消息. -![signalr-demo-chat](images/signalr-demo-chat.png) \ No newline at end of file +![signalr-demo-chat](images/signalr-demo-chat.png) + +## 备注 + +ABP框架不会更改SignalR. 就像在其他ASP.NET Core应用程序中一样,它也可以在基于ABP框架的应用程序中工作. + +参考[微软文档](https://docs.microsoft.com/zh-cn/aspnet/core/signalr/scale)托管和扩展您的应用程序,集成[Azure](https://docs.microsoft.com/zh-cn/aspnet/core/signalr/publish-to-azure-web-app)或[Redis底版](https://docs.microsoft.com/zh-cn/aspnet/core/signalr/redis-backplane)...等. + +## 另请参阅 + +* [微软SignalR文档](https://docs.microsoft.com/zh-cn/aspnet/core/signalr/introduction) \ No newline at end of file diff --git a/docs/zh-Hans/Text-Templating.md b/docs/zh-Hans/Text-Templating.md index 3d3941c607..0785dbba29 100644 --- a/docs/zh-Hans/Text-Templating.md +++ b/docs/zh-Hans/Text-Templating.md @@ -204,13 +204,14 @@ PascalCase 属性名(如 `UserName`) 在模板中用做小驼峰(如 `userName`) 假设你需要向用户发送电子邮件重置密码. 模板内容: ```` -{%{{{L "ResetMyPassword"}}}%} +{%{{{L "ResetMyPassword" model.name}}}%} ```` `L` 函数用于根据当前用户的文化来定位给定的Key,你需要在本地化文件中定义 `ResetMyPassword` 键: ````json -"ResetMyPassword": "Click here to reset your password" +"ResetMyPasswordTitle": "Reset my password", +"ResetMyPassword": "Hi {0}, Click here to reset your password" ```` 你还需要在模板定义提供程序类中声明要与此模板一起使用的本地化资源: @@ -234,6 +235,7 @@ var result = await _templateRenderer.RenderAsync( "PasswordReset", //the template name new PasswordResetModel { + Name = "john", Link = "https://abp.io/example-link?userId=123&token=ABC" } ); @@ -242,7 +244,7 @@ var result = await _templateRenderer.RenderAsync( 你可以看到以下本地化结果: ````csharp -Click here to reset your password +Hi john, Click here to reset your password ```` > 如果你为应用程序定义了 [默认本地化资源](Localization.md), 则无需声明模板定义的资源类型. diff --git a/docs/zh-Hans/Tutorials/Part-1.md b/docs/zh-Hans/Tutorials/Part-1.md index 9589c430b6..5ee6b31e08 100644 --- a/docs/zh-Hans/Tutorials/Part-1.md +++ b/docs/zh-Hans/Tutorials/Part-1.md @@ -30,7 +30,7 @@ ASP.NET Core {{UI_Value}} 系列教程包括三个3个部分: - [Part-2: 创建,编辑,删除书籍](Part-2.md) - [Part-3: 集成测试](Part-3.md) -> 你也可以观看由ABP社区成员为本教程录制的[视频课程](https://amazingsolutions.teachable.com/p/lets-build-the-booktore-application). +> 你也可以观看由ABP社区成员为本教程录制的[视频课程](https://amazingsolutions.teachable.com/p/lets-build-the-bookstore-application). ### 创建新项目 @@ -44,19 +44,19 @@ ASP.NET Core {{UI_Value}} 系列教程包括三个3个部分: abp new Acme.BookStore --template app --database-provider {{DB}} --ui {{UI_Text}} --mobile none ``` -![Creating project](./images/booktore-create-project-{{UI_Text}}.png) +![Creating project](./images/bookstore-create-project-{{UI_Text}}.png) ### 应用迁移 项目创建后,需要应用初始化迁移创建数据库. 运行 `Acme.BookStore.DbMigrator` 应用程序. 它会应用所有迁移,完成流程后你会看到以下结果,数据库已经准备好了! -![Migrations applied](./images/booktore-migrations-applied-{{UI_Text}}.png) +![Migrations applied](./images/bookstore-migrations-applied-{{UI_Text}}.png) > 另外你也可以在 Visual Studio 包管理控制台运行 `Update-Database` 命令应用迁移. #### 初始化数据库表 -![Initial database tables](./images/booktore-database-tables-{{DB}}.png) +![Initial database tables](./images/bookstore-database-tables-{{DB}}.png) ### 运行应用程序 @@ -64,7 +64,7 @@ abp new Acme.BookStore --template app --database-provider {{DB}} --ui {{UI_Text} 更多信息,参阅[入门教程](../../Getting-Started?UI={{UI}})的运行应用程序部分. -![Set as startup project](./images/booktore-start-project-{{UI_Text}}.png) +![Set as startup project](./images/bookstore-start-project-{{UI_Text}}.png) {{if UI == "NG"}} @@ -105,7 +105,7 @@ http://localhost:4200/ 下面的图片展示了从启动模板创建的项目是如何分层的. -![booktore-visual-studio-solution](./images/booktore-solution-structure-{{UI_Text}}.png) +![bookstore-visual-studio-solution](./images/bookstore-solution-structure-{{UI_Text}}.png) > 你可以查看[应用程序模板文档](../startup-templates/application#solution-structure)以详细了解解决方案结构. @@ -295,7 +295,7 @@ namespace Acme.BookStore 这个启动模板使用了[EF Core Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/)来创建并维护数据库结构.打开 **程序包管理器控制台(Package Manager Console) (PMC)** (工具/Nuget包管理器菜单) -![Open Package Manager Console](./images/booktore-open-package-manager-console.png) +![Open Package Manager Console](./images/bookstore-open-package-manager-console.png) 选择 `Acme.BookStore.EntityFrameworkCore.DbMigrations`作为默认的项目然后执行下面的命令: @@ -303,7 +303,7 @@ namespace Acme.BookStore Add-Migration "Created_Book_Entity" ``` -![booktore-pmc-add-book-migration](./images/booktore-pmc-add-book-migration-v2.png) +![bookstore-pmc-add-book-migration](./images/bookstore-pmc-add-book-migration-v2.png) 这样就会在 `Migrations` 文件夹中创建一个新的migration类.然后执行 `Update-Database` 命令更新数据库结构: @@ -311,7 +311,7 @@ Add-Migration "Created_Book_Entity" Update-Database ```` -![booktore-update-database-after-book-entity](./images/booktore-update-database-after-book-entity.png) +![bookstore-update-database-after-book-entity](./images/bookstore-update-database-after-book-entity.png) #### 添加示例数据 @@ -328,7 +328,7 @@ INSERT INTO AppBook (Id,CreationTime,[Name],[Type],PublishDate,Price) VALUES ('4fa024a1-95ac-49c6-a709-6af9e4d54b54', '2018-07-02', 'Pet Sematary',5,'1983-11-14','23.7') ``` -![booktore-book-table](./images/booktore-book-table.png) +![bookstore-book-table](./images/bookstore-book-table.png) {{end}} @@ -501,7 +501,7 @@ namespace Acme.BookStore 你会看到一些内置的接口和`Book`的接口,它们都是REST风格的: -![booktore-swagger](images/booktore-swagger.png) +![bookstore-swagger](images/bookstore-swagger.png) Swagger有一个很好的UI来测试API. 你可以尝试执行`[GET] /api/app/book` API来获取书籍列表. @@ -529,11 +529,11 @@ acme.bookStore.book.getList({}).done(function (result) { console.log(result); }) 运行这段代码会产生下面的输出: -![booktore-test-js-proxy-getlist](./images/booktore-test-js-proxy-getlist.png) +![bookstore-test-js-proxy-getlist](./images/bookstore-test-js-proxy-getlist.png) 你可以看到服务器返回的 **book list**.你还可以切换到开发者工具的 **network** 查看客户端到服务器端的通讯信息: -![booktore-test-js-proxy-getlist-network](./images/booktore-test-js-proxy-getlist-network.png) +![bookstore-test-js-proxy-getlist-network](./images/bookstore-test-js-proxy-getlist-network.png) 我们使用`create`方法 **创建一本新书**: @@ -555,7 +555,7 @@ successfully created the book with id: 439b0ea8-923e-8e1e-5d97-39f2c7ac4246 在 `Acme.BookStore.Web`项目的`Pages`文件夹下创建一个新的文件夹叫`Book`并添加一个名为`Index.cshtml`的Razor Page. -![booktore-add-index-page](./images/booktore-add-index-page-v2.png) +![bookstore-add-index-page](./images/bookstore-add-index-page-v2.png) 打开`Index.cshtml`并把内容修改成下面这样: @@ -621,7 +621,7 @@ namespace Acme.BookStore.Web.Menus 本地化文本位于`Acme.BookStore.Domain.Shared`项目的`Localization/BookStore`文件夹下: -![booktore-localization-files](./images/booktore-localization-files-v2.png) +![bookstore-localization-files](./images/bookstore-localization-files-v2.png) 打开`en.json`文件,将`Menu:BookStore`和`Menu:Book`键的本地化文本添加到文件末尾: @@ -653,7 +653,7 @@ namespace Acme.BookStore.Web.Menus 运行该应用程序,看到新菜单项已添加到顶部栏: -![booktore-menu-items](./images/booktore-new-menu-item.png) +![bookstore-menu-items](./images/bookstore-new-menu-item.png) 点击BookStore下Book子菜单项就会跳转到新增的书籍页面. @@ -701,7 +701,7 @@ namespace Acme.BookStore.Web.Menus 在`Pages/Book/`文件夹中创建 `index.js`文件 -![booktore-index-js-file](./images/booktore-index-js-file-v2.png) +![bookstore-index-js-file](./images/bookstore-index-js-file-v2.png) `index.js`的内容如下: @@ -727,7 +727,7 @@ $(function () { 最终的页面如下: -![Book list](./images/booktore-book-list-2.png) +![Book list](./images/bookstore-book-list-2.png) {{end}} @@ -760,7 +760,7 @@ yarn yarn ng generate module book --routing true ``` -![Generating book module](./images/booktore-creating-book-module-terminal.png) +![Generating book module](./images/bookstore-creating-book-module-terminal.png) #### 路由 @@ -793,7 +793,7 @@ import { ApplicationLayoutComponent } from '@abp/ng.theme.basic'; //==> added th yarn ng generate component book/book-list ``` -![Creating book list](./images/booktore-creating-book-list-terminal.png) +![Creating book list](./images/bookstore-creating-book-list-terminal.png) 打开 `app\book` 目录下的 `book.module.ts` 文件,使用以下内容替换它: @@ -849,7 +849,7 @@ yarn start 我们将看到book页面的 **book-list works!**: -![Initial book list page](./images/booktore-initial-book-list-page.png) +![Initial book list page](./images/bookstore-initial-book-list-page.png) #### 创建 BookState @@ -1048,11 +1048,11 @@ export class BookListComponent implements OnInit { 现在你可以在浏览器看到最终结果: -![Book list final result](./images/booktore-book-list.png) +![Book list final result](./images/bookstore-book-list.png) 项目的文件系统结构: -![Book list final result](./images/booktore-angular-file-tree.png) +![Book list final result](./images/bookstore-angular-file-tree.png) 在本教程中我们遵循了官方的[Angular风格指南](https://angular.io/guide/styleguide#file-tree). diff --git a/docs/zh-Hans/Tutorials/Part-2.md b/docs/zh-Hans/Tutorials/Part-2.md index 5e73c753e6..8dd64a49d0 100644 --- a/docs/zh-Hans/Tutorials/Part-2.md +++ b/docs/zh-Hans/Tutorials/Part-2.md @@ -29,7 +29,7 @@ end - **Part 2: 创建,编辑,删除书籍(本章)** - [Part-3: 集成测试](Part-3.md) -> 你也可以观看由ABP社区成员为本教程录制的[视频课程](https://amazingsolutions.teachable.com/p/lets-build-the-booktore-application). +> 你也可以观看由ABP社区成员为本教程录制的[视频课程](https://amazingsolutions.teachable.com/p/lets-build-the-bookstore-application). {{if UI == "MVC"}} @@ -37,13 +37,13 @@ end 通过本节, 你将会了解如何创建一个 modal form 来实现新增书籍的功能. 最终成果如下图所示: -![booktore-create-dialog](./images/booktore-create-dialog-2.png) +![bookstore-create-dialog](./images/bookstore-create-dialog-2.png) #### 新建 modal form 在 `Acme.BookStore.Web` 项目的 `Pages/Books` 目录下新建一个 `CreateModal.cshtml` Razor页面: -![booktore-add-create-dialog](./images/booktore-add-create-dialog-v2.png) +![bookstore-add-create-dialog](./images/bookstore-add-create-dialog-v2.png) ##### CreateModal.cshtml.cs @@ -130,7 +130,7 @@ namespace Acme.BookStore.Web.Pages.Books 如下图所示,只是在表格 **右上方** 添加了 **New book** 按钮: -![booktore-new-book-button](./images/booktore-new-book-button.png) +![bookstore-new-book-button](./images/bookstore-new-book-button.png) 打开 `Pages/book/index.js` 在 `datatable` 配置代码后面添加如下代码: @@ -155,7 +155,7 @@ $('#NewBookButton').click(function (e) { 在 `Acme.BookStore.Web` 项目的 `Pages/Books` 目录下新建一个名叫 `EditModal.cshtml` 的Razor页面: -![booktore-add-edit-dialog](./images/booktore-add-edit-dialog.png) +![bookstore-add-edit-dialog](./images/bookstore-add-edit-dialog.png) #### EditModal.cshtml.cs @@ -258,7 +258,7 @@ namespace Acme.BookStore.Web 我们将为表格每行添加下拉按钮 ("Actions") . 最终效果如下: -![booktore-book-table-actions](images/booktore-book-table-actions.png) +![bookstore-book-table-actions](images/bookstore-book-table-actions.png) 打开 `Pages/Books/Index.cshtml` 页面,并按下方所示修改表格部分的代码: @@ -450,6 +450,8 @@ $(function () { {{end}} +{{if UI == "NG"}} + ### 新增 Book 实体 下面的章节中,你将学习到如何创建一个新的模态对话框来新增Book实体. @@ -654,7 +656,7 @@ export class BookListComponent implements OnInit { 你可以打开浏览器,点击**New book**按钮看到模态框. -![Empty modal for new book](./images/booktore-empty-new-book-modal.png) +![Empty modal for new book](./images/bookstore-empty-new-book-modal.png) #### 添加响应式表单 @@ -878,7 +880,7 @@ export class BookListComponent implements OnInit { 现在你可以打开浏览器看到以下变化: -![New book modal](./images/booktore-new-book-form.png) +![New book modal](./images/bookstore-new-book-form.png) #### 保存图书 @@ -990,7 +992,7 @@ export class BookListComponent implements OnInit { 模态框最终看起来像这样: -![Save button to the modal](./images/booktore-new-book-form-v2.png) +![Save button to the modal](./images/bookstore-new-book-form-v2.png) ### 更新图书 @@ -1192,7 +1194,7 @@ export class BookListComponent implements OnInit { UI最终看起来像这样: -![Action buttons](./images/booktore-actions-buttons.png) +![Action buttons](./images/bookstore-actions-buttons.png) 打开 `app\app\book\book-list` 文件夹下的 `book-list.component.html` 文件,使用以下内容替换 `` 标签: @@ -1322,7 +1324,7 @@ delete(id: string) { `delete` 方法会显示一个确认弹层并订阅用户响应. 只在用户点击 `Yes` 按钮时分派动作. 确认弹层看起来如下: -![booktore-confirmation-popup](./images/booktore-confirmation-popup.png) +![bookstore-confirmation-popup](./images/bookstore-confirmation-popup.png) #### 添加删除按钮 @@ -1339,7 +1341,7 @@ delete(id: string) { 最终操作下拉框UI看起来如下: -![booktore-final-actions-dropdown](./images/booktore-final-actions-dropdown.png) +![bookstore-final-actions-dropdown](./images/bookstore-final-actions-dropdown.png) {{end}} diff --git a/docs/zh-Hans/UI/Angular/Component-Replacement.md b/docs/zh-Hans/UI/Angular/Component-Replacement.md index dc7c1a33c4..4465f34893 100644 --- a/docs/zh-Hans/UI/Angular/Component-Replacement.md +++ b/docs/zh-Hans/UI/Angular/Component-Replacement.md @@ -15,16 +15,20 @@ import { ..., AddReplaceableComponent } from '@abp/ng.core'; // imported AddRepl import { eIdentityComponents } from '@abp/ng.identity'; // imported eIdentityComponents enum import { Store } from '@ngxs/store'; // imported Store //... -export class AppComponent { + +@Component(/* component metadata */) +export class AppComponent implements OnInit { constructor(..., private store: Store) {} // injected Store ngOnInit() { + // added dispatch this.store.dispatch( new AddReplaceableComponent({ component: YourNewRoleComponent, key: eIdentityComponents.Roles, }), ); + //... } } @@ -54,7 +58,7 @@ yarn ng generate component shared/my-application-layout --export --entryComponen ``` -打开 `app.component.ts` 添加以下内容: +打开 `src/app` 文件夹下的 `app.component.ts` 文件添加以下内容: ```js import { ..., AddReplaceableComponent } from '@abp/ng.core'; // imported AddReplaceableComponent @@ -62,11 +66,13 @@ import { eThemeBasicComponents } from '@abp/ng.theme.basic'; // imported eThemeB import { MyApplicationLayoutComponent } from './shared/my-application-layout/my-application-layout.component'; // imported MyApplicationLayoutComponent import { Store } from '@ngxs/store'; // imported Store //... -export class AppComponent { + +@Component(/* component metadata */) +export class AppComponent implements OnInit { constructor(..., private store: Store) {} // injected Store ngOnInit() { - // added below content + // added dispatch this.store.dispatch( new AddReplaceableComponent({ component: MyApplicationLayoutComponent, @@ -79,6 +85,464 @@ export class AppComponent { } ``` +### 布局组件 + +![Layout Components](./images/layout-components.png) + +#### 如何替换LogoComponent + +![LogoComponent](./images/logo-component.png) + +在 `angular` 目录下运行以下命令创建新的组件 `LogoComponent`: + +```bash +yarn ng generate component logo --inlineTemplate --inlineStyle --entryComponent + +# You don't need the --entryComponent option in Angular 9 +``` + +打开 `src/app/logo` 目录下生成的 `logo.component.ts` 并使用以下内容替换它: + +```js +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-logo', + template: ` + + + logo + + `, +}) +export class LogoComponent {} +``` + +打开 `src/app` 目录下的 `app.component.ts` 做以下修改: + +```js +import { ..., AddReplaceableComponent } from '@abp/ng.core'; // imported AddReplaceableComponent +import { Store } from '@ngxs/store'; // imported Store +import { LogoComponent } from './logo/logo.component'; // imported NavItemsComponent +import { eThemeBasicComponents } from '@abp/ng.theme.basic'; // imported eThemeBasicComponents +//... + +@Component(/* component metadata */) +export class AppComponent implements OnInit { + constructor(..., private store: Store) {} // injected Store + + ngOnInit() { + //... + + // added dispatch + this.store.dispatch( + new AddReplaceableComponent({ + component: LogoComponent, + key: eThemeBasicComponents.Logo, + }), + ); + } +} +``` + +最终UI如下: + +![New logo](./images/replaced-logo-component.png) + +#### 如何替换RoutesComponent + +![RoutesComponent](./images/routes-component.png) + +在 `angular` 目录下运行以下命令创建新的组件 `RoutesComponent`: + + +```bash +yarn ng generate component routes --entryComponent + +# You don't need the --entryComponent option in Angular 9 +``` + +打开 `src/app/routes` 目录下生成的 `routes.component.ts` 并使用以下内容替换它: + +```js +import { ABP, ReplaceableComponents } from '@abp/ng.core'; +import { + Component, + HostBinding, + Inject, + Renderer2, + TrackByFunction, + AfterViewInit, +} from '@angular/core'; +import { fromEvent } from 'rxjs'; +import { debounceTime } from 'rxjs/operators'; + +@Component({ + selector: 'app-routes', + templateUrl: 'routes.component.html', +}) +export class RoutesComponent implements AfterViewInit { + @HostBinding('class.mx-auto') + marginAuto = true; + + smallScreen = window.innerWidth < 992; + + constructor(private renderer: Renderer2) {} + + ngAfterViewInit() { + fromEvent(window, 'resize') + .pipe(debounceTime(150)) + .subscribe(() => { + this.smallScreen = window.innerWidth < 992; + }); + } +} +``` + +打开 `src/app/routes` 目录下生成的 `routes.component.html` 并使用以下内容替换它: + +```html + +``` + +打开 `src/app` 目录下的 `app.component.ts` 做以下修改: + +```js +import { ..., AddReplaceableComponent } from '@abp/ng.core'; // imported AddReplaceableComponent +import { Store } from '@ngxs/store'; // imported Store +import { RoutesComponent } from './routes/routes.component'; // imported NavItemsComponent +import { eThemeBasicComponents } from '@abp/ng.theme.basic'; // imported eThemeBasicComponents +//... + +@Component(/* component metadata */) +export class AppComponent implements OnInit { + constructor(..., private store: Store) {} // injected Store + + ngOnInit() { + //... + + // added dispatch + this.store.dispatch( + new AddReplaceableComponent({ + component: RoutesComponent, + key: eThemeBasicComponents.Routes, + }), + ); + } +} +``` + +最终UI如下: + +![New routes](./images/replaced-routes-component.png) + +#### 如何替换NavItemsComponent + +![NavItemsComponent](./images/nav-items-component.png) + +在 `angular` 目录下运行以下命令创建新的组件 `NavItemsComponent`: + +```bash +yarn ng generate component nav-items --entryComponent + +# You don't need the --entryComponent option in Angular 9 +``` + +打开 `src/app/nav-items` 目录下生成的 `nav-items.component.ts` 并使用以下内容替换它: + +```js +import { + ApplicationConfiguration, + AuthService, + ConfigState, + SessionState, + SetLanguage, +} from '@abp/ng.core'; +import { Component, AfterViewInit } from '@angular/core'; +import { Navigate, RouterState } from '@ngxs/router-plugin'; +import { Select, Store } from '@ngxs/store'; +import { Observable, fromEvent } from 'rxjs'; +import { map, debounceTime } from 'rxjs/operators'; +import snq from 'snq'; + +@Component({ + selector: 'app-nav-items', + templateUrl: 'nav-items.component.html', +}) +export class NavItemsComponent implements AfterViewInit { + @Select(ConfigState.getOne('currentUser')) + currentUser$: Observable; + + @Select(ConfigState.getDeep('localization.languages')) + languages$: Observable; + + smallScreen = window.innerWidth < 992; + + get defaultLanguage$(): Observable { + return this.languages$.pipe( + map( + languages => + snq( + () => languages.find(lang => lang.cultureName === this.selectedLangCulture).displayName, + ), + '', + ), + ); + } + + get dropdownLanguages$(): Observable { + return this.languages$.pipe( + map( + languages => + snq(() => languages.filter(lang => lang.cultureName !== this.selectedLangCulture)), + [], + ), + ); + } + + get selectedLangCulture(): string { + return this.store.selectSnapshot(SessionState.getLanguage); + } + + constructor(private store: Store, private authService: AuthService) {} + + ngAfterViewInit() { + fromEvent(window, 'resize') + .pipe(debounceTime(150)) + .subscribe(() => { + this.smallScreen = window.innerWidth < 992; + }); + } + + onChangeLang(cultureName: string) { + this.store.dispatch(new SetLanguage(cultureName)); + } + + logout() { + this.authService.logout().subscribe(() => { + this.store.dispatch( + new Navigate(['/'], null, { + state: { redirectUrl: this.store.selectSnapshot(RouterState).state.url }, + }), + ); + }); + } +} +``` + +打开 `src/app/nav-items` 目录下生成的 `nav-items.component.html` 并使用以下内容替换它: + +```html + +``` + +打开 `src/app` 目录下的 `app.component.ts` 做以下修改: + +```js +import { ..., AddReplaceableComponent } from '@abp/ng.core'; // imported AddReplaceableComponent +import { Store } from '@ngxs/store'; // imported Store +import { NavItemsComponent } from './nav-items/nav-items.component'; // imported NavItemsComponent +import { eThemeBasicComponents } from '@abp/ng.theme.basic'; // imported eThemeBasicComponents +//... + +@Component(/* component metadata */) +export class AppComponent implements OnInit { + constructor(..., private store: Store) {} // injected Store + + ngOnInit() { + //... + + // added dispatch + this.store.dispatch( + new AddReplaceableComponent({ + component: NavItemsComponent, + key: eThemeBasicComponents.NavItems, + }), + ); + } +} +``` + +最终UI如下: + +![New nav-items](./images/replaced-nav-items-component.png) + ## 下一步是什么? -- [自定义设置页面](./Custom-Setting-Page.md) +- [自定义设置页面](./Custom-Setting-Page.md) \ No newline at end of file diff --git a/docs/zh-Hans/UI/Angular/List-Service.md b/docs/zh-Hans/UI/Angular/List-Service.md new file mode 100644 index 0000000000..6d5bc6f1ef --- /dev/null +++ b/docs/zh-Hans/UI/Angular/List-Service.md @@ -0,0 +1,163 @@ +# 使用ListService轻松查询列表 + +`ListService` 是一种实用程序服务,提供简单的分页,排序和搜索实现. + +## 入门 + +`ListService` **没有在根提供**. 原因是通过这种方式它会清除组件上的所有订阅. 你可以使用可选的 `LIST_QUERY_DEBOUNCE_TIME` 令牌调整debounce行为. + +```js +import { ListService } from '@abp/ng.core'; +import { BookDto } from '../models'; +import { BookService } from '../services'; + +@Component({ + /* class metadata here */ + providers: [ + // [Required] + ListService, + + // [Optional] + // Provide this token if you want a different debounce time. + // Default is 300. Cannot be 0. Any value below 100 is not recommended. + { provide: LIST_QUERY_DEBOUNCE_TIME, useValue: 500 }, + ], + template: ` + + `, +}) +class BookComponent { + items: BookDto[] = []; + count = 0; + + constructor( + public readonly list: ListService, + private bookService: BookService, + ) {} + + ngOnInit() { + // A function that gets query and returns an observable + const bookStreamCreator = query => this.bookService.getList(query); + + this.list.hookToQuery(bookStreamCreator).subscribe( + response => { + this.items = response.items; + this.count = response.count; + // If you use OnPush change detection strategy, + // call detectChanges method of ChangeDetectorRef here. + } + ); // Subscription is auto-cleared on destroy. + } +} +``` + +> 注意 `list` 是 `public` 并且 `readonly`. 因为我们将直接在组件的模板中使用 `ListService` 属性. 可以视为反模式,但是实现起来要快得多. 你可以改为使用公共组件属性. + +将 `ListService` 属性放入模板中,如下所示: + +```html + + + + + + + {%{{{ '::Name' | abpLocalization }}}%} + + + + + + + + {%{{{ data.name }}}%} + + +``` + +## 与Observables一起使用 + +你可以将Observables与Angular的[AsyncPipe](https://angular.io/guide/observables-in-angular#async-pipe)结合使用: + +```ts + book$ = this.list.hookToQuery(query => this.bookService.getListByInput(query)); +``` + +```html + + + + + + +``` + +...or... + + +```ts + @Select(BookState.getBooks) + books$: Observable; + + @Select(BookState.getBookCount) + bookCount$: Observable; + + ngOnInit() { + this.list.hookToQuery((query) => this.store.dispatch(new GetBooks(query))).subscribe(); + } +``` + +```html + + + + +``` + +## 如何在创建/更新/删除时刷新表 + +`ListService` 公开了一个 `get` 方法来触发当前查询的请求. 因此基本上每当创建,更新或删除操作解析时,你可以调用 `this.list.get();` 它会调用钩子流创建者. + +```ts +this.store.dispatch(new DeleteBook(id)).subscribe(this.list.get); +``` + +...or... + +```ts +this.bookService.createByInput(form.value) + .subscribe(() => { + this.list.get(); + + // Other subscription logic here + }) +``` + +## 如何在表中实现服务器端搜索 + +`ListService` 公开一个 `filter` 属性,该属性将使用当前查询和给定的搜索字符串触发一个请求. 你需要做的就是通过双向绑定将其绑定到输入元素. + +```html + + + +``` diff --git a/docs/zh-Hans/UI/Angular/Localization.md b/docs/zh-Hans/UI/Angular/Localization.md index 8e30cb7a50..60af06a976 100644 --- a/docs/zh-Hans/UI/Angular/Localization.md +++ b/docs/zh-Hans/UI/Angular/Localization.md @@ -130,6 +130,68 @@ this.store.selectSnapshot( 本地化资源存储在 `ConfigState` 的 `localization` 属性中. +## RTL支持 + +从v2.9开始,ABP支持RTL. 如果使用v2.9及更高版本生成新项目,你无需进行任何更改. 如果要从早期版本迁移项目,请按照以下2个步骤操作: + +#### 步骤 1. 为 Bootstrap LRT和RTL创建Chunks + +在[angular.json中找到样式配置](https://angular.io/guide/workspace-config#style-script-config)确保项目中的chunks含有 `bootstrap-rtl.min` 和 `bootstrap-ltr.min`: + +```json +{ + "projects": { + "MyProjectName": { + "architect": { + "build": { + "options": { + "styles": [ + { + "input": "node_modules/@abp/ng.theme.shared/styles/bootstrap-rtl.min.css", + "inject": false, + "bundleName": "bootstrap-rtl.min" + }, + { + "input": "node_modules/bootstrap/dist/css/bootstrap.min.css", + "inject": true, + "bundleName": "bootstrap-ltr.min" + }, + { + "input": "node_modules/@fortawesome/fontawesome-free/css/all.min.css", + "inject": true, + "bundleName": "fontawesome-all.min" + }, + { + "input": "node_modules/@fortawesome/fontawesome-free/css/v4-shims.min.css", + "inject": true, + "bundleName": "fontawesome-v4-shims.min" + }, + "apps/dev-app/src/styles.scss" + ], + } + } + } + } + } +} + +#### 步骤 2. 清除AppComponent中延迟加载的Fontawesome + +如果你如上所述为Fontawesome创建并且注入了chunks,就不再需要v2.9版本之前的实现的 `AppComponent` 延迟加载. 删除它们即可,新版的 `AppComponent` 如下所示: + +```js +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-root', + template: ` + + + `, +}) +export class AppComponent {} +``` + ## 另请参阅 * [ASP.NET Core中的本地化](../../Localization.md) diff --git a/docs/zh-Hans/docs-nav.json b/docs/zh-Hans/docs-nav.json index a3becc073b..23b17c1231 100644 --- a/docs/zh-Hans/docs-nav.json +++ b/docs/zh-Hans/docs-nav.json @@ -372,6 +372,10 @@ { "text": "TrackByService", "path": "UI/Angular/Track-By-Service.md" + }, + { + "text": "ListService", + "path": "UI/Angular/List-Service.md" } ] }, @@ -426,6 +430,10 @@ "text": "到PostgreSQL", "path": "Entity-Framework-Core-PostgreSQL.md" }, + { + "text": " Oracle", + "path": "Entity-Framework-Core-Oracle.md" + }, { "text": "到SQLite", "path": "Entity-Framework-Core-SQLite.md" @@ -446,6 +454,15 @@ } ] }, + { + "text": "实时", + "items": [ + { + "text": "SignalR集成", + "path": "SignalR-Integration.md" + } + ] + }, { "text": "后台服务", "items": [ diff --git a/docs/zh-Hans/images/hello-template.png b/docs/zh-Hans/images/hello-template.png new file mode 100644 index 0000000000..0a3ce2816a Binary files /dev/null and b/docs/zh-Hans/images/hello-template.png differ diff --git a/docs/zh-Hans/images/multiple-file-template.png b/docs/zh-Hans/images/multiple-file-template.png new file mode 100644 index 0000000000..a2e7327301 Binary files /dev/null and b/docs/zh-Hans/images/multiple-file-template.png differ diff --git a/docs/zh-Hans/images/virtual-file-explorer.png b/docs/zh-Hans/images/virtual-file-explorer.png new file mode 100644 index 0000000000..e49d0f2111 Binary files /dev/null and b/docs/zh-Hans/images/virtual-file-explorer.png differ diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln index 8e8dac2ce5..135afaf4d9 100644 --- a/framework/Volo.Abp.sln +++ b/framework/Volo.Abp.sln @@ -291,6 +291,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.SignalR EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests", "test\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests.csproj", "{79323211-E658-493E-9863-035AA4C3F913}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring", "src\Volo.Abp.BlobStoring\Volo.Abp.BlobStoring.csproj", "{A0CFBDD6-A3CB-438C-83F1-5025F12E2D42}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring.Tests", "test\Volo.Abp.BlobStoring.Tests\Volo.Abp.BlobStoring.Tests.csproj", "{D53A17BB-4E23-451D-AD9B-E1F6AC3F7958}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring.FileSystem", "src\Volo.Abp.BlobStoring.FileSystem\Volo.Abp.BlobStoring.FileSystem.csproj", "{02B1FBE2-850E-4612-ABC6-DD62BCF2DD6B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring.FileSystem.Tests", "test\Volo.Abp.BlobStoring.FileSystem.Tests\Volo.Abp.BlobStoring.FileSystem.Tests.csproj", "{68443D4A-1608-4039-B995-7AF4CF82E9F8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.EntityFrameworkCore.Oracle.Devart", "src\Volo.Abp.EntityFrameworkCore.Oracle.Devart\Volo.Abp.EntityFrameworkCore.Oracle.Devart.csproj", "{75E5C841-5F36-4C44-A532-57CB8E7FFE15}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -865,6 +875,26 @@ Global {79323211-E658-493E-9863-035AA4C3F913}.Debug|Any CPU.Build.0 = Debug|Any CPU {79323211-E658-493E-9863-035AA4C3F913}.Release|Any CPU.ActiveCfg = Release|Any CPU {79323211-E658-493E-9863-035AA4C3F913}.Release|Any CPU.Build.0 = Release|Any CPU + {A0CFBDD6-A3CB-438C-83F1-5025F12E2D42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A0CFBDD6-A3CB-438C-83F1-5025F12E2D42}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A0CFBDD6-A3CB-438C-83F1-5025F12E2D42}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A0CFBDD6-A3CB-438C-83F1-5025F12E2D42}.Release|Any CPU.Build.0 = Release|Any CPU + {D53A17BB-4E23-451D-AD9B-E1F6AC3F7958}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D53A17BB-4E23-451D-AD9B-E1F6AC3F7958}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D53A17BB-4E23-451D-AD9B-E1F6AC3F7958}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D53A17BB-4E23-451D-AD9B-E1F6AC3F7958}.Release|Any CPU.Build.0 = Release|Any CPU + {02B1FBE2-850E-4612-ABC6-DD62BCF2DD6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {02B1FBE2-850E-4612-ABC6-DD62BCF2DD6B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {02B1FBE2-850E-4612-ABC6-DD62BCF2DD6B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {02B1FBE2-850E-4612-ABC6-DD62BCF2DD6B}.Release|Any CPU.Build.0 = Release|Any CPU + {68443D4A-1608-4039-B995-7AF4CF82E9F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {68443D4A-1608-4039-B995-7AF4CF82E9F8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {68443D4A-1608-4039-B995-7AF4CF82E9F8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {68443D4A-1608-4039-B995-7AF4CF82E9F8}.Release|Any CPU.Build.0 = Release|Any CPU + {75E5C841-5F36-4C44-A532-57CB8E7FFE15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {75E5C841-5F36-4C44-A532-57CB8E7FFE15}.Debug|Any CPU.Build.0 = Debug|Any CPU + {75E5C841-5F36-4C44-A532-57CB8E7FFE15}.Release|Any CPU.ActiveCfg = Release|Any CPU + {75E5C841-5F36-4C44-A532-57CB8E7FFE15}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1012,6 +1042,11 @@ Global {B64FCE08-E9D2-4984-BF12-FE199F257416} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {8B758716-DCC9-4223-8421-5588D1597487} = {447C8A77-E5F0-4538-8687-7383196D04EA} {79323211-E658-493E-9863-035AA4C3F913} = {447C8A77-E5F0-4538-8687-7383196D04EA} + {A0CFBDD6-A3CB-438C-83F1-5025F12E2D42} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {D53A17BB-4E23-451D-AD9B-E1F6AC3F7958} = {447C8A77-E5F0-4538-8687-7383196D04EA} + {02B1FBE2-850E-4612-ABC6-DD62BCF2DD6B} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {68443D4A-1608-4039-B995-7AF4CF82E9F8} = {447C8A77-E5F0-4538-8687-7383196D04EA} + {75E5C841-5F36-4C44-A532-57CB8E7FFE15} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs index da99a3e948..0001f56c1c 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs @@ -1,6 +1,7 @@ using System; using Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending; using Volo.Abp.AspNetCore.Mvc.MultiTenancy; +using Volo.Abp.Timing; namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations { @@ -21,6 +22,10 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations public CurrentTenantDto CurrentTenant { get; set; } + public TimingDto Timing { get; set; } + + public ClockDto Clock { get; set; } + public ObjectExtensionsDto ObjectExtensions { get; set; } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ClockDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ClockDto.cs new file mode 100644 index 0000000000..2cff97f814 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ClockDto.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations +{ + public class ClockDto + { + public string Kind { get; set; } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/TimingDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/TimingDto.cs new file mode 100644 index 0000000000..bd95bfba0d --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/TimingDto.cs @@ -0,0 +1,24 @@ +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations +{ + public class TimingDto + { + public TimeZone TimeZone { get; set; } + } + + public class TimeZone + { + public IanaTimeZone Iana { get; set; } + + public WindowsTimeZone Windows { get; set; } + } + + public class WindowsTimeZone + { + public string TimeZoneId { get; set; } + } + + public class IanaTimeZone + { + public string TimeZoneName { get; set; } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelper.cs index 632bbd3a92..1188d68056 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/AbpTagHelper.cs @@ -1,5 +1,7 @@ -using System; +using System; using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.DependencyInjection; using Volo.Abp.Threading; @@ -8,17 +10,21 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers { public abstract class AbpTagHelper : TagHelper, ITransientDependency { - + } public abstract class AbpTagHelper : AbpTagHelper where TTagHelper : AbpTagHelper - where TService : class, IAbpTagHelperService + where TService : class, IAbpTagHelperService { protected TService Service { get; } public override int Order => Service.Order; + [HtmlAttributeNotBound] + [ViewContext] + public ViewContext ViewContext { get; set; } + protected AbpTagHelper(TService service) { Service = service; @@ -40,4 +46,4 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers return Service.ProcessAsync(context, output); } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelper.cs index 141d995fd7..d710793151 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelper.cs @@ -12,10 +12,6 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form [HtmlAttributeName("abp-model")] public ModelExpression Model { get; set; } - [HtmlAttributeNotBound] - [ViewContext] - public ViewContext ViewContext { get; set; } - public bool? SubmitButton { get; set; } public bool? RequiredSymbols { get; set; } = true; diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs index 16a700f4bc..efd3af2d2c 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs @@ -29,15 +29,11 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form [HtmlAttributeName("required-symbol")] public bool DisplayRequiredSymbol { get; set; } = true; - [HtmlAttributeNotBound] - [ViewContext] - public ViewContext ViewContext { get; set; } - [HtmlAttributeName("asp-format")] public string Format { get; set; } public string Name { get; set; } - + public string Value { get; set; } public AbpInputTagHelper(AbpInputTagHelperService tagHelperService) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpRadioInputTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpRadioInputTagHelper.cs index c299826a07..51982ae4eb 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpRadioInputTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpRadioInputTagHelper.cs @@ -18,10 +18,6 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form public IEnumerable AspItems { get; set; } - [HtmlAttributeNotBound] - [ViewContext] - public ViewContext ViewContext { get; set; } - public AbpRadioInputTagHelper(AbpRadioInputTagHelperService tagHelperService) : base(tagHelperService) { diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelper.cs index ae46336f77..4df9733d83 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelper.cs @@ -21,10 +21,6 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form [HtmlAttributeName("required-symbol")] public bool DisplayRequiredSymbol { get; set; } = true; - [HtmlAttributeNotBound] - [ViewContext] - public ViewContext ViewContext { get; set; } - public AbpSelectTagHelper(AbpSelectTagHelperService tagHelperService) : base(tagHelperService) { diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/AbpPaginationTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/AbpPaginationTagHelper.cs index 2e6f7cd743..8132623d00 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/AbpPaginationTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/AbpPaginationTagHelper.cs @@ -11,10 +11,6 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination public bool? ShowInfo { get; set; } - [HtmlAttributeNotBound] - [ViewContext] - public ViewContext ViewContext { get; set; } - public AbpPaginationTagHelper(AbpPaginationTagHelperService tagHelperService) : base(tagHelperService) { diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpBundleItemTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpBundleItemTagHelper.cs index ea9980e183..170c16c571 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpBundleItemTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpBundleItemTagHelper.cs @@ -3,9 +3,9 @@ using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers; namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers { - public abstract class AbpBundleItemTagHelper : AbpTagHelper, IBundleItemTagHelper + public abstract class AbpBundleItemTagHelper : AbpTagHelper, IBundleItemTagHelper where TTagHelper : AbpTagHelper, IBundleItemTagHelper - where TTagHelperService: AbpBundleItemTagHelperService + where TTagHelperService: AbpBundleItemTagHelperService { /// /// A file path. @@ -57,4 +57,4 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers protected abstract string GetFileExtension(); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpBundleItemTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpBundleItemTagHelperService.cs index fc59051720..eca80c5fcf 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpBundleItemTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpBundleItemTagHelperService.cs @@ -5,8 +5,9 @@ using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers; namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers { - public abstract class AbpBundleItemTagHelperService : AbpTagHelperService - where TTagHelper : TagHelper, IBundleItemTagHelper + public abstract class AbpBundleItemTagHelperService : AbpTagHelperService + where TTagHelper : AbpTagHelper, IBundleItemTagHelper + where TService : class, IAbpTagHelperService { protected AbpTagHelperResourceService ResourceService { get; } @@ -26,6 +27,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers else { await ResourceService.ProcessAsync( + TagHelper.ViewContext, context, output, new List @@ -37,4 +39,4 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers } } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpBundleTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpBundleTagHelperService.cs index a8eadce1e8..f0c76d5cc4 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpBundleTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpBundleTagHelperService.cs @@ -5,8 +5,9 @@ using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers; namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers { - public abstract class AbpBundleTagHelperService : AbpTagHelperService - where TTagHelper : TagHelper, IBundleTagHelper + public abstract class AbpBundleTagHelperService : AbpTagHelperService + where TTagHelper : AbpTagHelper, IBundleTagHelper + where TService : class, IAbpTagHelperService { protected AbpTagHelperResourceService ResourceService { get; } @@ -18,6 +19,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) { await ResourceService.ProcessAsync( + TagHelper.ViewContext, context, output, await GetBundleItems(context, output), @@ -33,4 +35,4 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers return bundleItems; } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpScriptBundleTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpScriptBundleTagHelperService.cs index c4b00b39cd..be6b7273e4 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpScriptBundleTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpScriptBundleTagHelperService.cs @@ -1,10 +1,10 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers { - public class AbpScriptBundleTagHelperService : AbpBundleTagHelperService + public class AbpScriptBundleTagHelperService : AbpBundleTagHelperService { - public AbpScriptBundleTagHelperService(AbpTagHelperScriptService resourceHelper) + public AbpScriptBundleTagHelperService(AbpTagHelperScriptService resourceHelper) : base(resourceHelper) { } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpScriptTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpScriptTagHelperService.cs index d31c7bd7ce..6321bfdc6c 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpScriptTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpScriptTagHelperService.cs @@ -1,10 +1,10 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers { - public class AbpScriptTagHelperService : AbpBundleItemTagHelperService + public class AbpScriptTagHelperService : AbpBundleItemTagHelperService { public AbpScriptTagHelperService(AbpTagHelperScriptService resourceService) : base(resourceService) { } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpStyleBundleTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpStyleBundleTagHelperService.cs index f52f3dac88..7d05e3a96c 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpStyleBundleTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpStyleBundleTagHelperService.cs @@ -1,10 +1,10 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers { - public class AbpStyleBundleTagHelperService : AbpBundleTagHelperService + public class AbpStyleBundleTagHelperService : AbpBundleTagHelperService { - public AbpStyleBundleTagHelperService(AbpTagHelperStyleService resourceHelper) + public AbpStyleBundleTagHelperService(AbpTagHelperStyleService resourceHelper) : base(resourceHelper) { } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpStyleTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpStyleTagHelperService.cs index bb828e47ae..7854e5157e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpStyleTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpStyleTagHelperService.cs @@ -1,10 +1,10 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers { - public class AbpStyleTagHelperService : AbpBundleItemTagHelperService + public class AbpStyleTagHelperService : AbpBundleItemTagHelperService { public AbpStyleTagHelperService(AbpTagHelperStyleService resourceService) : base(resourceService) { } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperResourceService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperResourceService.cs index 88387e8937..2971716b90 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperResourceService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperResourceService.cs @@ -5,6 +5,8 @@ using System.Collections.Generic; using System.Diagnostics; using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; @@ -20,7 +22,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers protected IWebContentFileProvider WebContentFileProvider { get; } protected IWebHostEnvironment HostingEnvironment { get; } protected readonly AbpBundlingOptions Options; - + protected AbpTagHelperResourceService( IBundleManager bundleManager, IWebContentFileProvider webContentFileProvider, @@ -36,11 +38,13 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers } public virtual async Task ProcessAsync( + [NotNull] ViewContext viewContext, [NotNull] TagHelperContext context, [NotNull] TagHelperOutput output, [NotNull] List bundleItems, [CanBeNull] string bundleName = null) { + Check.NotNull(viewContext, nameof(viewContext)); Check.NotNull(context, nameof(context)); Check.NotNull(output, nameof(output)); Check.NotNull(bundleItems, nameof(bundleItems)); @@ -69,7 +73,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers throw new AbpException($"Could not find the bundle file '{bundleFile}' from {nameof(IWebContentFileProvider)}"); } - AddHtmlTag(context, output, bundleFile + "?_v=" + file.LastModified.UtcTicks); + AddHtmlTag(viewContext, context, output, bundleFile + "?_v=" + file.LastModified.UtcTicks); } stopwatch.Stop(); @@ -80,11 +84,11 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers protected abstract Task> GetBundleFilesAsync(string bundleName); - protected abstract void AddHtmlTag(TagHelperContext context, TagHelperOutput output, string file); + protected abstract void AddHtmlTag(ViewContext viewContext, TagHelperContext context, TagHelperOutput output, string file); protected virtual string GenerateBundleName(List bundleItems) { return bundleItems.JoinAsString("|").ToMd5(); } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperScriptService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperScriptService.cs index b087db517f..6efbb793e7 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperScriptService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperScriptService.cs @@ -3,7 +3,11 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.Mvc.Routing; +using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Razor.TagHelpers; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; using Volo.Abp.AspNetCore.VirtualFileSystem; @@ -39,9 +43,9 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers return await BundleManager.GetScriptBundleFilesAsync(bundleName); } - protected override void AddHtmlTag(TagHelperContext context, TagHelperOutput output, string file) + protected override void AddHtmlTag(ViewContext viewContext, TagHelperContext context, TagHelperOutput output, string file) { - output.Content.AppendHtml($"{Environment.NewLine}"); + output.Content.AppendHtml($"{Environment.NewLine}"); } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperStyleService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperStyleService.cs index 2d273345cb..8612e4f8b4 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperStyleService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperStyleService.cs @@ -3,7 +3,11 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.Mvc.Routing; +using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Razor.TagHelpers; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; using Volo.Abp.AspNetCore.VirtualFileSystem; @@ -39,9 +43,9 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers return await BundleManager.GetStyleBundleFilesAsync(bundleName); } - protected override void AddHtmlTag(TagHelperContext context, TagHelperOutput output, string file) + protected override void AddHtmlTag(ViewContext viewContext, TagHelperContext context, TagHelperOutput output, string file) { - output.Content.AppendHtml($"{Environment.NewLine}"); + output.Content.AppendHtml($"{Environment.NewLine}"); } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy.csproj b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy.csproj index 2400b9c291..906f4fc2c9 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy.csproj +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy.csproj @@ -18,10 +18,8 @@ - - diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/cs.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/cs.json index 5887243cb4..05457dfa16 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/cs.json +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/cs.json @@ -9,4 +9,4 @@ "SwitchTenant": "Změnit tenant", "NotSelected": "Nevybrán" } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/BootstrapDatepicker/BootstrapDatepickerScriptContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/BootstrapDatepicker/BootstrapDatepickerScriptContributor.cs index 5a32cb7ca0..fb29a8f2a5 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/BootstrapDatepicker/BootstrapDatepickerScriptContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/BootstrapDatepicker/BootstrapDatepickerScriptContributor.cs @@ -9,6 +9,11 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.BootstrapDatepicker [DependsOn(typeof(JQueryScriptContributor))] public class BootstrapDatepickerScriptContributor : BundleContributor { + public static readonly Dictionary CultureMap = new Dictionary + { + {"zh-Hans", "zh-CN"} + }; + public override void ConfigureBundle(BundleConfigurationContext context) { context.Files.AddIfNotContains("/libs/bootstrap-datepicker/bootstrap-datepicker.min.js"); @@ -21,10 +26,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.BootstrapDatepicker ? "en" : CultureInfo.CurrentUICulture.Name; - if (TryAddCultureFile(context, MapCultureName(cultureName))) - { - return; - } + TryAddCultureFile(context, MapCultureName(cultureName)); } protected virtual bool TryAddCultureFile(BundleConfigurationContext context, string cultureName) @@ -42,7 +44,8 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.BootstrapDatepicker protected virtual string MapCultureName(string cultureName) { - return cultureName; + return CultureMap.GetOrDefault(cultureName) ?? + cultureName; } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/Default.cshtml b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/Default.cshtml index 0c6b5d8a3b..064a3756c4 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/Default.cshtml +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/Default.cshtml @@ -5,12 +5,13 @@ var elementId = string.IsNullOrEmpty(menuItem.ElementId) ? string.Empty : $"id=\"{menuItem.ElementId}\""; var cssClass = string.IsNullOrEmpty(menuItem.CssClass) ? string.Empty : menuItem.CssClass; var disabled = menuItem.IsDisabled ? "disabled" : string.Empty; + var url = string.IsNullOrEmpty(menuItem.Url) ? "#" : Url.Content(menuItem.Url); if (menuItem.IsLeaf) { if (menuItem.Url != null) { } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/_MenuItem.cshtml b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/_MenuItem.cshtml index eaec9fc7c4..108de2306a 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/_MenuItem.cshtml +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/_MenuItem.cshtml @@ -4,12 +4,13 @@ var elementId = string.IsNullOrEmpty(Model.ElementId) ? string.Empty : $"id=\"{Model.ElementId}\""; var cssClass = string.IsNullOrEmpty(Model.CssClass) ? string.Empty : Model.CssClass; var disabled = Model.IsDisabled ? "disabled" : string.Empty; + var url = string.IsNullOrEmpty(Model.Url) ? "#" : Url.Content(Model.Url); } @if (Model.IsLeaf) { if (Model.Url != null) { - + @if (Model.Icon != null) { if (Model.Icon.StartsWith("fa")) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/PageAlerts/Default.cshtml b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/PageAlerts/Default.cshtml index fa40f77f14..5bbc5b1ad0 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/PageAlerts/Default.cshtml +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/PageAlerts/Default.cshtml @@ -4,7 +4,7 @@ diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Toolbar/UserMenu/Default.cshtml b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Toolbar/UserMenu/Default.cshtml index d1738165d3..06a96edce7 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Toolbar/UserMenu/Default.cshtml +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Toolbar/UserMenu/Default.cshtml @@ -27,8 +27,9 @@ var elementId = string.IsNullOrEmpty(menuItem.ElementId) ? string.Empty : $"id=\"{menuItem.ElementId}\""; var cssClass = string.IsNullOrEmpty(menuItem.CssClass) ? string.Empty : menuItem.CssClass; var disabled = menuItem.IsDisabled ? "disabled" : string.Empty; + var url = string.IsNullOrEmpty(menuItem.Url) ? "#" : Url.Content(menuItem.Url); - + @if (menuItem.Icon != null) { if (menuItem.Icon.StartsWith("fa")) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml index e741f41e8f..2c8401aacd 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml @@ -56,7 +56,7 @@
- + @if (MultiTenancyOptions.Value.IsEnabled && (TenantResolveResultAccessor.Result?.AppliedResolvers?.Contains(CookieTenantResolveContributor.ContributorName) == true)) { diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.csproj b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.csproj index 43f5bc20bf..c390cfe58c 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.csproj +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.csproj @@ -17,17 +17,11 @@ - - - - - - @@ -36,5 +30,5 @@ - + diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.csproj b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.csproj index 66015b14a7..078563ace3 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.csproj +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Demo.csproj @@ -17,11 +17,6 @@ - - - - - diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpApplicationPath/AbpApplicationPathViewComponent.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpApplicationPath/AbpApplicationPathViewComponent.cs new file mode 100644 index 0000000000..32a89cdcb8 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpApplicationPath/AbpApplicationPathViewComponent.cs @@ -0,0 +1,19 @@ +using System; +using Microsoft.AspNetCore.Mvc; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Pages.Shared.Components.AbpApplicationPath +{ + public class AbpApplicationPathViewComponent : AbpViewComponent + { + public IViewComponentResult Invoke() + { + var applicationPath = ViewContext.HttpContext.Request.PathBase.Value; + var model = new AbpApplicationPathViewComponentModel + { + ApplicationPath = applicationPath == null ? "/" : applicationPath.EnsureEndsWith('/') + }; + + return View("~/Pages/Shared/Components/AbpApplicationPath/Default.cshtml",model); + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpApplicationPath/AbpApplicationPathViewComponentModel.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpApplicationPath/AbpApplicationPathViewComponentModel.cs new file mode 100644 index 0000000000..ecf50f63e8 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpApplicationPath/AbpApplicationPathViewComponentModel.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Pages.Shared.Components.AbpApplicationPath +{ + public class AbpApplicationPathViewComponentModel + { + public string ApplicationPath { get; set; } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpApplicationPath/Default.cshtml b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpApplicationPath/Default.cshtml new file mode 100644 index 0000000000..a9a9b1fa86 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpApplicationPath/Default.cshtml @@ -0,0 +1,4 @@ +@model Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Pages.Shared.Components.AbpApplicationPath.AbpApplicationPathViewComponentModel + diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageToolbar/Button/Default.cshtml b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageToolbar/Button/Default.cshtml index 10f2f7ceb2..bd6b1f6bd1 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageToolbar/Button/Default.cshtml +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageToolbar/Button/Default.cshtml @@ -5,9 +5,9 @@ icon="@Model.Icon" id="@Model.Id" busy-text="@Model.BusyText" - icon-type="@Model.IconType" - button-type="@Model.Type" - size="@Model.Size" + icon-type="@(Model.IconType)" + button-type="@(Model.Type)" + size="@(Model.Size)" disabled="@Model.Disabled" class="mx-1" /> \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Toolbars/IToolbarConfigurationContext.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Toolbars/IToolbarConfigurationContext.cs index f33b9fdebc..83fe1c1182 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Toolbars/IToolbarConfigurationContext.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Toolbars/IToolbarConfigurationContext.cs @@ -1,4 +1,9 @@ -using Volo.Abp.AspNetCore.Mvc.UI.Theming; +using System; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.Localization; +using Volo.Abp.AspNetCore.Mvc.UI.Theming; using Volo.Abp.DependencyInjection; namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Toolbars @@ -8,5 +13,20 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Toolbars ITheme Theme { get; } Toolbar Toolbar { get; } + + IAuthorizationService AuthorizationService { get; } + + IStringLocalizerFactory StringLocalizerFactory { get; } + + Task IsGrantedAsync(string policyName); + + [CanBeNull] + IStringLocalizer GetDefaultLocalizer(); + + [NotNull] + public IStringLocalizer GetLocalizer(); + + [NotNull] + public IStringLocalizer GetLocalizer(Type resourceType); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Toolbars/ToolbarConfigurationContext.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Toolbars/ToolbarConfigurationContext.cs index 4b696512bf..37f3308198 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Toolbars/ToolbarConfigurationContext.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Toolbars/ToolbarConfigurationContext.cs @@ -1,21 +1,72 @@ using System; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; using Volo.Abp.AspNetCore.Mvc.UI.Theming; namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Toolbars { public class ToolbarConfigurationContext : IToolbarConfigurationContext { + public IServiceProvider ServiceProvider { get; } + private readonly object _serviceProviderLock = new object(); + + private TRef LazyGetRequiredService(Type serviceType, ref TRef reference) + { + if (reference == null) + { + lock (_serviceProviderLock) + { + if (reference == null) + { + reference = (TRef)ServiceProvider.GetRequiredService(serviceType); + } + } + } + + return reference; + } + + public IAuthorizationService AuthorizationService => LazyGetRequiredService(typeof(IAuthorizationService), ref _authorizationService); + private IAuthorizationService _authorizationService; + + private IStringLocalizerFactory _stringLocalizerFactory; + public IStringLocalizerFactory StringLocalizerFactory => LazyGetRequiredService(typeof(IStringLocalizerFactory),ref _stringLocalizerFactory); + public ITheme Theme { get; } public Toolbar Toolbar { get; } - public IServiceProvider ServiceProvider { get; } - public ToolbarConfigurationContext(ITheme currentTheme, Toolbar toolbar, IServiceProvider serviceProvider) { Theme = currentTheme; Toolbar = toolbar; ServiceProvider = serviceProvider; } + + public Task IsGrantedAsync(string policyName) + { + return AuthorizationService.IsGrantedAsync(policyName); + } + + [CanBeNull] + public IStringLocalizer GetDefaultLocalizer() + { + return StringLocalizerFactory.CreateDefaultOrNull(); + } + + [NotNull] + public IStringLocalizer GetLocalizer() + { + return StringLocalizerFactory.Create(); + } + + [NotNull] + public IStringLocalizer GetLocalizer(Type resourceType) + { + return StringLocalizerFactory.Create(resourceType); + } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj index 7c4147bba5..71e1257a4d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj @@ -18,16 +18,10 @@ - - - - - - diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js index 6b8ed10120..beef0d1f0c 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js @@ -1,6 +1,10 @@ (function ($) { - function enableFormFeatures($forms, validate) { + abp.dom = abp.dom || {}; + + abp.dom.initializers = abp.dom.initializers || {}; + + abp.dom.initializers.initializeForms = function ($forms, validate) { if ($forms.length) { $forms.each(function () { var $form = $(this); @@ -30,9 +34,9 @@ } }); } - } + }; - function initializeScript($el) { + abp.dom.initializers.initializeScript = function ($el) { $el.findWithSelf('[data-script-class]').each(function () { var scriptClassName = $(this).attr('data-script-class'); if (!scriptClassName) { @@ -51,20 +55,86 @@ }); } - abp.dom.onNodeAdded(function (args) { - args.$el.findWithSelf('[data-toggle="tooltip"]').tooltip({ + abp.dom.initializers.initializeToolTips = function ($tooltips) { + $tooltips.tooltip({ container: 'body' }); + } - args.$el.findWithSelf('[data-toggle="popover"]').popover({ + abp.dom.initializers.initializePopovers = function ($popovers) { + $popovers.popover({ container: 'body' }); + } - args.$el.findWithSelf('.timeago').timeago(); + abp.dom.initializers.initializeTimeAgos = function ($timeagos) { + $timeagos.timeago(); + } - enableFormFeatures(args.$el.findWithSelf('form'), true); + abp.libs = abp.libs = abp.libs || {}; + abp.libs.bootstrapDatepicker = { + languageMap: { + 'zh-Hans': 'zh-CN' + }, + mapLanguageName: function (name) { + return abp.libs.bootstrapDatepicker.languageMap[abp.localization.currentCulture.name] || name; + }, + isLanguageMapped: function (name) { + return abp.libs.bootstrapDatepicker.languageMap[abp.localization.currentCulture.name] !== undefined; + }, + getCurrentLanguageConfig: function () { + var mappedName = abp.libs.bootstrapDatepicker.mapLanguageName(abp.localization.currentCulture.name); + return $.fn.datepicker.dates[mappedName]; + }, + normalizeLanguageConfig: function () { + var languageConfig = abp.libs.bootstrapDatepicker.getCurrentLanguageConfig(); + if (languageConfig) { + if (!languageConfig.format || abp.libs.bootstrapDatepicker.isLanguageMapped(abp.localization.currentCulture.name)) { + languageConfig.format = abp.localization.currentCulture.dateTimeFormat.shortDatePattern.toLowerCase(); + } + } + }, + getFormattedValue: function (isoFormattedValue) { + if (!isoFormattedValue) { + return isoFormattedValue; + } + return luxon + .DateTime + .fromISO(isoFormattedValue, { + locale: abp.localization.currentCulture.name + }).toLocaleString(); + }, + getOptions: function($input) { //$input may needed if developer wants to override this method + return { + todayBtn: "linked", + autoclose: true, + language: abp.libs.bootstrapDatepicker.mapLanguageName(abp.localization.currentCulture.cultureName) + }; + } + }; + + abp.dom.initializers.initializeDatepickers = function ($rootElement) { + $rootElement + .findWithSelf('input.datepicker,input[type=date]') + .each(function () { + var $input = $(this); + $input + .attr('type', 'text') + .val(abp.libs.bootstrapDatepicker.getFormattedValue($input.val())) + .datepicker(abp.libs.bootstrapDatepicker.getOptions($input)) + .on('hide', function (e) { + e.stopPropagation(); + }); + }); + } - initializeScript(args.$el); + abp.dom.onNodeAdded(function (args) { + abp.dom.initializers.initializeToolTips(args.$el.findWithSelf('[data-toggle="tooltip"]')); + abp.dom.initializers.initializePopovers(args.$el.findWithSelf('[data-toggle="popover"]')); + abp.dom.initializers.initializeTimeAgos(args.$el.findWithSelf('.timeago')); + abp.dom.initializers.initializeDatepickers(args.$el); + abp.dom.initializers.initializeForms(args.$el.findWithSelf('form'), true); + abp.dom.initializers.initializeScript(args.$el); }); abp.dom.onNodeRemoved(function (args) { @@ -73,19 +143,16 @@ }); }); - $(function () { - enableFormFeatures($('form')); - - $('[data-toggle="tooltip"]').tooltip({ - container: 'body' - }); - - $('[data-toggle="popover"]').popover({ - container: 'body' - }); - - $('.timeago').timeago(); + abp.event.on('abp.configurationInitialized', function () { + abp.libs.bootstrapDatepicker.normalizeLanguageConfig(); + }); + $(function () { + abp.dom.initializers.initializeToolTips($('[data-toggle="tooltip"]')); + abp.dom.initializers.initializePopovers($('[data-toggle="popover"]')); + abp.dom.initializers.initializeTimeAgos($('.timeago')); + abp.dom.initializers.initializeDatepickers($(document)); + abp.dom.initializers.initializeForms($('form')); $('[data-auto-focus="true"]').first().findWithSelf('input,select').focus(); }); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo.Abp.AspNetCore.Mvc.UI.Widgets.csproj b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo.Abp.AspNetCore.Mvc.UI.Widgets.csproj index 06afcf3f3e..9d0f384611 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo.Abp.AspNetCore.Mvc.UI.Widgets.csproj +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo.Abp.AspNetCore.Mvc.UI.Widgets.csproj @@ -17,9 +17,4 @@ - - - - - diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo.Abp.AspNetCore.Mvc.UI.csproj b/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo.Abp.AspNetCore.Mvc.UI.csproj index ffdaf02d94..d14d639fbb 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo.Abp.AspNetCore.Mvc.UI.csproj +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo.Abp.AspNetCore.Mvc.UI.csproj @@ -14,11 +14,6 @@ - - - - - diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Mvc/ViewFeatures/ViewContextExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Mvc/ViewFeatures/ViewContextExtensions.cs new file mode 100644 index 0000000000..6f09b5bb50 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Mvc/ViewFeatures/ViewContextExtensions.cs @@ -0,0 +1,15 @@ +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.Mvc.Routing; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.AspNetCore.Mvc.ViewFeatures +{ + public static class ViewContextExtensions + { + public static IUrlHelper GetUrlHelper(this ViewContext viewContext) + { + var urlHelperFactory = viewContext.HttpContext.RequestServices.GetRequiredService(); + return urlHelperFactory.GetUrlHelper(viewContext); + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpMvcBuilderExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpMvcBuilderExtensions.cs index 77b517bc45..c7f3777466 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpMvcBuilderExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpMvcBuilderExtensions.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System.Collections.Generic; +using System.Linq; using System.Reflection; using Microsoft.AspNetCore.Mvc.ApplicationParts; @@ -8,13 +9,23 @@ namespace Microsoft.Extensions.DependencyInjection { public static void AddApplicationPartIfNotExists(this IMvcBuilder mvcBuilder, Assembly assembly) { - if (mvcBuilder.PartManager.ApplicationParts.Any( + mvcBuilder.PartManager.ApplicationParts.AddIfNotContains(assembly); + } + + public static void AddApplicationPartIfNotExists(this IMvcCoreBuilder mvcCoreBuilder, Assembly assembly) + { + mvcCoreBuilder.PartManager.ApplicationParts.AddIfNotContains(assembly); + } + + public static void AddIfNotContains(this IList applicationParts, Assembly assembly) + { + if (applicationParts.Any( p => p is AssemblyPart assemblyPart && assemblyPart.Assembly == assembly)) { return; } - mvcBuilder.PartManager.ApplicationParts.Add(new AssemblyPart(assembly)); + applicationParts.Add(new AssemblyPart(assembly)); } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs index ee81e0a22e..263178e0ed 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs @@ -10,6 +10,7 @@ using Microsoft.Extensions.Options; using Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Net; using System.Reflection; @@ -75,14 +76,14 @@ namespace Volo.Abp.AspNetCore.Mvc (int) HttpStatusCode.NotImplemented, (int) HttpStatusCode.InternalServerError }; - + options.SupportedResponseTypes.AddIfNotContains(statusCodes.Select(statusCode => new ApiResponseType { Type = typeof(RemoteServiceErrorResponse), StatusCode = statusCode })); }); - + context.Services.PostConfigure(options => { if (options.MinifyGeneratedScript == null) @@ -126,10 +127,10 @@ namespace Volo.Abp.AspNetCore.Mvc return factory.Create(resourceType); } - return factory.CreateDefaultOrNull() ?? + return factory.CreateDefaultOrNull() ?? factory.Create(type); }; - }) + }) .AddViewLocalization(); //TODO: How to configure from the application? Also, consider to move to a UI module since APIs does not care about it. Configure(options => @@ -161,7 +162,7 @@ namespace Volo.Abp.AspNetCore.Mvc var application = context.Services.GetSingletonInstance(); partManager.FeatureProviders.Add(new AbpConventionalControllerFeatureProvider(application)); - partManager.ApplicationParts.Add(new AssemblyPart(typeof(AbpAspNetCoreMvcModule).Assembly)); + partManager.ApplicationParts.AddIfNotContains(typeof(AbpAspNetCoreMvcModule).Assembly); Configure(mvcOptions => { @@ -170,15 +171,23 @@ namespace Volo.Abp.AspNetCore.Mvc Configure(options => { - options.EndpointConfigureActions.Add(context => + options.EndpointConfigureActions.Add(endpointContext => { - context.Endpoints.MapControllerRoute("defaultWithArea", "{area}/{controller=Home}/{action=Index}/{id?}"); - context.Endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}"); - context.Endpoints.MapRazorPages(); + endpointContext.Endpoints.MapControllerRoute("defaultWithArea", "{area}/{controller=Home}/{action=Index}/{id?}"); + endpointContext.Endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}"); + endpointContext.Endpoints.MapRazorPages(); }); }); } + public override void PostConfigureServices(ServiceConfigurationContext context) + { + ApplicationPartSorter.Sort( + context.Services.GetSingletonInstance(), + context.Services.GetSingletonInstance() + ); + } + public override void OnApplicationInitialization(ApplicationInitializationContext context) { AddApplicationParts(context); @@ -220,12 +229,7 @@ namespace Volo.Abp.AspNetCore.Mvc { foreach (var moduleAssembly in moduleAssemblies) { - if (partManager.ApplicationParts.OfType().Any(p => p.Assembly == moduleAssembly)) - { - continue; - } - - partManager.ApplicationParts.Add(new AssemblyPart(moduleAssembly)); + partManager.ApplicationParts.AddIfNotContains(moduleAssembly); } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs index a68a38b274..f68d4d1450 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs @@ -15,6 +15,7 @@ using Volo.Abp.Features; using Volo.Abp.Localization; using Volo.Abp.MultiTenancy; using Volo.Abp.Settings; +using Volo.Abp.Timing; using Volo.Abp.Users; namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations @@ -31,6 +32,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations private readonly ISettingDefinitionManager _settingDefinitionManager; private readonly IFeatureDefinitionManager _featureDefinitionManager; private readonly ILanguageProvider _languageProvider; + private readonly ITimezoneProvider _timezoneProvider; + private readonly AbpClockOptions _abpClockOptions; private readonly ICachedObjectExtensionsDtoService _cachedObjectExtensionsDtoService; public AbpApplicationConfigurationAppService( @@ -44,6 +47,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations ISettingDefinitionManager settingDefinitionManager, IFeatureDefinitionManager featureDefinitionManager, ILanguageProvider languageProvider, + ITimezoneProvider timezoneProvider, + IOptions abpClockOptions, ICachedObjectExtensionsDtoService cachedObjectExtensionsDtoService) { _serviceProvider = serviceProvider; @@ -54,6 +59,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations _settingDefinitionManager = settingDefinitionManager; _featureDefinitionManager = featureDefinitionManager; _languageProvider = languageProvider; + _timezoneProvider = timezoneProvider; + _abpClockOptions = abpClockOptions.Value; _cachedObjectExtensionsDtoService = cachedObjectExtensionsDtoService; _localizationOptions = localizationOptions.Value; _multiTenancyOptions = multiTenancyOptions.Value; @@ -74,6 +81,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations Setting = await GetSettingConfigAsync(), MultiTenancy = GetMultiTenancy(), CurrentTenant = GetCurrentTenant(), + Timing = await GetTimingConfigAsync(), + Clock = GetClockConfig(), ObjectExtensions = _cachedObjectExtensionsDtoService.Get() }; @@ -226,5 +235,36 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations return result; } + + protected virtual async Task GetTimingConfigAsync() + { + var result = new TimingDto(); + + var windowsTimeZoneId = await _settingProvider.GetOrNullAsync(TimingSettingNames.TimeZone); + if (!windowsTimeZoneId.IsNullOrWhiteSpace()) + { + result.TimeZone = new TimeZone + { + Windows = new WindowsTimeZone + { + TimeZoneId = windowsTimeZoneId + }, + Iana = new IanaTimeZone() + { + TimeZoneName = _timezoneProvider.WindowsToIana(windowsTimeZoneId) + } + }; + } + + return result; + } + + protected virtual ClockDto GetClockConfig() + { + return new ClockDto + { + Kind = Enum.GetName(typeof(DateTimeKind), _abpClockOptions.Kind) + }; + } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationPartSorter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationPartSorter.cs new file mode 100644 index 0000000000..85fc13c840 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationPartSorter.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Microsoft.AspNetCore.Mvc.ApplicationParts; +using Volo.Abp.Modularity; + +namespace Volo.Abp.AspNetCore.Mvc +{ + /// + /// This class is used to align order of the MVC Application Parts with the order of + /// ABP module dependencies. + /// + public static class ApplicationPartSorter + { + public static void Sort(ApplicationPartManager partManager, IModuleContainer moduleContainer) + { + /* Performing a double Reverse() to preserve the original order for non-sorted parts + */ + + var dependencyDictionary = CreateDependencyDictionary(partManager, moduleContainer); + + var sortedParts = partManager + .ApplicationParts + .Reverse() //First Revers + .SortByDependencies(p => dependencyDictionary[p]); + + sortedParts.Reverse(); //Reverse again + + //Replace the original parts with the sorted parts + partManager.ApplicationParts.Clear(); + foreach (var applicationPart in sortedParts) + { + partManager.ApplicationParts.Add(applicationPart); + } + } + + private static Dictionary> CreateDependencyDictionary( + ApplicationPartManager partManager, IModuleContainer moduleContainer) + { + var dependencyDictionary = new Dictionary>(); + + foreach (var applicationPart in partManager.ApplicationParts) + { + dependencyDictionary[applicationPart] = + CreateDependencyList(applicationPart, partManager, moduleContainer); + } + + return dependencyDictionary; + } + + private static List CreateDependencyList( + ApplicationPart applicationPart, + ApplicationPartManager partManager, + IModuleContainer moduleContainer) + { + var list = new List(); + + if (applicationPart is AssemblyPart assemblyPart) + { + AddDependencies(list, assemblyPart, partManager, moduleContainer); + } + else if (applicationPart is CompiledRazorAssemblyPart compiledRazorAssemblyPart) + { + AddDependencies(list, compiledRazorAssemblyPart, partManager, moduleContainer); + } + + return list; + } + + private static void AddDependencies( + List list, + AssemblyPart assemblyPart, + ApplicationPartManager partManager, + IModuleContainer moduleContainer) + { + var dependedAssemblyParts = GetDependedAssemblyParts( + partManager, + moduleContainer, + assemblyPart + ); + + list.AddRange(dependedAssemblyParts); + + foreach (var dependedAssemblyPart in dependedAssemblyParts) + { + var viewsPart = GetViewsPartOrNull(partManager, dependedAssemblyPart); + if (viewsPart != null) + { + list.Add(viewsPart); + } + } + } + + private static void AddDependencies( + List list, + CompiledRazorAssemblyPart compiledRazorAssemblyPart, + ApplicationPartManager partManager, + IModuleContainer moduleContainer) + { + if (!compiledRazorAssemblyPart.Name.EndsWith(".Views")) + { + return; + } + + var originalAssemblyPart = GetOriginalAssemblyPartOrNull(compiledRazorAssemblyPart, partManager); + if (originalAssemblyPart == null) + { + return; + } + + list.Add(originalAssemblyPart); + } + + private static AssemblyPart[] GetDependedAssemblyParts( + ApplicationPartManager partManager, + IModuleContainer moduleContainer, + AssemblyPart assemblyPart) + { + var moduleDescriptor = GetModuleDescriptorForAssemblyOrNull(moduleContainer, assemblyPart.Assembly); + if (moduleDescriptor == null) + { + return Array.Empty(); + } + + var moduleDependedAssemblies = moduleDescriptor + .Dependencies + .Select(d => d.Assembly) + .ToArray(); + + return partManager.ApplicationParts + .OfType() + .Where(a => a.Assembly.IsIn(moduleDependedAssemblies)) + .Distinct() + .ToArray(); + } + + private static CompiledRazorAssemblyPart GetViewsPartOrNull(ApplicationPartManager partManager, + ApplicationPart assemblyPart) + { + var viewsAssemblyName = assemblyPart.Name + ".Views"; + return partManager + .ApplicationParts + .OfType() + .FirstOrDefault(p => p.Name == viewsAssemblyName); + } + + private static AssemblyPart GetOriginalAssemblyPartOrNull( + CompiledRazorAssemblyPart compiledRazorAssemblyPart, + ApplicationPartManager partManager) + { + var originalAssemblyName = compiledRazorAssemblyPart.Name.RemovePostFix(".Views"); + return partManager.ApplicationParts + .OfType() + .FirstOrDefault(p => p.Name == originalAssemblyName); + } + + private static IAbpModuleDescriptor GetModuleDescriptorForAssemblyOrNull( + IModuleContainer moduleContainer, + Assembly assembly) + { + return moduleContainer + .Modules + .FirstOrDefault(m => m.Assembly == assembly); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Authentication/ChallengeAccountController.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Authentication/ChallengeAccountController.cs index 36e0bebfd6..15854e10a6 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Authentication/ChallengeAccountController.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Authentication/ChallengeAccountController.cs @@ -86,7 +86,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Authentication protected virtual string GetAppHomeUrl() { - return "/"; + return "~/"; } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Localization/AbpLanguagesController.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Localization/AbpLanguagesController.cs index 3f25eb2f84..06b9c9a620 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Localization/AbpLanguagesController.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Localization/AbpLanguagesController.cs @@ -32,14 +32,14 @@ namespace Volo.Abp.AspNetCore.Mvc.Localization return Redirect(GetRedirectUrl(returnUrl)); } - return Redirect("/"); + return Redirect("~/"); } private string GetRedirectUrl(string returnUrl) { if (returnUrl.IsNullOrEmpty()) { - return "/"; + return "~/"; } if (Url.IsLocalUrl(returnUrl)) @@ -47,7 +47,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Localization return returnUrl; } - return "/"; + return "~/"; } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Validation/ValidationAttributeHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Validation/ValidationAttributeHelper.cs index 955b83c48f..14ff42e320 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Validation/ValidationAttributeHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Validation/ValidationAttributeHelper.cs @@ -1,8 +1,5 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; using System.Reflection; -using System.Text; namespace Volo.Abp.AspNetCore.Mvc.Validation { @@ -11,8 +8,22 @@ namespace Volo.Abp.AspNetCore.Mvc.Validation private static readonly PropertyInfo ValidationAttributeErrorMessageStringProperty = typeof(ValidationAttribute) .GetProperty("ErrorMessageString", BindingFlags.Instance | BindingFlags.NonPublic); + private static readonly PropertyInfo ValidationAttributeCustomErrorMessageSetProperty = typeof(ValidationAttribute) + .GetProperty("CustomErrorMessageSet", BindingFlags.Instance | BindingFlags.NonPublic); + public static void SetDefaultErrorMessage(ValidationAttribute validationAttribute) { + if (validationAttribute is StringLengthAttribute stringLength && stringLength.MinimumLength != 0) + { + var customErrorMessageSet = ValidationAttributeCustomErrorMessageSetProperty.GetValue(validationAttribute) as bool?; + if (customErrorMessageSet != true) + { + validationAttribute.ErrorMessage = + "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}."; + return; + } + } + validationAttribute.ErrorMessage = ValidationAttributeErrorMessageStringProperty.GetValue(validationAttribute) as string; } diff --git a/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Routing/EndpointRouteBuilderContext.cs b/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Routing/EndpointRouteBuilderContext.cs index 80f25d2d38..32735b8c22 100644 --- a/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Routing/EndpointRouteBuilderContext.cs +++ b/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Routing/EndpointRouteBuilderContext.cs @@ -5,6 +5,7 @@ namespace Microsoft.AspNetCore.Routing public class EndpointRouteBuilderContext { public IEndpointRouteBuilder Endpoints { get; } + public IServiceProvider ScopeServiceProvider { get; } public EndpointRouteBuilderContext( diff --git a/framework/src/Volo.Abp.AutoMapper/AutoMapper/AbpAutoMapperExtensibleDtoExtensions.cs b/framework/src/Volo.Abp.AutoMapper/AutoMapper/AbpAutoMapperExtensibleDtoExtensions.cs index 40be84cb6f..d26ec97bf2 100644 --- a/framework/src/Volo.Abp.AutoMapper/AutoMapper/AbpAutoMapperExtensibleDtoExtensions.cs +++ b/framework/src/Volo.Abp.AutoMapper/AutoMapper/AbpAutoMapperExtensibleDtoExtensions.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Volo.Abp.AutoMapper; using Volo.Abp.Data; using Volo.Abp.ObjectExtending; @@ -35,5 +36,13 @@ namespace AutoMapper }) ); } + + public static IMappingExpression IgnoreExtraProperties( + this IMappingExpression mappingExpression) + where TDestination : IHasExtraProperties + where TSource : IHasExtraProperties + { + return mappingExpression.Ignore(x => x.ExtraProperties); + } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.AutoMapper/Volo.Abp.AutoMapper.csproj b/framework/src/Volo.Abp.AutoMapper/Volo.Abp.AutoMapper.csproj index 327f2078d1..2fb39be54f 100644 --- a/framework/src/Volo.Abp.AutoMapper/Volo.Abp.AutoMapper.csproj +++ b/framework/src/Volo.Abp.AutoMapper/Volo.Abp.AutoMapper.csproj @@ -15,6 +15,7 @@ + diff --git a/framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AbpAutoMapperModule.cs b/framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AbpAutoMapperModule.cs index 1590e879b1..214872e371 100644 --- a/framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AbpAutoMapperModule.cs +++ b/framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AbpAutoMapperModule.cs @@ -2,6 +2,7 @@ using AutoMapper; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; +using Volo.Abp.Auditing; using Volo.Abp.Modularity; using Volo.Abp.ObjectExtending; using Volo.Abp.ObjectMapping; @@ -10,7 +11,9 @@ namespace Volo.Abp.AutoMapper { [DependsOn( typeof(AbpObjectMappingModule), - typeof(AbpObjectExtendingModule))] + typeof(AbpObjectExtendingModule), + typeof(AbpAuditingModule) + )] public class AbpAutoMapperModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) diff --git a/framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AutoMapperExpressionExtensions.cs b/framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AutoMapperExpressionExtensions.cs index d8cc559d0f..3a2094d68c 100644 --- a/framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AutoMapperExpressionExtensions.cs +++ b/framework/src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AutoMapperExpressionExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Linq.Expressions; using AutoMapper; +using Volo.Abp.Auditing; namespace Volo.Abp.AutoMapper { @@ -10,5 +11,141 @@ namespace Volo.Abp.AutoMapper { return mappingExpression.ForMember(destinationMember, opts => opts.Ignore()); } + + public static IMappingExpression IgnoreHasCreationTimeProperties( + this IMappingExpression mappingExpression) + where TDestination : IHasCreationTime + { + return mappingExpression.Ignore(x => x.CreationTime); + } + + public static IMappingExpression IgnoreMayHaveCreatorProperties( + this IMappingExpression mappingExpression) + where TDestination : IMayHaveCreator + { + return mappingExpression.Ignore(x => x.CreatorId); + } + + public static IMappingExpression IgnoreCreationAuditedObjectProperties( + this IMappingExpression mappingExpression) + where TDestination : ICreationAuditedObject + { + return mappingExpression + .IgnoreHasCreationTimeProperties() + .IgnoreMayHaveCreatorProperties(); + } + + public static IMappingExpression IgnoreHasModificationTimeProperties( + this IMappingExpression mappingExpression) + where TDestination : IHasModificationTime + { + return mappingExpression.Ignore(x => x.LastModificationTime); + } + + public static IMappingExpression IgnoreModificationAuditedObjectProperties( + this IMappingExpression mappingExpression) + where TDestination : IModificationAuditedObject + { + return mappingExpression + .IgnoreHasModificationTimeProperties() + .Ignore(x => x.LastModifierId); + } + + public static IMappingExpression IgnoreAuditedObjectProperties( + this IMappingExpression mappingExpression) + where TDestination : IAuditedObject + { + return mappingExpression + .IgnoreCreationAuditedObjectProperties() + .IgnoreModificationAuditedObjectProperties(); + } + + public static IMappingExpression IgnoreSoftDeleteProperties( + this IMappingExpression mappingExpression) + where TDestination : ISoftDelete + { + return mappingExpression.Ignore(x => x.IsDeleted); + } + + public static IMappingExpression IgnoreHasDeletionTimeProperties( + this IMappingExpression mappingExpression) + where TDestination : IHasDeletionTime + { + return mappingExpression + .IgnoreSoftDeleteProperties() + .Ignore(x => x.DeletionTime); + } + + public static IMappingExpression IgnoreDeletionAuditedObjectProperties( + this IMappingExpression mappingExpression) + where TDestination : IDeletionAuditedObject + { + return mappingExpression + .IgnoreHasDeletionTimeProperties() + .Ignore(x => x.DeleterId); + } + + public static IMappingExpression IgnoreFullAuditedObjectProperties( + this IMappingExpression mappingExpression) + where TDestination : IFullAuditedObject + { + return mappingExpression + .IgnoreAuditedObjectProperties() + .IgnoreDeletionAuditedObjectProperties(); + } + + public static IMappingExpression IgnoreMayHaveCreatorProperties( + this IMappingExpression mappingExpression) + where TDestination : IMayHaveCreator + { + return mappingExpression + .Ignore(x => x.Creator); + } + + public static IMappingExpression IgnoreCreationAuditedObjectProperties( + this IMappingExpression mappingExpression) + where TDestination : ICreationAuditedObject + { + return mappingExpression + .IgnoreCreationAuditedObjectProperties() + .IgnoreMayHaveCreatorProperties(); + } + + public static IMappingExpression IgnoreModificationAuditedObjectProperties( + this IMappingExpression mappingExpression) + where TDestination : IModificationAuditedObject + { + return mappingExpression + .IgnoreModificationAuditedObjectProperties() + .Ignore(x => x.LastModifier); + } + + public static IMappingExpression IgnoreAuditedObjectProperties( + this IMappingExpression mappingExpression) + where TDestination : IAuditedObject + { + return mappingExpression + .IgnoreCreationAuditedObjectProperties() + .IgnoreModificationAuditedObjectProperties(); + } + + public static IMappingExpression IgnoreDeletionAuditedObjectProperties( + this IMappingExpression mappingExpression) + where TDestination : IDeletionAuditedObject + { + return mappingExpression + .IgnoreDeletionAuditedObjectProperties() + .Ignore(x => x.Deleter); + } + + + public static IMappingExpression IgnoreFullAuditedObjectProperties( + this IMappingExpression mappingExpression) + where TDestination : IFullAuditedObject + { + return mappingExpression + .IgnoreAuditedObjectProperties() + .IgnoreDeletionAuditedObjectProperties(); + } } } diff --git a/framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/AbpBackgroundWorkersQuartzModule.cs b/framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/AbpBackgroundWorkersQuartzModule.cs index 3c4f7131cb..7823502013 100644 --- a/framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/AbpBackgroundWorkersQuartzModule.cs +++ b/framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/AbpBackgroundWorkersQuartzModule.cs @@ -17,7 +17,7 @@ namespace Volo.Abp.BackgroundWorkers.Quartz { context.Services.AddConventionalRegistrar(new AbpQuartzConventionalRegistrar()); } - + public override void OnPreApplicationInitialization(ApplicationInitializationContext context) { var options = context.ServiceProvider.GetService>().Value; @@ -30,9 +30,8 @@ namespace Volo.Abp.BackgroundWorkers.Quartz public override void OnApplicationInitialization(ApplicationInitializationContext context) { - var options = context.ServiceProvider.GetService>().Value; var quartzBackgroundWorkerOptions = context.ServiceProvider.GetService>().Value; - if (options.IsEnabled && quartzBackgroundWorkerOptions.IsAutoRegisterEnabled) + if (quartzBackgroundWorkerOptions.IsAutoRegisterEnabled) { var backgroundWorkerManager = context.ServiceProvider.GetService(); var works = context.ServiceProvider.GetServices().Where(x=>x.AutoRegister); diff --git a/framework/src/Volo.Abp.BlobStoring.FileSystem/FodyWeavers.xml b/framework/src/Volo.Abp.BlobStoring.FileSystem/FodyWeavers.xml new file mode 100644 index 0000000000..be0de3a908 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.FileSystem/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.FileSystem/FodyWeavers.xsd b/framework/src/Volo.Abp.BlobStoring.FileSystem/FodyWeavers.xsd new file mode 100644 index 0000000000..3f3946e282 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.FileSystem/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo.Abp.BlobStoring.FileSystem.csproj b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo.Abp.BlobStoring.FileSystem.csproj new file mode 100644 index 0000000000..03fcbbd374 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo.Abp.BlobStoring.FileSystem.csproj @@ -0,0 +1,21 @@ + + + + + + + netstandard2.0 + Volo.Abp.BlobStoring.FileSystem + Volo.Abp.BlobStoring.FileSystem + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + diff --git a/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/AbpBlobStoringFileSystemModule.cs b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/AbpBlobStoringFileSystemModule.cs new file mode 100644 index 0000000000..249bbfaa78 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/AbpBlobStoringFileSystemModule.cs @@ -0,0 +1,13 @@ +using System; +using Volo.Abp.Modularity; + +namespace Volo.Abp.BlobStoring.FileSystem +{ + [DependsOn( + typeof(AbpBlobStoringModule) + )] + public class AbpBlobStoringFileSystemModule : AbpModule + { + + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/DefaultBlobFilePathCalculator.cs b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/DefaultBlobFilePathCalculator.cs new file mode 100644 index 0000000000..1857b2e736 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/DefaultBlobFilePathCalculator.cs @@ -0,0 +1,40 @@ +using System.IO; +using Volo.Abp.DependencyInjection; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.BlobStoring.FileSystem +{ + public class DefaultBlobFilePathCalculator : IBlobFilePathCalculator, ITransientDependency + { + protected ICurrentTenant CurrentTenant { get; } + + public DefaultBlobFilePathCalculator(ICurrentTenant currentTenant) + { + CurrentTenant = currentTenant; + } + + public virtual string Calculate(BlobProviderArgs args) + { + var fileSystemConfiguration = args.Configuration.GetFileSystemConfiguration(); + var blobPath = fileSystemConfiguration.BasePath; + + if (CurrentTenant.Id == null) + { + blobPath = Path.Combine(blobPath, "host"); + } + else + { + blobPath = Path.Combine(blobPath, "tenants", CurrentTenant.Id.Value.ToString("D")); + } + + if (fileSystemConfiguration.AppendContainerNameToBasePath) + { + blobPath = Path.Combine(blobPath, args.ContainerName); + } + + blobPath = Path.Combine(blobPath, args.BlobName); + + return blobPath; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobContainerConfigurationExtensions.cs b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobContainerConfigurationExtensions.cs new file mode 100644 index 0000000000..24357554eb --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobContainerConfigurationExtensions.cs @@ -0,0 +1,24 @@ +using System; + +namespace Volo.Abp.BlobStoring.FileSystem +{ + public static class FileSystemBlobContainerConfigurationExtensions + { + public static FileSystemBlobProviderConfiguration GetFileSystemConfiguration( + this BlobContainerConfiguration containerConfiguration) + { + return new FileSystemBlobProviderConfiguration(containerConfiguration); + } + + public static BlobContainerConfiguration UseFileSystem( + this BlobContainerConfiguration containerConfiguration, + Action fileSystemConfigureAction) + { + containerConfiguration.ProviderType = typeof(FileSystemBlobProvider); + + fileSystemConfigureAction(new FileSystemBlobProviderConfiguration(containerConfiguration)); + + return containerConfiguration; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobProvider.cs b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobProvider.cs new file mode 100644 index 0000000000..aafba5b696 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobProvider.cs @@ -0,0 +1,72 @@ +using System.IO; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.IO; + +namespace Volo.Abp.BlobStoring.FileSystem +{ + public class FileSystemBlobProvider : BlobProviderBase, ITransientDependency + { + protected IBlobFilePathCalculator FilePathCalculator { get; } + + public FileSystemBlobProvider(IBlobFilePathCalculator filePathCalculator) + { + FilePathCalculator = filePathCalculator; + } + + public override async Task SaveAsync(BlobProviderSaveArgs args) + { + var filePath = FilePathCalculator.Calculate(args); + + if (!args.OverrideExisting && await ExistsAsync(filePath)) + { + throw new BlobAlreadyExistsException($"Saving BLOB '{args.BlobName}' does already exists in the container '{args.ContainerName}'! Set {nameof(args.OverrideExisting)} if it should be overwritten."); + } + + DirectoryHelper.CreateIfNotExists(Path.GetDirectoryName(filePath)); + + var fileMode = args.OverrideExisting + ? FileMode.Create + : FileMode.CreateNew; + + using (var fileStream = File.Open(filePath, fileMode, FileAccess.Write)) + { + await args.BlobStream.CopyToAsync( + fileStream, + args.CancellationToken + ); + + await fileStream.FlushAsync(); + } + } + + public override Task DeleteAsync(BlobProviderDeleteArgs args) + { + var filePath = FilePathCalculator.Calculate(args); + return Task.FromResult(FileHelper.DeleteIfExists(filePath)); + } + + public override Task ExistsAsync(BlobProviderExistsArgs args) + { + var filePath = FilePathCalculator.Calculate(args); + return ExistsAsync(filePath); + } + + public override Task GetOrNullAsync(BlobProviderGetArgs args) + { + var filePath = FilePathCalculator.Calculate(args); + + if (!File.Exists(filePath)) + { + return Task.FromResult(null); + } + + return Task.FromResult(File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)); + } + + protected virtual Task ExistsAsync(string filePath) + { + return Task.FromResult(File.Exists(filePath)); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobProviderConfiguration.cs b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobProviderConfiguration.cs new file mode 100644 index 0000000000..3d3a5d5968 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobProviderConfiguration.cs @@ -0,0 +1,27 @@ +namespace Volo.Abp.BlobStoring.FileSystem +{ + public class FileSystemBlobProviderConfiguration + { + public string BasePath + { + get => _containerConfiguration.GetConfiguration(FileSystemBlobProviderConfigurationNames.BasePath); + set => _containerConfiguration.SetConfiguration(FileSystemBlobProviderConfigurationNames.BasePath, Check.NotNullOrWhiteSpace(value, nameof(value))); + } + + /// + /// Default value: true. + /// + public bool AppendContainerNameToBasePath + { + get => _containerConfiguration.GetConfigurationOrDefault(FileSystemBlobProviderConfigurationNames.AppendContainerNameToBasePath, true); + set => _containerConfiguration.SetConfiguration(FileSystemBlobProviderConfigurationNames.AppendContainerNameToBasePath, value); + } + + private readonly BlobContainerConfiguration _containerConfiguration; + + public FileSystemBlobProviderConfiguration(BlobContainerConfiguration containerConfiguration) + { + _containerConfiguration = containerConfiguration; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobProviderConfigurationNames.cs b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobProviderConfigurationNames.cs new file mode 100644 index 0000000000..56991fa0ea --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobProviderConfigurationNames.cs @@ -0,0 +1,8 @@ +namespace Volo.Abp.BlobStoring.FileSystem +{ + public static class FileSystemBlobProviderConfigurationNames + { + public const string BasePath = "FileSystem.BasePath"; + public const string AppendContainerNameToBasePath = "FileSystem.AppendContainerNameToBasePath"; + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/IBlobFilePathCalculator.cs b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/IBlobFilePathCalculator.cs new file mode 100644 index 0000000000..2e7c6b551a --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/IBlobFilePathCalculator.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.BlobStoring.FileSystem +{ + public interface IBlobFilePathCalculator + { + string Calculate(BlobProviderArgs args); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/FodyWeavers.xml b/framework/src/Volo.Abp.BlobStoring/FodyWeavers.xml new file mode 100644 index 0000000000..be0de3a908 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/FodyWeavers.xsd b/framework/src/Volo.Abp.BlobStoring/FodyWeavers.xsd new file mode 100644 index 0000000000..3f3946e282 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo.Abp.BlobStoring.csproj b/framework/src/Volo.Abp.BlobStoring/Volo.Abp.BlobStoring.csproj new file mode 100644 index 0000000000..850f98b5f1 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo.Abp.BlobStoring.csproj @@ -0,0 +1,23 @@ + + + + + + + netstandard2.0 + Volo.Abp.BlobStoring + Volo.Abp.BlobStoring + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + + diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/AbpBlobStoringModule.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/AbpBlobStoringModule.cs new file mode 100644 index 0000000000..35970ea169 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/AbpBlobStoringModule.cs @@ -0,0 +1,28 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Threading; + +namespace Volo.Abp.BlobStoring +{ + [DependsOn( + typeof(AbpMultiTenancyModule), + typeof(AbpThreadingModule) + )] + public class AbpBlobStoringModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddTransient( + typeof(IBlobContainer<>), + typeof(BlobContainer<>) + ); + + context.Services.AddTransient( + typeof(IBlobContainer), + serviceProvider => serviceProvider + .GetRequiredService>() + ); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/AbpBlobStoringOptions.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/AbpBlobStoringOptions.cs new file mode 100644 index 0000000000..4ed0ce69a0 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/AbpBlobStoringOptions.cs @@ -0,0 +1,12 @@ +namespace Volo.Abp.BlobStoring +{ + public class AbpBlobStoringOptions + { + public BlobContainerConfigurations Containers { get; } + + public AbpBlobStoringOptions() + { + Containers = new BlobContainerConfigurations(); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobAlreadyExistsException.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobAlreadyExistsException.cs new file mode 100644 index 0000000000..51eaf29783 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobAlreadyExistsException.cs @@ -0,0 +1,31 @@ +using System; +using System.Runtime.Serialization; + +namespace Volo.Abp.BlobStoring +{ + public class BlobAlreadyExistsException : AbpException + { + public BlobAlreadyExistsException() + { + + } + + public BlobAlreadyExistsException(string message) + : base(message) + { + + } + + public BlobAlreadyExistsException(string message, Exception innerException) + : base(message, innerException) + { + + } + + public BlobAlreadyExistsException(SerializationInfo serializationInfo, StreamingContext context) + : base(serializationInfo, context) + { + + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainer.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainer.cs new file mode 100644 index 0000000000..d165a40316 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainer.cs @@ -0,0 +1,199 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Threading; + +namespace Volo.Abp.BlobStoring +{ + public class BlobContainer : IBlobContainer + where TContainer : class + { + private readonly IBlobContainer _container; + + public BlobContainer(IBlobContainerFactory blobContainerFactory) + { + _container = blobContainerFactory.Create(); + } + + public Task SaveAsync( + string name, + Stream stream, + bool overrideExisting = false, + CancellationToken cancellationToken = default) + { + return _container.SaveAsync( + name, + stream, + overrideExisting, + cancellationToken + ); + } + + public Task DeleteAsync( + string name, + CancellationToken cancellationToken = default) + { + return _container.DeleteAsync( + name, + cancellationToken + ); + } + + public Task ExistsAsync( + string name, + CancellationToken cancellationToken = default) + { + return _container.ExistsAsync( + name, + cancellationToken + ); + } + + public Task GetAsync( + string name, + CancellationToken cancellationToken = default) + { + return _container.GetAsync( + name, + cancellationToken + ); + } + + public Task GetOrNullAsync( + string name, + CancellationToken cancellationToken = default) + { + return _container.GetOrNullAsync( + name, + cancellationToken + ); + } + } + + public class BlobContainer : IBlobContainer + { + protected string ContainerName { get; } + + protected BlobContainerConfiguration Configuration { get; } + + protected IBlobProvider Provider { get; } + + protected ICurrentTenant CurrentTenant { get; } + + protected ICancellationTokenProvider CancellationTokenProvider { get; } + + public BlobContainer( + string containerName, + BlobContainerConfiguration configuration, + IBlobProvider provider, + ICurrentTenant currentTenant, + ICancellationTokenProvider cancellationTokenProvider) + { + ContainerName = containerName; + Configuration = configuration; + Provider = provider; + CurrentTenant = currentTenant; + CancellationTokenProvider = cancellationTokenProvider; + } + + public virtual async Task SaveAsync( + string name, + Stream stream, + bool overrideExisting = false, + CancellationToken cancellationToken = default) + { + using (CurrentTenant.Change(GetTenantIdOrNull())) + { + await Provider.SaveAsync( + new BlobProviderSaveArgs( + ContainerName, + Configuration, + name, + stream, + overrideExisting, + CancellationTokenProvider.FallbackToProvider(cancellationToken) + ) + ); + } + } + + public virtual async Task DeleteAsync( + string name, + CancellationToken cancellationToken = default) + { + using (CurrentTenant.Change(GetTenantIdOrNull())) + { + return await Provider.DeleteAsync( + new BlobProviderDeleteArgs( + ContainerName, + Configuration, + name, + CancellationTokenProvider.FallbackToProvider(cancellationToken) + ) + ); + } + } + + public virtual async Task ExistsAsync( + string name, + CancellationToken cancellationToken = default) + { + using (CurrentTenant.Change(GetTenantIdOrNull())) + { + return await Provider.ExistsAsync( + new BlobProviderExistsArgs( + ContainerName, + Configuration, + name, + CancellationTokenProvider.FallbackToProvider(cancellationToken) + ) + ); + } + } + + public virtual async Task GetAsync( + string name, + CancellationToken cancellationToken = default) + { + var stream = await GetOrNullAsync(name, cancellationToken); + + if (stream == null) + { + //TODO: Consider to throw some type of "not found" exception and handle on the HTTP status side + throw new AbpException( + $"Could not found the requested BLOB '{name}' in the container '{ContainerName}'!"); + } + + return stream; + } + + public virtual async Task GetOrNullAsync( + string name, + CancellationToken cancellationToken = default) + { + using (CurrentTenant.Change(GetTenantIdOrNull())) + { + return await Provider.GetOrNullAsync( + new BlobProviderGetArgs( + ContainerName, + Configuration, + name, + CancellationTokenProvider.FallbackToProvider(cancellationToken) + ) + ); + } + } + + protected virtual Guid? GetTenantIdOrNull() + { + if (!Configuration.IsMultiTenant) + { + return null; + } + + return CurrentTenant.Id; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfiguration.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfiguration.cs new file mode 100644 index 0000000000..46f13a1c3d --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfiguration.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using JetBrains.Annotations; + +namespace Volo.Abp.BlobStoring +{ + public class BlobContainerConfiguration + { + /// + /// The provider to be used to store BLOBs of this container. + /// + public Type ProviderType { get; set; } + + /// + /// Indicates whether this container is multi-tenant or not. + /// + /// If this is false and your application is multi-tenant, + /// then the container is shared by all tenants in the system. + /// + /// This can be true even if your application is not multi-tenant. + /// + /// Default: true. + /// + public bool IsMultiTenant { get; set; } = true; + + [NotNull] private readonly Dictionary _properties; + + [CanBeNull] private readonly BlobContainerConfiguration _fallbackConfiguration; + + public BlobContainerConfiguration(BlobContainerConfiguration fallbackConfiguration = null) + { + _fallbackConfiguration = fallbackConfiguration; + _properties = new Dictionary(); + } + + [CanBeNull] + public T GetConfigurationOrDefault(string name, T defaultValue = default) + { + return (T) GetConfigurationOrNull(name, defaultValue); + } + + [CanBeNull] + public object GetConfigurationOrNull(string name, object defaultValue = null) + { + return _properties.GetOrDefault(name) ?? + _fallbackConfiguration?.GetConfigurationOrNull(name, defaultValue) ?? + defaultValue; + } + + [NotNull] + public BlobContainerConfiguration SetConfiguration([NotNull] string name, [CanBeNull] object value) + { + Check.NotNullOrWhiteSpace(name, nameof(name)); + Check.NotNull(value, nameof(value)); + + _properties[name] = value; + + return this; + } + + [NotNull] + public BlobContainerConfiguration ClearConfiguration([NotNull] string name) + { + Check.NotNullOrWhiteSpace(name, nameof(name)); + + _properties.Remove(name); + + return this; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfigurationExtensions.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfigurationExtensions.cs new file mode 100644 index 0000000000..ca437b0ad0 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfigurationExtensions.cs @@ -0,0 +1,27 @@ +using JetBrains.Annotations; + +namespace Volo.Abp.BlobStoring +{ + public static class BlobContainerConfigurationExtensions + { + public static T GetConfiguration( + [NotNull] this BlobContainerConfiguration containerConfiguration, + [NotNull] string name) + { + return (T) containerConfiguration.GetConfiguration(name); + } + + public static object GetConfiguration( + [NotNull] this BlobContainerConfiguration containerConfiguration, + [NotNull] string name) + { + var value = containerConfiguration.GetConfigurationOrNull(name); + if (value == null) + { + throw new AbpException($"Could not find the configuration value for '{name}'!"); + } + + return value; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfigurationProviderExtensions.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfigurationProviderExtensions.cs new file mode 100644 index 0000000000..a199d8a72f --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfigurationProviderExtensions.cs @@ -0,0 +1,11 @@ +namespace Volo.Abp.BlobStoring +{ + public static class BlobContainerConfigurationProviderExtensions + { + public static BlobContainerConfiguration Get( + this IBlobContainerConfigurationProvider configurationProvider) + { + return configurationProvider.Get(BlobContainerNameAttribute.GetContainerName()); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfigurations.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfigurations.cs new file mode 100644 index 0000000000..ed2c5afe04 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfigurations.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using JetBrains.Annotations; + +namespace Volo.Abp.BlobStoring +{ + public class BlobContainerConfigurations + { + private BlobContainerConfiguration Default => GetConfiguration(); + + private readonly Dictionary _containers; + + public BlobContainerConfigurations() + { + _containers = new Dictionary + { + //Add default container + [BlobContainerNameAttribute.GetContainerName()] = new BlobContainerConfiguration() + }; + } + + public BlobContainerConfigurations Configure( + Action configureAction) + { + return Configure( + BlobContainerNameAttribute.GetContainerName(), + configureAction + ); + } + + public BlobContainerConfigurations Configure( + [NotNull] string name, + [NotNull] Action configureAction) + { + Check.NotNullOrWhiteSpace(name, nameof(name)); + Check.NotNull(configureAction, nameof(configureAction)); + + configureAction( + _containers.GetOrAdd( + name, + () => new BlobContainerConfiguration(Default) + ) + ); + + return this; + } + + public BlobContainerConfigurations ConfigureDefault(Action configureAction) + { + configureAction(Default); + return this; + } + + public BlobContainerConfigurations ConfigureAll(Action configureAction) + { + foreach (var container in _containers) + { + configureAction(container.Key, container.Value); + } + + return this; + } + + [NotNull] + public BlobContainerConfiguration GetConfiguration() + { + return GetConfiguration(BlobContainerNameAttribute.GetContainerName()); + } + + [NotNull] + public BlobContainerConfiguration GetConfiguration([NotNull] string name) + { + Check.NotNullOrWhiteSpace(name, nameof(name)); + + return _containers.GetOrDefault(name) ?? + Default; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerExtensions.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerExtensions.cs new file mode 100644 index 0000000000..58ae4ac593 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerExtensions.cs @@ -0,0 +1,57 @@ +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Volo.Abp.BlobStoring +{ + public static class BlobContainerExtensions + { + public static async Task SaveAsync( + this IBlobContainer container, + string name, + byte[] bytes, + bool overrideExisting = false, + CancellationToken cancellationToken = default + ) + { + using (var memoryStream = new MemoryStream(bytes)) + { + await container.SaveAsync( + name, + memoryStream, + overrideExisting, + cancellationToken + ); + } + } + + public static async Task GetAllBytesAsync( + this IBlobContainer container, + string name, + CancellationToken cancellationToken = default) + { + using (var stream = await container.GetAsync(name, cancellationToken)) + { + return await stream.GetAllBytesAsync(cancellationToken); + } + } + + public static async Task GetAllBytesOrNullAsync( + this IBlobContainer container, + string name, + CancellationToken cancellationToken = default) + { + var stream = await container.GetOrNullAsync(name, cancellationToken); + if (stream == null) + { + return null; + } + + using (stream) + { + return await stream.GetAllBytesAsync(cancellationToken); + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerFactory.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerFactory.cs new file mode 100644 index 0000000000..62f6aa7a73 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerFactory.cs @@ -0,0 +1,43 @@ +using System.Threading; +using Volo.Abp.DependencyInjection; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Threading; + +namespace Volo.Abp.BlobStoring +{ + public class BlobContainerFactory : IBlobContainerFactory, ITransientDependency + { + protected IBlobProviderSelector ProviderSelector { get; } + + protected IBlobContainerConfigurationProvider ConfigurationProvider { get; } + + protected ICurrentTenant CurrentTenant { get; } + + protected ICancellationTokenProvider CancellationTokenProvider { get; } + + public BlobContainerFactory( + IBlobContainerConfigurationProvider configurationProvider, + ICurrentTenant currentTenant, + ICancellationTokenProvider cancellationTokenProvider, + IBlobProviderSelector providerSelector) + { + ConfigurationProvider = configurationProvider; + CurrentTenant = currentTenant; + CancellationTokenProvider = cancellationTokenProvider; + ProviderSelector = providerSelector; + } + + public virtual IBlobContainer Create(string name, CancellationToken cancellationToken = default) + { + var configuration = ConfigurationProvider.Get(name); + + return new BlobContainer( + name, + configuration, + ProviderSelector.Get(name), + CurrentTenant, + CancellationTokenProvider + ); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerFactoryExtensions.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerFactoryExtensions.cs new file mode 100644 index 0000000000..34483c1849 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerFactoryExtensions.cs @@ -0,0 +1,26 @@ +using System.Threading; + +namespace Volo.Abp.BlobStoring +{ + public static class BlobContainerFactoryExtensions + { + /// + /// Gets a named container. + /// + /// The blob container manager + /// Cancellation token + /// + /// The container object. + /// + public static IBlobContainer Create( + this IBlobContainerFactory blobContainerFactory, + CancellationToken cancellationToken = default + ) + { + return blobContainerFactory.Create( + BlobContainerNameAttribute.GetContainerName(), + cancellationToken + ); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerNameAttribute.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerNameAttribute.cs new file mode 100644 index 0000000000..12bd91d669 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerNameAttribute.cs @@ -0,0 +1,41 @@ +using System; +using System.Reflection; +using JetBrains.Annotations; + +namespace Volo.Abp.BlobStoring +{ + public class BlobContainerNameAttribute : Attribute + { + [NotNull] + public string Name { get; } + + public BlobContainerNameAttribute([NotNull] string name) + { + Check.NotNullOrWhiteSpace(name, nameof(name)); + + Name = name; + } + + public virtual string GetName(Type type) + { + return Name; + } + + public static string GetContainerName() + { + return GetContainerName(typeof(T)); + } + + public static string GetContainerName(Type type) + { + var nameAttribute = type.GetCustomAttribute(); + + if (nameAttribute == null) + { + return type.FullName; + } + + return nameAttribute.GetName(type); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderArgs.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderArgs.cs new file mode 100644 index 0000000000..fcc264977a --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderArgs.cs @@ -0,0 +1,31 @@ +using System.Threading; +using JetBrains.Annotations; + +namespace Volo.Abp.BlobStoring +{ + public abstract class BlobProviderArgs + { + [NotNull] + public string ContainerName { get; } + + [NotNull] + public BlobContainerConfiguration Configuration { get; } + + [NotNull] + public string BlobName { get; } + + public CancellationToken CancellationToken { get; } + + protected BlobProviderArgs( + [NotNull] string containerName, + [NotNull] BlobContainerConfiguration configuration, + [NotNull] string blobName, + CancellationToken cancellationToken = default) + { + ContainerName = Check.NotNullOrWhiteSpace(containerName, nameof(containerName)); + Configuration = Check.NotNull(configuration, nameof(configuration)); + BlobName = Check.NotNullOrWhiteSpace(blobName, nameof(blobName)); + CancellationToken = cancellationToken; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderBase.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderBase.cs new file mode 100644 index 0000000000..b1aafb6903 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderBase.cs @@ -0,0 +1,16 @@ +using System.IO; +using System.Threading.Tasks; + +namespace Volo.Abp.BlobStoring +{ + public abstract class BlobProviderBase : IBlobProvider + { + public abstract Task SaveAsync(BlobProviderSaveArgs args); + + public abstract Task DeleteAsync(BlobProviderDeleteArgs args); + + public abstract Task ExistsAsync(BlobProviderExistsArgs args); + + public abstract Task GetOrNullAsync(BlobProviderGetArgs args); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderDeleteArgs.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderDeleteArgs.cs new file mode 100644 index 0000000000..146074d444 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderDeleteArgs.cs @@ -0,0 +1,21 @@ +using System.Threading; +using JetBrains.Annotations; + +namespace Volo.Abp.BlobStoring +{ + public class BlobProviderDeleteArgs : BlobProviderArgs + { + public BlobProviderDeleteArgs( + [NotNull] string containerName, + [NotNull] BlobContainerConfiguration configuration, + [NotNull] string blobName, + CancellationToken cancellationToken = default) + : base( + containerName, + configuration, + blobName, + cancellationToken) + { + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderExistsArgs.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderExistsArgs.cs new file mode 100644 index 0000000000..dee2e144f7 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderExistsArgs.cs @@ -0,0 +1,21 @@ +using System.Threading; +using JetBrains.Annotations; + +namespace Volo.Abp.BlobStoring +{ + public class BlobProviderExistsArgs : BlobProviderArgs + { + public BlobProviderExistsArgs( + [NotNull] string containerName, + [NotNull] BlobContainerConfiguration configuration, + [NotNull] string blobName, + CancellationToken cancellationToken = default) + : base( + containerName, + configuration, + blobName, + cancellationToken) + { + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderGetArgs.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderGetArgs.cs new file mode 100644 index 0000000000..f3087e9805 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderGetArgs.cs @@ -0,0 +1,21 @@ +using System.Threading; +using JetBrains.Annotations; + +namespace Volo.Abp.BlobStoring +{ + public class BlobProviderGetArgs : BlobProviderArgs + { + public BlobProviderGetArgs( + [NotNull] string containerName, + [NotNull] BlobContainerConfiguration configuration, + [NotNull] string blobName, + CancellationToken cancellationToken = default) + : base( + containerName, + configuration, + blobName, + cancellationToken) + { + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderSaveArgs.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderSaveArgs.cs new file mode 100644 index 0000000000..b8af43e4fd --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderSaveArgs.cs @@ -0,0 +1,31 @@ +using System.IO; +using System.Threading; +using JetBrains.Annotations; + +namespace Volo.Abp.BlobStoring +{ + public class BlobProviderSaveArgs : BlobProviderArgs + { + [NotNull] + public Stream BlobStream { get; } + + public bool OverrideExisting { get; } + + public BlobProviderSaveArgs( + [NotNull] string containerName, + [NotNull] BlobContainerConfiguration configuration, + [NotNull] string blobName, + [NotNull] Stream blobStream, + bool overrideExisting = false, + CancellationToken cancellationToken = default) + : base( + containerName, + configuration, + blobName, + cancellationToken) + { + BlobStream = Check.NotNull(blobStream, nameof(blobStream)); + OverrideExisting = overrideExisting; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderSelectorExtensions.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderSelectorExtensions.cs new file mode 100644 index 0000000000..1d484ebd2e --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderSelectorExtensions.cs @@ -0,0 +1,15 @@ +using JetBrains.Annotations; + +namespace Volo.Abp.BlobStoring +{ + public static class BlobProviderSelectorExtensions + { + public static IBlobProvider Get( + [NotNull] this IBlobProviderSelector selector) + { + Check.NotNull(selector, nameof(selector)); + + return selector.Get(BlobContainerNameAttribute.GetContainerName()); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/DefaultBlobContainerConfigurationProvider.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/DefaultBlobContainerConfigurationProvider.cs new file mode 100644 index 0000000000..a30d584c9d --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/DefaultBlobContainerConfigurationProvider.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.BlobStoring +{ + public class DefaultBlobContainerConfigurationProvider : IBlobContainerConfigurationProvider, ITransientDependency + { + protected AbpBlobStoringOptions Options { get; } + + public DefaultBlobContainerConfigurationProvider(IOptions options) + { + Options = options.Value; + } + + public virtual BlobContainerConfiguration Get(string name) + { + return Options.Containers.GetConfiguration(name); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/DefaultBlobProviderSelector.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/DefaultBlobProviderSelector.cs new file mode 100644 index 0000000000..5f604155ca --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/DefaultBlobProviderSelector.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using JetBrains.Annotations; +using Volo.Abp.DependencyInjection; +using Volo.Abp.DynamicProxy; + +namespace Volo.Abp.BlobStoring +{ + public class DefaultBlobProviderSelector : IBlobProviderSelector, ITransientDependency + { + protected IEnumerable BlobProviders { get; } + + protected IBlobContainerConfigurationProvider ConfigurationProvider { get; } + + public DefaultBlobProviderSelector( + IBlobContainerConfigurationProvider configurationProvider, + IEnumerable blobProviders) + { + ConfigurationProvider = configurationProvider; + BlobProviders = blobProviders; + } + + [NotNull] + public virtual IBlobProvider Get([NotNull] string containerName) + { + Check.NotNull(containerName, nameof(containerName)); + + var configuration = ConfigurationProvider.Get(containerName); + + if (!BlobProviders.Any()) + { + throw new AbpException("No BLOB Storage provider was registered! At least one provider must be registered to be able to use the Blog Storing System."); + } + + foreach (var provider in BlobProviders) + { + if (ProxyHelper.GetUnProxiedType(provider).IsAssignableTo(configuration.ProviderType)) + { + return provider; + } + } + + throw new AbpException( + $"Could not find the BLOB Storage provider with the type ({configuration.ProviderType.AssemblyQualifiedName}) configured for the container {containerName} and no default provider was set." + ); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/DefaultContainer.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/DefaultContainer.cs new file mode 100644 index 0000000000..64f907e564 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/DefaultContainer.cs @@ -0,0 +1,8 @@ +namespace Volo.Abp.BlobStoring +{ + [BlobContainerName("Default")] + public class DefaultContainer + { + + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobContainer.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobContainer.cs new file mode 100644 index 0000000000..a596565b5f --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobContainer.cs @@ -0,0 +1,88 @@ +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Volo.Abp.BlobStoring +{ + public interface IBlobContainer : IBlobContainer + where TContainer: class + { + + } + + public interface IBlobContainer + { + /// + /// Saves a blob to the container. + /// + /// The name of the blob + /// A stream for the blob + /// + /// Set true to override if there is already a blob in the container with the given name. + /// If set to false (default), throws exception if there is already a blob in the container with the given name. + /// + /// Cancellation token + Task SaveAsync( + string name, + Stream stream, + bool overrideExisting = false, + CancellationToken cancellationToken = default + ); + + /// + /// Deletes a blob from the container. + /// + /// The name of the blob + /// Cancellation token + /// + /// Returns true if actually deleted the blob. + /// Returns false if the blob with the given was not exists. + /// + Task DeleteAsync( + string name, + CancellationToken cancellationToken = default + ); + + /// + /// Checks if a blob does exists in the container. + /// + /// The name of the blob + /// Cancellation token + Task ExistsAsync( + string name, + CancellationToken cancellationToken = default + ); + + /// + /// Gets a blob from the container. + /// It actually gets a to read the blob data. + /// It throws exception if there is no blob with the given . + /// Use if you want to get null if there is no blob with the given . + /// + /// The name of the blob + /// Cancellation token + /// + /// A to read the blob data. + /// + Task GetAsync( + string name, + CancellationToken cancellationToken = default + ); + + /// + /// Gets a blob from the container, or returns null if there is no blob with the given . + /// It actually gets a to read the blob data. + /// + /// The name of the blob + /// Cancellation token + /// + /// A to read the blob data. + /// + Task GetOrNullAsync( + string name, + CancellationToken cancellationToken = default + ); + + //TODO: Create shortcut extension methods: GetAsArraryAsync, GetAsStringAsync(encoding) (and null versions) + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobContainerConfigurationProvider.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobContainerConfigurationProvider.cs new file mode 100644 index 0000000000..f984a903a2 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobContainerConfigurationProvider.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.BlobStoring +{ + public interface IBlobContainerConfigurationProvider + { + BlobContainerConfiguration Get(string name); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobContainerFactory.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobContainerFactory.cs new file mode 100644 index 0000000000..8434e3ae23 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobContainerFactory.cs @@ -0,0 +1,20 @@ +using System.Threading; + +namespace Volo.Abp.BlobStoring +{ + public interface IBlobContainerFactory + { + /// + /// Gets a named container. + /// + /// The name of the container + /// Cancellation token + /// + /// The container object. + /// + IBlobContainer Create( + string name, + CancellationToken cancellationToken = default + ); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobProvider.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobProvider.cs new file mode 100644 index 0000000000..bb9b42d37d --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobProvider.cs @@ -0,0 +1,16 @@ +using System.IO; +using System.Threading.Tasks; + +namespace Volo.Abp.BlobStoring +{ + public interface IBlobProvider + { + Task SaveAsync(BlobProviderSaveArgs args); + + Task DeleteAsync(BlobProviderDeleteArgs args); + + Task ExistsAsync(BlobProviderExistsArgs args); + + Task GetOrNullAsync(BlobProviderGetArgs args); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobProviderSelector.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobProviderSelector.cs new file mode 100644 index 0000000000..defb8f0804 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobProviderSelector.cs @@ -0,0 +1,10 @@ +using JetBrains.Annotations; + +namespace Volo.Abp.BlobStoring +{ + public interface IBlobProviderSelector + { + [NotNull] + IBlobProvider Get([NotNull] string containerName); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs index f5a5a44980..dbffa820c8 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs @@ -28,11 +28,12 @@ namespace Volo.Abp.Cli options.Commands["add-package"] = typeof(AddPackageCommand); options.Commands["add-module"] = typeof(AddModuleCommand); options.Commands["login"] = typeof(LoginCommand); - options.Commands["logout"] = typeof(LogoutCommand); - options.Commands["generate-proxy"] = typeof(GenerateProxyCommand); + options.Commands["logout"] = typeof(LogoutCommand); + options.Commands["generate-proxy"] = typeof(GenerateProxyCommand); options.Commands["suite"] = typeof(SuiteCommand); options.Commands["switch-to-preview"] = typeof(SwitchNightlyPreviewCommand); - options.Commands["switch-to-stable"] = typeof(SwitchStableCommand); + options.Commands["switch-to-stable"] = typeof(SwitchStableCommand); + options.Commands["translate"] = typeof(TranslateCommand); }); } } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/GenerateProxyCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/GenerateProxyCommand.cs index 667aa77757..2b524d9272 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/GenerateProxyCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/GenerateProxyCommand.cs @@ -13,6 +13,7 @@ using Volo.Abp.DependencyInjection; using System.Collections.Generic; using System.Linq; using System.Net; +using Volo.Abp.Reflection; namespace Volo.Abp.Cli.Commands { @@ -516,7 +517,16 @@ namespace Volo.Abp.Cli.Commands baseTypeKebabCase = "@abp/ng.core"; baseTypeName = baseType.Split("Volo.Abp.Application.Dtos")[1].Split("<")[0].TrimStart('.'); - customBaseTypeName = baseType.Split("Volo.Abp.Application.Dtos")[1].Replace("System.Guid", "string").TrimStart('.'); + customBaseTypeName = baseType.Split("Volo.Abp.Application.Dtos")[1].TrimStart('.'); + if (customBaseTypeName.Contains("<")) + { + var genericType = customBaseTypeName.Split("<")[1].TrimEnd('>'); + var customBaseType = Type.GetType(genericType); + if (customBaseType != null) + { + customBaseTypeName = customBaseTypeName.Replace(genericType, TypeHelper.GetSimplifiedName(customBaseType)); + } + } } if (baseTypeName.Contains("guid") || baseTypeName.Contains("Guid")) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/GetSourceCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/GetSourceCommand.cs index c57833c1f7..0a6155fa67 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/GetSourceCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/GetSourceCommand.cs @@ -48,16 +48,22 @@ namespace Volo.Abp.Cli.Commands var outputFolder = GetOutPutFolder(commandLineArgs); Logger.LogInformation("Output folder: " + outputFolder); - var gitHubLocalRepositoryPath = commandLineArgs.Options.GetOrNull(Options.GitHubLocalRepositoryPath.Long); - if (gitHubLocalRepositoryPath != null) + var gitHubAbpLocalRepositoryPath = commandLineArgs.Options.GetOrNull(Options.GitHubAbpLocalRepositoryPath.Long); + if (gitHubAbpLocalRepositoryPath != null) { - Logger.LogInformation("GitHub Local Repository Path: " + gitHubLocalRepositoryPath); + Logger.LogInformation("GitHub Abp Local Repository Path: " + gitHubAbpLocalRepositoryPath); + } + + var gitHubVoloLocalRepositoryPath = commandLineArgs.Options.GetOrNull(Options.GitHubVoloLocalRepositoryPath.Long); + if (gitHubVoloLocalRepositoryPath != null) + { + Logger.LogInformation("GitHub Volo Local Repository Path: " + gitHubVoloLocalRepositoryPath); } commandLineArgs.Options.Add(CliConsts.Command, commandLineArgs.Command); await _sourceCodeDownloadService.DownloadAsync( - commandLineArgs.Target, outputFolder, version, gitHubLocalRepositoryPath, commandLineArgs.Options); + commandLineArgs.Target, outputFolder, version, gitHubAbpLocalRepositoryPath, gitHubVoloLocalRepositoryPath, commandLineArgs.Options); } private static string GetOutPutFolder(CommandLineArgs commandLineArgs) @@ -117,11 +123,16 @@ namespace Volo.Abp.Cli.Commands public const string Long = "output-folder"; } - public static class GitHubLocalRepositoryPath + public static class GitHubAbpLocalRepositoryPath { public const string Long = "abp-path"; } + public static class GitHubVoloLocalRepositoryPath + { + public const string Long = "volo-path"; + } + public static class Version { public const string Short = "v"; diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs index 5605f357bf..c55e68916c 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs @@ -86,10 +86,16 @@ namespace Volo.Abp.Cli.Commands Logger.LogInformation("Mobile App: " + mobileApp); } - var gitHubLocalRepositoryPath = commandLineArgs.Options.GetOrNull(Options.GitHubLocalRepositoryPath.Long); - if (gitHubLocalRepositoryPath != null) + var gitHubAbpLocalRepositoryPath = commandLineArgs.Options.GetOrNull(Options.GitHubAbpLocalRepositoryPath.Long); + if (gitHubAbpLocalRepositoryPath != null) { - Logger.LogInformation("GitHub Local Repository Path: " + gitHubLocalRepositoryPath); + Logger.LogInformation("GitHub Abp Local Repository Path: " + gitHubAbpLocalRepositoryPath); + } + + var gitHubVoloLocalRepositoryPath = commandLineArgs.Options.GetOrNull(Options.GitHubVoloLocalRepositoryPath.Long); + if (gitHubVoloLocalRepositoryPath != null) + { + Logger.LogInformation("GitHub Volo Local Repository Path: " + gitHubVoloLocalRepositoryPath); } var templateSource = commandLineArgs.Options.GetOrNull(Options.TemplateSource.Short, Options.TemplateSource.Long); @@ -127,7 +133,8 @@ namespace Volo.Abp.Cli.Commands databaseProvider, uiFramework, mobileApp, - gitHubLocalRepositoryPath, + gitHubAbpLocalRepositoryPath, + gitHubVoloLocalRepositoryPath, templateSource, commandLineArgs.Options, connectionString @@ -299,11 +306,16 @@ namespace Volo.Abp.Cli.Commands public const string Long = "output-folder"; } - public static class GitHubLocalRepositoryPath + public static class GitHubAbpLocalRepositoryPath { public const string Long = "abp-path"; } + public static class GitHubVoloLocalRepositoryPath + { + public const string Long = "volo-path"; + } + public static class Version { public const string Short = "v"; diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/SourceCodeDownloadService.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/SourceCodeDownloadService.cs index 7c8d068e1e..ca01314758 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/SourceCodeDownloadService.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/SourceCodeDownloadService.cs @@ -25,7 +25,7 @@ namespace Volo.Abp.Cli.Commands.Services Logger = NullLogger.Instance; } - public async Task DownloadAsync(string moduleName, string outputFolder, string version, string gitHubLocalRepositoryPath, AbpCommandLineOptions options) + public async Task DownloadAsync(string moduleName, string outputFolder, string version, string gitHubAbpLocalRepositoryPath, string gitHubVoloLocalRepositoryPath, AbpCommandLineOptions options) { Logger.LogInformation("Downloading source code of " + moduleName); Logger.LogInformation("Version: " + version); @@ -39,7 +39,8 @@ namespace Volo.Abp.Cli.Commands.Services DatabaseProvider.NotSpecified, UiFramework.NotSpecified, null, - gitHubLocalRepositoryPath, + gitHubAbpLocalRepositoryPath, + gitHubVoloLocalRepositoryPath, null, options ) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/TranslateCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/TranslateCommand.cs new file mode 100644 index 0000000000..39dbeafd3c --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/TranslateCommand.cs @@ -0,0 +1,444 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Volo.Abp.Cli.Args; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Cli.Commands +{ + public class TranslateCommand: IConsoleCommand, ITransientDependency + { + public ILogger Logger { get; set; } + + public Task ExecuteAsync(CommandLineArgs commandLineArgs) + { + var currentDirectory = Directory.GetCurrentDirectory(); + + var apply = commandLineArgs.Options.ContainsKey(Options.Apply.Short) || commandLineArgs.Options.ContainsKey(Options.Apply.Long); + if (apply) + { + var inputFile = Path.Combine(currentDirectory, + commandLineArgs.Options.GetOrNull(Options.File.Short, Options.File.Long) + ?? "abp-translation.json"); + + Logger.LogInformation("Abp translate apply..."); + Logger.LogInformation("Input file: " + inputFile); + + ApplyAbpTranslateInfo(currentDirectory, inputFile); + } + else + { + var targetCulture = commandLineArgs.Options.GetOrNull(Options.Culture.Short, Options.Culture.Long); + if (targetCulture == null) + { + throw new CliUsageException( + "Target culture is missing!" + + Environment.NewLine + Environment.NewLine + + GetUsageInfo() + ); + } + + var referenceCulture = commandLineArgs.Options.GetOrNull(Options.ReferenceCulture.Short, Options.ReferenceCulture.Long) + ?? "en"; + + var outputFile = Path.Combine(currentDirectory, + commandLineArgs.Options.GetOrNull(Options.Output.Short, Options.Output.Long) + ?? "abp-translation.json"); + + var allValues = commandLineArgs.Options.ContainsKey(Options.AllValues.Short) || + commandLineArgs.Options.ContainsKey(Options.AllValues.Long); + + Logger.LogInformation("Abp translate..."); + Logger.LogInformation("Target culture: " + targetCulture); + Logger.LogInformation("Reference culture: " + referenceCulture); + Logger.LogInformation("Output file: " + outputFile); + + if (allValues) + { + Logger.LogInformation("Include all keys"); + } + + var translateInfo = GetAbpTranslateInfo(currentDirectory, targetCulture, referenceCulture, allValues); + + File.WriteAllText(outputFile, JsonConvert.SerializeObject(translateInfo, Formatting.Indented)); + + Logger.LogInformation($"The translation file has been created."); + } + + return Task.CompletedTask; + } + + private AbpTranslateInfo GetAbpTranslateInfo(string directory, string targetCultureName, string referenceCultureName, bool allValues) + { + var translateInfo = new AbpTranslateInfo + { + ReferenceCulture = referenceCultureName, + TargetCulture = targetCultureName, + Resources = new List() + }; + + var referenceCultureFiles = GetCultureJsonFiles(directory, referenceCultureName); + foreach (var filePath in referenceCultureFiles) + { + var directoryName = Path.GetDirectoryName(filePath) ?? string.Empty; + + var referenceLocalizationInfo = GetAbpLocalizationInfoOrNull(filePath); + if (referenceLocalizationInfo == null) // Not abp json file + { + continue; + } + + var resource = new AbpTranslateResource + { + ResourcePath = directoryName, + Texts = new List() + }; + + foreach (var text in referenceLocalizationInfo.Texts) + { + resource.Texts.Add(new AbpTranslateResourceText + { + LocalizationKey = text.Name, + Reference = text.Value, + Target = string.Empty + }); + } + + //Use target json file content to fill resource texts + var targetFile = Path.Combine(directoryName, $"{targetCultureName}.json"); + if (File.Exists(targetFile)) + { + var targetLocalizationInfo = GetAbpLocalizationInfoOrNull(targetFile); + foreach (var referenceResourceText in resource.Texts) + { + var text = targetLocalizationInfo.Texts.FirstOrDefault(x => x.Name == referenceResourceText.LocalizationKey); + referenceResourceText.Target = text?.Value ?? string.Empty; + } + } + + if (!allValues) + { + //Only include missing keys. + resource.Texts.RemoveAll(x => !x.Target.Equals(string.Empty)); + } + + if (resource.Texts.Any()) + { + translateInfo.Resources.Add(resource); + } + } + + return translateInfo; + } + + private void ApplyAbpTranslateInfo(string directory, string filename) + { + var translateJsonPath = Path.Combine(directory, filename); + if (!File.Exists(translateJsonPath)) + { + throw new CliUsageException( + $"{translateJsonPath} file does not exist.." + + Environment.NewLine + Environment.NewLine + + GetUsageInfo() + ); + } + + var translateInfo = GetAbpTranslateInfo(translateJsonPath); + foreach (var resource in translateInfo.Resources) + { + var targetFile = Path.Combine(resource.ResourcePath, translateInfo.TargetCulture + ".json"); + var targetLocalizationInfo = File.Exists(targetFile) + ? GetAbpLocalizationInfoOrNull(targetFile) + : new AbpLocalizationInfo() + { + Culture = translateInfo.TargetCulture, + Texts = new List() + }; + + if (targetLocalizationInfo == null) + { + throw new CliUsageException( + $"Failed to get localization information from {targetFile} file." + + Environment.NewLine + Environment.NewLine + + GetUsageInfo() + ); + } + + var referenceFile = Path.Combine(resource.ResourcePath, translateInfo.ReferenceCulture + ".json"); + if (!File.Exists(referenceFile)) + { + throw new CliUsageException( + $"{referenceFile} file does not exist.." + + Environment.NewLine + Environment.NewLine + + GetUsageInfo() + ); + } + var referenceLocalizationInfo = GetAbpLocalizationInfoOrNull(referenceFile); + if (referenceLocalizationInfo == null) + { + throw new CliUsageException( + $"Failed to get localization information from {referenceFile} file." + + Environment.NewLine + Environment.NewLine + + GetUsageInfo() + ); + } + + foreach (var text in resource.Texts) + { + var targetText = targetLocalizationInfo.Texts.FirstOrDefault(x => x.Name == text.LocalizationKey); + if (targetText != null) + { + Logger.LogInformation($"Update translation: {targetText.Name} => " + text.Target); + targetText.Value = text.Target; + } + else + { + Logger.LogInformation($"Create translation: {text.LocalizationKey} => " + text.Target); + targetLocalizationInfo.Texts.Add(new NameValue(text.LocalizationKey, text.Target)); + } + } + + Logger.LogInformation($"Write translation json to {targetFile}."); + + // sort keys + targetLocalizationInfo = SortLocalizedKeys(targetLocalizationInfo, referenceLocalizationInfo); + File.WriteAllText(targetFile, AbpLocalizationInfoToJsonFile(targetLocalizationInfo)); + + // remove translate json file(abp-translation.json) + File.Delete(translateJsonPath); + Logger.LogInformation($"Delete the {translateJsonPath} file, if you need to translate again, please re-run the [abp translate] command."); + } + } + + private static IEnumerable GetCultureJsonFiles(string path,string cultureName) + { + var excludeDirectory = new List() + { + "node_modules", + Path.Combine("bin", "debug"), + Path.Combine("obj", "debug") + }; + + var allCultureInfos = CultureInfo.GetCultures(CultureTypes.AllCultures); + + return Directory.GetFiles(path, "*.json", SearchOption.AllDirectories) + .Where(file => excludeDirectory.All(x => file.IndexOf(x, StringComparison.OrdinalIgnoreCase) == -1)) + .Where(jsonFile => allCultureInfos.Any(culture => jsonFile.EndsWith($"{cultureName}.json", StringComparison.OrdinalIgnoreCase))); + } + + private AbpLocalizationInfo GetAbpLocalizationInfoOrNull(string path) + { + if (!File.Exists(path)) + { + throw new CliUsageException( + $"File {path} does not exist!" + + Environment.NewLine + Environment.NewLine + + GetUsageInfo() + ); + } + + var json = File.ReadAllText(path); + JObject jObject; + try + { + jObject = JObject.Parse(json); + } + catch (Exception e) + { + return null; + } + + var culture = jObject.GetValue("culture"); + var texts = jObject.GetValue("texts"); + if (culture == null || texts == null) + { + return null; + } + + var localizationInfo = new AbpLocalizationInfo + { + Culture = culture.Value(), + Texts = new List() + }; + + foreach (var text in texts) + { + var property = (text as JProperty); + localizationInfo.Texts.Add(new NameValue(property?.Name, property?.Value.Value())); + } + + return localizationInfo; + } + + private static string AbpLocalizationInfoToJsonFile(AbpLocalizationInfo localizationInfo) + { + var jObject = new JObject {{"culture", localizationInfo.Culture}}; + var value = new JObject(); + foreach (var text in localizationInfo.Texts) + { + value.Add(text.Name, text.Value); + } + jObject.Add("texts", value); + return jObject.ToString(); + } + + private AbpTranslateInfo GetAbpTranslateInfo(string path) + { + if (!File.Exists(path)) + { + throw new CliUsageException( + $"File {path} does not exist!" + + Environment.NewLine + Environment.NewLine + + GetUsageInfo() + ); + } + + AbpTranslateInfo translateInfo; + try + { + translateInfo = JsonConvert.DeserializeObject(File.ReadAllText(path)); + } + catch (Exception e) + { + throw new CliUsageException( + e.Message + + Environment.NewLine + Environment.NewLine + + GetUsageInfo() + ); + } + + return translateInfo; + } + + private static AbpLocalizationInfo SortLocalizedKeys(AbpLocalizationInfo targetLocalizationInfo, AbpLocalizationInfo referenceLocalizationInfo) + { + var sortedLocalizationInfo = new AbpLocalizationInfo + { + Culture = targetLocalizationInfo.Culture, + Texts = new List() + }; + + foreach (var targetText in referenceLocalizationInfo.Texts.Select(text => + targetLocalizationInfo.Texts.FirstOrDefault(x => x.Name == text.Name)) + .Where(targetText => targetText != null)) + { + sortedLocalizationInfo.Texts.Add(targetText); + } + + return sortedLocalizationInfo; + } + + public string GetUsageInfo() + { + var sb = new StringBuilder(); + + sb.AppendLine(""); + sb.AppendLine("Usage:"); + sb.AppendLine(""); + sb.AppendLine(" abp translate [options]"); + sb.AppendLine(""); + sb.AppendLine("Options:"); + sb.AppendLine(""); + sb.AppendLine("--culture|-c Target culture. eg: zh-Hans"); + sb.AppendLine("--reference-culture|-r Default: en"); + sb.AppendLine("--output|-o Output file name, Default abp-translation.json"); + sb.AppendLine("--all-values|-all Include all keys. Default false"); + sb.AppendLine("--apply|-a Creates or updates the file for the translated culture."); + sb.AppendLine("--file|-f Default: abp-translation.json"); + sb.AppendLine(""); + sb.AppendLine("Examples:"); + sb.AppendLine(""); + sb.AppendLine(" abp translate -c zh-Hans"); + sb.AppendLine(" abp translate -c zh-Hans -r en -a"); + sb.AppendLine(" abp translate --apply"); + sb.AppendLine(" abp translate -a -f my-translation.json"); + sb.AppendLine(""); + sb.AppendLine("See the documentation for more info: https://docs.abp.io/en/abp/latest/CLI"); + + return sb.ToString(); + } + + public string GetShortDescription() + { + return "Mainly used to translate ABP's resources (JSON files) easier."; + } + + public static class Options + { + public static class Culture + { + public const string Short = "c"; + public const string Long = "culture"; + } + + public static class ReferenceCulture + { + public const string Short = "r"; + public const string Long = "reference-culture"; + } + + public static class Output + { + public const string Short = "o"; + public const string Long = "output"; + } + + public static class AllValues + { + public const string Short = "all"; + public const string Long = "all-values"; + } + + public static class Apply + { + public const string Short = "a"; + public const string Long = "apply"; + } + + public static class File + { + public const string Short = "f"; + public const string Long = "file"; + } + } + + public class AbpTranslateInfo + { + public string ReferenceCulture { get; set; } + + public string TargetCulture { get; set; } + + public List Resources { get; set; } + } + + public class AbpTranslateResource + { + public string ResourcePath { get; set; } + + public List Texts { get; set; } + } + + public class AbpTranslateResourceText + { + public string LocalizationKey { get; set; } + + public string Reference { get; set; } + + public string Target { get; set; } + } + + public class AbpLocalizationInfo + { + public string Culture { get; set; } + + public List Texts { get; set; } + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ProjectReferenceReplaceStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ProjectReferenceReplaceStep.cs index c7a6cc90f0..698c7f1d8d 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ProjectReferenceReplaceStep.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ProjectReferenceReplaceStep.cs @@ -22,10 +22,13 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps return; } + var localVoloRepoPath = context.BuildArgs.VoloGitHubLocalRepositoryPath; + new ProjectReferenceReplacer.LocalProjectPathReferenceReplacer( context.Files, context.Module?.Namespace ?? "MyCompanyName.MyProjectName", - localAbpRepoPath + localAbpRepoPath, + localVoloRepoPath ).Run(); } else @@ -177,12 +180,14 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps public class LocalProjectPathReferenceReplacer : ProjectReferenceReplacer { - private readonly string _gitHubLocalRepositoryPath; + private readonly string _gitHubAbpLocalRepositoryPath; + private readonly string _gitHubVoloLocalRepositoryPath; - public LocalProjectPathReferenceReplacer(List entries, string projectName, string gitHubLocalRepositoryPath) + public LocalProjectPathReferenceReplacer(List entries, string projectName, string gitHubAbpLocalRepositoryPath, string gitHubVoloLocalRepositoryPath) : base(entries, projectName) { - _gitHubLocalRepositoryPath = gitHubLocalRepositoryPath; + _gitHubAbpLocalRepositoryPath = gitHubAbpLocalRepositoryPath; + _gitHubVoloLocalRepositoryPath = gitHubVoloLocalRepositoryPath; } protected override XmlElement GetNewReferenceNode(XmlDocument doc, string oldNodeIncludeValue) @@ -204,9 +209,17 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps includeValue = includeValue.TrimStart('\\'); } - includeValue = _gitHubLocalRepositoryPath.EnsureEndsWith('\\') + includeValue; + if (!string.IsNullOrWhiteSpace(_gitHubVoloLocalRepositoryPath)) + { + if (includeValue.StartsWith("abp\\", StringComparison.InvariantCultureIgnoreCase)) + { + return _gitHubAbpLocalRepositoryPath.EnsureEndsWith('\\') + includeValue.Substring("abp\\".Length); + } + + return _gitHubVoloLocalRepositoryPath.EnsureEndsWith('\\') + "abp\\" + includeValue; + } - return includeValue; + return _gitHubAbpLocalRepositoryPath.EnsureEndsWith('\\') + includeValue; } } } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ProjectBuildArgs.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ProjectBuildArgs.cs index 70cb98dff0..52fa76e551 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ProjectBuildArgs.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ProjectBuildArgs.cs @@ -24,6 +24,9 @@ namespace Volo.Abp.Cli.ProjectBuilding [CanBeNull] public string AbpGitHubLocalRepositoryPath { get; set; } + [CanBeNull] + public string VoloGitHubLocalRepositoryPath { get; set; } + [CanBeNull] public string TemplateSource { get; set; } @@ -41,6 +44,7 @@ namespace Volo.Abp.Cli.ProjectBuilding UiFramework uiFramework = UiFramework.NotSpecified, MobileApp? mobileApp = null, [CanBeNull] string abpGitHubLocalRepositoryPath = null, + [CanBeNull] string voloGitHubLocalRepositoryPath = null, [CanBeNull] string templateSource = null, Dictionary extraProperties = null, [CanBeNull] string connectionString = null) @@ -52,6 +56,7 @@ namespace Volo.Abp.Cli.ProjectBuilding UiFramework = uiFramework; MobileApp = mobileApp; AbpGitHubLocalRepositoryPath = abpGitHubLocalRepositoryPath; + VoloGitHubLocalRepositoryPath = voloGitHubLocalRepositoryPath; TemplateSource = templateSource; ExtraProperties = extraProperties ?? new Dictionary(); ConnectionString = connectionString; diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmGlobalPackagesChecker.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmGlobalPackagesChecker.cs index 59c262e065..7c8ba63e29 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmGlobalPackagesChecker.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmGlobalPackagesChecker.cs @@ -31,7 +31,7 @@ namespace Volo.Abp.Cli.ProjectModification protected virtual string GetInstalledNpmPackages() { Logger.LogInformation("Checking installed npm global packages..."); - return CmdHelper.RunCmdAndGetOutput("npm list -g --depth 0"); + return CmdHelper.RunCmdAndGetOutput("npm list -g --depth 0 --silent"); } protected virtual void InstallYarn() diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NuGetPackageTarget.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NuGetPackageTarget.cs index 9af114dfff..890053049f 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NuGetPackageTarget.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NuGetPackageTarget.cs @@ -11,6 +11,7 @@ HttpApiClient = 6, Web = 7, EntityFrameworkCore = 8, - MongoDB = 9 + MongoDB = 9, + SignalR = 10 } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectFinder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectFinder.cs index b2f5421e6e..d4b3a62b94 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectFinder.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectFinder.cs @@ -41,6 +41,10 @@ namespace Volo.Abp.Cli.ProjectModification return FindProjectEndsWith(projectFiles, assemblyNames, ".HttpApi"); case NuGetPackageTarget.HttpApiClient: return FindProjectEndsWith(projectFiles, assemblyNames, ".HttpApi.Client"); + case NuGetPackageTarget.SignalR: + return FindProjectEndsWith(projectFiles, assemblyNames, ".SignalR") ?? + FindProjectEndsWith(projectFiles, assemblyNames, ".Web") ?? + FindProjectEndsWith(projectFiles, assemblyNames, ".HttpApi.Host"); default: return null; } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs index 51a8ee40df..a6ed12730a 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs @@ -129,6 +129,7 @@ namespace Volo.Abp.Cli.ProjectModification targetModuleFolder, version, null, + null, null ); diff --git a/framework/src/Volo.Abp.Cli/Volo/Abp/Cli/Program.cs b/framework/src/Volo.Abp.Cli/Volo/Abp/Cli/Program.cs index 2ccf9bb9a1..c78d5f93f3 100644 --- a/framework/src/Volo.Abp.Cli/Volo/Abp/Cli/Program.cs +++ b/framework/src/Volo.Abp.Cli/Volo/Abp/Cli/Program.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.DependencyInjection; +using System; +using Microsoft.Extensions.DependencyInjection; using Serilog; using Serilog.Events; using System.IO; @@ -10,6 +11,8 @@ namespace Volo.Abp.Cli { private static void Main(string[] args) { + Console.OutputEncoding = System.Text.Encoding.UTF8; + Log.Logger = new LoggerConfiguration() .MinimumLevel.Information() .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) diff --git a/framework/src/Volo.Abp.Core/System/AbpObjectExtensions.cs b/framework/src/Volo.Abp.Core/System/AbpObjectExtensions.cs index 36896ea59a..49e0d6dea6 100644 --- a/framework/src/Volo.Abp.Core/System/AbpObjectExtensions.cs +++ b/framework/src/Volo.Abp.Core/System/AbpObjectExtensions.cs @@ -42,5 +42,50 @@ namespace System { return list.Contains(item); } + + /// + /// Can be used to conditionally perform a function + /// on an object and return the modified or the original object. + /// It is useful for chained calls. + /// + /// An object + /// A condition + /// A function that is executed only if the condition is true + /// Type of the object + /// + /// Returns the modified object (by the if the is true) + /// or the original object if the is false + /// + public static T If(this T obj, bool condition, Func func) + { + if (condition) + { + return func(obj); + } + + return obj; + } + + /// + /// Can be used to conditionally perform an action + /// on an object and return the original object. + /// It is useful for chained calls on the object. + /// + /// An object + /// A condition + /// An action that is executed only if the condition is true + /// Type of the object + /// + /// Returns the original object. + /// + public static T If(this T obj, bool condition, Action action) + { + if (condition) + { + action(obj); + } + + return obj; + } } } diff --git a/framework/src/Volo.Abp.Core/System/Collections/Generic/AbpListExtensions.cs b/framework/src/Volo.Abp.Core/System/Collections/Generic/AbpListExtensions.cs index a123236a4e..ec7a640a39 100644 --- a/framework/src/Volo.Abp.Core/System/Collections/Generic/AbpListExtensions.cs +++ b/framework/src/Volo.Abp.Core/System/Collections/Generic/AbpListExtensions.cs @@ -180,7 +180,10 @@ namespace System.Collections.Generic /// The type of the members of values. /// A list of objects to sort /// Function to resolve the dependencies - /// + /// + /// Returns a new list ordered by dependencies. + /// If A depends on B, then B will come before than A in the resulting list. + /// public static List SortByDependencies(this IEnumerable source, Func> getDependencies) { /* See: http://www.codeproject.com/Articles/869059/Topological-sorting-in-Csharp diff --git a/framework/src/Volo.Abp.Core/System/IO/AbpStreamExtensions.cs b/framework/src/Volo.Abp.Core/System/IO/AbpStreamExtensions.cs index e87ab67527..466d73b2a7 100644 --- a/framework/src/Volo.Abp.Core/System/IO/AbpStreamExtensions.cs +++ b/framework/src/Volo.Abp.Core/System/IO/AbpStreamExtensions.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System.Threading; +using System.Threading.Tasks; namespace System.IO { @@ -13,13 +14,22 @@ namespace System.IO } } - public static async Task GetAllBytesAsync(this Stream stream) + public static async Task GetAllBytesAsync(this Stream stream, CancellationToken cancellationToken = default) { using (var memoryStream = new MemoryStream()) { - await stream.CopyToAsync(memoryStream); + await stream.CopyToAsync(memoryStream, cancellationToken); return memoryStream.ToArray(); } } + + public static Task CopyToAsync(this Stream stream, Stream destination, CancellationToken cancellationToken) + { + return stream.CopyToAsync( + destination, + 81920, //this is already the default value, but needed to set to be able to pass the cancellationToken + cancellationToken + ); + } } } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs b/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs index 8036760f6a..1e5b251ca0 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.DependencyInjection; @@ -42,6 +43,7 @@ namespace Volo.Abp services.AddCoreAbpServices(this, options); Modules = LoadModules(services, options); + ConfigureServices(); } public virtual void Shutdown() @@ -75,7 +77,7 @@ namespace Volo.Abp } } - private IReadOnlyList LoadModules(IServiceCollection services, AbpApplicationCreationOptions options) + protected virtual IReadOnlyList LoadModules(IServiceCollection services, AbpApplicationCreationOptions options) { return services .GetSingletonInstance() @@ -85,5 +87,75 @@ namespace Volo.Abp options.PlugInSources ); } + + //TODO: We can extract a new class for this + protected virtual void ConfigureServices() + { + var context = new ServiceConfigurationContext(Services); + Services.AddSingleton(context); + + foreach (var module in Modules) + { + if (module.Instance is AbpModule abpModule) + { + abpModule.ServiceConfigurationContext = context; + } + } + + //PreConfigureServices + foreach (var module in Modules.Where(m => m.Instance is IPreConfigureServices)) + { + try + { + ((IPreConfigureServices)module.Instance).PreConfigureServices(context); + } + catch (Exception ex) + { + throw new AbpInitializationException($"An error occurred during {nameof(IPreConfigureServices.PreConfigureServices)} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex); + } + } + + //ConfigureServices + foreach (var module in Modules) + { + if (module.Instance is AbpModule abpModule) + { + if (!abpModule.SkipAutoServiceRegistration) + { + Services.AddAssembly(module.Type.Assembly); + } + } + + try + { + module.Instance.ConfigureServices(context); + } + catch (Exception ex) + { + throw new AbpInitializationException($"An error occurred during {nameof(IAbpModule.ConfigureServices)} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex); + } + } + + //PostConfigureServices + foreach (var module in Modules.Where(m => m.Instance is IPostConfigureServices)) + { + try + { + ((IPostConfigureServices)module.Instance).PostConfigureServices(context); + } + catch (Exception ex) + { + throw new AbpInitializationException($"An error occurred during {nameof(IPostConfigureServices.PostConfigureServices)} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex); + } + } + + foreach (var module in Modules) + { + if (module.Instance is AbpModule abpModule) + { + abpModule.ServiceConfigurationContext = null; + } + } + } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/AbpException.cs b/framework/src/Volo.Abp.Core/Volo/Abp/AbpException.cs index 39792ecf34..ccc4a57dbe 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/AbpException.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/AbpException.cs @@ -8,38 +8,23 @@ namespace Volo.Abp ///
public class AbpException : Exception { - /// - /// Creates a new object. - /// public AbpException() { } - /// - /// Creates a new object. - /// - /// Exception message public AbpException(string message) : base(message) { } - /// - /// Creates a new object. - /// - /// Exception message - /// Inner exception public AbpException(string message, Exception innerException) : base(message, innerException) { } - /// - /// Constructor for serializing. - /// public AbpException(SerializationInfo serializationInfo, StreamingContext context) : base(serializationInfo, context) { diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/AbpInitializationException.cs b/framework/src/Volo.Abp.Core/Volo/Abp/AbpInitializationException.cs index 1bcd513168..31d93d69bd 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/AbpInitializationException.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/AbpInitializationException.cs @@ -5,38 +5,23 @@ namespace Volo.Abp { public class AbpInitializationException : AbpException { - /// - /// Creates a new object. - /// public AbpInitializationException() { } - /// - /// Creates a new object. - /// - /// Exception message public AbpInitializationException(string message) : base(message) { } - /// - /// Creates a new object. - /// - /// Exception message - /// Inner exception public AbpInitializationException(string message, Exception innerException) : base(message, innerException) { } - /// - /// Constructor for serializing. - /// public AbpInitializationException(SerializationInfo serializationInfo, StreamingContext context) : base(serializationInfo, context) { diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/AbpShutdownException.cs b/framework/src/Volo.Abp.Core/Volo/Abp/AbpShutdownException.cs new file mode 100644 index 0000000000..1261c4f8b3 --- /dev/null +++ b/framework/src/Volo.Abp.Core/Volo/Abp/AbpShutdownException.cs @@ -0,0 +1,31 @@ +using System; +using System.Runtime.Serialization; + +namespace Volo.Abp +{ + public class AbpShutdownException : AbpException + { + public AbpShutdownException() + { + + } + + public AbpShutdownException(string message) + : base(message) + { + + } + + public AbpShutdownException(string message, Exception innerException) + : base(message, innerException) + { + + } + + public AbpShutdownException(SerializationInfo serializationInfo, StreamingContext context) + : base(serializationInfo, context) + { + + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DynamicProxy/ProxyHelper.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DynamicProxy/ProxyHelper.cs index 96a1915e26..db7ff4b869 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/DynamicProxy/ProxyHelper.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DynamicProxy/ProxyHelper.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using System.Reflection; namespace Volo.Abp.DynamicProxy @@ -6,6 +7,7 @@ namespace Volo.Abp.DynamicProxy public static class ProxyHelper { private const string ProxyNamespace = "Castle.Proxies"; + /// /// Returns dynamic proxy target object if this is a proxied object, otherwise returns the given object. /// It supports Castle Dynamic Proxies. @@ -28,5 +30,10 @@ namespace Volo.Abp.DynamicProxy return targetField.GetValue(obj); } + + public static Type GetUnProxiedType(object obj) + { + return UnProxy(obj).GetType(); + } } } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/IO/DirectoryHelper.cs b/framework/src/Volo.Abp.Core/Volo/Abp/IO/DirectoryHelper.cs index b864695430..661ba76b52 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/IO/DirectoryHelper.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/IO/DirectoryHelper.cs @@ -17,6 +17,30 @@ namespace Volo.Abp.IO } } + public static void DeleteIfExists(string directory) + { + if (Directory.Exists(directory)) + { + Directory.Delete(directory); + } + } + + public static void DeleteIfExists(string directory, bool recursive) + { + if (Directory.Exists(directory)) + { + Directory.Delete(directory, recursive); + } + } + + public static void CreateIfNotExists(DirectoryInfo directory) + { + if (!directory.Exists) + { + directory.Create(); + } + } + public static bool IsSubDirectoryOf([NotNull] string parentDirectoryPath, [NotNull] string childDirectoryPath) { Check.NotNull(parentDirectoryPath, nameof(parentDirectoryPath)); @@ -28,7 +52,8 @@ namespace Volo.Abp.IO ); } - public static bool IsSubDirectoryOf([NotNull] DirectoryInfo parentDirectory, [NotNull] DirectoryInfo childDirectory) + public static bool IsSubDirectoryOf([NotNull] DirectoryInfo parentDirectory, + [NotNull] DirectoryInfo childDirectory) { Check.NotNull(parentDirectory, nameof(parentDirectory)); Check.NotNull(childDirectory, nameof(childDirectory)); @@ -58,10 +83,7 @@ namespace Volo.Abp.IO Directory.SetCurrentDirectory(targetDirectory); - return new DisposeAction(() => - { - Directory.SetCurrentDirectory(currentDirectory); - }); + return new DisposeAction(() => { Directory.SetCurrentDirectory(currentDirectory); }); } } -} +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/IO/FileHelper.cs b/framework/src/Volo.Abp.Core/Volo/Abp/IO/FileHelper.cs index a945784352..8f4a15bdf5 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/IO/FileHelper.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/IO/FileHelper.cs @@ -17,12 +17,15 @@ namespace Volo.Abp.IO /// Checks and deletes given file if it does exists. /// /// Path of the file - public static void DeleteIfExists(string filePath) + public static bool DeleteIfExists(string filePath) { - if (File.Exists(filePath)) + if (!File.Exists(filePath)) { - File.Delete(filePath); + return false; } + + File.Delete(filePath); + return true; } /// diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleLoader.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleLoader.cs index 374e1adb78..7e7e69d457 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleLoader.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleLoader.cs @@ -20,7 +20,6 @@ namespace Volo.Abp.Modularity var modules = GetDescriptors(services, startupModuleType, plugInSources); modules = SortByDependency(modules, startupModuleType); - ConfigureServices(modules, services); return modules.ToArray(); } @@ -89,75 +88,6 @@ namespace Volo.Abp.Modularity return module; } - protected virtual void ConfigureServices(List modules, IServiceCollection services) - { - var context = new ServiceConfigurationContext(services); - services.AddSingleton(context); - - foreach (var module in modules) - { - if (module.Instance is AbpModule abpModule) - { - abpModule.ServiceConfigurationContext = context; - } - } - - //PreConfigureServices - foreach (var module in modules.Where(m => m.Instance is IPreConfigureServices)) - { - try - { - ((IPreConfigureServices)module.Instance).PreConfigureServices(context); - } - catch (Exception ex) - { - throw new AbpInitializationException($"An error occurred during {nameof(IPreConfigureServices.PreConfigureServices)} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex); - } - } - - //ConfigureServices - foreach (var module in modules) - { - if (module.Instance is AbpModule abpModule) - { - if (!abpModule.SkipAutoServiceRegistration) - { - services.AddAssembly(module.Type.Assembly); - } - } - - try - { - module.Instance.ConfigureServices(context); - } - catch (Exception ex) - { - throw new AbpInitializationException($"An error occurred during {nameof(IAbpModule.ConfigureServices)} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex); - } - } - - //PostConfigureServices - foreach (var module in modules.Where(m => m.Instance is IPostConfigureServices)) - { - try - { - ((IPostConfigureServices)module.Instance).PostConfigureServices(context); - } - catch (Exception ex) - { - throw new AbpInitializationException($"An error occurred during {nameof(IPostConfigureServices.PostConfigureServices)} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex); - } - } - - foreach (var module in modules) - { - if (module.Instance is AbpModule abpModule) - { - abpModule.ServiceConfigurationContext = null; - } - } - } - protected virtual void SetDependencies(List modules, AbpModuleDescriptor module) { foreach (var dependedModuleType in AbpModuleHelper.FindDependedModuleTypes(module.Type)) diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleManager.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleManager.cs index e594a24603..70b9036150 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleManager.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleManager.cs @@ -44,7 +44,7 @@ namespace Volo.Abp.Modularity } catch (Exception ex) { - throw new AbpInitializationException($"An error occurred during the initialize {contributor.GetType().FullName} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex); + throw new AbpInitializationException($"An error occurred during the initialize {contributor.GetType().FullName} phase of the module {module.Type.AssemblyQualifiedName}: {ex.Message}. See the inner exception for details.", ex); } } } @@ -76,7 +76,7 @@ namespace Volo.Abp.Modularity } catch (Exception ex) { - throw new AbpInitializationException($"An error occurred during the shutdown {contributor.GetType().FullName} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex); + throw new AbpShutdownException($"An error occurred during the shutdown {contributor.GetType().FullName} phase of the module {module.Type.AssemblyQualifiedName}: {ex.Message}. See the inner exception for details.", ex); } } } diff --git a/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Localization/Resources/AbpDdd/cs.json b/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Localization/Resources/AbpDdd/cs.json new file mode 100644 index 0000000000..cfe5995a67 --- /dev/null +++ b/framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Localization/Resources/AbpDdd/cs.json @@ -0,0 +1,6 @@ +{ + "culture": "cs", + "texts": { + "MaxResultCountExceededExceptionMessage": "{0} nemůže být více než {1}! Navyšte {2}.{3} na straně serveru pro více výsledků." + } +} diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Services/IDomainService.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Services/IDomainService.cs index 112243de44..5f57de7c9e 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Services/IDomainService.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Services/IDomainService.cs @@ -3,7 +3,7 @@ using Volo.Abp.DependencyInjection; namespace Volo.Abp.Domain.Services { /// - /// This interface must be implemented by all domain services to identify them by convention. + /// This interface can be implemented by all domain services to identify them by convention. /// public interface IDomainService : ITransientDependency { diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Localization/cs.json b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Localization/cs.json index c4b1aaad40..12a4b89b13 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Localization/cs.json +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Localization/cs.json @@ -18,7 +18,8 @@ "Description:Abp.Mailing.Smtp.Password": "Heslo pro uživatelské jméno spojené s přihlašovacími údaji.", "Description:Abp.Mailing.Smtp.Domain": "Název domény nebo počítače, který ověřuje přihlašovací údaje.", "Description:Abp.Mailing.Smtp.EnableSsl": "Zda SmtpClient používá SSL k šifrování připojení.", - "Description:Abp.Mailing.Smtp.UseDefaultCredentials": "Zda jsou výchozí přihlašovací údaje odesílány s požadavky." + "Description:Abp.Mailing.Smtp.UseDefaultCredentials": "Zda jsou výchozí přihlašovací údaje odesílány s požadavky.", + "TextTemplate:StandardEmailTemplates.Layout": "Výchozí šablona rozložení emailu", + "TextTemplate:StandardEmailTemplates.Message": "Jednoduchá šablona zprávy pro emaily" } } - \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Microsoft/EntityFrameworkCore/AbpMySqlModelBuilderExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Microsoft/EntityFrameworkCore/AbpMySqlModelBuilderExtensions.cs new file mode 100644 index 0000000000..cda61ee70b --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Microsoft/EntityFrameworkCore/AbpMySqlModelBuilderExtensions.cs @@ -0,0 +1,13 @@ +using Volo.Abp.EntityFrameworkCore; + +namespace Microsoft.EntityFrameworkCore +{ + public static class AbpMySqlModelBuilderExtensions + { + public static void UseMySQL( + this ModelBuilder modelBuilder) + { + modelBuilder.SetDatabaseProvider(EfCoreDatabaseProvider.MySql); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/FodyWeavers.xml b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/FodyWeavers.xml new file mode 100644 index 0000000000..be0de3a908 --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/FodyWeavers.xsd b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/FodyWeavers.xsd new file mode 100644 index 0000000000..3f3946e282 --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Microsoft/EntityFrameworkCore/AbpOracleModelBuilderExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Microsoft/EntityFrameworkCore/AbpOracleModelBuilderExtensions.cs new file mode 100644 index 0000000000..8feec1715b --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Microsoft/EntityFrameworkCore/AbpOracleModelBuilderExtensions.cs @@ -0,0 +1,13 @@ +using Volo.Abp.EntityFrameworkCore; + +namespace Microsoft.EntityFrameworkCore +{ + public static class AbpOracleModelBuilderExtensions + { + public static void UseOracle( + this ModelBuilder modelBuilder) + { + modelBuilder.SetDatabaseProvider(EfCoreDatabaseProvider.Oracle); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo.Abp.EntityFrameworkCore.Oracle.Devart.csproj b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo.Abp.EntityFrameworkCore.Oracle.Devart.csproj new file mode 100644 index 0000000000..c4763c96ca --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo.Abp.EntityFrameworkCore.Oracle.Devart.csproj @@ -0,0 +1,25 @@ + + + + + + + netstandard2.1 + Volo.Abp.EntityFrameworkCore.Oracle.Devart + Volo.Abp.EntityFrameworkCore.Oracle.Devart + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + + + + diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/AbpDbContextConfigurationContextOracleDevartExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/AbpDbContextConfigurationContextOracleDevartExtensions.cs new file mode 100644 index 0000000000..482ee8b9a8 --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/AbpDbContextConfigurationContextOracleDevartExtensions.cs @@ -0,0 +1,26 @@ +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using System; +using Devart.Data.Oracle.Entity; +using Volo.Abp.EntityFrameworkCore.DependencyInjection; + +namespace Volo.Abp.EntityFrameworkCore +{ + public static class AbpDbContextConfigurationContextOracleDevartExtensions + { + public static DbContextOptionsBuilder UseOracle( + [NotNull] this AbpDbContextConfigurationContext context, + [CanBeNull] Action oracleOptionsAction = null, + bool useExistingConnectionIfAvailable = false) + { + if (useExistingConnectionIfAvailable && context.ExistingConnection != null) + { + return context.DbContextOptions.UseOracle(context.ExistingConnection, oracleOptionsAction); + } + else + { + return context.DbContextOptions.UseOracle(context.ConnectionString, oracleOptionsAction); + } + } + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/AbpDbContextOptionsOracleDevartExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/AbpDbContextOptionsOracleDevartExtensions.cs new file mode 100644 index 0000000000..20887ceb79 --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/AbpDbContextOptionsOracleDevartExtensions.cs @@ -0,0 +1,32 @@ +using JetBrains.Annotations; +using System; +using Devart.Data.Oracle.Entity; + +namespace Volo.Abp.EntityFrameworkCore +{ + public static class AbpDbContextOptionsOracleDevartExtensions + { + public static void UseOracle( + [NotNull] this AbpDbContextOptions options, + [CanBeNull] Action oracleOptionsAction = null, + bool useExistingConnectionIfAvailable = false) + { + options.Configure(context => + { + context.UseOracle(oracleOptionsAction, useExistingConnectionIfAvailable); + }); + } + + public static void UseOracle( + [NotNull] this AbpDbContextOptions options, + [CanBeNull] Action oracleOptionsAction = null, + bool useExistingConnectionIfAvailable = false) + where TDbContext : AbpDbContext + { + options.Configure(context => + { + context.UseOracle(oracleOptionsAction, useExistingConnectionIfAvailable); + }); + } + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/Oracle/Devart/AbpEntityFrameworkCoreOracleDevartModule.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/Oracle/Devart/AbpEntityFrameworkCoreOracleDevartModule.cs new file mode 100644 index 0000000000..ec28190702 --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/Oracle/Devart/AbpEntityFrameworkCoreOracleDevartModule.cs @@ -0,0 +1,12 @@ +using Volo.Abp.Modularity; + +namespace Volo.Abp.EntityFrameworkCore.Oracle.Devart +{ + [DependsOn( + typeof(AbpEntityFrameworkCoreModule) + )] + public class AbpEntityFrameworkCoreOracleDevartModule : AbpModule + { + + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Microsoft/EntityFrameworkCore/AbpPostgreSqlModelBuilderExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Microsoft/EntityFrameworkCore/AbpPostgreSqlModelBuilderExtensions.cs new file mode 100644 index 0000000000..e3312a6fd1 --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Microsoft/EntityFrameworkCore/AbpPostgreSqlModelBuilderExtensions.cs @@ -0,0 +1,13 @@ +using Volo.Abp.EntityFrameworkCore; + +namespace Microsoft.EntityFrameworkCore +{ + public static class AbpPostgreSqlModelBuilderExtensions + { + public static void UsePostgreSql( + this ModelBuilder modelBuilder) + { + modelBuilder.SetDatabaseProvider(EfCoreDatabaseProvider.PostgreSql); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Microsoft/EntityFrameworkCore/AbpSqlServerModelBuilderExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Microsoft/EntityFrameworkCore/AbpSqlServerModelBuilderExtensions.cs new file mode 100644 index 0000000000..8f96e28903 --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Microsoft/EntityFrameworkCore/AbpSqlServerModelBuilderExtensions.cs @@ -0,0 +1,13 @@ +using Volo.Abp.EntityFrameworkCore; + +namespace Microsoft.EntityFrameworkCore +{ + public static class AbpSqlServerModelBuilderExtensions + { + public static void UseSqlServer( + this ModelBuilder modelBuilder) + { + modelBuilder.SetDatabaseProvider(EfCoreDatabaseProvider.SqlServer); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo.Abp.EntityFrameworkCore.SqlServer.csproj b/framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo.Abp.EntityFrameworkCore.SqlServer.csproj index 74d9e15537..309e53274f 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo.Abp.EntityFrameworkCore.SqlServer.csproj +++ b/framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo.Abp.EntityFrameworkCore.SqlServer.csproj @@ -19,7 +19,7 @@ - + diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Microsoft/EntityFrameworkCore/AbpSqliteModelBuilderExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Microsoft/EntityFrameworkCore/AbpSqliteModelBuilderExtensions.cs new file mode 100644 index 0000000000..01f0a9be38 --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Microsoft/EntityFrameworkCore/AbpSqliteModelBuilderExtensions.cs @@ -0,0 +1,13 @@ +using Volo.Abp.EntityFrameworkCore; + +namespace Microsoft.EntityFrameworkCore +{ + public static class AbpSqliteModelBuilderExtensions + { + public static void UseSqlite( + this ModelBuilder modelBuilder) + { + modelBuilder.SetDatabaseProvider(EfCoreDatabaseProvider.Sqlite); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo.Abp.EntityFrameworkCore.Sqlite.csproj b/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo.Abp.EntityFrameworkCore.Sqlite.csproj index 385ad9068a..9f4f26c45b 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo.Abp.EntityFrameworkCore.Sqlite.csproj +++ b/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo.Abp.EntityFrameworkCore.Sqlite.csproj @@ -15,7 +15,7 @@ - + diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Microsoft/EntityFrameworkCore/AbpModelBuilderExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Microsoft/EntityFrameworkCore/AbpModelBuilderExtensions.cs new file mode 100644 index 0000000000..7148cb69bd --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Microsoft/EntityFrameworkCore/AbpModelBuilderExtensions.cs @@ -0,0 +1,95 @@ +using Volo.Abp.EntityFrameworkCore; + +namespace Microsoft.EntityFrameworkCore +{ + public static class AbpModelBuilderExtensions + { + private const string ModelDatabaseProviderAnnotationKey = "_Abp_DatabaseProvider"; + + public static void SetDatabaseProvider( + this ModelBuilder modelBuilder, + EfCoreDatabaseProvider databaseProvider) + { + modelBuilder.Model.SetAnnotation(ModelDatabaseProviderAnnotationKey, databaseProvider); + } + + public static void ClearDatabaseProvider( + this ModelBuilder modelBuilder) + { + modelBuilder.Model.RemoveAnnotation(ModelDatabaseProviderAnnotationKey); + } + + public static EfCoreDatabaseProvider? GetDatabaseProvider( + this ModelBuilder modelBuilder + ) + { + return (EfCoreDatabaseProvider?) modelBuilder.Model[ModelDatabaseProviderAnnotationKey]; + } + + public static bool IsUsingMySQL( + this ModelBuilder modelBuilder) + { + return modelBuilder.GetDatabaseProvider() == EfCoreDatabaseProvider.MySql; + } + + public static bool IsUsingOracle( + this ModelBuilder modelBuilder) + { + return modelBuilder.GetDatabaseProvider() == EfCoreDatabaseProvider.Oracle; + } + + public static bool IsUsingSqlServer( + this ModelBuilder modelBuilder) + { + return modelBuilder.GetDatabaseProvider() == EfCoreDatabaseProvider.SqlServer; + } + + public static bool IsUsingPostgreSql( + this ModelBuilder modelBuilder) + { + return modelBuilder.GetDatabaseProvider() == EfCoreDatabaseProvider.PostgreSql; + } + + public static bool IsUsingSqlite( + this ModelBuilder modelBuilder) + { + return modelBuilder.GetDatabaseProvider() == EfCoreDatabaseProvider.Sqlite; + } + + public static void UseInMemory( + this ModelBuilder modelBuilder) + { + modelBuilder.SetDatabaseProvider(EfCoreDatabaseProvider.InMemory); + } + + public static bool IsUsingInMemory( + this ModelBuilder modelBuilder) + { + return modelBuilder.GetDatabaseProvider() == EfCoreDatabaseProvider.InMemory; + } + + public static void UseCosmos( + this ModelBuilder modelBuilder) + { + modelBuilder.SetDatabaseProvider(EfCoreDatabaseProvider.Cosmos); + } + + public static bool IsUsingCosmos( + this ModelBuilder modelBuilder) + { + return modelBuilder.GetDatabaseProvider() == EfCoreDatabaseProvider.Cosmos; + } + + public static void UseFirebird( + this ModelBuilder modelBuilder) + { + modelBuilder.SetDatabaseProvider(EfCoreDatabaseProvider.Firebird); + } + + public static bool IsUsingFirebird( + this ModelBuilder modelBuilder) + { + return modelBuilder.GetDatabaseProvider() == EfCoreDatabaseProvider.Firebird; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo.Abp.EntityFrameworkCore.csproj b/framework/src/Volo.Abp.EntityFrameworkCore/Volo.Abp.EntityFrameworkCore.csproj index a1418c84d3..3ce20b3f4e 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo.Abp.EntityFrameworkCore.csproj +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo.Abp.EntityFrameworkCore.csproj @@ -20,8 +20,8 @@ - - + + diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs index 2b35523916..2c49f6408a 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs @@ -92,6 +92,8 @@ namespace Volo.Abp.EntityFrameworkCore { base.OnModelCreating(modelBuilder); + TrySetDatabaseProvider(modelBuilder); + foreach (var entityType in modelBuilder.Model.GetEntityTypes()) { ConfigureBasePropertiesMethodInfo @@ -108,6 +110,40 @@ namespace Volo.Abp.EntityFrameworkCore } } + protected virtual void TrySetDatabaseProvider(ModelBuilder modelBuilder) + { + var provider = GetDatabaseProviderOrNull(modelBuilder); + if (provider != null) + { + modelBuilder.SetDatabaseProvider(provider.Value); + } + } + + protected virtual EfCoreDatabaseProvider? GetDatabaseProviderOrNull(ModelBuilder modelBuilder) + { + switch (Database.ProviderName) + { + case "Microsoft.EntityFrameworkCore.SqlServer": + return EfCoreDatabaseProvider.SqlServer; + case "Npgsql.EntityFrameworkCore.PostgreSQL": + return EfCoreDatabaseProvider.PostgreSql; + case "Pomelo.EntityFrameworkCore.MySql": + return EfCoreDatabaseProvider.MySql; + case "Devart.Data.Oracle.Entity.EFCore": + return EfCoreDatabaseProvider.Oracle; + case "Microsoft.EntityFrameworkCore.Sqlite": + return EfCoreDatabaseProvider.Sqlite; + case "Microsoft.EntityFrameworkCore.InMemory": + return EfCoreDatabaseProvider.InMemory; + case "FirebirdSql.EntityFrameworkCore.Firebird": + return EfCoreDatabaseProvider.Firebird; + case "Microsoft.EntityFrameworkCore.Cosmos": + return EfCoreDatabaseProvider.Cosmos; + default: + return null; + } + } + public override async Task SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default) { try diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DependencyInjection/DbContextOptionsFactory.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DependencyInjection/DbContextOptionsFactory.cs index 00b73ff910..a2d52eac48 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DependencyInjection/DbContextOptionsFactory.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DependencyInjection/DbContextOptionsFactory.cs @@ -7,7 +7,7 @@ using Volo.Abp.Data; namespace Volo.Abp.EntityFrameworkCore.DependencyInjection { - internal static class DbContextOptionsFactory + public static class DbContextOptionsFactory { public static DbContextOptions Create(IServiceProvider serviceProvider) where TDbContext : AbpDbContext @@ -94,4 +94,4 @@ namespace Volo.Abp.EntityFrameworkCore.DependencyInjection ); } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiDescriptionFinder.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiDescriptionFinder.cs index 2e8eb41844..a9b51eca6a 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiDescriptionFinder.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiDescriptionFinder.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Net.Http; using System.Reflection; using System.Threading.Tasks; using Newtonsoft.Json; @@ -14,8 +15,6 @@ namespace Volo.Abp.Http.Client.DynamicProxying { public ICancellationTokenProvider CancellationTokenProvider { get; set; } - protected IDynamicProxyHttpClientFactory HttpClientFactory { get; } - protected IApiDescriptionCache Cache { get; } private static readonly JsonSerializerSettings SharedJsonSerializerSettings = new JsonSerializerSettings @@ -23,18 +22,15 @@ namespace Volo.Abp.Http.Client.DynamicProxying ContractResolver = new CamelCasePropertyNamesContractResolver() }; - public ApiDescriptionFinder( - IApiDescriptionCache cache, - IDynamicProxyHttpClientFactory httpClientFactory) + public ApiDescriptionFinder(IApiDescriptionCache cache) { Cache = cache; - HttpClientFactory = httpClientFactory; CancellationTokenProvider = NullCancellationTokenProvider.Instance; } - public async Task FindActionAsync(string baseUrl, Type serviceType, MethodInfo method) + public async Task FindActionAsync(HttpClient client, string baseUrl, Type serviceType, MethodInfo method) { - var apiDescription = await GetApiDescriptionAsync(baseUrl); + var apiDescription = await GetApiDescriptionAsync(client, baseUrl); //TODO: Cache finding? @@ -56,8 +52,8 @@ namespace Volo.Abp.Http.Client.DynamicProxying var found = true; for (int i = 0; i < methodParameters.Length; i++) - { - if (!TypeMatches(action.ParametersOnMethod[i], methodParameters[i])) + { + if (!TypeMatches(action.ParametersOnMethod[i], methodParameters[i])) { found = false; break; @@ -76,33 +72,30 @@ namespace Volo.Abp.Http.Client.DynamicProxying throw new AbpException($"Could not found remote action for method: {method} on the URL: {baseUrl}"); } - public virtual async Task GetApiDescriptionAsync(string baseUrl) + public virtual async Task GetApiDescriptionAsync(HttpClient client, string baseUrl) { - return await Cache.GetAsync(baseUrl, () => GetApiDescriptionFromServerAsync(baseUrl)); + return await Cache.GetAsync(baseUrl, () => GetApiDescriptionFromServerAsync(client, baseUrl)); } - protected virtual async Task GetApiDescriptionFromServerAsync(string baseUrl) + protected virtual async Task GetApiDescriptionFromServerAsync(HttpClient client, string baseUrl) { - using (var client = HttpClientFactory.Create()) - { - var response = await client.GetAsync( - baseUrl.EnsureEndsWith('/') + "api/abp/api-definition", - CancellationTokenProvider.Token - ); + var response = await client.GetAsync( + baseUrl.EnsureEndsWith('/') + "api/abp/api-definition", + CancellationTokenProvider.Token + ); - if (!response.IsSuccessStatusCode) - { - throw new AbpException("Remote service returns error! StatusCode = " + response.StatusCode); - } + if (!response.IsSuccessStatusCode) + { + throw new AbpException("Remote service returns error! StatusCode = " + response.StatusCode); + } - var content = await response.Content.ReadAsStringAsync(); + var content = await response.Content.ReadAsStringAsync(); - var result = JsonConvert.DeserializeObject( - content, - typeof(ApplicationApiDescriptionModel), SharedJsonSerializerSettings); + var result = JsonConvert.DeserializeObject( + content, + typeof(ApplicationApiDescriptionModel), SharedJsonSerializerSettings); - return (ApplicationApiDescriptionModel)result; - } + return (ApplicationApiDescriptionModel)result; } protected virtual bool TypeMatches(MethodParameterApiDescriptionModel actionParameter, ParameterInfo methodParameter) diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs index 031ea7d99b..533b76aa6c 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs @@ -106,7 +106,7 @@ namespace Volo.Abp.Http.Client.DynamicProxying private async Task MakeRequestAndGetResultAsync(IAbpMethodInvocation invocation) { var responseAsString = await MakeRequestAsync(invocation); - + if (typeof(T) == typeof(string)) { return (T)Convert.ChangeType(responseAsString, typeof(T)); @@ -122,7 +122,7 @@ namespace Volo.Abp.Http.Client.DynamicProxying var client = HttpClientFactory.Create(clientConfig.RemoteServiceName); - var action = await ApiDescriptionFinder.FindActionAsync(remoteServiceConfig.BaseUrl, typeof(TService), invocation.Method); + var action = await ApiDescriptionFinder.FindActionAsync(client, remoteServiceConfig.BaseUrl, typeof(TService), invocation.Method); var apiVersion = GetApiVersionInfo(action); var url = remoteServiceConfig.BaseUrl.EnsureEndsWith('/') + UrlBuilder.GenerateUrlWithParameters(action, invocation.ArgumentsDictionary, apiVersion); @@ -252,4 +252,4 @@ namespace Volo.Abp.Http.Client.DynamicProxying return CancellationTokenProvider.Token; } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/IApiDescriptionFinder.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/IApiDescriptionFinder.cs index 010d55706e..e957752330 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/IApiDescriptionFinder.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/IApiDescriptionFinder.cs @@ -1,4 +1,5 @@ using System; +using System.Net.Http; using System.Reflection; using System.Threading.Tasks; using Volo.Abp.Http.Modeling; @@ -7,8 +8,8 @@ namespace Volo.Abp.Http.Client.DynamicProxying { public interface IApiDescriptionFinder { - Task FindActionAsync(string baseUrl, Type serviceType, MethodInfo invocationMethod); + Task FindActionAsync(HttpClient client, string baseUrl, Type serviceType, MethodInfo invocationMethod); - Task GetApiDescriptionAsync(string baseUrl); + Task GetApiDescriptionAsync(HttpClient client, string baseUrl); } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs b/framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs index c9a10c5432..7f9672ce69 100644 --- a/framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs +++ b/framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs @@ -32,6 +32,8 @@ namespace Volo.Abp.Http.ProxyScripting.Generators.JQuery AddModuleScript(script, module); } + AddInitializedEventTrigger(script); + return script.ToString(); } @@ -189,6 +191,12 @@ namespace Volo.Abp.Http.ProxyScripting.Generators.JQuery return result; } + + private static void AddInitializedEventTrigger(StringBuilder script) + { + script.AppendLine(); + script.AppendLine("abp.event.trigger('abp.serviceProxyScriptInitialized');"); + } private static string GetNormalizedTypeName(string typeWithAssemblyName) { diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/Resources/AbpLocalization/cs.json b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/Resources/AbpLocalization/cs.json index bea8d0379b..2b9ef7890f 100644 --- a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/Resources/AbpLocalization/cs.json +++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/Resources/AbpLocalization/cs.json @@ -2,6 +2,6 @@ "culture": "cs", "texts": { "DisplayName:Abp.Localization.DefaultLanguage": "Výchozí jazyk", - "Description:Abp.Localization.DefaultLanguage": "Váchozí jazyk aplikace." + "Description:Abp.Localization.DefaultLanguage": "Výchozí jazyk aplikace." } } diff --git a/framework/src/Volo.Abp.Timing/Volo.Abp.Timing.csproj b/framework/src/Volo.Abp.Timing/Volo.Abp.Timing.csproj index 4f9eb21f2f..53ef23fd62 100644 --- a/framework/src/Volo.Abp.Timing/Volo.Abp.Timing.csproj +++ b/framework/src/Volo.Abp.Timing/Volo.Abp.Timing.csproj @@ -14,8 +14,19 @@ + + + + + + + - + + + + + diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/AbpTimingModule.cs b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/AbpTimingModule.cs index d48cc01ef7..3db42c2e55 100644 --- a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/AbpTimingModule.cs +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/AbpTimingModule.cs @@ -1,9 +1,32 @@ -using Volo.Abp.Modularity; +using Volo.Abp.Localization; +using Volo.Abp.Localization.Resources.AbpLocalization; +using Volo.Abp.Modularity; +using Volo.Abp.Settings; +using Volo.Abp.Timing.Localization.Resources.AbpTiming; +using Volo.Abp.VirtualFileSystem; namespace Volo.Abp.Timing { + [DependsOn( + typeof(AbpVirtualFileSystemModule), + typeof(AbpSettingsModule) + )] public class AbpTimingModule : AbpModule { + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.FileSets.AddEmbedded(); + }); + Configure(options => + { + options + .Resources + .Add("en") + .AddVirtualJson("/Volo/Abp/Timing/Localization"); + }); + } } } diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/ITimezoneProvider.cs b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/ITimezoneProvider.cs new file mode 100644 index 0000000000..3ee1566803 --- /dev/null +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/ITimezoneProvider.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; + +namespace Volo.Abp.Timing +{ + public interface ITimezoneProvider + { + List GetWindowsTimezones(); + + List GetIanaTimezones(); + + string WindowsToIana(string windowsTimeZoneId); + + string IanaToWindows(string ianaTimeZoneName); + + TimeZoneInfo GetTimeZoneInfo(string windowsOrIanaTimeZoneId); + } +} diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/AbpTimingResource.cs b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/AbpTimingResource.cs new file mode 100644 index 0000000000..71f32d17cb --- /dev/null +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/AbpTimingResource.cs @@ -0,0 +1,10 @@ +using Volo.Abp.Localization; + +namespace Volo.Abp.Timing.Localization.Resources.AbpTiming +{ + [LocalizationResourceName("AbpTiming")] + public class AbpTimingResource + { + + } +} diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/cs.json b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/cs.json new file mode 100644 index 0000000000..e8782abe99 --- /dev/null +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/cs.json @@ -0,0 +1,7 @@ +{ + "culture": "cs", + "texts": { + "DisplayName:Abp.Timing.Timezone": "Časové pásmo", + "Description:Abp.Timing.Timezone": "Časové pásmo aplikace" + } +} diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/en.json b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/en.json new file mode 100644 index 0000000000..c017c3ebea --- /dev/null +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/en.json @@ -0,0 +1,7 @@ +{ + "culture": "en", + "texts": { + "DisplayName:Abp.Timing.Timezone": "Timezone", + "Description:Abp.Timing.Timezone": "Application time zone" + } +} diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/tr.json b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/tr.json new file mode 100644 index 0000000000..18139d6080 --- /dev/null +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/tr.json @@ -0,0 +1,7 @@ +{ + "culture": "tr", + "texts": { + "DisplayName:Abp.Timing.Timezone": "saat dilimi", + "Description:Abp.Timing.Timezone": "Uygulama saat dilimi" + } +} diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/zh-Hans.json b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/zh-Hans.json new file mode 100644 index 0000000000..41315bbfb0 --- /dev/null +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/zh-Hans.json @@ -0,0 +1,7 @@ +{ + "culture": "zh-Hans", + "texts": { + "DisplayName:Abp.Timing.Timezone": "时区", + "Description:Abp.Timing.Timezone": "应用程序的时区" + } +} diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/zh-Hant.json b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/zh-Hant.json new file mode 100644 index 0000000000..5836792e3d --- /dev/null +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/Localization/zh-Hant.json @@ -0,0 +1,7 @@ +{ + "culture": "zh-Hant", + "texts": { + "DisplayName:Abp.Timing.Timezone": "時區", + "Description:Abp.Timing.Timezone": "應用程序的時區" + } +} diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TZConvertTimezoneProvider.cs b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TZConvertTimezoneProvider.cs new file mode 100644 index 0000000000..551b26e488 --- /dev/null +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TZConvertTimezoneProvider.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using TimeZoneConverter; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Timing +{ + public class TZConvertTimezoneProvider : ITimezoneProvider, ITransientDependency + { + public virtual List GetWindowsTimezones() + { + return TZConvert.KnownIanaTimeZoneNames.OrderBy(x => x).Select(x => new NameValue(x, x)).ToList(); + } + + public virtual List GetIanaTimezones() + { + return TZConvert.KnownIanaTimeZoneNames.OrderBy(x => x).Select(x => new NameValue(x, x)).ToList(); + } + + public virtual string WindowsToIana(string windowsTimeZoneId) + { + return TZConvert.WindowsToIana(windowsTimeZoneId); + } + + public virtual string IanaToWindows(string ianaTimeZoneName) + { + return TZConvert.IanaToWindows(ianaTimeZoneName); + } + + public virtual TimeZoneInfo GetTimeZoneInfo(string windowsOrIanaTimeZoneId) + { + return TZConvert.GetTimeZoneInfo(windowsOrIanaTimeZoneId); + } + } +} diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TimingSettingNames.cs b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TimingSettingNames.cs new file mode 100644 index 0000000000..15ba1bc9f9 --- /dev/null +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TimingSettingNames.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.Timing +{ + public static class TimingSettingNames + { + public const string TimeZone = "Abp.Timing.TimeZone"; + } +} diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TimingSettingProvider.cs b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TimingSettingProvider.cs new file mode 100644 index 0000000000..c2ebf73cfd --- /dev/null +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/TimingSettingProvider.cs @@ -0,0 +1,25 @@ +using Volo.Abp.Localization; +using Volo.Abp.Settings; +using Volo.Abp.Timing.Localization.Resources.AbpTiming; + +namespace Volo.Abp.Timing +{ + public class TimingSettingProvider : SettingDefinitionProvider + { + public override void Define(ISettingDefinitionContext context) + { + context.Add( + new SettingDefinition(TimingSettingNames.TimeZone, + "UTC", + L("DisplayName:Abp.Timing.Timezone"), + L("Description:Abp.Timing.Timezone"), + isVisibleToClients: true) + ); + } + + private static LocalizableString L(string name) + { + return LocalizableString.Create(name); + } + } +} diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo.Abp.UI.Navigation.csproj b/framework/src/Volo.Abp.UI.Navigation/Volo.Abp.UI.Navigation.csproj index 5236bf0404..77d95d186d 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo.Abp.UI.Navigation.csproj +++ b/framework/src/Volo.Abp.UI.Navigation/Volo.Abp.UI.Navigation.csproj @@ -20,6 +20,7 @@ + diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/AbpUiNavigationModule.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/AbpUiNavigationModule.cs index 47889336a8..80c160e4a5 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/AbpUiNavigationModule.cs +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/AbpUiNavigationModule.cs @@ -1,12 +1,12 @@ -using Volo.Abp.Localization; +using Volo.Abp.Authorization; +using Volo.Abp.Localization; using Volo.Abp.Modularity; -using Volo.Abp.UI.Navigation; using Volo.Abp.UI.Navigation.Localization.Resource; using Volo.Abp.VirtualFileSystem; namespace Volo.Abp.UI.Navigation { - [DependsOn(typeof(AbpUiModule))] + [DependsOn(typeof(AbpUiModule), typeof(AbpAuthorizationModule))] public class AbpUiNavigationModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuItem.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuItem.cs index d4960c883e..4400fe7b46 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuItem.cs +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuItem.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using JetBrains.Annotations; @@ -101,7 +102,7 @@ namespace Volo.Abp.UI.Navigation Name = name; DisplayName = displayName; - Url = url; + Url = url?.EnsureStartsWith('~'); Icon = icon; Order = order; CustomData = customData; @@ -133,4 +134,4 @@ namespace Volo.Abp.UI.Navigation return $"[ApplicationMenuItem] Name = {Name}"; } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/DefaultMenuContributor.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/DefaultMenuContributor.cs index 9956133584..8e2558a3b7 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/DefaultMenuContributor.cs +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/DefaultMenuContributor.cs @@ -1,8 +1,5 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Localization; -using System.Threading.Tasks; +using System.Threading.Tasks; using Volo.Abp.UI.Navigation.Localization.Resource; -using Volo.Abp.UI.Navigation; namespace Volo.Abp.UI.Navigation { @@ -16,8 +13,7 @@ namespace Volo.Abp.UI.Navigation protected virtual void Configure(MenuConfigurationContext context) { - var l = context.ServiceProvider - .GetRequiredService>(); + var l = context.GetLocalizer(); if (context.Menu.Name == StandardMenus.Main) { diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/IMenuConfigurationContext.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/IMenuConfigurationContext.cs index 92b0a89c5e..cde960064a 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/IMenuConfigurationContext.cs +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/IMenuConfigurationContext.cs @@ -1,4 +1,5 @@ -using System; +using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.Localization; using Volo.Abp.DependencyInjection; namespace Volo.Abp.UI.Navigation @@ -7,6 +8,8 @@ namespace Volo.Abp.UI.Navigation { ApplicationMenu Menu { get; } - //TODO: Add Localization, Authorization components since they are most used components on menu creation! + IAuthorizationService AuthorizationService { get; } + + IStringLocalizerFactory StringLocalizerFactory { get; } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/cs.json b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/cs.json index b2c46465c0..987afffef5 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/cs.json +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/cs.json @@ -3,4 +3,4 @@ "texts": { "Menu:Administration": "Administrace" } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/MenuConfigurationContext.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/MenuConfigurationContext.cs index 4231d6f2df..e5550d2d09 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/MenuConfigurationContext.cs +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/MenuConfigurationContext.cs @@ -1,17 +1,68 @@ using System; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; namespace Volo.Abp.UI.Navigation { public class MenuConfigurationContext : IMenuConfigurationContext { - public ApplicationMenu Menu { get; } - public IServiceProvider ServiceProvider { get; } + private readonly object _serviceProviderLock = new object(); + + private TRef LazyGetRequiredService(Type serviceType, ref TRef reference) + { + if (reference == null) + { + lock (_serviceProviderLock) + { + if (reference == null) + { + reference = (TRef)ServiceProvider.GetRequiredService(serviceType); + } + } + } + + return reference; + } + + public IAuthorizationService AuthorizationService => LazyGetRequiredService(typeof(IAuthorizationService), ref _authorizationService); + private IAuthorizationService _authorizationService; + + private IStringLocalizerFactory _stringLocalizerFactory; + public IStringLocalizerFactory StringLocalizerFactory => LazyGetRequiredService(typeof(IStringLocalizerFactory),ref _stringLocalizerFactory); + + public ApplicationMenu Menu { get; } public MenuConfigurationContext(ApplicationMenu menu, IServiceProvider serviceProvider) { Menu = menu; ServiceProvider = serviceProvider; } + + public Task IsGrantedAsync(string policyName) + { + return AuthorizationService.IsGrantedAsync(policyName); + } + + [CanBeNull] + public IStringLocalizer GetDefaultLocalizer() + { + return StringLocalizerFactory.CreateDefaultOrNull(); + } + + [NotNull] + public IStringLocalizer GetLocalizer() + { + return StringLocalizerFactory.Create(); + } + + [NotNull] + public IStringLocalizer GetLocalizer(Type resourceType) + { + return StringLocalizerFactory.Create(resourceType); + } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/cs.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/cs.json index 3fba79a2b3..b457790e08 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/cs.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/cs.json @@ -13,11 +13,13 @@ "DefaultErrorMessage404": "Prostředek nenalezen!", "DefaultErrorMessage404Detail": "Vyžádaný prostředek nebyl na serveru nalezen!", "EntityNotFoundErrorMessage": "Neexistující entita {0} s id = {1}!", + "Languages": "Jazyky", "Error": "Chyba", "AreYouSure": "Jste si jisti?", "Cancel": "Zrušit", "Yes": "Ano", "No": "Ne", + "Ok": "Ok", "Close": "Zavřít", "Save": "Uložit", "SavingWithThreeDot": "Ukládám...", @@ -25,6 +27,8 @@ "Delete": "Smazat", "Edit": "Upravit", "Refresh": "Obnovit", + "Language": "Jazyk", + "LoadMore": "Načíst více", "ProcessingWithThreeDot": "Zpracovávám...", "LoadingWithThreeDot": "Nahrávám...", "Welcome": "Vítejte", @@ -42,7 +46,7 @@ "PagerInfo{0}{1}{2}": "Zobrazeno od {0} do {1} z celkem {2} záznamů", "PagerInfoEmpty": "Zobrazeno od 0 do 0 z celkem 0 záznamů", "PagerInfoFiltered": "(filtrováno ze všech _MAX_ záznamů)", - "NoDataAvailableInDatatable": "V tabulce nejsou žádná data", + "NoDataAvailableInDatatable": "Nejsou žádná data", "PagerShowMenuEntries": "Zobrazit _MENU_ záznamů", "DatatableActionDropdownDefaultText": "Akce", "ChangePassword": "Změnit heslo", @@ -56,4 +60,4 @@ "GoHomePage": "Přejít na domovskou stránku", "GoBack": "Jít zpět" } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/cs.json b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/cs.json index e3a0453e08..dce07f8c91 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/cs.json +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/cs.json @@ -31,4 +31,4 @@ "ThisFieldIsNotAValidFullyQualifiedHttpHttpsOrFtpUrl": "Pole není platná plně kvalifikovaná adresa http, https, nebo ftp URL.", "ThisFieldIsInvalid.": "Pole je neplatné." } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/App/LocalizationTestController.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/App/LocalizationTestController.cs index 1a9b6e7de8..1defcf4809 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/App/LocalizationTestController.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/App/LocalizationTestController.cs @@ -9,11 +9,13 @@ namespace Volo.Abp.AspNetCore.App { public IActionResult HelloJohn() { + // ReSharper disable once Mvc.ViewNotResolved return View(); } public IActionResult PersonForm() { + // ReSharper disable once Mvc.ViewNotResolved return View(new PersonModel()); } diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/App/SimpleController.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/App/SimpleController.cs index ffa8322592..38d0fa9ba4 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/App/SimpleController.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/App/SimpleController.cs @@ -12,11 +12,13 @@ namespace Volo.Abp.AspNetCore.App public ActionResult About() { + // ReSharper disable once Mvc.ViewNotResolved return View(); } public ActionResult ExceptionOnRazor() { + // ReSharper disable once Mvc.ViewNotResolved return View(); } } diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/Resource/cs.json b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/Resource/cs.json index 65c0603c50..f4ee60bf77 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/Resource/cs.json +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/Resource/cs.json @@ -2,6 +2,6 @@ "culture": "cs", "texts": { "BirthDate": "Narozeniny", - "Value1": "Hodnota jedna" + "Value1": "Hodnota jedna" } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController.cs index 6bb9a5ae94..57cb40219b 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController.cs @@ -35,7 +35,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Validation public class ValidationTest1Model { [Required] - [MinLength(2)] + [StringLength(5, MinimumLength = 2)] public string Value1 { get; set; } } @@ -53,4 +53,4 @@ namespace Volo.Abp.AspNetCore.Mvc.Validation } } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController_Tests.cs index 361db7845a..99e027f85a 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController_Tests.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Validation/ValidationTestController_Tests.cs @@ -30,7 +30,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Validation { var result = await GetResponseAsObjectAsync("/api/validation-test/object-result-action?value1=a", HttpStatusCode.BadRequest); //value1 has min length of 2 chars. result.Error.ValidationErrors.Length.ShouldBeGreaterThan(0); - result.Error.ValidationErrors[0].Message.ShouldBe("Değer Bir alanı en az '2' uzunluğunda bir metin ya da dizi olmalıdır."); + result.Error.ValidationErrors[0].Message.ShouldBe("Değer Bir alanı en az 2, en fazla 5 uzunluğunda bir metin olmalıdır."); } } diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo.Abp.Auditing.Tests.csproj b/framework/test/Volo.Abp.Auditing.Tests/Volo.Abp.Auditing.Tests.csproj index d7c8d84149..3c11330181 100644 --- a/framework/test/Volo.Abp.Auditing.Tests/Volo.Abp.Auditing.Tests.csproj +++ b/framework/test/Volo.Abp.Auditing.Tests/Volo.Abp.Auditing.Tests.csproj @@ -14,9 +14,8 @@ - + - diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AbpAuditingTestModule.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AbpAuditingTestModule.cs index 077658ab83..ad7b62902d 100644 --- a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AbpAuditingTestModule.cs +++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AbpAuditingTestModule.cs @@ -7,6 +7,7 @@ using Volo.Abp.Auditing.App.Entities; using Volo.Abp.Auditing.App.EntityFrameworkCore; using Volo.Abp.Autofac; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore.Sqlite; using Volo.Abp.Modularity; namespace Volo.Abp.Auditing @@ -14,7 +15,7 @@ namespace Volo.Abp.Auditing [DependsOn( typeof(AbpTestBaseModule), typeof(AbpAutofacModule), - typeof(AbpEntityFrameworkCoreModule) + typeof(AbpEntityFrameworkCoreSqliteModule) )] public class AbpAuditingTestModule : AbpModule { diff --git a/framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AutoMapperExpressionExtensions_Tests.cs b/framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AutoMapperExpressionExtensions_Tests.cs new file mode 100644 index 0000000000..f97450a749 --- /dev/null +++ b/framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AutoMapperExpressionExtensions_Tests.cs @@ -0,0 +1,177 @@ +using System; +using AutoMapper; +using Shouldly; +using Volo.Abp.Auditing; +using Xunit; + +namespace Volo.Abp.AutoMapper +{ + public class AutoMapperExpressionExtensions_Tests + { + [Fact] + public void Should_Ignore_Configured_Property() + { + var mapper = CreateMapper( + cfg => cfg + .CreateMap() + .Ignore(x => x.Value2) + .Ignore(x => x.Value3) + ); + + var obj2 = mapper.Map( + new SimpleClass1 + { + Value1 = "v1", + Value2 = "v2" + } + ); + + obj2.Value1.ShouldBe("v1"); + obj2.Value2.ShouldBeNull(); + obj2.Value3.ShouldBeNull(); + } + + [Fact] + public void Should_Ignore_Audit_Properties() + { + var mapper = CreateMapper( + cfg => cfg + .CreateMap() + .IgnoreFullAuditedObjectProperties() + ); + + var obj2 = mapper.Map( + new SimpleClassAudited1 + { + CreationTime = DateTime.Now, + CreatorId = Guid.NewGuid(), + LastModificationTime = DateTime.Now, + LastModifierId = Guid.NewGuid(), + DeleterId = Guid.NewGuid(), + DeletionTime = DateTime.Now, + IsDeleted = true + } + ); + + obj2.CreationTime.ShouldBe(default); + obj2.CreatorId.ShouldBeNull(); + obj2.LastModificationTime.ShouldBe(default); + obj2.LastModifierId.ShouldBeNull(); + obj2.DeleterId.ShouldBeNull(); + obj2.DeletionTime.ShouldBeNull(); + obj2.IsDeleted.ShouldBeFalse(); + } + + [Fact] + public void Should_Ignore_Audit_Properties_With_User() + { + var mapper = CreateMapper( + cfg => cfg + .CreateMap() + .IgnoreFullAuditedObjectProperties() + ); + + var obj2 = mapper.Map( + new SimpleClassAuditedWithUser1 + { + CreationTime = DateTime.Now, + CreatorId = Guid.NewGuid(), + LastModificationTime = DateTime.Now, + LastModifierId = Guid.NewGuid(), + DeleterId = Guid.NewGuid(), + DeletionTime = DateTime.Now, + IsDeleted = true, + Creator = new SimpleUser(), + Deleter = new SimpleUser(), + LastModifier = new SimpleUser() + } + ); + + obj2.CreationTime.ShouldBe(default); + obj2.CreatorId.ShouldBeNull(); + obj2.LastModificationTime.ShouldBe(default); + obj2.LastModifierId.ShouldBeNull(); + obj2.DeleterId.ShouldBeNull(); + obj2.DeletionTime.ShouldBeNull(); + obj2.IsDeleted.ShouldBeFalse(); + obj2.Creator.ShouldBeNull(); + obj2.Deleter.ShouldBeNull(); + obj2.LastModifier.ShouldBeNull(); + } + + private static IMapper CreateMapper(Action configure) + { + var configuration = new MapperConfiguration(configure); + configuration.AssertConfigurationIsValid(); + return configuration.CreateMapper(); + } + + public class SimpleClass1 + { + public string Value1 { get; set; } + public string Value2 { get; set; } + } + + public class SimpleClass2 + { + public string Value1 { get; set; } + public string Value2 { get; set; } + public string Value3 { get; set; } + } + + public class SimpleClassAudited1 : IFullAuditedObject + { + public DateTime CreationTime { get; set; } + public Guid? CreatorId { get; set; } + public DateTime? LastModificationTime { get; set; } + public Guid? LastModifierId { get; set; } + public bool IsDeleted { get; set; } + public DateTime? DeletionTime { get; set; } + public Guid? DeleterId { get; set; } + } + + public class SimpleClassAudited2 : IFullAuditedObject + { + public DateTime CreationTime { get; set; } + public Guid? CreatorId { get; set; } + public DateTime? LastModificationTime { get; set; } + public Guid? LastModifierId { get; set; } + public bool IsDeleted { get; set; } + public DateTime? DeletionTime { get; set; } + public Guid? DeleterId { get; set; } + } + + public class SimpleClassAuditedWithUser1 : IFullAuditedObject + { + public DateTime CreationTime { get; set; } + public Guid? CreatorId { get; set; } + public DateTime? LastModificationTime { get; set; } + public Guid? LastModifierId { get; set; } + public bool IsDeleted { get; set; } + public DateTime? DeletionTime { get; set; } + public Guid? DeleterId { get; set; } + public SimpleUser Creator { get; set; } + public SimpleUser LastModifier { get; set; } + public SimpleUser Deleter { get; set; } + } + + public class SimpleClassAuditedWithUser2 : IFullAuditedObject + { + public DateTime CreationTime { get; set; } + public Guid? CreatorId { get; set; } + public DateTime? LastModificationTime { get; set; } + public Guid? LastModifierId { get; set; } + public bool IsDeleted { get; set; } + public DateTime? DeletionTime { get; set; } + public Guid? DeleterId { get; set; } + public SimpleUser Creator { get; set; } + public SimpleUser LastModifier { get; set; } + public SimpleUser Deleter { get; set; } + } + + public class SimpleUser + { + + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo.Abp.BlobStoring.FileSystem.Tests.csproj b/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo.Abp.BlobStoring.FileSystem.Tests.csproj new file mode 100644 index 0000000000..b2741af89a --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo.Abp.BlobStoring.FileSystem.Tests.csproj @@ -0,0 +1,18 @@ + + + + + + netcoreapp3.1 + + + + + + + + + + + + diff --git a/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/AbpBlobStoringFileSystemTestModule.cs b/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/AbpBlobStoringFileSystemTestModule.cs new file mode 100644 index 0000000000..a18f8e40af --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/AbpBlobStoringFileSystemTestModule.cs @@ -0,0 +1,43 @@ +using System; +using System.IO; +using Volo.Abp.IO; +using Volo.Abp.Modularity; + +namespace Volo.Abp.BlobStoring.FileSystem +{ + [DependsOn( + typeof(AbpBlobStoringFileSystemModule), + typeof(AbpBlobStoringTestModule) + )] + public class AbpBlobStoringFileSystemTestModule : AbpModule + { + private readonly string _testDirectoryPath; + + public AbpBlobStoringFileSystemTestModule() + { + _testDirectoryPath = Path.Combine( + Path.GetTempPath(), + Guid.NewGuid().ToString("N") + ); + } + + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.Containers.ConfigureAll((containerName, containerConfiguration) => + { + containerConfiguration.UseFileSystem(fileSystem => + { + fileSystem.BasePath = _testDirectoryPath; + }); + }); + }); + } + + public override void OnApplicationShutdown(ApplicationShutdownContext context) + { + DirectoryHelper.DeleteIfExists(_testDirectoryPath, true); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/AbpBlobStoringTestBase.cs b/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/AbpBlobStoringTestBase.cs new file mode 100644 index 0000000000..8fdd832a5b --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/AbpBlobStoringTestBase.cs @@ -0,0 +1,12 @@ +using Volo.Abp.Testing; + +namespace Volo.Abp.BlobStoring.FileSystem +{ + public abstract class AbpBlobStoringFileSystemTestBase : AbpIntegratedTest + { + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/BlogFilePathCalculator_Tests.cs b/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/BlogFilePathCalculator_Tests.cs new file mode 100644 index 0000000000..b7382a7f59 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/BlogFilePathCalculator_Tests.cs @@ -0,0 +1,75 @@ +using System; +using System.IO; +using Shouldly; +using Volo.Abp.MultiTenancy; +using Xunit; + +namespace Volo.Abp.BlobStoring.FileSystem +{ + public class BlogFilePathCalculator_Tests : AbpBlobStoringFileSystemTestBase + { + private readonly IBlobFilePathCalculator _calculator; + private readonly ICurrentTenant _currentTenant; + + public BlogFilePathCalculator_Tests() + { + _calculator = GetRequiredService(); + _currentTenant = GetRequiredService(); + } + + [Fact] + public void Default_Settings() + { + var separator = Path.DirectorySeparatorChar; + + _calculator.Calculate( + GetArgs($"C:{separator}my-files", "my-container", "my-blob") + ).ShouldBe($"C:{separator}my-files{separator}host{separator}my-container{separator}my-blob"); + } + + [Fact] + public void Default_Settings_With_TenantId() + { + var separator = Path.DirectorySeparatorChar; + var tenantId = Guid.NewGuid(); + + using (_currentTenant.Change(tenantId)) + { + _calculator.Calculate( + GetArgs($"C:{separator}my-files", "my-container", "my-blob") + ).ShouldBe($"C:{separator}my-files{separator}tenants{separator}{tenantId:D}{separator}my-container{separator}my-blob"); + } + } + + [Fact] + public void AppendContainerNameToBasePath_Set_To_False() + { + var separator = Path.DirectorySeparatorChar; + + _calculator.Calculate( + GetArgs($"C:{separator}my-files", "my-container", "my-blob", appendContainerNameToBasePath: false) + ).ShouldBe($"C:{separator}my-files{separator}host{separator}my-blob"); + } + + private static BlobProviderArgs GetArgs( + string basePath, + string containerName, + string blobName, + bool? appendContainerNameToBasePath = null) + { + return new BlobProviderGetArgs( + containerName, + new BlobContainerConfiguration() + .UseFileSystem(fs => + { + fs.BasePath = basePath; + if (appendContainerNameToBasePath.HasValue) + { + fs.AppendContainerNameToBasePath = appendContainerNameToBasePath.Value; + } + }), + blobName + ); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobContainer_Tests.cs b/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobContainer_Tests.cs new file mode 100644 index 0000000000..cf2ca82398 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobContainer_Tests.cs @@ -0,0 +1,10 @@ +namespace Volo.Abp.BlobStoring.FileSystem +{ + public class FileSystemBlobContainer_Tests : BlobContainer_Tests + { + public FileSystemBlobContainer_Tests() + { + + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo.Abp.BlobStoring.Tests.csproj b/framework/test/Volo.Abp.BlobStoring.Tests/Volo.Abp.BlobStoring.Tests.csproj new file mode 100644 index 0000000000..d9535ff176 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo.Abp.BlobStoring.Tests.csproj @@ -0,0 +1,17 @@ + + + + + + netcoreapp3.1 + + + + + + + + + + + diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/AbpBlobStoringOptions_Tests.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/AbpBlobStoringOptions_Tests.cs new file mode 100644 index 0000000000..53c7c2079d --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/AbpBlobStoringOptions_Tests.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using Microsoft.Extensions.Options; +using Shouldly; +using Volo.Abp.BlobStoring.Fakes; +using Volo.Abp.BlobStoring.TestObjects; +using Xunit; + +namespace Volo.Abp.BlobStoring +{ + public class AbpBlobStoringOptions_Tests : AbpBlobStoringTestBase + { + private readonly IBlobContainerConfigurationProvider _configurationProvider; + + public AbpBlobStoringOptions_Tests() + { + _configurationProvider = GetRequiredService(); + } + + [Fact] + public void Should_Property_Set_And_Get_Options_For_Different_Containers() + { + var testContainer1Config = _configurationProvider.Get(); + testContainer1Config.ProviderType.ShouldBe(typeof(FakeBlobProvider1)); + testContainer1Config.GetConfigurationOrDefault("TestConfig1").ShouldBe("TestValue1"); + testContainer1Config.GetConfigurationOrDefault("TestConfigDefault").ShouldBe("TestValueDefault"); + + var testContainer2Config = _configurationProvider.Get(); + testContainer2Config.ProviderType.ShouldBe(typeof(FakeBlobProvider2)); + testContainer2Config.GetConfigurationOrNull("TestConfig2").ShouldBe("TestValue2"); + testContainer2Config.GetConfigurationOrNull("TestConfigDefault").ShouldBe("TestValueDefault"); + } + + [Fact] + public void Should_Fallback_To_Default_Configuration_If_Not_Specialized() + { + var config = _configurationProvider.Get(); + config.ProviderType.ShouldBe(typeof(FakeBlobProvider1)); + config.GetConfigurationOrNull("TestConfigDefault").ShouldBe("TestValueDefault"); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/AbpBlobStoringTestBase.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/AbpBlobStoringTestBase.cs new file mode 100644 index 0000000000..07e34c599f --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/AbpBlobStoringTestBase.cs @@ -0,0 +1,12 @@ +using Volo.Abp.Testing; + +namespace Volo.Abp.BlobStoring +{ + public abstract class AbpBlobStoringTestBase : AbpIntegratedTest + { + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/AbpBlobStoringTestModule.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/AbpBlobStoringTestModule.cs new file mode 100644 index 0000000000..0d150a20ea --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/AbpBlobStoringTestModule.cs @@ -0,0 +1,43 @@ +using Microsoft.Extensions.DependencyInjection; +using NSubstitute; +using Volo.Abp.Autofac; +using Volo.Abp.BlobStoring.Fakes; +using Volo.Abp.BlobStoring.TestObjects; +using Volo.Abp.Modularity; + +namespace Volo.Abp.BlobStoring +{ + [DependsOn( + typeof(AbpBlobStoringModule), + typeof(AbpTestBaseModule), + typeof(AbpAutofacModule) + )] + public class AbpBlobStoringTestModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddSingleton(Substitute.For()); + context.Services.AddSingleton(Substitute.For()); + + Configure(options => + { + options.Containers + .ConfigureDefault(container => + { + container.SetConfiguration("TestConfigDefault", "TestValueDefault"); + container.ProviderType = typeof(FakeBlobProvider1); + }) + .Configure(container => + { + container.SetConfiguration("TestConfig1", "TestValue1"); + container.ProviderType = typeof(FakeBlobProvider1); + }) + .Configure(container => + { + container.SetConfiguration("TestConfig2", "TestValue2"); + container.ProviderType = typeof(FakeBlobProvider2); + }); + }); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobContainerFactory_Tests.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobContainerFactory_Tests.cs new file mode 100644 index 0000000000..359470106e --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobContainerFactory_Tests.cs @@ -0,0 +1,76 @@ +using System.Threading.Tasks; +using NSubstitute; +using Volo.Abp.BlobStoring.Fakes; +using Volo.Abp.BlobStoring.TestObjects; +using Xunit; + +namespace Volo.Abp.BlobStoring +{ + public class BlobContainerFactory_Tests : AbpBlobStoringTestBase + { + private readonly IBlobContainerFactory _factory; + private readonly FakeProviders _fakeProviders; + + public BlobContainerFactory_Tests() + { + _factory = GetRequiredService(); + _fakeProviders = GetRequiredService(); + } + + [Fact] + public async Task Should_Create_Containers_With_Configured_Providers() + { + // TestContainer1 with FakeBlobProvider1 + + await _fakeProviders.Provider1 + .DidNotReceiveWithAnyArgs() + .ExistsAsync(default); + + await _factory + .Create() + .ExistsAsync("TestBlob1"); + + await _fakeProviders.Provider1 + .Received(1) + .ExistsAsync(Arg.Is(args => + args.ContainerName == BlobContainerNameAttribute.GetContainerName() && + args.BlobName == "TestBlob1" + ) + ); + + // TestContainer2 with FakeBlobProvider2 + + await _fakeProviders.Provider2 + .DidNotReceiveWithAnyArgs() + .ExistsAsync(default); + + await _factory + .Create() + .ExistsAsync("TestBlob2"); + + await _fakeProviders.Provider2 + .Received(1) + .ExistsAsync(Arg.Is(args => + args.ContainerName == BlobContainerNameAttribute.GetContainerName() && + args.BlobName == "TestBlob2" + ) + ); + + // TestContainer3 with FakeBlobProvider1 (default provider) + + _fakeProviders.Provider1.ClearReceivedCalls(); + + await _factory + .Create() + .ExistsAsync("TestBlob3"); + + await _fakeProviders.Provider1 + .Received(1) + .ExistsAsync(Arg.Is(t => + t.ContainerName == BlobContainerNameAttribute.GetContainerName() && + t.BlobName == "TestBlob3" + ) + ); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobContainerNameAttribute_Tests.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobContainerNameAttribute_Tests.cs new file mode 100644 index 0000000000..242b195a0c --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobContainerNameAttribute_Tests.cs @@ -0,0 +1,25 @@ +using Shouldly; +using Volo.Abp.BlobStoring.TestObjects; +using Xunit; + +namespace Volo.Abp.BlobStoring +{ + public class BlobContainerNameAttribute_Tests + { + [Fact] + public void Should_Get_Specified_Name() + { + BlobContainerNameAttribute + .GetContainerName() + .ShouldBe("Test2"); + } + + [Fact] + public void Should_Get_Full_Class_Name_If_Not_Specified() + { + BlobContainerNameAttribute + .GetContainerName() + .ShouldBe(typeof(TestContainer1).FullName); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobContainer_Injection_Tests.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobContainer_Injection_Tests.cs new file mode 100644 index 0000000000..bd4ec2c47a --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobContainer_Injection_Tests.cs @@ -0,0 +1,32 @@ +using Shouldly; +using Volo.Abp.BlobStoring.TestObjects; +using Xunit; + +namespace Volo.Abp.BlobStoring +{ + public class BlobContainer_Injection_Tests : AbpBlobStoringTestBase + { + [Fact] + public void Should_Inject_DefaultContainer_For_Non_Generic_Interface() + { + GetRequiredService() + .ShouldBeOfType>(); + } + + [Fact] + public void Should_Inject_Specified_Container_For_Generic_Interface() + { + GetRequiredService>() + .ShouldBeOfType>(); + + GetRequiredService>() + .ShouldBeOfType>(); + + GetRequiredService>() + .ShouldBeOfType>(); + + GetRequiredService>() + .ShouldBeOfType>(); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobContainer_Tests.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobContainer_Tests.cs new file mode 100644 index 0000000000..091c1b7c95 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobContainer_Tests.cs @@ -0,0 +1,105 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.BlobStoring.TestObjects; +using Volo.Abp.Modularity; +using Volo.Abp.Testing; +using Xunit; + +namespace Volo.Abp.BlobStoring +{ + public abstract class BlobContainer_Tests : AbpIntegratedTest + where TStartupModule : IAbpModule + { + protected IBlobContainer Container { get; } + + protected BlobContainer_Tests() + { + Container = GetRequiredService>(); + } + + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } + + [Theory] + [InlineData("test-blob-1")] + [InlineData("test-blob-1.txt")] + [InlineData("test-folder/test-blob-1")] + public async Task Should_Save_And_Get_Blobs(string blobName) + { + var testContent = "test content".GetBytes(); + await Container.SaveAsync(blobName, testContent); + + var result = await Container.GetAllBytesAsync(blobName); + result.SequenceEqual(testContent).ShouldBeTrue(); + } + + [Fact] + public async Task Should_Overwrite_Pre_Saved_Blob_If_Requested() + { + var blobName = "test-blob-1"; + + var testContent = "test content".GetBytes(); + await Container.SaveAsync(blobName, testContent); + + var testContentOverwritten = "test content overwritten".GetBytes(); + await Container.SaveAsync(blobName, testContentOverwritten, true); + + var result = await Container.GetAllBytesAsync(blobName); + result.SequenceEqual(testContentOverwritten).ShouldBeTrue(); + } + + [Fact] + public async Task Should_Not_Allow_To_Overwrite_Pre_Saved_Blob_By_Default() + { + var blobName = "test-blob-1"; + + var testContent = "test content".GetBytes(); + await Container.SaveAsync(blobName, testContent); + + var testContentOverwritten = "test content overwritten".GetBytes(); + await Assert.ThrowsAsync(() => + Container.SaveAsync(blobName, testContentOverwritten) + ); + } + + [Theory] + [InlineData("test-blob-1")] + [InlineData("test-blob-1.txt")] + [InlineData("test-folder/test-blob-1")] + public async Task Should_Delete_Saved_Blobs(string blobName) + { + await Container.SaveAsync(blobName, "test content".GetBytes()); + (await Container.GetAllBytesAsync(blobName)).ShouldNotBeNull(); + + await Container.DeleteAsync(blobName); + (await Container.GetAllBytesOrNullAsync(blobName)).ShouldBeNull(); + } + + [Theory] + [InlineData("test-blob-1")] + [InlineData("test-blob-1.txt")] + [InlineData("test-folder/test-blob-1")] + public async Task Saved_Blobs_Should_Exists(string blobName) + { + await Container.SaveAsync(blobName, "test content".GetBytes()); + (await Container.ExistsAsync(blobName)).ShouldBeTrue(); + + await Container.DeleteAsync(blobName); + (await Container.ExistsAsync(blobName)).ShouldBeFalse(); + } + + [Theory] + [InlineData("test-blob-1")] + [InlineData("test-blob-1.txt")] + [InlineData("test-folder/test-blob-1")] + public async Task Unknown_Blobs_Should_Not_Exists(string blobName) + { + await Container.DeleteAsync(blobName); + (await Container.ExistsAsync(blobName)).ShouldBeFalse(); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobProviderSelector_Tests.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobProviderSelector_Tests.cs new file mode 100644 index 0000000000..f36129c19d --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobProviderSelector_Tests.cs @@ -0,0 +1,31 @@ +using Shouldly; +using Volo.Abp.BlobStoring.Fakes; +using Volo.Abp.BlobStoring.TestObjects; +using Volo.Abp.DynamicProxy; +using Xunit; + +namespace Volo.Abp.BlobStoring +{ + public class BlobProviderSelector_Tests : AbpBlobStoringTestBase + { + private readonly IBlobProviderSelector _selector; + + public BlobProviderSelector_Tests() + { + _selector = GetRequiredService(); + } + + [Fact] + public void Should_Select_Default_Provider_If_Not_Configured() + { + _selector.Get().ShouldBeAssignableTo(); + } + + [Fact] + public void Should_Select_Configured_Provider() + { + _selector.Get().ShouldBeAssignableTo(); + _selector.Get().ShouldBeAssignableTo(); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/Fakes/FakeBlobProvider1.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/Fakes/FakeBlobProvider1.cs new file mode 100644 index 0000000000..38fef18d66 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/Fakes/FakeBlobProvider1.cs @@ -0,0 +1,33 @@ +using System.IO; +using System.Threading.Tasks; + +namespace Volo.Abp.BlobStoring.Fakes +{ + public class FakeBlobProvider1 : IBlobProvider + { + public virtual Task SaveAsync(BlobProviderSaveArgs args) + { + throw new System.NotImplementedException(); + } + + public virtual Task DeleteAsync(BlobProviderDeleteArgs args) + { + throw new System.NotImplementedException(); + } + + public virtual Task ExistsAsync(BlobProviderExistsArgs args) + { + throw new System.NotImplementedException(); + } + + public virtual Task GetAsync(BlobProviderGetArgs args) + { + throw new System.NotImplementedException(); + } + + public virtual Task GetOrNullAsync(BlobProviderGetArgs args) + { + throw new System.NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/Fakes/FakeBlobProvider2.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/Fakes/FakeBlobProvider2.cs new file mode 100644 index 0000000000..dc0e40d3d8 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/Fakes/FakeBlobProvider2.cs @@ -0,0 +1,33 @@ +using System.IO; +using System.Threading.Tasks; + +namespace Volo.Abp.BlobStoring.Fakes +{ + public class FakeBlobProvider2 : IBlobProvider + { + public virtual Task SaveAsync(BlobProviderSaveArgs args) + { + throw new System.NotImplementedException(); + } + + public virtual Task DeleteAsync(BlobProviderDeleteArgs args) + { + throw new System.NotImplementedException(); + } + + public virtual Task ExistsAsync(BlobProviderExistsArgs args) + { + throw new System.NotImplementedException(); + } + + public virtual Task GetAsync(BlobProviderGetArgs args) + { + throw new System.NotImplementedException(); + } + + public virtual Task GetOrNullAsync(BlobProviderGetArgs args) + { + throw new System.NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/Fakes/FakeProviders.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/Fakes/FakeProviders.cs new file mode 100644 index 0000000000..e66e8b6adc --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/Fakes/FakeProviders.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using System.Linq; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.BlobStoring.Fakes +{ + public class FakeProviders : ISingletonDependency + { + public FakeBlobProvider1 Provider1 { get; } + + public FakeBlobProvider2 Provider2 { get; } + + public FakeProviders(IEnumerable providers) + { + Provider1 = providers.OfType().Single(); + Provider2 = providers.OfType().Single(); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/TestObjects/TestContainer1.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/TestObjects/TestContainer1.cs new file mode 100644 index 0000000000..61c84a1148 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/TestObjects/TestContainer1.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.BlobStoring.TestObjects +{ + public class TestContainer1 + { + + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/TestObjects/TestContainer2.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/TestObjects/TestContainer2.cs new file mode 100644 index 0000000000..3193da3749 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/TestObjects/TestContainer2.cs @@ -0,0 +1,8 @@ +namespace Volo.Abp.BlobStoring.TestObjects +{ + [BlobContainerName("Test2")] + public class TestContainer2 + { + + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/TestObjects/TestContainer3.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/TestObjects/TestContainer3.cs new file mode 100644 index 0000000000..b96a71c1b7 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/TestObjects/TestContainer3.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.BlobStoring.TestObjects +{ + public class TestContainer3 + { + + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Core.Tests/System/Collections/Generic/AbpListExtensions_Tests.cs b/framework/test/Volo.Abp.Core.Tests/System/Collections/Generic/AbpListExtensions_Tests.cs index 93e2ef5824..9c8926f38a 100644 --- a/framework/test/Volo.Abp.Core.Tests/System/Collections/Generic/AbpListExtensions_Tests.cs +++ b/framework/test/Volo.Abp.Core.Tests/System/Collections/Generic/AbpListExtensions_Tests.cs @@ -1,5 +1,6 @@ using System.Linq; using Shouldly; +using Volo.Abp; using Xunit; namespace System.Collections.Generic @@ -171,5 +172,36 @@ namespace System.Collections.Generic list[1].ShouldBe(42); list[2].ShouldBe(3); } + + [Fact] + public void SortByDependencies() + { + var dependencies = new Dictionary + { + {'A', new[] {'B', 'G'}}, + {'B', new[] {'C', 'E'}}, + {'C', new[] {'D'}}, + {'D', new char[0]}, + {'E', new[] {'C', 'F'}}, + {'F', new[] {'C'}}, + {'G', new[] {'F'}} + }; + + for (int i = 0; i < 3; i++) + { + var list = RandomHelper + .GenerateRandomizedList(new char[] {'A', 'B', 'C', 'D', 'E', 'F', 'G'}); + + list = list.SortByDependencies(c => dependencies[c]); + + foreach (var dependency in dependencies) + { + foreach (var dependedValue in dependency.Value) + { + list.IndexOf(dependency.Key).ShouldBeGreaterThan(list.IndexOf(dependedValue)); + } + } + } + } } -} +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Core.Tests/System/ObjectExtension_Test.cs b/framework/test/Volo.Abp.Core.Tests/System/ObjectExtension_Test.cs index 4345c60491..be429658ed 100644 --- a/framework/test/Volo.Abp.Core.Tests/System/ObjectExtension_Test.cs +++ b/framework/test/Volo.Abp.Core.Tests/System/ObjectExtension_Test.cs @@ -52,5 +52,22 @@ namespace System str = null; str.IsIn("a", "b", "c").ShouldBe(false); } + + [Fact] + public void If_Tests() + { + var value = 0; + + value = value.If(true, v => v + 1); + value.ShouldBe(1); + + value = value.If(false, v => v + 1); + value.ShouldBe(1); + + value = value + .If(true, v => v + 3) + .If(false, v => v + 5); + value.ShouldBe(4); + } } } diff --git a/framework/test/Volo.Abp.Dapper.Tests/Volo.Abp.Dapper.Tests.csproj b/framework/test/Volo.Abp.Dapper.Tests/Volo.Abp.Dapper.Tests.csproj index a845149b32..1bb64ef52d 100644 --- a/framework/test/Volo.Abp.Dapper.Tests/Volo.Abp.Dapper.Tests.csproj +++ b/framework/test/Volo.Abp.Dapper.Tests/Volo.Abp.Dapper.Tests.csproj @@ -14,7 +14,6 @@ - diff --git a/framework/test/Volo.Abp.Dapper.Tests/Volo/Abp/Dapper/AbpDapperTestModule.cs b/framework/test/Volo.Abp.Dapper.Tests/Volo/Abp/Dapper/AbpDapperTestModule.cs index 02b7575052..7bc8c4f6d2 100644 --- a/framework/test/Volo.Abp.Dapper.Tests/Volo/Abp/Dapper/AbpDapperTestModule.cs +++ b/framework/test/Volo.Abp.Dapper.Tests/Volo/Abp/Dapper/AbpDapperTestModule.cs @@ -1,5 +1,6 @@ using Volo.Abp.Autofac; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore.Sqlite; using Volo.Abp.Modularity; namespace Volo.Abp.Dapper @@ -7,11 +8,10 @@ namespace Volo.Abp.Dapper [DependsOn( typeof(AbpEntityFrameworkCoreTestModule), typeof(AbpDapperModule), - typeof(AbpAutofacModule))] + typeof(AbpAutofacModule) + )] public class AbpDapperTestModule : AbpModule { - public override void ConfigureServices(ServiceConfigurationContext context) - { - } + } } \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/cs.json b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/cs.json new file mode 100644 index 0000000000..6c764b21a2 --- /dev/null +++ b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/cs.json @@ -0,0 +1,6 @@ +{ + "culture": "cs", + "texts": { + "hello": "Ahoj" + } +} diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/en.json b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/en.json index 70d0b2b52e..fd029791dc 100644 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/en.json +++ b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/en.json @@ -1,5 +1,5 @@ { - "culture": "de", + "culture": "en", "texts": { "hello": "Hello" } diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo.Abp.EntityFrameworkCore.Tests.csproj b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo.Abp.EntityFrameworkCore.Tests.csproj index f2d2ca162c..a154e6acaf 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo.Abp.EntityFrameworkCore.Tests.csproj +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo.Abp.EntityFrameworkCore.Tests.csproj @@ -9,14 +9,13 @@ - + - diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs index 240211d74d..a1ca2e2846 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs @@ -6,6 +6,7 @@ using Microsoft.EntityFrameworkCore.Storage; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Autofac; using Volo.Abp.EntityFrameworkCore.Domain; +using Volo.Abp.EntityFrameworkCore.Sqlite; using Volo.Abp.EntityFrameworkCore.TestApp.SecondContext; using Volo.Abp.EntityFrameworkCore.TestApp.ThirdDbContext; using Volo.Abp.Modularity; @@ -16,7 +17,7 @@ using Volo.Abp.Timing; namespace Volo.Abp.EntityFrameworkCore { - [DependsOn(typeof(AbpEntityFrameworkCoreModule))] + [DependsOn(typeof(AbpEntityFrameworkCoreSqliteModule))] [DependsOn(typeof(TestAppModule))] [DependsOn(typeof(AbpAutofacModule))] [DependsOn(typeof(AbpEfCoreTestSecondContextModule))] diff --git a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/CountryNames/cs.json b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/CountryNames/cs.json index 6f249267c0..db2b0ccc4a 100644 --- a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/CountryNames/cs.json +++ b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/CountryNames/cs.json @@ -4,4 +4,4 @@ "USA": "Spojené státy americké", "Brazil": "Brazílie" } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/Validation/cs.json b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/Validation/cs.json index 9fd414c139..f573dae436 100644 --- a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/Validation/cs.json +++ b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/Validation/cs.json @@ -4,4 +4,4 @@ "ThisFieldIsRequired": "Toto pole je povinné", "MaxLenghtErrorMessage": "Toto pole může mít nanejvýš '{0}' znaků" } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/cs.json b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/cs.json index 88593c2ba8..433cb0b72e 100644 --- a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/cs.json +++ b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/cs.json @@ -8,4 +8,4 @@ "Universe": "Vesmír", "FortyTwo": "Čtyřicet dva" } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/SourceExt/cs.json b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/SourceExt/cs.json index 8054f6de70..a345698004 100644 --- a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/SourceExt/cs.json +++ b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/SourceExt/cs.json @@ -3,4 +3,4 @@ "texts": { "SeeYou": "Měj se" } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoDbFixture.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoDbFixture.cs index f401d0d6d4..3fa239ebbb 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoDbFixture.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoDbFixture.cs @@ -5,12 +5,18 @@ namespace Volo.Abp.MongoDB { public class MongoDbFixture : IDisposable { - private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); - public static readonly string ConnectionString = MongoDbRunner.ConnectionString; + private static readonly MongoDbRunner MongoDbRunner; + public static readonly string ConnectionString; + + static MongoDbFixture() + { + MongoDbRunner = MongoDbRunner.Start(); + ConnectionString = MongoDbRunner.ConnectionString; + } public void Dispose() { MongoDbRunner?.Dispose(); } } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/cs.json b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/cs.json new file mode 100644 index 0000000000..de5a5b79e3 --- /dev/null +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/cs.json @@ -0,0 +1,7 @@ +{ + "culture": "cs", + "texts": { + "HelloText": "Ahoj {0}", + "HowAreYou": "jak se máš?" + } +} diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/cs.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/cs.json index ac8b58ee1e..120060c4f4 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/cs.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/cs.json @@ -11,6 +11,7 @@ "InvalidUserNameOrPassword": "Neplatné uživatelské jméno nebo heslo!", "LoginIsNotAllowed": "Není vám dovoleno se přihlásit! Musíte první potvrdit email/telefonní číslo.", "SelfRegistrationDisabledMessage": "Vlastní registrace uživatele není povolena. K vytvoření účtu kontaktujte správce.", + "LocalLoginDisabledMessage": "Místní přihlášení je pro tuto aplikaci zakázáno.", "Login": "Přihlásit", "Cancel": "Zrušit", "Register": "Registrovat", @@ -41,4 +42,4 @@ "DisplayName:Abp.Account.EnableLocalLogin": "Ověření pomocí místního účtu", "Description:Abp.Account.EnableLocalLogin": "Označuje, zda server umožní uživatelům ověřovat se pomocí místního účtu." } -} \ No newline at end of file +} diff --git a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj index a38a51a16c..17a164e612 100644 --- a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj +++ b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Volo.Abp.Account.Web.IdentityServer.csproj @@ -17,10 +17,8 @@ - - diff --git a/modules/account/src/Volo.Abp.Account.Web/AbpAccountUserMenuContributor.cs b/modules/account/src/Volo.Abp.Account.Web/AbpAccountUserMenuContributor.cs index 20c42cc4c1..28cb7c7e0a 100644 --- a/modules/account/src/Volo.Abp.Account.Web/AbpAccountUserMenuContributor.cs +++ b/modules/account/src/Volo.Abp.Account.Web/AbpAccountUserMenuContributor.cs @@ -16,8 +16,8 @@ namespace Volo.Abp.Account.Web return Task.CompletedTask; } - var uiResource = context.ServiceProvider.GetRequiredService>(); - var accountResource = context.ServiceProvider.GetRequiredService>(); + var uiResource = context.GetLocalizer(); + var accountResource = context.GetLocalizer(); context.Menu.AddItem(new ApplicationMenuItem("Account.Manage", accountResource["ManageYourProfile"], url: "/Account/Manage", icon: "fa fa-cog", order: 1000, null)); context.Menu.AddItem(new ApplicationMenuItem("Account.Logout", uiResource["Logout"], url: "/Account/Logout", icon: "fa fa-power-off", order: int.MaxValue - 1000)); @@ -25,4 +25,4 @@ namespace Volo.Abp.Account.Web return Task.CompletedTask; } } -} \ No newline at end of file +} diff --git a/modules/account/src/Volo.Abp.Account.Web/Modules/Account/Components/Toolbar/UserLoginLink/Default.cshtml b/modules/account/src/Volo.Abp.Account.Web/Modules/Account/Components/Toolbar/UserLoginLink/Default.cshtml index 451a65b2dc..2331409857 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Modules/Account/Components/Toolbar/UserLoginLink/Default.cshtml +++ b/modules/account/src/Volo.Abp.Account.Web/Modules/Account/Components/Toolbar/UserLoginLink/Default.cshtml @@ -1,4 +1,4 @@ @using Localization.Resources.AbpUi @using Microsoft.AspNetCore.Mvc.Localization @inject IHtmlLocalizer L -@L["Login"] +@L["Login"] diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs index f5dbba8b2f..41bdd93647 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Volo.Abp.Account.Localization; @@ -73,7 +74,7 @@ namespace Volo.Abp.Account.Web.Pages.Account protected virtual string GetAppHomeUrl() { - return "/"; //TODO: ??? + return "~/"; //TODO: ??? } } } diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs index 3b5a5b2cd1..87c7131305 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs @@ -59,7 +59,7 @@ namespace Volo.Abp.Account.Web.Pages.Account await SignInManager.SignInAsync(user, isPersistent: false); - return Redirect(ReturnUrl ?? "/"); //TODO: How to ensure safety? IdentityServer requires it however it should be checked somehow! + return Redirect(ReturnUrl ?? "~/"); //TODO: How to ensure safety? IdentityServer requires it however it should be checked somehow! } protected virtual async Task CheckSelfRegistrationAsync() diff --git a/modules/account/src/Volo.Abp.Account.Web/Volo.Abp.Account.Web.csproj b/modules/account/src/Volo.Abp.Account.Web/Volo.Abp.Account.Web.csproj index 09e4b2cc6f..a77cbad333 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Volo.Abp.Account.Web.csproj +++ b/modules/account/src/Volo.Abp.Account.Web/Volo.Abp.Account.Web.csproj @@ -17,22 +17,18 @@ - - - - - + diff --git a/modules/account/test/Volo.Abp.Account.Application.Tests/Volo.Abp.Account.Application.Tests.csproj b/modules/account/test/Volo.Abp.Account.Application.Tests/Volo.Abp.Account.Application.Tests.csproj index e98baaf3ab..f4c7094d4f 100644 --- a/modules/account/test/Volo.Abp.Account.Application.Tests/Volo.Abp.Account.Application.Tests.csproj +++ b/modules/account/test/Volo.Abp.Account.Application.Tests/Volo.Abp.Account.Application.Tests.csproj @@ -7,9 +7,7 @@ - - - + @@ -21,6 +19,7 @@ + diff --git a/modules/account/test/Volo.Abp.Account.Application.Tests/Volo/Abp/Account/AbpAccountApplicationTestModule.cs b/modules/account/test/Volo.Abp.Account.Application.Tests/Volo/Abp/Account/AbpAccountApplicationTestModule.cs index 1adac8a6ef..94baf4a03d 100644 --- a/modules/account/test/Volo.Abp.Account.Application.Tests/Volo/Abp/Account/AbpAccountApplicationTestModule.cs +++ b/modules/account/test/Volo.Abp.Account.Application.Tests/Volo/Abp/Account/AbpAccountApplicationTestModule.cs @@ -5,6 +5,7 @@ using Microsoft.EntityFrameworkCore.Storage; using Volo.Abp.Authorization; using Volo.Abp.Autofac; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore.Sqlite; using Volo.Abp.Identity.AspNetCore; using Volo.Abp.Identity.EntityFrameworkCore; using Volo.Abp.Modularity; @@ -19,7 +20,8 @@ namespace Volo.Abp.Account typeof(AbpIdentityAspNetCoreModule), typeof(AbpAccountApplicationModule), typeof(AbpIdentityEntityFrameworkCoreModule), - typeof(AbpPermissionManagementEntityFrameworkCoreModule) + typeof(AbpPermissionManagementEntityFrameworkCoreModule), + typeof(AbpEntityFrameworkCoreSqliteModule) )] public class AbpAccountApplicationTestModule : AbpModule { diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/AuditLogConsts.cs b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/AuditLogConsts.cs index 1a592fc8b8..504e7451e0 100644 --- a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/AuditLogConsts.cs +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain.Shared/Volo/Abp/AuditLogging/AuditLogConsts.cs @@ -16,6 +16,9 @@ public const int MaxExceptionsLength = 4000; + //TODO: Replace with MaxExceptionsLength in v3.0 + public static int MaxExceptionsLengthValue { get; set; } = MaxExceptionsLength; + public const int MaxCommentsLength = 256; public const int MaxUrlLength = 256; diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/AuditLog.cs b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/AuditLog.cs index 752ccb953c..63dbd4802b 100644 --- a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/AuditLog.cs +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/AuditLog.cs @@ -99,7 +99,7 @@ namespace Volo.Abp.AuditLogging Exceptions = auditInfo .Exceptions? .JoinAsString(Environment.NewLine) - .Truncate(AuditLogConsts.MaxExceptionsLength); + .Truncate(AuditLogConsts.MaxExceptionsLengthValue); Comments = auditInfo .Comments? diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.EntityFrameworkCore/Volo/Abp/AuditLogging/EntityFrameworkCore/AbpAuditLoggingDbContextModelBuilderExtensions.cs b/modules/audit-logging/src/Volo.Abp.AuditLogging.EntityFrameworkCore/Volo/Abp/AuditLogging/EntityFrameworkCore/AbpAuditLoggingDbContextModelBuilderExtensions.cs index 619eed057c..b44242e511 100644 --- a/modules/audit-logging/src/Volo.Abp.AuditLogging.EntityFrameworkCore/Volo/Abp/AuditLogging/EntityFrameworkCore/AbpAuditLoggingDbContextModelBuilderExtensions.cs +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.EntityFrameworkCore/Volo/Abp/AuditLogging/EntityFrameworkCore/AbpAuditLoggingDbContextModelBuilderExtensions.cs @@ -36,7 +36,10 @@ namespace Volo.Abp.AuditLogging.EntityFrameworkCore b.Property(x => x.HttpMethod).HasMaxLength(AuditLogConsts.MaxHttpMethodLength).HasColumnName(nameof(AuditLog.HttpMethod)); b.Property(x => x.Url).HasMaxLength(AuditLogConsts.MaxUrlLength).HasColumnName(nameof(AuditLog.Url)); b.Property(x => x.HttpStatusCode).HasColumnName(nameof(AuditLog.HttpStatusCode)); - b.Property(x => x.Exceptions).HasMaxLength(AuditLogConsts.MaxExceptionsLength).HasColumnName(nameof(AuditLog.Exceptions)); + + if (builder.IsUsingOracle()) { AuditLogConsts.MaxExceptionsLengthValue = 2000; } + b.Property(x => x.Exceptions).HasMaxLength(AuditLogConsts.MaxExceptionsLengthValue).HasColumnName(nameof(AuditLog.Exceptions)); + b.Property(x => x.Comments).HasMaxLength(AuditLogConsts.MaxCommentsLength).HasColumnName(nameof(AuditLog.Comments)); b.Property(x => x.ExecutionDuration).HasColumnName(nameof(AuditLog.ExecutionDuration)); b.Property(x => x.ImpersonatorTenantId).HasColumnName(nameof(AuditLog.ImpersonatorTenantId)); diff --git a/modules/audit-logging/test/Volo.Abp.AuditLogging.EntityFrameworkCore.Tests/Volo.Abp.AuditLogging.EntityFrameworkCore.Tests.csproj b/modules/audit-logging/test/Volo.Abp.AuditLogging.EntityFrameworkCore.Tests/Volo.Abp.AuditLogging.EntityFrameworkCore.Tests.csproj index 8868a88e37..c85e570040 100644 --- a/modules/audit-logging/test/Volo.Abp.AuditLogging.EntityFrameworkCore.Tests/Volo.Abp.AuditLogging.EntityFrameworkCore.Tests.csproj +++ b/modules/audit-logging/test/Volo.Abp.AuditLogging.EntityFrameworkCore.Tests/Volo.Abp.AuditLogging.EntityFrameworkCore.Tests.csproj @@ -10,12 +10,12 @@ + - diff --git a/modules/audit-logging/test/Volo.Abp.AuditLogging.EntityFrameworkCore.Tests/Volo/Abp/AuditLogging/EntityFrameworkCore/AbpAuditLoggingEntityFrameworkCoreTestModule.cs b/modules/audit-logging/test/Volo.Abp.AuditLogging.EntityFrameworkCore.Tests/Volo/Abp/AuditLogging/EntityFrameworkCore/AbpAuditLoggingEntityFrameworkCoreTestModule.cs index 442aa50c8e..0883f5f123 100644 --- a/modules/audit-logging/test/Volo.Abp.AuditLogging.EntityFrameworkCore.Tests/Volo/Abp/AuditLogging/EntityFrameworkCore/AbpAuditLoggingEntityFrameworkCoreTestModule.cs +++ b/modules/audit-logging/test/Volo.Abp.AuditLogging.EntityFrameworkCore.Tests/Volo/Abp/AuditLogging/EntityFrameworkCore/AbpAuditLoggingEntityFrameworkCoreTestModule.cs @@ -4,13 +4,15 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore.Sqlite; using Volo.Abp.Modularity; namespace Volo.Abp.AuditLogging.EntityFrameworkCore { [DependsOn( typeof(AbpAuditLoggingTestBaseModule), - typeof(AbpAuditLoggingEntityFrameworkCoreModule) + typeof(AbpAuditLoggingEntityFrameworkCoreModule), + typeof(AbpEntityFrameworkCoreSqliteModule) )] public class AbpAuditLoggingEntityFrameworkCoreTestModule : AbpModule { @@ -20,9 +22,9 @@ namespace Volo.Abp.AuditLogging.EntityFrameworkCore Configure(options => { - options.Configure(abpDbContextConfigurationContext => + options.Configure(ctx => { - abpDbContextConfigurationContext.DbContextOptions.UseSqlite(sqliteConnection); + ctx.DbContextOptions.UseSqlite(sqliteConnection); }); }); } diff --git a/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/MongoDbFixture.cs b/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/MongoDbFixture.cs index 33803c1af4..e217faf961 100644 --- a/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/MongoDbFixture.cs +++ b/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/MongoDbFixture.cs @@ -5,12 +5,18 @@ namespace Volo.Abp.AuditLogging.MongoDB { public class MongoDbFixture : IDisposable { - private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); - public static readonly string ConnectionString = MongoDbRunner.ConnectionString; - + private static readonly MongoDbRunner MongoDbRunner; + public static readonly string ConnectionString; + + static MongoDbFixture() + { + MongoDbRunner = MongoDbRunner.Start(); + ConnectionString = MongoDbRunner.ConnectionString; + } + public void Dispose() { MongoDbRunner?.Dispose(); } } -} \ No newline at end of file +} diff --git a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Volo.Abp.BackgroundJobs.DemoApp.csproj b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Volo.Abp.BackgroundJobs.DemoApp.csproj index 062db7437c..e6f1a834ec 100644 --- a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Volo.Abp.BackgroundJobs.DemoApp.csproj +++ b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp/Volo.Abp.BackgroundJobs.DemoApp.csproj @@ -6,7 +6,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/modules/background-jobs/test/Volo.Abp.BackgroundJobs.EntityFrameworkCore.Tests/Volo.Abp.BackgroundJobs.EntityFrameworkCore.Tests.csproj b/modules/background-jobs/test/Volo.Abp.BackgroundJobs.EntityFrameworkCore.Tests/Volo.Abp.BackgroundJobs.EntityFrameworkCore.Tests.csproj index e37ab4380f..e419b3302a 100644 --- a/modules/background-jobs/test/Volo.Abp.BackgroundJobs.EntityFrameworkCore.Tests/Volo.Abp.BackgroundJobs.EntityFrameworkCore.Tests.csproj +++ b/modules/background-jobs/test/Volo.Abp.BackgroundJobs.EntityFrameworkCore.Tests/Volo.Abp.BackgroundJobs.EntityFrameworkCore.Tests.csproj @@ -10,13 +10,12 @@ + - - - + diff --git a/modules/background-jobs/test/Volo.Abp.BackgroundJobs.EntityFrameworkCore.Tests/Volo/Abp/BackgroundJobs/EntityFrameworkCore/AbpBackgroundJobsEntityFrameworkCoreTestModule.cs b/modules/background-jobs/test/Volo.Abp.BackgroundJobs.EntityFrameworkCore.Tests/Volo/Abp/BackgroundJobs/EntityFrameworkCore/AbpBackgroundJobsEntityFrameworkCoreTestModule.cs index 65ed1194cf..042bbf00a3 100644 --- a/modules/background-jobs/test/Volo.Abp.BackgroundJobs.EntityFrameworkCore.Tests/Volo/Abp/BackgroundJobs/EntityFrameworkCore/AbpBackgroundJobsEntityFrameworkCoreTestModule.cs +++ b/modules/background-jobs/test/Volo.Abp.BackgroundJobs.EntityFrameworkCore.Tests/Volo/Abp/BackgroundJobs/EntityFrameworkCore/AbpBackgroundJobsEntityFrameworkCoreTestModule.cs @@ -4,13 +4,15 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore.Sqlite; using Volo.Abp.Modularity; namespace Volo.Abp.BackgroundJobs.EntityFrameworkCore { [DependsOn( typeof(AbpBackgroundJobsTestBaseModule), - typeof(AbpBackgroundJobsEntityFrameworkCoreModule) + typeof(AbpBackgroundJobsEntityFrameworkCoreModule), + typeof(AbpEntityFrameworkCoreSqliteModule) )] public class AbpBackgroundJobsEntityFrameworkCoreTestModule : AbpModule { diff --git a/modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo/Abp/BackgroundJobs/MongoDB/MongoDbFixture.cs b/modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo/Abp/BackgroundJobs/MongoDB/MongoDbFixture.cs index 3ae1d8fc10..c358bed276 100644 --- a/modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo/Abp/BackgroundJobs/MongoDB/MongoDbFixture.cs +++ b/modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo/Abp/BackgroundJobs/MongoDB/MongoDbFixture.cs @@ -5,12 +5,18 @@ namespace Volo.Abp.BackgroundJobs.MongoDB { public class MongoDbFixture : IDisposable { - private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); - public static readonly string ConnectionString = MongoDbRunner.ConnectionString; + private static readonly MongoDbRunner MongoDbRunner; + public static readonly string ConnectionString; + + static MongoDbFixture() + { + MongoDbRunner = MongoDbRunner.Start(); + ConnectionString = MongoDbRunner.ConnectionString; + } public void Dispose() { MongoDbRunner?.Dispose(); } } -} \ No newline at end of file +} diff --git a/modules/blob-storing-database/Volo.Abp.BlobStoring.Database.sln b/modules/blob-storing-database/Volo.Abp.BlobStoring.Database.sln new file mode 100644 index 0000000000..e33e90fa86 --- /dev/null +++ b/modules/blob-storing-database/Volo.Abp.BlobStoring.Database.sln @@ -0,0 +1,90 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29001.49 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.BlobStoring.Database.Domain.Shared", "src\Volo.Abp.BlobStoring.Database.Domain.Shared\Volo.Abp.BlobStoring.Database.Domain.Shared.csproj", "{D64C1577-4929-4B60-939E-96DE1534891A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.BlobStoring.Database.Domain", "src\Volo.Abp.BlobStoring.Database.Domain\Volo.Abp.BlobStoring.Database.Domain.csproj", "{F2840BC7-0188-4606-9126-DADD0F5ABF7A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{649A3FFA-182F-4E56-9717-E6A9A2BEC545}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.BlobStoring.Database.EntityFrameworkCore", "src\Volo.Abp.BlobStoring.Database.EntityFrameworkCore\Volo.Abp.BlobStoring.Database.EntityFrameworkCore.csproj", "{0CE86223-D31D-4315-A1F5-87BA3EE1B844}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.BlobStoring.Database.MongoDB", "src\Volo.Abp.BlobStoring.Database.MongoDB\Volo.Abp.BlobStoring.Database.MongoDB.csproj", "{F1C58097-4C08-4D88-8976-6B3389391481}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.BlobStoring.Database.TestBase", "test\Volo.Abp.BlobStoring.Database.TestBase\Volo.Abp.BlobStoring.Database.TestBase.csproj", "{C5BB573D-3030-4BCB-88B7-F6A85C32766C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.BlobStoring.Database.EntityFrameworkCore.Tests", "test\Volo.Abp.BlobStoring.Database.EntityFrameworkCore.Tests\Volo.Abp.BlobStoring.Database.EntityFrameworkCore.Tests.csproj", "{527F645C-C1FC-406E-8479-81386C8ECF13}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.BlobStoring.Database.MongoDB.Tests", "test\Volo.Abp.BlobStoring.Database.MongoDB.Tests\Volo.Abp.BlobStoring.Database.MongoDB.Tests.csproj", "{D0AD9179-125C-40B2-A8EE-CD4C1EE24BB6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.BlobStoring.Database.Domain.Tests", "test\Volo.Abp.BlobStoring.Database.Domain.Tests\Volo.Abp.BlobStoring.Database.Domain.Tests.csproj", "{E60895E5-79C4-447D-88B7-85CB5BA336A4}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "host", "host", "{9E416AD3-1EC8-439A-9CFD-AAFFA89E2AEB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlobStoring.Database.Host.ConsoleApp.ConsoleApp", "host\BlobStoring.Database.Host.ConsoleApp\src\BlobStoring.Database.Host.ConsoleApp.ConsoleApp\BlobStoring.Database.Host.ConsoleApp.ConsoleApp.csproj", "{C3C4CB94-4F73-4D04-9076-8D98D5C6B475}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D64C1577-4929-4B60-939E-96DE1534891A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D64C1577-4929-4B60-939E-96DE1534891A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D64C1577-4929-4B60-939E-96DE1534891A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D64C1577-4929-4B60-939E-96DE1534891A}.Release|Any CPU.Build.0 = Release|Any CPU + {F2840BC7-0188-4606-9126-DADD0F5ABF7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F2840BC7-0188-4606-9126-DADD0F5ABF7A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F2840BC7-0188-4606-9126-DADD0F5ABF7A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F2840BC7-0188-4606-9126-DADD0F5ABF7A}.Release|Any CPU.Build.0 = Release|Any CPU + {0CE86223-D31D-4315-A1F5-87BA3EE1B844}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0CE86223-D31D-4315-A1F5-87BA3EE1B844}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0CE86223-D31D-4315-A1F5-87BA3EE1B844}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0CE86223-D31D-4315-A1F5-87BA3EE1B844}.Release|Any CPU.Build.0 = Release|Any CPU + {F1C58097-4C08-4D88-8976-6B3389391481}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1C58097-4C08-4D88-8976-6B3389391481}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1C58097-4C08-4D88-8976-6B3389391481}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1C58097-4C08-4D88-8976-6B3389391481}.Release|Any CPU.Build.0 = Release|Any CPU + {C5BB573D-3030-4BCB-88B7-F6A85C32766C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C5BB573D-3030-4BCB-88B7-F6A85C32766C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C5BB573D-3030-4BCB-88B7-F6A85C32766C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C5BB573D-3030-4BCB-88B7-F6A85C32766C}.Release|Any CPU.Build.0 = Release|Any CPU + {527F645C-C1FC-406E-8479-81386C8ECF13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {527F645C-C1FC-406E-8479-81386C8ECF13}.Debug|Any CPU.Build.0 = Debug|Any CPU + {527F645C-C1FC-406E-8479-81386C8ECF13}.Release|Any CPU.ActiveCfg = Release|Any CPU + {527F645C-C1FC-406E-8479-81386C8ECF13}.Release|Any CPU.Build.0 = Release|Any CPU + {D0AD9179-125C-40B2-A8EE-CD4C1EE24BB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D0AD9179-125C-40B2-A8EE-CD4C1EE24BB6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D0AD9179-125C-40B2-A8EE-CD4C1EE24BB6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D0AD9179-125C-40B2-A8EE-CD4C1EE24BB6}.Release|Any CPU.Build.0 = Release|Any CPU + {E60895E5-79C4-447D-88B7-85CB5BA336A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E60895E5-79C4-447D-88B7-85CB5BA336A4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E60895E5-79C4-447D-88B7-85CB5BA336A4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E60895E5-79C4-447D-88B7-85CB5BA336A4}.Release|Any CPU.Build.0 = Release|Any CPU + {C3C4CB94-4F73-4D04-9076-8D98D5C6B475}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C3C4CB94-4F73-4D04-9076-8D98D5C6B475}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C3C4CB94-4F73-4D04-9076-8D98D5C6B475}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C3C4CB94-4F73-4D04-9076-8D98D5C6B475}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {D64C1577-4929-4B60-939E-96DE1534891A} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545} + {F2840BC7-0188-4606-9126-DADD0F5ABF7A} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545} + {0CE86223-D31D-4315-A1F5-87BA3EE1B844} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545} + {F1C58097-4C08-4D88-8976-6B3389391481} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545} + {C5BB573D-3030-4BCB-88B7-F6A85C32766C} = {CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D} + {527F645C-C1FC-406E-8479-81386C8ECF13} = {CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D} + {D0AD9179-125C-40B2-A8EE-CD4C1EE24BB6} = {CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D} + {E60895E5-79C4-447D-88B7-85CB5BA336A4} = {CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D} + {C3C4CB94-4F73-4D04-9076-8D98D5C6B475} = {9E416AD3-1EC8-439A-9CFD-AAFFA89E2AEB} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {4324B3B4-B60B-4E3C-91D8-59576B4E26DD} + EndGlobalSection +EndGlobal diff --git a/modules/blob-storing-database/Volo.Abp.BlobStoring.Database.sln.DotSettings b/modules/blob-storing-database/Volo.Abp.BlobStoring.Database.sln.DotSettings new file mode 100644 index 0000000000..cb0b2c919f --- /dev/null +++ b/modules/blob-storing-database/Volo.Abp.BlobStoring.Database.sln.DotSettings @@ -0,0 +1,23 @@ + + True + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + Required + Required + Required + Required + False + True + False + False + True + False + False + SQL + \ No newline at end of file diff --git a/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/BlobStoring.Database.Host.ConsoleApp.sln b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/BlobStoring.Database.Host.ConsoleApp.sln new file mode 100644 index 0000000000..250233c828 --- /dev/null +++ b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/BlobStoring.Database.Host.ConsoleApp.sln @@ -0,0 +1,21 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlobStoring.Database.Host.ConsoleApp.ConsoleApp", "src\BlobStoring.Database.Host.ConsoleApp.ConsoleApp\BlobStoring.Database.Host.ConsoleApp.ConsoleApp.csproj", "{00A2F7A3-BEC3-48F4-A91C-5A336C32A5D2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E8067AED-2B6E-4134-AAF8-9101457D709A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {00A2F7A3-BEC3-48F4-A91C-5A336C32A5D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {00A2F7A3-BEC3-48F4-A91C-5A336C32A5D2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {00A2F7A3-BEC3-48F4-A91C-5A336C32A5D2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {00A2F7A3-BEC3-48F4-A91C-5A336C32A5D2}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {00A2F7A3-BEC3-48F4-A91C-5A336C32A5D2} = {E8067AED-2B6E-4134-AAF8-9101457D709A} + EndGlobalSection +EndGlobal diff --git a/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/BlobService.cs b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/BlobService.cs new file mode 100644 index 0000000000..58924871d1 --- /dev/null +++ b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/BlobService.cs @@ -0,0 +1,23 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.BlobStoring; +using Volo.Abp.DependencyInjection; + +namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp +{ + public class BlobService : IBlobService, ITransientDependency + { + private readonly IBlobContainer _container; + + public BlobService(IBlobContainer container) + { + _container = container; + } + + public async Task SaveFile(string fileName = "File Name", string fileContent = "File Content") + { + await _container.SaveAsync(fileName, fileContent.GetBytes(), true); + Console.WriteLine($"File: {fileName} is successfully saved"); + } + } +} diff --git a/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/BlobStoring.Database.Host.ConsoleApp.ConsoleApp.csproj b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/BlobStoring.Database.Host.ConsoleApp.ConsoleApp.csproj new file mode 100644 index 0000000000..61af5fc4ce --- /dev/null +++ b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/BlobStoring.Database.Host.ConsoleApp.ConsoleApp.csproj @@ -0,0 +1,32 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/ConsoleAppConsoleAppHostedService.cs b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/ConsoleAppConsoleAppHostedService.cs new file mode 100644 index 0000000000..06336ad75a --- /dev/null +++ b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/ConsoleAppConsoleAppHostedService.cs @@ -0,0 +1,32 @@ +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Serilog; +using Volo.Abp; + +namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp +{ + public class ConsoleAppConsoleAppHostedService : IHostedService + { + public async Task StartAsync(CancellationToken cancellationToken) + { + using (var application = AbpApplicationFactory.Create(options => + { + options.UseAutofac(); + options.Services.AddLogging(c => c.AddSerilog()); + })) + { + application.Initialize(); + + var blobService = application.ServiceProvider.GetService(); + + await blobService.SaveFile("Test File 2", "Test Content 2"); + + application.Shutdown(); + } + } + + public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; + } +} diff --git a/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/ConsoleAppConsoleAppModule.cs b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/ConsoleAppConsoleAppModule.cs new file mode 100644 index 0000000000..5a6691c439 --- /dev/null +++ b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/ConsoleAppConsoleAppModule.cs @@ -0,0 +1,56 @@ +using BlobStoring.Database.Host.ConsoleApp.ConsoleApp.EfCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Autofac; +using Volo.Abp.BlobStoring; +using Volo.Abp.BlobStoring.Database; +using Volo.Abp.BlobStoring.Database.EntityFrameworkCore; +using Volo.Abp.Data; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore.SqlServer; +using Volo.Abp.Modularity; + +namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp +{ + + [DependsOn( + typeof(AbpAutofacModule), + typeof(AbpEntityFrameworkCoreSqlServerModule), + typeof(BlobStoringDatabaseEntityFrameworkCoreModule) + )] + public class ConsoleAppConsoleAppModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + ConfigureEntityFramework(context); + + context.Services.AddSingleton(); + + Configure(options => + { + options.Containers.ConfigureDefault(container => + { + container.ProviderType = typeof(DatabaseBlobProvider); + }); + }); + } + + private void ConfigureEntityFramework(ServiceConfigurationContext context) + { + Configure(options => + { + options.ConnectionStrings.Default = "Server=localhost;Database=BlobStoring_Host;Trusted_Connection=True;MultipleActiveResultSets=true"; + }); + + context.Services.AddAbpDbContext(options => + { + options.AddDefaultRepositories(true); + }); + + Configure(x => + { + x.UseSqlServer(); + }); + } + } +} diff --git a/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/EfCore/BlobStoringHostDbContext.cs b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/EfCore/BlobStoringHostDbContext.cs new file mode 100644 index 0000000000..f7ffc72d43 --- /dev/null +++ b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/EfCore/BlobStoringHostDbContext.cs @@ -0,0 +1,20 @@ +using Microsoft.EntityFrameworkCore; +using Volo.Abp.BlobStoring.Database.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; + +namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.EfCore +{ + public class BlobStoringHostDbContext : AbpDbContext + { + public BlobStoringHostDbContext(DbContextOptions options) : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.ConfigureBlobStoring(); + } + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/EfCore/BlobStoringHostDbContextFactory.cs b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/EfCore/BlobStoringHostDbContextFactory.cs new file mode 100644 index 0000000000..e5c482f49f --- /dev/null +++ b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/EfCore/BlobStoringHostDbContextFactory.cs @@ -0,0 +1,29 @@ +using System.IO; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.Extensions.Configuration; + +namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.EfCore +{ + public class BlobStoringHostDbContextFactory : IDesignTimeDbContextFactory + { + public BlobStoringHostDbContext CreateDbContext(string[] args) + { + var configuration = BuildConfiguration(); + + var builder = new DbContextOptionsBuilder() + .UseSqlServer(configuration.GetConnectionString("Default")); + + return new BlobStoringHostDbContext(builder.Options); + } + + private static IConfigurationRoot BuildConfiguration() + { + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false); + + return builder.Build(); + } + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/EfCore/BlobStoringHostDbSchemaMigrator.cs b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/EfCore/BlobStoringHostDbSchemaMigrator.cs new file mode 100644 index 0000000000..6494eb43c7 --- /dev/null +++ b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/EfCore/BlobStoringHostDbSchemaMigrator.cs @@ -0,0 +1,26 @@ +using System; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.DependencyInjection; + +namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.EfCore +{ + public class BlobStoringHostDbSchemaMigrator : IBlobStoringHostDbSchemaMigrator, ITransientDependency + { + private readonly IServiceProvider _serviceProvider; + + public BlobStoringHostDbSchemaMigrator(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + public async Task MigrateAsync() + { + await _serviceProvider + .GetRequiredService() + .Database + .MigrateAsync(); + } + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/EfCore/IBlobStoringHostDbSchemaMigrator.cs b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/EfCore/IBlobStoringHostDbSchemaMigrator.cs new file mode 100644 index 0000000000..27e14557b6 --- /dev/null +++ b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/EfCore/IBlobStoringHostDbSchemaMigrator.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.EfCore +{ + public interface IBlobStoringHostDbSchemaMigrator + { + Task MigrateAsync(); + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/IBlobService.cs b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/IBlobService.cs new file mode 100644 index 0000000000..32daf0b0fc --- /dev/null +++ b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/IBlobService.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp +{ + public interface IBlobService + { + Task SaveFile(string fileName = "File Name", string fileContent = "File Content"); + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200531134550_Initial.Designer.cs b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200531134550_Initial.Designer.cs new file mode 100644 index 0000000000..23fe326a47 --- /dev/null +++ b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200531134550_Initial.Designer.cs @@ -0,0 +1,97 @@ +// +using System; +using BlobStoring.Database.Host.ConsoleApp.ConsoleApp.EfCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Volo.Abp.EntityFrameworkCore; + +namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.Migrations +{ + [DbContext(typeof(BlobStoringHostDbContext))] + [Migration("20200531134550_Initial")] + partial class Initial + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) + .HasAnnotation("ProductVersion", "3.1.4") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlob", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("ContainerId") + .HasColumnType("uniqueidentifier"); + + b.Property("Content") + .HasColumnType("varbinary(max)") + .HasMaxLength(2147483647); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.Property("TenantId") + .HasColumnName("TenantId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ContainerId", "Name"); + + b.ToTable("AbpBlobs"); + }); + + modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlobContainer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(128)") + .HasMaxLength(128); + + b.Property("TenantId") + .HasColumnName("TenantId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name"); + + b.ToTable("AbpBlobContainers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200531134550_Initial.cs b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200531134550_Initial.cs new file mode 100644 index 0000000000..d1ee75ac7f --- /dev/null +++ b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200531134550_Initial.cs @@ -0,0 +1,62 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.Migrations +{ + public partial class Initial : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AbpBlobContainers", + columns: table => new + { + Id = table.Column(nullable: false), + ExtraProperties = table.Column(nullable: true), + ConcurrencyStamp = table.Column(nullable: true), + TenantId = table.Column(nullable: true), + Name = table.Column(maxLength: 128, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpBlobContainers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpBlobs", + columns: table => new + { + Id = table.Column(nullable: false), + ExtraProperties = table.Column(nullable: true), + ConcurrencyStamp = table.Column(nullable: true), + ContainerId = table.Column(nullable: false), + TenantId = table.Column(nullable: true), + Name = table.Column(maxLength: 256, nullable: false), + Content = table.Column(maxLength: 2147483647, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpBlobs", x => x.Id); + }); + + migrationBuilder.CreateIndex( + name: "IX_AbpBlobContainers_TenantId_Name", + table: "AbpBlobContainers", + columns: new[] { "TenantId", "Name" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpBlobs_TenantId_ContainerId_Name", + table: "AbpBlobs", + columns: new[] { "TenantId", "ContainerId", "Name" }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AbpBlobContainers"); + + migrationBuilder.DropTable( + name: "AbpBlobs"); + } + } +} diff --git a/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200531143839_FK_Added.Designer.cs b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200531143839_FK_Added.Designer.cs new file mode 100644 index 0000000000..1214d7f500 --- /dev/null +++ b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200531143839_FK_Added.Designer.cs @@ -0,0 +1,108 @@ +// +using System; +using BlobStoring.Database.Host.ConsoleApp.ConsoleApp.EfCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Volo.Abp.EntityFrameworkCore; + +namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.Migrations +{ + [DbContext(typeof(BlobStoringHostDbContext))] + [Migration("20200531143839_FK_Added")] + partial class FK_Added + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) + .HasAnnotation("ProductVersion", "3.1.4") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlob", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("ContainerId") + .HasColumnType("uniqueidentifier"); + + b.Property("Content") + .HasColumnType("varbinary(max)") + .HasMaxLength(2147483647); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.Property("TenantId") + .HasColumnName("TenantId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("ContainerId"); + + b.HasIndex("TenantId", "ContainerId", "Name"); + + b.ToTable("AbpBlobs"); + }); + + modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlobContainer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(128)") + .HasMaxLength(128); + + b.Property("TenantId") + .HasColumnName("TenantId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name"); + + b.ToTable("AbpBlobContainers"); + }); + + modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlob", b => + { + b.HasOne("Volo.Abp.BlobStoring.Database.DatabaseBlobContainer", null) + .WithMany() + .HasForeignKey("ContainerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200531143839_FK_Added.cs b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200531143839_FK_Added.cs new file mode 100644 index 0000000000..4411063f94 --- /dev/null +++ b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/20200531143839_FK_Added.cs @@ -0,0 +1,34 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.Migrations +{ + public partial class FK_Added : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateIndex( + name: "IX_AbpBlobs_ContainerId", + table: "AbpBlobs", + column: "ContainerId"); + + migrationBuilder.AddForeignKey( + name: "FK_AbpBlobs_AbpBlobContainers_ContainerId", + table: "AbpBlobs", + column: "ContainerId", + principalTable: "AbpBlobContainers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_AbpBlobs_AbpBlobContainers_ContainerId", + table: "AbpBlobs"); + + migrationBuilder.DropIndex( + name: "IX_AbpBlobs_ContainerId", + table: "AbpBlobs"); + } + } +} diff --git a/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/BlobStoringHostDbContextModelSnapshot.cs b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/BlobStoringHostDbContextModelSnapshot.cs new file mode 100644 index 0000000000..278eb1f72c --- /dev/null +++ b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Migrations/BlobStoringHostDbContextModelSnapshot.cs @@ -0,0 +1,106 @@ +// +using System; +using BlobStoring.Database.Host.ConsoleApp.ConsoleApp.EfCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Volo.Abp.EntityFrameworkCore; + +namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp.Migrations +{ + [DbContext(typeof(BlobStoringHostDbContext))] + partial class BlobStoringHostDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) + .HasAnnotation("ProductVersion", "3.1.4") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlob", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("ContainerId") + .HasColumnType("uniqueidentifier"); + + b.Property("Content") + .HasColumnType("varbinary(max)") + .HasMaxLength(2147483647); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.Property("TenantId") + .HasColumnName("TenantId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("ContainerId"); + + b.HasIndex("TenantId", "ContainerId", "Name"); + + b.ToTable("AbpBlobs"); + }); + + modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlobContainer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(128)") + .HasMaxLength(128); + + b.Property("TenantId") + .HasColumnName("TenantId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name"); + + b.ToTable("AbpBlobContainers"); + }); + + modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlob", b => + { + b.HasOne("Volo.Abp.BlobStoring.Database.DatabaseBlobContainer", null) + .WithMany() + .HasForeignKey("ContainerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/ProfilePictureContainer.cs b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/ProfilePictureContainer.cs new file mode 100644 index 0000000000..dd7a683ba5 --- /dev/null +++ b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/ProfilePictureContainer.cs @@ -0,0 +1,7 @@ +namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp +{ + public class ProfilePictureContainer + { + + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Program.cs b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Program.cs new file mode 100644 index 0000000000..3ae0be0e44 --- /dev/null +++ b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/Program.cs @@ -0,0 +1,51 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Serilog; +using Serilog.Events; + +namespace BlobStoring.Database.Host.ConsoleApp.ConsoleApp +{ + public class Program + { + public static async Task Main(string[] args) + { + Log.Logger = new LoggerConfiguration() +#if DEBUG + .MinimumLevel.Debug() +#else + .MinimumLevel.Information() +#endif + .MinimumLevel.Override("Microsoft", LogEventLevel.Information) + .Enrich.FromLogContext() + .WriteTo.Async(c => c.File("Logs/logs.txt")) + .WriteTo.Console() + .CreateLogger(); + + try + { + Log.Information("Starting console host."); + await CreateHostBuilder(args).RunConsoleAsync(); + return 0; + } + catch (Exception ex) + { + Log.Fatal(ex, "Host terminated unexpectedly!"); + return 1; + } + finally + { + Log.CloseAndFlush(); + } + + } + + internal static IHostBuilder CreateHostBuilder(string[] args) => + Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder(args) + .ConfigureServices((hostContext, services) => + { + services.AddHostedService(); + }); + } +} diff --git a/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/appsettings.json b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/appsettings.json new file mode 100644 index 0000000000..2e1ff4f771 --- /dev/null +++ b/modules/blob-storing-database/host/BlobStoring.Database.Host.ConsoleApp/src/BlobStoring.Database.Host.ConsoleApp.ConsoleApp/appsettings.json @@ -0,0 +1,5 @@ +{ + "ConnectionStrings": { + "Default": "Server=localhost;Database=BlobStoring_Host;Trusted_Connection=True;MultipleActiveResultSets=true" + } +} diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/FodyWeavers.xml b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/FodyWeavers.xml new file mode 100644 index 0000000000..be0de3a908 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/FodyWeavers.xsd b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/FodyWeavers.xsd new file mode 100644 index 0000000000..3f3946e282 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo.Abp.BlobStoring.Database.Domain.Shared.csproj b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo.Abp.BlobStoring.Database.Domain.Shared.csproj new file mode 100644 index 0000000000..d7f9289c12 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo.Abp.BlobStoring.Database.Domain.Shared.csproj @@ -0,0 +1,20 @@ + + + + + + + netstandard2.0 + + + + + + + + + + + + + diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/BlobStoringDatabaseDomainSharedModule.cs b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/BlobStoringDatabaseDomainSharedModule.cs new file mode 100644 index 0000000000..836c6f5a15 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/BlobStoringDatabaseDomainSharedModule.cs @@ -0,0 +1,37 @@ +using Volo.Abp.Modularity; +using Volo.Abp.Localization; +using Volo.Abp.BlobStoring.Database.Localization; +using Volo.Abp.Localization.ExceptionHandling; +using Volo.Abp.Validation; +using Volo.Abp.Validation.Localization; +using Volo.Abp.VirtualFileSystem; + +namespace Volo.Abp.BlobStoring.Database +{ + [DependsOn( + typeof(AbpValidationModule) + )] + public class BlobStoringDatabaseDomainSharedModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.FileSets.AddEmbedded(); + }); + + Configure(options => + { + options.Resources + .Add("en") + .AddBaseTypes(typeof(AbpValidationResource)) + .AddVirtualJson("/Volo/Abp/BlobStoring/Database/Localization"); + }); + + Configure(options => + { + options.MapCodeNamespace("BlobStoringDatabase", typeof(BlobStoringDatabaseResource)); + }); + } + } +} diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/BlobStoringDatabaseErrorCodes.cs b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/BlobStoringDatabaseErrorCodes.cs new file mode 100644 index 0000000000..fe066e74e5 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/BlobStoringDatabaseErrorCodes.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.BlobStoring.Database +{ + public static class BlobStoringDatabaseErrorCodes + { + //Add your business exception error codes here... + } +} diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/DatabaseBlobConsts.cs b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/DatabaseBlobConsts.cs new file mode 100644 index 0000000000..c2cd33f098 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/DatabaseBlobConsts.cs @@ -0,0 +1,15 @@ +namespace Volo.Abp.BlobStoring.Database +{ + public static class DatabaseBlobConsts + { + /// + /// Default value: 256. + /// + public static int MaxNameLength { get; set; } = 256; + + /// + /// Default value: (2GB). + /// + public static int MaxContentLength { get; set; } = int.MaxValue; + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/DatabaseContainerConsts.cs b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/DatabaseContainerConsts.cs new file mode 100644 index 0000000000..0a71451f9f --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/DatabaseContainerConsts.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.BlobStoring.Database +{ + public static class DatabaseContainerConsts + { + public const int MaxNameLength = 128; + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/Localization/BlobStoringDatabaseResource.cs b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/Localization/BlobStoringDatabaseResource.cs new file mode 100644 index 0000000000..6ee2f79810 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/Localization/BlobStoringDatabaseResource.cs @@ -0,0 +1,10 @@ +using Volo.Abp.Localization; + +namespace Volo.Abp.BlobStoring.Database.Localization +{ + [LocalizationResourceName("BlobStoringDatabase")] + public class BlobStoringDatabaseResource + { + + } +} diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/Localization/cs.json b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/Localization/cs.json new file mode 100644 index 0000000000..7e0fbb38b3 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/Localization/cs.json @@ -0,0 +1,6 @@ +{ + "culture": "cs", + "texts": { + "ManageYourProfile": "Spravovat profil" + } +} diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/Localization/en.json b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/Localization/en.json new file mode 100644 index 0000000000..bb66921a1d --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/Localization/en.json @@ -0,0 +1,6 @@ +{ + "culture": "en", + "texts": { + "ManageYourProfile": "Manage your profile" + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/Localization/pl-PL.json b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/Localization/pl-PL.json new file mode 100644 index 0000000000..3ea7b190ee --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/Localization/pl-PL.json @@ -0,0 +1,6 @@ +{ + "culture": "pl-PL", + "texts": { + + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/Localization/pt-BR.json b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/Localization/pt-BR.json new file mode 100644 index 0000000000..6d746df04c --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/Localization/pt-BR.json @@ -0,0 +1,6 @@ +{ + "culture": "pt-BR", + "texts": { + + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/Localization/sl.json b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/Localization/sl.json new file mode 100644 index 0000000000..687d42579c --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/Localization/sl.json @@ -0,0 +1,6 @@ +{ + "culture": "sl", + "texts": { + "ManageYourProfile": "Upravljajte svojim profilom" + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/Localization/tr.json b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/Localization/tr.json new file mode 100644 index 0000000000..2b48193353 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/Localization/tr.json @@ -0,0 +1,6 @@ +{ + "culture": "tr", + "texts": { + "ManageYourProfile": "Profil ynetimi" + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/Localization/vi.json b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/Localization/vi.json new file mode 100644 index 0000000000..d8eb5f3c75 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/Localization/vi.json @@ -0,0 +1,6 @@ +{ + "culture": "vi", + "texts": { + + } +} diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/Localization/zh-Hans.json b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/Localization/zh-Hans.json new file mode 100644 index 0000000000..99586f01c9 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/Localization/zh-Hans.json @@ -0,0 +1,6 @@ +{ + "culture": "zh-Hans", + "texts": { + "ManageYourProfile": "管理个人资料" + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/Localization/zh-Hant.json b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/Localization/zh-Hant.json new file mode 100644 index 0000000000..ceea055597 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain.Shared/Volo/Abp/BlobStoring/Database/Localization/zh-Hant.json @@ -0,0 +1,6 @@ +{ + "culture": "zh-Hant", + "texts": { + "ManageYourProfile": "管理個人資料" + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/FodyWeavers.xml b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/FodyWeavers.xml new file mode 100644 index 0000000000..be0de3a908 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/FodyWeavers.xsd b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/FodyWeavers.xsd new file mode 100644 index 0000000000..3f3946e282 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo.Abp.BlobStoring.Database.Domain.csproj b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo.Abp.BlobStoring.Database.Domain.csproj new file mode 100644 index 0000000000..9e52acf207 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo.Abp.BlobStoring.Database.Domain.csproj @@ -0,0 +1,17 @@ + + + + + + + netstandard2.0 + + + + + + + + + + diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/BlobStoringDatabaseDbProperties.cs b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/BlobStoringDatabaseDbProperties.cs new file mode 100644 index 0000000000..b673ad853f --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/BlobStoringDatabaseDbProperties.cs @@ -0,0 +1,13 @@ +using Volo.Abp.Data; + +namespace Volo.Abp.BlobStoring.Database +{ + public static class BlobStoringDatabaseDbProperties + { + public static string DbTablePrefix { get; set; } = AbpCommonDbProperties.DbTablePrefix; + + public static string DbSchema { get; set; } = AbpCommonDbProperties.DbSchema; + + public const string ConnectionStringName = "AbpBlobStoring"; + } +} diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/BlobStoringDatabaseDomainModule.cs b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/BlobStoringDatabaseDomainModule.cs new file mode 100644 index 0000000000..e49f9ce63b --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/BlobStoringDatabaseDomainModule.cs @@ -0,0 +1,15 @@ +using Volo.Abp.Domain; +using Volo.Abp.Modularity; + +namespace Volo.Abp.BlobStoring.Database +{ + [DependsOn( + typeof(AbpDddDomainModule), + typeof(AbpBlobStoringModule), + typeof(BlobStoringDatabaseDomainSharedModule) + )] + public class BlobStoringDatabaseDomainModule : AbpModule + { + + } +} diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/DatabaseBlob.cs b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/DatabaseBlob.cs new file mode 100644 index 0000000000..2b089a8b21 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/DatabaseBlob.cs @@ -0,0 +1,44 @@ +using JetBrains.Annotations; +using System; +using Volo.Abp.Domain.Entities; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.BlobStoring.Database +{ + public class DatabaseBlob : AggregateRoot, IMultiTenant + { + public virtual Guid ContainerId { get; protected set; } + + public virtual Guid? TenantId { get; protected set; } + + public virtual string Name { get; protected set; } + + public virtual byte[] Content { get; protected set; } + + public DatabaseBlob(Guid id, Guid containerId, [NotNull] string name, [NotNull] byte[] content, Guid? tenantId = null) + : base(id) + { + Name = Check.NotNullOrWhiteSpace(name, nameof(name), DatabaseBlobConsts.MaxNameLength); + ContainerId = containerId; + Content = CheckContentLength(content); + TenantId = tenantId; + } + + public virtual void SetContent(byte[] content) + { + Content = CheckContentLength(content); + } + + protected virtual byte[] CheckContentLength(byte[] content) + { + Check.NotNull(content, nameof(content)); + + if (content.Length >= DatabaseBlobConsts.MaxContentLength) + { + throw new AbpException($"Blob content size cannot be more than {DatabaseBlobConsts.MaxContentLength} Bytes."); + } + + return content; + } + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/DatabaseBlobContainer.cs b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/DatabaseBlobContainer.cs new file mode 100644 index 0000000000..4342d9092c --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/DatabaseBlobContainer.cs @@ -0,0 +1,21 @@ +using System; +using JetBrains.Annotations; +using Volo.Abp.Domain.Entities; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.BlobStoring.Database +{ + public class DatabaseBlobContainer : AggregateRoot, IMultiTenant //TODO: Rename to BlobContainer + { + public virtual Guid? TenantId { get; protected set; } + + public virtual string Name { get; protected set; } + + public DatabaseBlobContainer(Guid id, [NotNull] string name, Guid? tenantId = null) + : base(id) + { + Name = Check.NotNullOrWhiteSpace(name, nameof(name), DatabaseContainerConsts.MaxNameLength); + TenantId = tenantId; + } + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/DatabaseBlobContainerConfigurationExtensions.cs b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/DatabaseBlobContainerConfigurationExtensions.cs new file mode 100644 index 0000000000..8ac2e057f3 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/DatabaseBlobContainerConfigurationExtensions.cs @@ -0,0 +1,12 @@ +namespace Volo.Abp.BlobStoring.Database +{ + public static class DatabaseBlobContainerConfigurationExtensions + { + public static BlobContainerConfiguration UseDatabase( + this BlobContainerConfiguration containerConfiguration) + { + containerConfiguration.ProviderType = typeof(DatabaseBlobProvider); + return containerConfiguration; + } + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/DatabaseBlobProvider.cs b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/DatabaseBlobProvider.cs new file mode 100644 index 0000000000..3edf68b053 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/DatabaseBlobProvider.cs @@ -0,0 +1,120 @@ +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Guids; + +namespace Volo.Abp.BlobStoring.Database +{ + public class DatabaseBlobProvider : BlobProviderBase, ITransientDependency + { + protected IDatabaseBlobRepository DatabaseBlobRepository { get; } + protected IDatabaseBlobContainerRepository DatabaseBlobContainerRepository { get; } + protected IGuidGenerator GuidGenerator { get; } + + public DatabaseBlobProvider( + IDatabaseBlobRepository databaseBlobRepository, + IDatabaseBlobContainerRepository databaseBlobContainerRepository, + IGuidGenerator guidGenerator) + { + DatabaseBlobRepository = databaseBlobRepository; + DatabaseBlobContainerRepository = databaseBlobContainerRepository; + GuidGenerator = guidGenerator; + } + + public override async Task SaveAsync(BlobProviderSaveArgs args) + { + var container = await GetOrCreateContainerAsync(args.ContainerName, args.CancellationToken); + + var blob = await DatabaseBlobRepository.FindAsync(container.Id, args.BlobName, + args.CancellationToken); + + var content = await args.BlobStream.GetAllBytesAsync(args.CancellationToken); + + if (blob != null) + { + if (!args.OverrideExisting) + { + throw new BlobAlreadyExistsException( + $"Saving BLOB '{args.BlobName}' does already exists in the container '{args.ContainerName}'! Set {nameof(args.OverrideExisting)} if it should be overwritten."); + } + + blob.SetContent(content); + await DatabaseBlobRepository.UpdateAsync(blob); + } + else + { + blob = new DatabaseBlob(GuidGenerator.Create(), container.Id, args.BlobName, content); + await DatabaseBlobRepository.InsertAsync(blob); + } + } + + public override async Task DeleteAsync(BlobProviderDeleteArgs args) + { + var container = + await DatabaseBlobContainerRepository.FindAsync(args.ContainerName, + args.CancellationToken); + + if (container == null) + { + return false; + } + + return await DatabaseBlobRepository.DeleteAsync(container.Id, args.BlobName, + args.CancellationToken); + } + + public override async Task ExistsAsync(BlobProviderExistsArgs args) + { + var container = + await DatabaseBlobContainerRepository.FindAsync(args.ContainerName, + args.CancellationToken); + + if (container == null) + { + return false; + } + + return await DatabaseBlobRepository.ExistsAsync(container.Id, args.BlobName, + args.CancellationToken); + } + + public override async Task GetOrNullAsync(BlobProviderGetArgs args) + { + var container = + await DatabaseBlobContainerRepository.FindAsync(args.ContainerName, + args.CancellationToken); + + if (container == null) + { + return null; + } + + var blob = await DatabaseBlobRepository.FindAsync(container.Id, args.BlobName, + args.CancellationToken); + + if (blob == null) + { + return null; + } + + return new MemoryStream(blob.Content); + } + + protected virtual async Task GetOrCreateContainerAsync( + string name, + CancellationToken cancellationToken = default) + { + var container = await DatabaseBlobContainerRepository.FindAsync(name, cancellationToken); + if (container != null) + { + return container; + } + + container = new DatabaseBlobContainer(GuidGenerator.Create(), name); + await DatabaseBlobContainerRepository.InsertAsync(container, cancellationToken: cancellationToken); + + return container; + } + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/IDatabaseBlobContainerRepository.cs b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/IDatabaseBlobContainerRepository.cs new file mode 100644 index 0000000000..713d05ca11 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/IDatabaseBlobContainerRepository.cs @@ -0,0 +1,13 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Volo.Abp.Domain.Repositories; + +namespace Volo.Abp.BlobStoring.Database +{ + public interface IDatabaseBlobContainerRepository : IBasicRepository + { + Task FindAsync([NotNull] string name, CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/IDatabaseBlobRepository.cs b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/IDatabaseBlobRepository.cs new file mode 100644 index 0000000000..06afcfd28c --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/IDatabaseBlobRepository.cs @@ -0,0 +1,17 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Volo.Abp.Domain.Repositories; + +namespace Volo.Abp.BlobStoring.Database +{ + public interface IDatabaseBlobRepository : IBasicRepository + { + Task FindAsync(Guid containerId, [NotNull] string name, CancellationToken cancellationToken = default); + + Task ExistsAsync(Guid containerId, [NotNull] string name, CancellationToken cancellationToken = default); + + Task DeleteAsync(Guid containerId, [NotNull] string name, CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/FodyWeavers.xml b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/FodyWeavers.xml new file mode 100644 index 0000000000..be0de3a908 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/FodyWeavers.xsd b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/FodyWeavers.xsd new file mode 100644 index 0000000000..3f3946e282 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.csproj b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.csproj new file mode 100644 index 0000000000..20c155c894 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.csproj @@ -0,0 +1,16 @@ + + + + + + + netstandard2.0 + + + + + + + + + diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/Volo/Abp/BlobStoring/Database/EntityFrameworkCore/BlobStoringDatabaseEntityFrameworkCoreModule.cs b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/Volo/Abp/BlobStoring/Database/EntityFrameworkCore/BlobStoringDatabaseEntityFrameworkCoreModule.cs new file mode 100644 index 0000000000..26e8a5f024 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/Volo/Abp/BlobStoring/Database/EntityFrameworkCore/BlobStoringDatabaseEntityFrameworkCoreModule.cs @@ -0,0 +1,23 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Modularity; + +namespace Volo.Abp.BlobStoring.Database.EntityFrameworkCore +{ + [DependsOn( + typeof(BlobStoringDatabaseDomainModule), + typeof(AbpEntityFrameworkCoreModule) + )] + public class BlobStoringDatabaseEntityFrameworkCoreModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddAbpDbContext(options => + { + options.AddRepository(); + + options.AddRepository(); + }); + } + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/Volo/Abp/BlobStoring/Database/EntityFrameworkCore/BlobStoringDbContext.cs b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/Volo/Abp/BlobStoring/Database/EntityFrameworkCore/BlobStoringDbContext.cs new file mode 100644 index 0000000000..00780c628b --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/Volo/Abp/BlobStoring/Database/EntityFrameworkCore/BlobStoringDbContext.cs @@ -0,0 +1,27 @@ +using Microsoft.EntityFrameworkCore; +using Volo.Abp.Data; +using Volo.Abp.EntityFrameworkCore; + +namespace Volo.Abp.BlobStoring.Database.EntityFrameworkCore +{ + [ConnectionStringName(BlobStoringDatabaseDbProperties.ConnectionStringName)] + public class BlobStoringDbContext : AbpDbContext, IBlobStoringDbContext + { + public DbSet BlobContainers { get; set; } + + public DbSet Blobs { get; set; } + + public BlobStoringDbContext(DbContextOptions options) + : base(options) + { + + } + + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + builder.ConfigureBlobStoring(); + } + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/Volo/Abp/BlobStoring/Database/EntityFrameworkCore/BlobStoringDbContextModelCreatingExtensions.cs b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/Volo/Abp/BlobStoring/Database/EntityFrameworkCore/BlobStoringDbContextModelCreatingExtensions.cs new file mode 100644 index 0000000000..be8625e9e6 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/Volo/Abp/BlobStoring/Database/EntityFrameworkCore/BlobStoringDbContextModelCreatingExtensions.cs @@ -0,0 +1,51 @@ +using Microsoft.EntityFrameworkCore; +using System; +using Volo.Abp.EntityFrameworkCore.Modeling; + +namespace Volo.Abp.BlobStoring.Database.EntityFrameworkCore +{ + public static class BlobStoringDbContextModelCreatingExtensions + { + public static void ConfigureBlobStoring( + this ModelBuilder builder, + Action optionsAction = null) + { + Check.NotNull(builder, nameof(builder)); + + var options = new BlobStoringModelBuilderConfigurationOptions( + BlobStoringDatabaseDbProperties.DbTablePrefix, + BlobStoringDatabaseDbProperties.DbSchema + ); + + optionsAction?.Invoke(options); + + builder.Entity(b => + { + b.ToTable(options.TablePrefix + "BlobContainers", options.Schema); + + b.ConfigureByConvention(); + + b.Property(p => p.Name).IsRequired().HasMaxLength(DatabaseContainerConsts.MaxNameLength); + + b.HasMany().WithOne().HasForeignKey(p => p.ContainerId); + + b.HasIndex(x => new {x.TenantId, x.Name}); + }); + + builder.Entity(b => + { + b.ToTable(options.TablePrefix + "Blobs", options.Schema); + + b.ConfigureByConvention(); + + b.Property(p => p.ContainerId).IsRequired(); //TODO: Foreign key! + b.Property(p => p.Name).IsRequired().HasMaxLength(DatabaseBlobConsts.MaxNameLength); + b.Property(p => p.Content).HasMaxLength(DatabaseBlobConsts.MaxContentLength); + + b.HasOne().WithMany().HasForeignKey(p => p.ContainerId); + + b.HasIndex(x => new {x.TenantId, x.ContainerId, x.Name}); + }); + } + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/Volo/Abp/BlobStoring/Database/EntityFrameworkCore/BlobStoringModelBuilderConfigurationOptions.cs b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/Volo/Abp/BlobStoring/Database/EntityFrameworkCore/BlobStoringModelBuilderConfigurationOptions.cs new file mode 100644 index 0000000000..fce10576be --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/Volo/Abp/BlobStoring/Database/EntityFrameworkCore/BlobStoringModelBuilderConfigurationOptions.cs @@ -0,0 +1,18 @@ +using JetBrains.Annotations; +using Volo.Abp.EntityFrameworkCore.Modeling; + +namespace Volo.Abp.BlobStoring.Database.EntityFrameworkCore +{ + public class BlobStoringModelBuilderConfigurationOptions : AbpModelBuilderConfigurationOptions + { + public BlobStoringModelBuilderConfigurationOptions( + [NotNull] string tablePrefix = "", + [CanBeNull] string schema = null) + : base( + tablePrefix, + schema) + { + + } + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/Volo/Abp/BlobStoring/Database/EntityFrameworkCore/EfCoreDatabaseBlobContainerRepository.cs b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/Volo/Abp/BlobStoring/Database/EntityFrameworkCore/EfCoreDatabaseBlobContainerRepository.cs new file mode 100644 index 0000000000..e1d0764807 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/Volo/Abp/BlobStoring/Database/EntityFrameworkCore/EfCoreDatabaseBlobContainerRepository.cs @@ -0,0 +1,23 @@ +using Microsoft.EntityFrameworkCore; +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; + +namespace Volo.Abp.BlobStoring.Database.EntityFrameworkCore +{ + public class EfCoreDatabaseBlobContainerRepository : EfCoreRepository, IDatabaseBlobContainerRepository + { + public EfCoreDatabaseBlobContainerRepository(IDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + } + + public virtual async Task FindAsync(string name, CancellationToken cancellationToken = default) + { + return await DbSet.FirstOrDefaultAsync(x => x.Name == name, GetCancellationToken(cancellationToken)); + } + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/Volo/Abp/BlobStoring/Database/EntityFrameworkCore/EfCoreDatabaseBlobRepository.cs b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/Volo/Abp/BlobStoring/Database/EntityFrameworkCore/EfCoreDatabaseBlobRepository.cs new file mode 100644 index 0000000000..f4d1a83466 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/Volo/Abp/BlobStoring/Database/EntityFrameworkCore/EfCoreDatabaseBlobRepository.cs @@ -0,0 +1,53 @@ +using Microsoft.EntityFrameworkCore; +using System; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; + +namespace Volo.Abp.BlobStoring.Database.EntityFrameworkCore +{ + public class EfCoreDatabaseBlobRepository : EfCoreRepository, IDatabaseBlobRepository + { + public EfCoreDatabaseBlobRepository(IDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + } + + public virtual async Task FindAsync( + Guid containerId, + string name, + CancellationToken cancellationToken = default) + { + return await DbSet.FirstOrDefaultAsync( + x => x.ContainerId == containerId && x.Name == name, + GetCancellationToken(cancellationToken) + ); + } + + public virtual async Task ExistsAsync( + Guid containerId, + string name, + CancellationToken cancellationToken = default) + { + return await DbSet.AnyAsync( + x => x.ContainerId == containerId && x.Name == name, + GetCancellationToken(cancellationToken)); + } + + public virtual async Task DeleteAsync( + Guid containerId, + string name, + CancellationToken cancellationToken = default) + { + var blob = await FindAsync(containerId, name, cancellationToken); + if (blob == null) + { + return false; + } + + await base.DeleteAsync(blob.Id, cancellationToken: GetCancellationToken(cancellationToken)); + return true; + } + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/Volo/Abp/BlobStoring/Database/EntityFrameworkCore/IBlobStoringDbContext.cs b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/Volo/Abp/BlobStoring/Database/EntityFrameworkCore/IBlobStoringDbContext.cs new file mode 100644 index 0000000000..16e99cbc9a --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.EntityFrameworkCore/Volo/Abp/BlobStoring/Database/EntityFrameworkCore/IBlobStoringDbContext.cs @@ -0,0 +1,14 @@ +using Microsoft.EntityFrameworkCore; +using Volo.Abp.Data; +using Volo.Abp.EntityFrameworkCore; + +namespace Volo.Abp.BlobStoring.Database.EntityFrameworkCore +{ + [ConnectionStringName(BlobStoringDatabaseDbProperties.ConnectionStringName)] + public interface IBlobStoringDbContext : IEfCoreDbContext + { + DbSet BlobContainers { get; } + + DbSet Blobs { get; } + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/FodyWeavers.xml b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/FodyWeavers.xml new file mode 100644 index 0000000000..be0de3a908 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/FodyWeavers.xsd b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/FodyWeavers.xsd new file mode 100644 index 0000000000..3f3946e282 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/Volo.Abp.BlobStoring.Database.MongoDB.csproj b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/Volo.Abp.BlobStoring.Database.MongoDB.csproj new file mode 100644 index 0000000000..dee196df48 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/Volo.Abp.BlobStoring.Database.MongoDB.csproj @@ -0,0 +1,16 @@ + + + + + + + netstandard2.0 + + + + + + + + + diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/Volo/Abp/BlobStoring/Database/MongoDB/BlobStoringDatabaseMongoDbModule.cs b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/Volo/Abp/BlobStoring/Database/MongoDB/BlobStoringDatabaseMongoDbModule.cs new file mode 100644 index 0000000000..1ad7341a25 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/Volo/Abp/BlobStoring/Database/MongoDB/BlobStoringDatabaseMongoDbModule.cs @@ -0,0 +1,22 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; +using Volo.Abp.MongoDB; + +namespace Volo.Abp.BlobStoring.Database.MongoDB +{ + [DependsOn( + typeof(BlobStoringDatabaseDomainModule), + typeof(AbpMongoDbModule) + )] + public class BlobStoringDatabaseMongoDbModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddMongoDbContext(options => + { + options.AddRepository(); + options.AddRepository(); + }); + } + } +} diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/Volo/Abp/BlobStoring/Database/MongoDB/BlobStoringMongoDbContext.cs b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/Volo/Abp/BlobStoring/Database/MongoDB/BlobStoringMongoDbContext.cs new file mode 100644 index 0000000000..b07847a204 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/Volo/Abp/BlobStoring/Database/MongoDB/BlobStoringMongoDbContext.cs @@ -0,0 +1,21 @@ +using MongoDB.Driver; +using Volo.Abp.Data; +using Volo.Abp.MongoDB; + +namespace Volo.Abp.BlobStoring.Database.MongoDB +{ + [ConnectionStringName(BlobStoringDatabaseDbProperties.ConnectionStringName)] + public class BlobStoringMongoDbContext : AbpMongoDbContext, IBlobStoringMongoDbContext + { + public IMongoCollection BlobContainers => Collection(); + + public IMongoCollection Blobs => Collection(); + + protected override void CreateModel(IMongoModelBuilder modelBuilder) + { + base.CreateModel(modelBuilder); + + modelBuilder.ConfigureBlobStoring(); + } + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/Volo/Abp/BlobStoring/Database/MongoDB/BlobStoringMongoDbContextExtensions.cs b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/Volo/Abp/BlobStoring/Database/MongoDB/BlobStoringMongoDbContextExtensions.cs new file mode 100644 index 0000000000..f96f991bc9 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/Volo/Abp/BlobStoring/Database/MongoDB/BlobStoringMongoDbContextExtensions.cs @@ -0,0 +1,31 @@ +using System; +using Volo.Abp.MongoDB; + +namespace Volo.Abp.BlobStoring.Database.MongoDB +{ + public static class BlobStoringMongoDbContextExtensions + { + public static void ConfigureBlobStoring( + this IMongoModelBuilder builder, + Action optionsAction = null) + { + Check.NotNull(builder, nameof(builder)); + + var options = new BlobStoringMongoModelBuilderConfigurationOptions( + BlobStoringDatabaseDbProperties.DbTablePrefix + ); + + optionsAction?.Invoke(options); + + builder.Entity(b => + { + b.CollectionName = options.CollectionPrefix + "BlobContainers"; + }); + + builder.Entity(b => + { + b.CollectionName = options.CollectionPrefix + "Blobs"; + }); + } + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/Volo/Abp/BlobStoring/Database/MongoDB/BlobStoringMongoModelBuilderConfigurationOptions.cs b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/Volo/Abp/BlobStoring/Database/MongoDB/BlobStoringMongoModelBuilderConfigurationOptions.cs new file mode 100644 index 0000000000..f0f786e1dd --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/Volo/Abp/BlobStoring/Database/MongoDB/BlobStoringMongoModelBuilderConfigurationOptions.cs @@ -0,0 +1,14 @@ +using JetBrains.Annotations; +using Volo.Abp.MongoDB; + +namespace Volo.Abp.BlobStoring.Database.MongoDB +{ + public class BlobStoringMongoModelBuilderConfigurationOptions : AbpMongoModelBuilderConfigurationOptions + { + public BlobStoringMongoModelBuilderConfigurationOptions( + [NotNull] string collectionPrefix = "") + : base(collectionPrefix) + { + } + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/Volo/Abp/BlobStoring/Database/MongoDB/IBlobStoringMongoDbContext.cs b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/Volo/Abp/BlobStoring/Database/MongoDB/IBlobStoringMongoDbContext.cs new file mode 100644 index 0000000000..d7a9eb3966 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/Volo/Abp/BlobStoring/Database/MongoDB/IBlobStoringMongoDbContext.cs @@ -0,0 +1,14 @@ +using MongoDB.Driver; +using Volo.Abp.Data; +using Volo.Abp.MongoDB; + +namespace Volo.Abp.BlobStoring.Database.MongoDB +{ + [ConnectionStringName(BlobStoringDatabaseDbProperties.ConnectionStringName)] + public interface IBlobStoringMongoDbContext : IAbpMongoDbContext + { + IMongoCollection BlobContainers { get; } + + IMongoCollection Blobs { get; } + } +} diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/Volo/Abp/BlobStoring/Database/MongoDB/MongoDbDatabaseBlobContainerRepository.cs b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/Volo/Abp/BlobStoring/Database/MongoDB/MongoDbDatabaseBlobContainerRepository.cs new file mode 100644 index 0000000000..9777b329f8 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/Volo/Abp/BlobStoring/Database/MongoDB/MongoDbDatabaseBlobContainerRepository.cs @@ -0,0 +1,21 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories.MongoDB; +using Volo.Abp.MongoDB; + +namespace Volo.Abp.BlobStoring.Database.MongoDB +{ + public class MongoDbDatabaseBlobContainerRepository : MongoDbRepository, IDatabaseBlobContainerRepository + { + public MongoDbDatabaseBlobContainerRepository(IMongoDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + } + + public virtual async Task FindAsync(string name, CancellationToken cancellationToken = default) + { + return await base.FindAsync(x => x.Name == name, cancellationToken: GetCancellationToken(cancellationToken)); + } + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/Volo/Abp/BlobStoring/Database/MongoDB/MongoDbDatabaseBlobRepository.cs b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/Volo/Abp/BlobStoring/Database/MongoDB/MongoDbDatabaseBlobRepository.cs new file mode 100644 index 0000000000..60d9e3d148 --- /dev/null +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.MongoDB/Volo/Abp/BlobStoring/Database/MongoDB/MongoDbDatabaseBlobRepository.cs @@ -0,0 +1,45 @@ +using MongoDB.Driver.Linq; +using System; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories.MongoDB; +using Volo.Abp.MongoDB; + +namespace Volo.Abp.BlobStoring.Database.MongoDB +{ + public class MongoDbDatabaseBlobRepository : MongoDbRepository, IDatabaseBlobRepository + { + public MongoDbDatabaseBlobRepository(IMongoDbContextProvider dbContextProvider) : base(dbContextProvider) + { + } + + public virtual async Task FindAsync(Guid containerId, string name, CancellationToken cancellationToken = default) + { + return await GetMongoQueryable().FirstOrDefaultAsync( + x => x.ContainerId == containerId && + x.Name == name, + GetCancellationToken(cancellationToken)); + } + + public virtual async Task ExistsAsync(Guid containerId, string name, CancellationToken cancellationToken = default) + { + return await GetMongoQueryable().AnyAsync( + x => x.ContainerId == containerId && + x.Name == name, + GetCancellationToken(cancellationToken)); + } + + public virtual async Task DeleteAsync(Guid containerId, string name, CancellationToken cancellationToken = default) + { + var blob = await FindAsync(containerId, name, cancellationToken); + + if (blob == null) + { + return false; + } + + await base.DeleteAsync(blob, cancellationToken: GetCancellationToken(cancellationToken)); + return true; + } + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.Domain.Tests/BlobStoringDatabaseDomainTestBase.cs b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.Domain.Tests/BlobStoringDatabaseDomainTestBase.cs new file mode 100644 index 0000000000..405ac84008 --- /dev/null +++ b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.Domain.Tests/BlobStoringDatabaseDomainTestBase.cs @@ -0,0 +1,10 @@ +namespace Volo.Abp.BlobStoring.Database +{ + /* Inherit from this class for your domain layer tests. + * See SampleManager_Tests for example. + */ + public abstract class BlobStoringDatabaseDomainTestBase : BlobStoringDatabaseTestBase + { + + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.Domain.Tests/BlobStoringDatabaseDomainTestModule.cs b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.Domain.Tests/BlobStoringDatabaseDomainTestModule.cs new file mode 100644 index 0000000000..a7ad7427f8 --- /dev/null +++ b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.Domain.Tests/BlobStoringDatabaseDomainTestModule.cs @@ -0,0 +1,13 @@ +using Volo.Abp.BlobStoring.Database.EntityFrameworkCore; +using Volo.Abp.Modularity; + +namespace Volo.Abp.BlobStoring.Database +{ + [DependsOn( + typeof(BlobStoringDatabaseEntityFrameworkCoreTestModule) + )] + public class BlobStoringDatabaseDomainTestModule : AbpModule + { + + } +} diff --git a/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.Domain.Tests/Volo.Abp.BlobStoring.Database.Domain.Tests.csproj b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.Domain.Tests/Volo.Abp.BlobStoring.Database.Domain.Tests.csproj new file mode 100644 index 0000000000..20de7c154e --- /dev/null +++ b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.Domain.Tests/Volo.Abp.BlobStoring.Database.Domain.Tests.csproj @@ -0,0 +1,13 @@ + + + + netcoreapp3.1 + Volo.Abp.BlobStoring.Database + + + + + + + + diff --git a/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.Tests/EntityFrameworkCore/BlobStoringDatabaseEntityFrameworkCoreTestBase.cs b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.Tests/EntityFrameworkCore/BlobStoringDatabaseEntityFrameworkCoreTestBase.cs new file mode 100644 index 0000000000..d465616047 --- /dev/null +++ b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.Tests/EntityFrameworkCore/BlobStoringDatabaseEntityFrameworkCoreTestBase.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.BlobStoring.Database.EntityFrameworkCore +{ + public abstract class BlobStoringDatabaseEntityFrameworkCoreTestBase : BlobStoringDatabaseTestBase + { + + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.Tests/EntityFrameworkCore/BlobStoringDatabaseEntityFrameworkCoreTestModule.cs b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.Tests/EntityFrameworkCore/BlobStoringDatabaseEntityFrameworkCoreTestModule.cs new file mode 100644 index 0000000000..9b9c235cc2 --- /dev/null +++ b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.Tests/EntityFrameworkCore/BlobStoringDatabaseEntityFrameworkCoreTestModule.cs @@ -0,0 +1,41 @@ +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Modularity; + +namespace Volo.Abp.BlobStoring.Database.EntityFrameworkCore +{ + [DependsOn( + typeof(BlobStoringDatabaseTestBaseModule), + typeof(BlobStoringDatabaseEntityFrameworkCoreModule) + )] + public class BlobStoringDatabaseEntityFrameworkCoreTestModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + var sqliteConnection = CreateDatabaseAndGetConnection(); + + Configure(options => + { + options.Configure(abpDbContextConfigurationContext => + { + abpDbContextConfigurationContext.DbContextOptions.UseSqlite(sqliteConnection); + }); + }); + } + + private static SqliteConnection CreateDatabaseAndGetConnection() + { + var connection = new SqliteConnection("Data Source=:memory:"); + connection.Open(); + + new BlobStoringDbContext( + new DbContextOptionsBuilder().UseSqlite(connection).Options + ).GetService().CreateTables(); + + return connection; + } + } +} diff --git a/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.Tests/EntityFrameworkCore/EfCoreContainer_Tests.cs b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.Tests/EntityFrameworkCore/EfCoreContainer_Tests.cs new file mode 100644 index 0000000000..bed4dae9c9 --- /dev/null +++ b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.Tests/EntityFrameworkCore/EfCoreContainer_Tests.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.BlobStoring.Database.EntityFrameworkCore +{ + public class EfCoreContainer_Tests : BlobContainer_Tests + { + + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.Tests/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.Tests.csproj b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.Tests/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.Tests.csproj new file mode 100644 index 0000000000..8bdc5080aa --- /dev/null +++ b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.Tests/Volo.Abp.BlobStoring.Database.EntityFrameworkCore.Tests.csproj @@ -0,0 +1,17 @@ + + + + netcoreapp3.1 + Volo.Abp.BlobStoring.Database + + + + + + + + + + + + diff --git a/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.MongoDB.Tests/MongoDB/BlobStoringDatabaseMongoDbTestBase.cs b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.MongoDB.Tests/MongoDB/BlobStoringDatabaseMongoDbTestBase.cs new file mode 100644 index 0000000000..c96746cbb2 --- /dev/null +++ b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.MongoDB.Tests/MongoDB/BlobStoringDatabaseMongoDbTestBase.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.BlobStoring.Database.MongoDB +{ + public abstract class BlobStoringDatabaseMongoDbTestBase : BlobStoringDatabaseTestBase + { + + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.MongoDB.Tests/MongoDB/BlobStoringDatabaseMongoDbTestModule.cs b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.MongoDB.Tests/MongoDB/BlobStoringDatabaseMongoDbTestModule.cs new file mode 100644 index 0000000000..03eb96f6d3 --- /dev/null +++ b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.MongoDB.Tests/MongoDB/BlobStoringDatabaseMongoDbTestModule.cs @@ -0,0 +1,25 @@ +using System; +using Volo.Abp.Data; +using Volo.Abp.Modularity; + +namespace Volo.Abp.BlobStoring.Database.MongoDB +{ + [DependsOn( + typeof(BlobStoringDatabaseTestBaseModule), + typeof(BlobStoringDatabaseMongoDbModule) + )] + public class BlobStoringDatabaseMongoDbTestModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + var connectionString = MongoDbFixture.ConnectionString.EnsureEndsWith('/') + + "Db_" + + Guid.NewGuid().ToString("N"); + + Configure(options => + { + options.ConnectionStrings.Default = connectionString; + }); + } + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.MongoDB.Tests/MongoDB/MongoDbContainer_Tests.cs b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.MongoDB.Tests/MongoDB/MongoDbContainer_Tests.cs new file mode 100644 index 0000000000..d8f5eec2b7 --- /dev/null +++ b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.MongoDB.Tests/MongoDB/MongoDbContainer_Tests.cs @@ -0,0 +1,10 @@ +using Xunit; + +namespace Volo.Abp.BlobStoring.Database.MongoDB +{ + [Collection(MongoTestCollection.Name)] + public class MongoDbContainer_Tests : BlobContainer_Tests + { + + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.MongoDB.Tests/MongoDB/MongoDbFixture.cs b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.MongoDB.Tests/MongoDB/MongoDbFixture.cs new file mode 100644 index 0000000000..a7cfb4f4b6 --- /dev/null +++ b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.MongoDB.Tests/MongoDB/MongoDbFixture.cs @@ -0,0 +1,22 @@ +using System; +using Mongo2Go; + +namespace Volo.Abp.BlobStoring.Database.MongoDB +{ + public class MongoDbFixture : IDisposable + { + private static readonly MongoDbRunner MongoDbRunner; + public static readonly string ConnectionString; + + static MongoDbFixture() + { + MongoDbRunner = MongoDbRunner.Start(); + ConnectionString = MongoDbRunner.ConnectionString; + } + + public void Dispose() + { + MongoDbRunner?.Dispose(); + } + } +} diff --git a/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.MongoDB.Tests/MongoDB/MongoTestCollection.cs b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.MongoDB.Tests/MongoDB/MongoTestCollection.cs new file mode 100644 index 0000000000..b9778249e4 --- /dev/null +++ b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.MongoDB.Tests/MongoDB/MongoTestCollection.cs @@ -0,0 +1,10 @@ +using Xunit; + +namespace Volo.Abp.BlobStoring.Database.MongoDB +{ + [CollectionDefinition(Name)] + public class MongoTestCollection : ICollectionFixture + { + public const string Name = "MongoDB Collection"; + } +} \ No newline at end of file diff --git a/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.MongoDB.Tests/Volo.Abp.BlobStoring.Database.MongoDB.Tests.csproj b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.MongoDB.Tests/Volo.Abp.BlobStoring.Database.MongoDB.Tests.csproj new file mode 100644 index 0000000000..9b37676068 --- /dev/null +++ b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.MongoDB.Tests/Volo.Abp.BlobStoring.Database.MongoDB.Tests.csproj @@ -0,0 +1,15 @@ + + + + netcoreapp3.1 + Volo.Abp.BlobStoring.Database + + + + + + + + + + diff --git a/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.TestBase/BlobStoringDatabaseTestBase.cs b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.TestBase/BlobStoringDatabaseTestBase.cs new file mode 100644 index 0000000000..7b426f55dc --- /dev/null +++ b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.TestBase/BlobStoringDatabaseTestBase.cs @@ -0,0 +1,59 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp; +using Volo.Abp.Modularity; +using Volo.Abp.Uow; +using Volo.Abp.Testing; + +namespace Volo.Abp.BlobStoring.Database +{ + public abstract class BlobStoringDatabaseTestBase : AbpIntegratedTest + where TStartupModule : IAbpModule + { + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } + + protected virtual Task WithUnitOfWorkAsync(Func func) + { + return WithUnitOfWorkAsync(new AbpUnitOfWorkOptions(), func); + } + + protected virtual async Task WithUnitOfWorkAsync(AbpUnitOfWorkOptions options, Func action) + { + using (var scope = ServiceProvider.CreateScope()) + { + var uowManager = scope.ServiceProvider.GetRequiredService(); + + using (var uow = uowManager.Begin(options)) + { + await action(); + + await uow.CompleteAsync(); + } + } + } + + protected virtual Task WithUnitOfWorkAsync(Func> func) + { + return WithUnitOfWorkAsync(new AbpUnitOfWorkOptions(), func); + } + + protected virtual async Task WithUnitOfWorkAsync(AbpUnitOfWorkOptions options, Func> func) + { + using (var scope = ServiceProvider.CreateScope()) + { + var uowManager = scope.ServiceProvider.GetRequiredService(); + + using (var uow = uowManager.Begin(options)) + { + var result = await func(); + await uow.CompleteAsync(); + return result; + } + } + } + } +} diff --git a/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.TestBase/BlobStoringDatabaseTestBaseModule.cs b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.TestBase/BlobStoringDatabaseTestBaseModule.cs new file mode 100644 index 0000000000..96e3bcaa90 --- /dev/null +++ b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.TestBase/BlobStoringDatabaseTestBaseModule.cs @@ -0,0 +1,30 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Authorization; +using Volo.Abp.Autofac; +using Volo.Abp.Modularity; + +namespace Volo.Abp.BlobStoring.Database +{ + [DependsOn( + typeof(AbpAutofacModule), + typeof(AbpTestBaseModule), + typeof(AbpAuthorizationModule), + typeof(BlobStoringDatabaseDomainModule), + typeof(AbpBlobStoringTestModule) + )] + public class BlobStoringDatabaseTestBaseModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddAlwaysAllowAuthorization(); + + Configure(options => + { + options.Containers.ConfigureAll((containerName, containerConfiguration) => + { + containerConfiguration.UseDatabase(); + }); + }); + } + } +} diff --git a/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.TestBase/Security/FakeCurrentPrincipalAccessor.cs b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.TestBase/Security/FakeCurrentPrincipalAccessor.cs new file mode 100644 index 0000000000..e1b9aa1813 --- /dev/null +++ b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.TestBase/Security/FakeCurrentPrincipalAccessor.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.Security.Claims; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Security.Claims; + +namespace Volo.Abp.BlobStoring.Database.Security +{ + [Dependency(ReplaceServices = true)] + public class FakeCurrentPrincipalAccessor : ThreadCurrentPrincipalAccessor + { + public override ClaimsPrincipal GetClaimsPrincipal() + { + return GetPrincipal(); + } + + private ClaimsPrincipal _principal; + + private ClaimsPrincipal GetPrincipal() + { + if (_principal == null) + { + lock (this) + { + if (_principal == null) + { + _principal = new ClaimsPrincipal( + new ClaimsIdentity( + new List + { + new Claim(AbpClaimTypes.UserId,"2e701e62-0953-4dd3-910b-dc6cc93ccb0d"), + new Claim(AbpClaimTypes.UserName,"admin"), + new Claim(AbpClaimTypes.Email,"admin@abp.io") + } + ) + ); + } + } + } + + return _principal; + } + } +} diff --git a/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.TestBase/Volo.Abp.BlobStoring.Database.TestBase.csproj b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.TestBase/Volo.Abp.BlobStoring.Database.TestBase.csproj new file mode 100644 index 0000000000..8dfa603e98 --- /dev/null +++ b/modules/blob-storing-database/test/Volo.Abp.BlobStoring.Database.TestBase/Volo.Abp.BlobStoring.Database.TestBase.csproj @@ -0,0 +1,22 @@ + + + + netcoreapp3.1 + Volo.Abp.BlobStoring.Database + + + + + + + + + + + + + + + + + diff --git a/modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/20200525053003_Added_OrganizationUnits.Designer.cs b/modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/20200525053003_Added_OrganizationUnits.Designer.cs new file mode 100644 index 0000000000..2a61fc9f9b --- /dev/null +++ b/modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/20200525053003_Added_OrganizationUnits.Designer.cs @@ -0,0 +1,1090 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Volo.BloggingTestApp.EntityFrameworkCore; + +namespace Volo.BloggingTestApp.EntityFrameworkCore.Migrations +{ + [DbContext(typeof(BloggingTestAppDbContext))] + [Migration("20200525053003_Added_OrganizationUnits")] + partial class Added_OrganizationUnits + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.2") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasColumnName("ConcurrencyStamp") + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.Property("Description") + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties") + .HasColumnType("nvarchar(max)"); + + b.Property("IsStatic") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.Property("Regex") + .HasColumnType("nvarchar(512)") + .HasMaxLength(512); + + b.Property("RegexDescription") + .HasColumnType("nvarchar(128)") + .HasMaxLength(128); + + b.Property("Required") + .HasColumnType("bit"); + + b.Property("ValueType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("AbpClaimTypes"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasColumnName("ConcurrencyStamp") + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDefault") + .HasColumnName("IsDefault") + .HasColumnType("bit"); + + b.Property("IsPublic") + .HasColumnName("IsPublic") + .HasColumnType("bit"); + + b.Property("IsStatic") + .HasColumnName("IsStatic") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.Property("NormalizedName") + .IsRequired() + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.Property("TenantId") + .HasColumnName("TenantId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName"); + + b.ToTable("AbpRoles"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(1024)") + .HasMaxLength(1024); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnName("TenantId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AbpRoleClaims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AccessFailedCount") + .ValueGeneratedOnAdd() + .HasColumnName("AccessFailedCount") + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .HasColumnName("CreationTime") + .HasColumnType("datetime2"); + + b.Property("CreatorId") + .HasColumnName("CreatorId") + .HasColumnType("uniqueidentifier"); + + b.Property("DeleterId") + .HasColumnName("DeleterId") + .HasColumnType("uniqueidentifier"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime") + .HasColumnType("datetime2"); + + b.Property("Email") + .IsRequired() + .HasColumnName("Email") + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.Property("EmailConfirmed") + .ValueGeneratedOnAdd() + .HasColumnName("EmailConfirmed") + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime") + .HasColumnType("datetime2"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId") + .HasColumnType("uniqueidentifier"); + + b.Property("LockoutEnabled") + .ValueGeneratedOnAdd() + .HasColumnName("LockoutEnabled") + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("Name") + .HasColumnName("Name") + .HasColumnType("nvarchar(64)") + .HasMaxLength(64); + + b.Property("NormalizedEmail") + .IsRequired() + .HasColumnName("NormalizedEmail") + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .IsRequired() + .HasColumnName("NormalizedUserName") + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.Property("PasswordHash") + .HasColumnName("PasswordHash") + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.Property("PhoneNumber") + .HasColumnName("PhoneNumber") + .HasColumnType("nvarchar(16)") + .HasMaxLength(16); + + b.Property("PhoneNumberConfirmed") + .ValueGeneratedOnAdd() + .HasColumnName("PhoneNumberConfirmed") + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("SecurityStamp") + .IsRequired() + .HasColumnName("SecurityStamp") + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.Property("Surname") + .HasColumnName("Surname") + .HasColumnType("nvarchar(64)") + .HasMaxLength(64); + + b.Property("TenantId") + .HasColumnName("TenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("TwoFactorEnabled") + .ValueGeneratedOnAdd() + .HasColumnName("TwoFactorEnabled") + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("UserName") + .IsRequired() + .HasColumnName("UserName") + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("Email"); + + b.HasIndex("NormalizedEmail"); + + b.HasIndex("NormalizedUserName"); + + b.HasIndex("UserName"); + + b.ToTable("AbpUsers"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(1024)") + .HasMaxLength(1024); + + b.Property("TenantId") + .HasColumnName("TenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserClaims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(64)") + .HasMaxLength(64); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(128)") + .HasMaxLength(128); + + b.Property("ProviderKey") + .IsRequired() + .HasColumnType("nvarchar(196)") + .HasMaxLength(196); + + b.Property("TenantId") + .HasColumnName("TenantId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UserId", "LoginProvider"); + + b.HasIndex("LoginProvider", "ProviderKey"); + + b.ToTable("AbpUserLogins"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnName("CreationTime") + .HasColumnType("datetime2"); + + b.Property("CreatorId") + .HasColumnName("CreatorId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnName("TenantId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("OrganizationUnitId", "UserId"); + + b.HasIndex("UserId", "OrganizationUnitId"); + + b.ToTable("AbpUserOrganizationUnits"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnName("TenantId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId", "UserId"); + + b.ToTable("AbpUserRoles"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(64)") + .HasMaxLength(64); + + b.Property("Name") + .HasColumnType("nvarchar(128)") + .HasMaxLength(128); + + b.Property("TenantId") + .HasColumnName("TenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AbpUserTokens"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Code") + .IsRequired() + .HasColumnName("Code") + .HasColumnType("nvarchar(95)") + .HasMaxLength(95); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .HasColumnName("CreationTime") + .HasColumnType("datetime2"); + + b.Property("CreatorId") + .HasColumnName("CreatorId") + .HasColumnType("uniqueidentifier"); + + b.Property("DeleterId") + .HasColumnName("DeleterId") + .HasColumnType("uniqueidentifier"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime") + .HasColumnType("datetime2"); + + b.Property("DisplayName") + .IsRequired() + .HasColumnName("DisplayName") + .HasColumnType("nvarchar(128)") + .HasMaxLength(128); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime") + .HasColumnType("datetime2"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId") + .HasColumnType("uniqueidentifier"); + + b.Property("ParentId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnName("TenantId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("Code"); + + b.HasIndex("ParentId"); + + b.ToTable("AbpOrganizationUnits"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnName("CreationTime") + .HasColumnType("datetime2"); + + b.Property("CreatorId") + .HasColumnName("CreatorId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnName("TenantId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("OrganizationUnitId", "RoleId"); + + b.HasIndex("RoleId", "OrganizationUnitId"); + + b.ToTable("AbpOrganizationUnitRoles"); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(128)") + .HasMaxLength(128); + + b.Property("ProviderKey") + .IsRequired() + .HasColumnType("nvarchar(64)") + .HasMaxLength(64); + + b.Property("ProviderName") + .IsRequired() + .HasColumnType("nvarchar(64)") + .HasMaxLength(64); + + b.Property("TenantId") + .HasColumnName("TenantId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpPermissionGrants"); + }); + + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(128)") + .HasMaxLength(128); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(64)") + .HasMaxLength(64); + + b.Property("ProviderName") + .HasColumnType("nvarchar(64)") + .HasMaxLength(64); + + b.Property("Value") + .IsRequired() + .HasColumnType("nvarchar(2048)") + .HasMaxLength(2048); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpSettings"); + }); + + modelBuilder.Entity("Volo.Blogging.Blogs.Blog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .HasColumnName("CreationTime") + .HasColumnType("datetime2"); + + b.Property("CreatorId") + .HasColumnName("CreatorId") + .HasColumnType("uniqueidentifier"); + + b.Property("DeleterId") + .HasColumnName("DeleterId") + .HasColumnType("uniqueidentifier"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasColumnName("Description") + .HasColumnType("nvarchar(1024)") + .HasMaxLength(1024); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime") + .HasColumnType("datetime2"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasColumnName("Name") + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.Property("ShortName") + .IsRequired() + .HasColumnName("ShortName") + .HasColumnType("nvarchar(32)") + .HasMaxLength(32); + + b.HasKey("Id"); + + b.ToTable("BlgBlogs"); + }); + + modelBuilder.Entity("Volo.Blogging.Comments.Comment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .HasColumnName("CreationTime") + .HasColumnType("datetime2"); + + b.Property("CreatorId") + .HasColumnName("CreatorId") + .HasColumnType("uniqueidentifier"); + + b.Property("DeleterId") + .HasColumnName("DeleterId") + .HasColumnType("uniqueidentifier"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime") + .HasColumnType("datetime2"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime") + .HasColumnType("datetime2"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId") + .HasColumnType("uniqueidentifier"); + + b.Property("PostId") + .HasColumnName("PostId") + .HasColumnType("uniqueidentifier"); + + b.Property("RepliedCommentId") + .HasColumnName("RepliedCommentId") + .HasColumnType("uniqueidentifier"); + + b.Property("Text") + .IsRequired() + .HasColumnName("Text") + .HasColumnType("nvarchar(1024)") + .HasMaxLength(1024); + + b.HasKey("Id"); + + b.HasIndex("PostId"); + + b.HasIndex("RepliedCommentId"); + + b.ToTable("BlgComments"); + }); + + modelBuilder.Entity("Volo.Blogging.Posts.Post", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("BlogId") + .HasColumnName("BlogId") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("Content") + .HasColumnName("Content") + .HasColumnType("nvarchar(max)") + .HasMaxLength(1048576); + + b.Property("CoverImage") + .IsRequired() + .HasColumnName("CoverImage") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .HasColumnName("CreationTime") + .HasColumnType("datetime2"); + + b.Property("CreatorId") + .HasColumnName("CreatorId") + .HasColumnType("uniqueidentifier"); + + b.Property("DeleterId") + .HasColumnName("DeleterId") + .HasColumnType("uniqueidentifier"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasColumnName("Description") + .HasColumnType("nvarchar(1000)") + .HasMaxLength(1000); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime") + .HasColumnType("datetime2"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId") + .HasColumnType("uniqueidentifier"); + + b.Property("ReadCount") + .HasColumnType("int"); + + b.Property("Title") + .IsRequired() + .HasColumnName("Title") + .HasColumnType("nvarchar(512)") + .HasMaxLength(512); + + b.Property("Url") + .IsRequired() + .HasColumnName("Url") + .HasColumnType("nvarchar(64)") + .HasMaxLength(64); + + b.HasKey("Id"); + + b.HasIndex("BlogId"); + + b.ToTable("BlgPosts"); + }); + + modelBuilder.Entity("Volo.Blogging.Posts.PostTag", b => + { + b.Property("PostId") + .HasColumnName("PostId") + .HasColumnType("uniqueidentifier"); + + b.Property("TagId") + .HasColumnName("TagId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnName("CreationTime") + .HasColumnType("datetime2"); + + b.Property("CreatorId") + .HasColumnName("CreatorId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("PostId", "TagId"); + + b.HasIndex("TagId"); + + b.ToTable("BlgPostTags"); + }); + + modelBuilder.Entity("Volo.Blogging.Tagging.Tag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("BlogId") + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .HasColumnName("CreationTime") + .HasColumnType("datetime2"); + + b.Property("CreatorId") + .HasColumnName("CreatorId") + .HasColumnType("uniqueidentifier"); + + b.Property("DeleterId") + .HasColumnName("DeleterId") + .HasColumnType("uniqueidentifier"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasColumnName("Description") + .HasColumnType("nvarchar(512)") + .HasMaxLength(512); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime") + .HasColumnType("datetime2"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasColumnName("Name") + .HasColumnType("nvarchar(64)") + .HasMaxLength(64); + + b.Property("UsageCount") + .HasColumnName("UsageCount") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("BlgTags"); + }); + + modelBuilder.Entity("Volo.Blogging.Users.BlogUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .IsRequired() + .HasColumnName("Email") + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.Property("EmailConfirmed") + .ValueGeneratedOnAdd() + .HasColumnName("EmailConfirmed") + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnName("Name") + .HasColumnType("nvarchar(64)") + .HasMaxLength(64); + + b.Property("PhoneNumber") + .HasColumnName("PhoneNumber") + .HasColumnType("nvarchar(16)") + .HasMaxLength(16); + + b.Property("PhoneNumberConfirmed") + .ValueGeneratedOnAdd() + .HasColumnName("PhoneNumberConfirmed") + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("Surname") + .HasColumnName("Surname") + .HasColumnType("nvarchar(64)") + .HasMaxLength(64); + + b.Property("TenantId") + .HasColumnName("TenantId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserName") + .IsRequired() + .HasColumnName("UserName") + .HasColumnType("nvarchar(256)") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.ToTable("BlgUsers"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany("Claims") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Claims") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Logins") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("OrganizationUnits") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Roles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Tokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("ParentId"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany("Roles") + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Blogging.Comments.Comment", b => + { + b.HasOne("Volo.Blogging.Posts.Post", null) + .WithMany() + .HasForeignKey("PostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Blogging.Comments.Comment", null) + .WithMany() + .HasForeignKey("RepliedCommentId"); + }); + + modelBuilder.Entity("Volo.Blogging.Posts.Post", b => + { + b.HasOne("Volo.Blogging.Blogs.Blog", null) + .WithMany() + .HasForeignKey("BlogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Blogging.Posts.PostTag", b => + { + b.HasOne("Volo.Blogging.Posts.Post", null) + .WithMany("Tags") + .HasForeignKey("PostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Blogging.Tagging.Tag", null) + .WithMany() + .HasForeignKey("TagId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/20200525053003_Added_OrganizationUnits.cs b/modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/20200525053003_Added_OrganizationUnits.cs new file mode 100644 index 0000000000..38ec0e5542 --- /dev/null +++ b/modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/20200525053003_Added_OrganizationUnits.cs @@ -0,0 +1,127 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Volo.BloggingTestApp.EntityFrameworkCore.Migrations +{ + public partial class Added_OrganizationUnits : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AbpOrganizationUnits", + columns: table => new + { + Id = table.Column(nullable: false), + ExtraProperties = table.Column(nullable: true), + ConcurrencyStamp = table.Column(nullable: true), + CreationTime = table.Column(nullable: false), + CreatorId = table.Column(nullable: true), + LastModificationTime = table.Column(nullable: true), + LastModifierId = table.Column(nullable: true), + IsDeleted = table.Column(nullable: false, defaultValue: false), + DeleterId = table.Column(nullable: true), + DeletionTime = table.Column(nullable: true), + TenantId = table.Column(nullable: true), + ParentId = table.Column(nullable: true), + Code = table.Column(maxLength: 95, nullable: false), + DisplayName = table.Column(maxLength: 128, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpOrganizationUnits", x => x.Id); + table.ForeignKey( + name: "FK_AbpOrganizationUnits_AbpOrganizationUnits_ParentId", + column: x => x.ParentId, + principalTable: "AbpOrganizationUnits", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "AbpOrganizationUnitRoles", + columns: table => new + { + RoleId = table.Column(nullable: false), + OrganizationUnitId = table.Column(nullable: false), + CreationTime = table.Column(nullable: false), + CreatorId = table.Column(nullable: true), + TenantId = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpOrganizationUnitRoles", x => new { x.OrganizationUnitId, x.RoleId }); + table.ForeignKey( + name: "FK_AbpOrganizationUnitRoles_AbpOrganizationUnits_OrganizationUnitId", + column: x => x.OrganizationUnitId, + principalTable: "AbpOrganizationUnits", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AbpOrganizationUnitRoles_AbpRoles_RoleId", + column: x => x.RoleId, + principalTable: "AbpRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpUserOrganizationUnits", + columns: table => new + { + UserId = table.Column(nullable: false), + OrganizationUnitId = table.Column(nullable: false), + CreationTime = table.Column(nullable: false), + CreatorId = table.Column(nullable: true), + TenantId = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUserOrganizationUnits", x => new { x.OrganizationUnitId, x.UserId }); + table.ForeignKey( + name: "FK_AbpUserOrganizationUnits_AbpOrganizationUnits_OrganizationUnitId", + column: x => x.OrganizationUnitId, + principalTable: "AbpOrganizationUnits", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AbpUserOrganizationUnits_AbpUsers_UserId", + column: x => x.UserId, + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_AbpOrganizationUnitRoles_RoleId_OrganizationUnitId", + table: "AbpOrganizationUnitRoles", + columns: new[] { "RoleId", "OrganizationUnitId" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpOrganizationUnits_Code", + table: "AbpOrganizationUnits", + column: "Code"); + + migrationBuilder.CreateIndex( + name: "IX_AbpOrganizationUnits_ParentId", + table: "AbpOrganizationUnits", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX_AbpUserOrganizationUnits_UserId_OrganizationUnitId", + table: "AbpUserOrganizationUnits", + columns: new[] { "UserId", "OrganizationUnitId" }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AbpOrganizationUnitRoles"); + + migrationBuilder.DropTable( + name: "AbpUserOrganizationUnits"); + + migrationBuilder.DropTable( + name: "AbpOrganizationUnits"); + } + } +} diff --git a/modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/BloggingTestAppDbContextModelSnapshot.cs b/modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/BloggingTestAppDbContextModelSnapshot.cs index 2b0641a680..00e9389e8f 100644 --- a/modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/BloggingTestAppDbContextModelSnapshot.cs +++ b/modules/blogging/app/Volo.BloggingTestApp.EntityFrameworkCore/Migrations/BloggingTestAppDbContextModelSnapshot.cs @@ -347,6 +347,33 @@ namespace Volo.BloggingTestApp.EntityFrameworkCore.Migrations b.ToTable("AbpUserLogins"); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnName("CreationTime") + .HasColumnType("datetime2"); + + b.Property("CreatorId") + .HasColumnName("CreatorId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnName("TenantId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("OrganizationUnitId", "UserId"); + + b.HasIndex("UserId", "OrganizationUnitId"); + + b.ToTable("AbpUserOrganizationUnits"); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => { b.Property("UserId") @@ -391,6 +418,106 @@ namespace Volo.BloggingTestApp.EntityFrameworkCore.Migrations b.ToTable("AbpUserTokens"); }); + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Code") + .IsRequired() + .HasColumnName("Code") + .HasColumnType("nvarchar(95)") + .HasMaxLength(95); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .HasColumnName("CreationTime") + .HasColumnType("datetime2"); + + b.Property("CreatorId") + .HasColumnName("CreatorId") + .HasColumnType("uniqueidentifier"); + + b.Property("DeleterId") + .HasColumnName("DeleterId") + .HasColumnType("uniqueidentifier"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime") + .HasColumnType("datetime2"); + + b.Property("DisplayName") + .IsRequired() + .HasColumnName("DisplayName") + .HasColumnType("nvarchar(128)") + .HasMaxLength(128); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime") + .HasColumnType("datetime2"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId") + .HasColumnType("uniqueidentifier"); + + b.Property("ParentId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnName("TenantId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("Code"); + + b.HasIndex("ParentId"); + + b.ToTable("AbpOrganizationUnits"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnName("CreationTime") + .HasColumnType("datetime2"); + + b.Property("CreatorId") + .HasColumnName("CreatorId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnName("TenantId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("OrganizationUnitId", "RoleId"); + + b.HasIndex("RoleId", "OrganizationUnitId"); + + b.ToTable("AbpOrganizationUnitRoles"); + }); + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => { b.Property("Id") @@ -858,6 +985,21 @@ namespace Volo.BloggingTestApp.EntityFrameworkCore.Migrations .IsRequired(); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("OrganizationUnits") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => { b.HasOne("Volo.Abp.Identity.IdentityRole", null) @@ -882,6 +1024,28 @@ namespace Volo.BloggingTestApp.EntityFrameworkCore.Migrations .IsRequired(); }); + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("ParentId"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany("Roles") + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + modelBuilder.Entity("Volo.Blogging.Comments.Comment", b => { b.HasOne("Volo.Blogging.Posts.Post", null) diff --git a/modules/blogging/app/Volo.BloggingTestApp/Volo.BloggingTestApp.csproj b/modules/blogging/app/Volo.BloggingTestApp/Volo.BloggingTestApp.csproj index 511b683e0e..a677553ee5 100644 --- a/modules/blogging/app/Volo.BloggingTestApp/Volo.BloggingTestApp.csproj +++ b/modules/blogging/app/Volo.BloggingTestApp/Volo.BloggingTestApp.csproj @@ -18,8 +18,8 @@ - - + + diff --git a/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Localization/Resources/Blogging/ApplicationContracts/cs.json b/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Localization/Resources/Blogging/ApplicationContracts/cs.json index 9b781b029e..5b11dd808c 100644 --- a/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Localization/Resources/Blogging/ApplicationContracts/cs.json +++ b/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Localization/Resources/Blogging/ApplicationContracts/cs.json @@ -11,4 +11,4 @@ "Permission:Create": "Vytvořit", "Permission:Delete": "Smazat" } -} \ No newline at end of file +} diff --git a/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Comments/CommentAppService.cs b/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Comments/CommentAppService.cs index cf34edb434..8ceb20427e 100644 --- a/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Comments/CommentAppService.cs +++ b/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Comments/CommentAppService.cs @@ -86,6 +86,8 @@ namespace Volo.Blogging.Comments comment = await _commentRepository.InsertAsync(comment); + await CurrentUnitOfWork.SaveChangesAsync(); + return ObjectMapper.Map(comment); } diff --git a/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Localization/Resources/cs.json b/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Localization/Resources/cs.json index 5e49b65288..2761ca0278 100644 --- a/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Localization/Resources/cs.json +++ b/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Localization/Resources/cs.json @@ -42,6 +42,8 @@ "CreationTime": "Vytvořeno", "Description": "Popis", "Blogs": "Blogy", - "Tags": "Tagy" + "Tags": "Tagy", + "ShareOn": "Sdílet na", + "TitleLengthWarning": "Zachovejte velikost titulku pod 60 znaků kvůli SEO!" } -} \ No newline at end of file +} diff --git a/modules/blogging/src/Volo.Blogging.Web/BloggingMenuContributor.cs b/modules/blogging/src/Volo.Blogging.Web/BloggingMenuContributor.cs index cf1f12ed02..15d9e75184 100644 --- a/modules/blogging/src/Volo.Blogging.Web/BloggingMenuContributor.cs +++ b/modules/blogging/src/Volo.Blogging.Web/BloggingMenuContributor.cs @@ -19,15 +19,14 @@ namespace Volo.Blogging private async Task ConfigureMainMenu(MenuConfigurationContext context) { - var authorizationService = context.ServiceProvider.GetRequiredService(); - var l = context.ServiceProvider.GetRequiredService>(); + var l = context.GetLocalizer(); - if (await authorizationService.IsGrantedAsync(BloggingPermissions.Blogs.Management)) + if (await context.IsGrantedAsync(BloggingPermissions.Blogs.Management)) { var managementRootMenuItem = new ApplicationMenuItem("BlogManagement", l["Menu:BlogManagement"]); //TODO: Using the same permission. Reconsider. - if (await authorizationService.IsGrantedAsync(BloggingPermissions.Blogs.Management)) + if (await context.IsGrantedAsync(BloggingPermissions.Blogs.Management)) { managementRootMenuItem.AddItem(new ApplicationMenuItem("BlogManagement.Blogs", l["Menu:Blogs"], "/Admin/Blogs")); } diff --git a/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Detail.cshtml b/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Detail.cshtml index 2efd9b1d22..bf8c121688 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Detail.cshtml +++ b/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Detail.cshtml @@ -151,7 +151,7 @@ } else { - @L["LeaveComment"] + @L["LeaveComment"] } @@ -342,7 +342,7 @@ } else { - @L["LeaveComment"] + @L["LeaveComment"] } diff --git a/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/new.js b/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/new.js index 3edacd49b5..f5686e64e6 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/new.js +++ b/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/new.js @@ -118,6 +118,9 @@ $titleLengthWarning.hide(); } + title = title.replace(' &', ' '); + title = title.replace('& ', ' '); + title = title.replace('&', ''); title = title.replace(' ', '-'); title = title.replace(new RegExp(' ', 'g'), '-'); reflectedChange = true; diff --git a/modules/blogging/src/Volo.Blogging.Web/Volo.Blogging.Web.csproj b/modules/blogging/src/Volo.Blogging.Web/Volo.Blogging.Web.csproj index 19d156f10b..3760fa0539 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Volo.Blogging.Web.csproj +++ b/modules/blogging/src/Volo.Blogging.Web/Volo.Blogging.Web.csproj @@ -23,10 +23,8 @@ - - diff --git a/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/BloggingDomainTestModule.cs b/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/BloggingDomainTestModule.cs index 01ab4b07c7..a910ae1048 100644 --- a/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/BloggingDomainTestModule.cs +++ b/modules/blogging/test/Volo.Blogging.Domain.Tests/Volo/Abp/Blogging/BloggingDomainTestModule.cs @@ -1,4 +1,5 @@ using Volo.Abp.Modularity; +using Volo.Blogging.EntityFrameworkCore; namespace Volo.Blogging { diff --git a/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo.Blogging.EntityFrameworkCore.Tests.csproj b/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo.Blogging.EntityFrameworkCore.Tests.csproj index ffae92b6c8..1061e14347 100644 --- a/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo.Blogging.EntityFrameworkCore.Tests.csproj +++ b/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo.Blogging.EntityFrameworkCore.Tests.csproj @@ -6,11 +6,13 @@ - - - + + + + + diff --git a/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Blogs/BlogRepository_Tests.cs b/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Blogs/BlogRepository_Tests.cs index 4735926cc2..61ff1a6b44 100644 --- a/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Blogs/BlogRepository_Tests.cs +++ b/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Blogs/BlogRepository_Tests.cs @@ -1,4 +1,5 @@ using Volo.Blogging.Blogs; +using Volo.Blogging.EntityFrameworkCore; namespace Volo.Blogging { diff --git a/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Comments/CommentRepository_Tests.cs b/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Comments/CommentRepository_Tests.cs index 5013e633fb..28a821dfa0 100644 --- a/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Comments/CommentRepository_Tests.cs +++ b/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Comments/CommentRepository_Tests.cs @@ -1,4 +1,5 @@ using Volo.Blogging.Comments; +using Volo.Blogging.EntityFrameworkCore; namespace Volo.Blogging { diff --git a/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/EntityFrameworkCore/BloggingEntityFrameworkCoreTestModule.cs b/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/EntityFrameworkCore/BloggingEntityFrameworkCoreTestModule.cs index 59f0be4a24..0a27448f6e 100644 --- a/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/EntityFrameworkCore/BloggingEntityFrameworkCoreTestModule.cs +++ b/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/EntityFrameworkCore/BloggingEntityFrameworkCoreTestModule.cs @@ -4,14 +4,15 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; using Volo.Abp; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore.Sqlite; using Volo.Abp.Modularity; -using Volo.Blogging.EntityFrameworkCore; -namespace Volo.Blogging +namespace Volo.Blogging.EntityFrameworkCore { [DependsOn( typeof(BloggingEntityFrameworkCoreModule), - typeof(BloggingTestBaseModule) + typeof(BloggingTestBaseModule), + typeof(AbpEntityFrameworkCoreSqliteModule) )] public class BloggingEntityFrameworkCoreTestModule : AbpModule { diff --git a/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Posts/PostRepository_Tests.cs b/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Posts/PostRepository_Tests.cs index e23247de86..25526a7fed 100644 --- a/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Posts/PostRepository_Tests.cs +++ b/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Posts/PostRepository_Tests.cs @@ -1,4 +1,5 @@ -using Volo.Blogging.Posts; +using Volo.Blogging.EntityFrameworkCore; +using Volo.Blogging.Posts; namespace Volo.Blogging { diff --git a/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Tagging/TagRepository_Tests.cs b/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Tagging/TagRepository_Tests.cs index 5de23c7665..889768a7f2 100644 --- a/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Tagging/TagRepository_Tests.cs +++ b/modules/blogging/test/Volo.Blogging.EntityFrameworkCore.Tests/Volo/Blogging/Tagging/TagRepository_Tests.cs @@ -1,4 +1,5 @@ -using Volo.Blogging.Tagging; +using Volo.Blogging.EntityFrameworkCore; +using Volo.Blogging.Tagging; namespace Volo.Blogging { diff --git a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/MongoDB/MongoDbFixture.cs b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/MongoDB/MongoDbFixture.cs index d5fbfb4451..71824397b4 100644 --- a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/MongoDB/MongoDbFixture.cs +++ b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/MongoDB/MongoDbFixture.cs @@ -5,12 +5,18 @@ namespace Volo.Blogging.MongoDB { public class MongoDbFixture : IDisposable { - private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); - public static readonly string ConnectionString = MongoDbRunner.ConnectionString; + private static readonly MongoDbRunner MongoDbRunner; + public static readonly string ConnectionString; + + static MongoDbFixture() + { + MongoDbRunner = MongoDbRunner.Start(); + ConnectionString = MongoDbRunner.ConnectionString; + } public void Dispose() { MongoDbRunner?.Dispose(); } } -} \ No newline at end of file +} diff --git a/modules/client-simulation/src/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj b/modules/client-simulation/src/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj index 57b87b9e72..e8358805b7 100644 --- a/modules/client-simulation/src/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj +++ b/modules/client-simulation/src/Volo.ClientSimulation.Web/Volo.ClientSimulation.Web.csproj @@ -20,11 +20,9 @@ - - diff --git a/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20200312050853_init.Designer.cs b/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20200525053411_init.Designer.cs similarity index 78% rename from modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20200312050853_init.Designer.cs rename to modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20200525053411_init.Designer.cs index 0309e4cbc4..2de428f388 100644 --- a/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20200312050853_init.Designer.cs +++ b/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20200525053411_init.Designer.cs @@ -10,14 +10,14 @@ using VoloDocs.EntityFrameworkCore; namespace VoloDocs.EntityFrameworkCore.Migrations { [DbContext(typeof(VoloDocsDbContext))] - [Migration("20200312050853_init")] + [Migration("20200525053411_init")] partial class init { protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "3.1.1") + .HasAnnotation("ProductVersion", "3.1.2") .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); @@ -109,6 +109,7 @@ namespace VoloDocs.EntityFrameworkCore.Migrations .HasMaxLength(256); b.Property("TenantId") + .HasColumnName("TenantId") .HasColumnType("uniqueidentifier"); b.HasKey("Id"); @@ -136,6 +137,7 @@ namespace VoloDocs.EntityFrameworkCore.Migrations .HasColumnType("uniqueidentifier"); b.Property("TenantId") + .HasColumnName("TenantId") .HasColumnType("uniqueidentifier"); b.HasKey("Id"); @@ -305,6 +307,7 @@ namespace VoloDocs.EntityFrameworkCore.Migrations .HasMaxLength(1024); b.Property("TenantId") + .HasColumnName("TenantId") .HasColumnType("uniqueidentifier"); b.Property("UserId") @@ -336,6 +339,7 @@ namespace VoloDocs.EntityFrameworkCore.Migrations .HasMaxLength(196); b.Property("TenantId") + .HasColumnName("TenantId") .HasColumnType("uniqueidentifier"); b.HasKey("UserId", "LoginProvider"); @@ -345,6 +349,33 @@ namespace VoloDocs.EntityFrameworkCore.Migrations b.ToTable("AbpUserLogins"); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnName("CreationTime") + .HasColumnType("datetime2"); + + b.Property("CreatorId") + .HasColumnName("CreatorId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnName("TenantId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("OrganizationUnitId", "UserId"); + + b.HasIndex("UserId", "OrganizationUnitId"); + + b.ToTable("AbpUserOrganizationUnits"); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => { b.Property("UserId") @@ -354,6 +385,7 @@ namespace VoloDocs.EntityFrameworkCore.Migrations .HasColumnType("uniqueidentifier"); b.Property("TenantId") + .HasColumnName("TenantId") .HasColumnType("uniqueidentifier"); b.HasKey("UserId", "RoleId"); @@ -377,6 +409,7 @@ namespace VoloDocs.EntityFrameworkCore.Migrations .HasMaxLength(128); b.Property("TenantId") + .HasColumnName("TenantId") .HasColumnType("uniqueidentifier"); b.Property("Value") @@ -387,6 +420,106 @@ namespace VoloDocs.EntityFrameworkCore.Migrations b.ToTable("AbpUserTokens"); }); + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Code") + .IsRequired() + .HasColumnName("Code") + .HasColumnType("nvarchar(95)") + .HasMaxLength(95); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .HasColumnName("CreationTime") + .HasColumnType("datetime2"); + + b.Property("CreatorId") + .HasColumnName("CreatorId") + .HasColumnType("uniqueidentifier"); + + b.Property("DeleterId") + .HasColumnName("DeleterId") + .HasColumnType("uniqueidentifier"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime") + .HasColumnType("datetime2"); + + b.Property("DisplayName") + .IsRequired() + .HasColumnName("DisplayName") + .HasColumnType("nvarchar(128)") + .HasMaxLength(128); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime") + .HasColumnType("datetime2"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId") + .HasColumnType("uniqueidentifier"); + + b.Property("ParentId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnName("TenantId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("Code"); + + b.HasIndex("ParentId"); + + b.ToTable("AbpOrganizationUnits"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnName("CreationTime") + .HasColumnType("datetime2"); + + b.Property("CreatorId") + .HasColumnName("CreatorId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnName("TenantId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("OrganizationUnitId", "RoleId"); + + b.HasIndex("RoleId", "OrganizationUnitId"); + + b.ToTable("AbpOrganizationUnitRoles"); + }); + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => { b.Property("Id") @@ -409,6 +542,7 @@ namespace VoloDocs.EntityFrameworkCore.Migrations .HasMaxLength(64); b.Property("TenantId") + .HasColumnName("TenantId") .HasColumnType("uniqueidentifier"); b.HasKey("Id"); @@ -635,6 +769,21 @@ namespace VoloDocs.EntityFrameworkCore.Migrations .IsRequired(); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("OrganizationUnits") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => { b.HasOne("Volo.Abp.Identity.IdentityRole", null) @@ -659,6 +808,28 @@ namespace VoloDocs.EntityFrameworkCore.Migrations .IsRequired(); }); + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("ParentId"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany("Roles") + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + modelBuilder.Entity("Volo.Docs.Documents.DocumentContributor", b => { b.HasOne("Volo.Docs.Documents.Document", null) diff --git a/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20200312050853_init.cs b/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20200525053411_init.cs similarity index 76% rename from modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20200312050853_init.cs rename to modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20200525053411_init.cs index 938fbc6a1b..00854e6745 100644 --- a/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20200312050853_init.cs +++ b/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20200525053411_init.cs @@ -27,6 +27,36 @@ namespace VoloDocs.EntityFrameworkCore.Migrations table.PrimaryKey("PK_AbpClaimTypes", x => x.Id); }); + migrationBuilder.CreateTable( + name: "AbpOrganizationUnits", + columns: table => new + { + Id = table.Column(nullable: false), + ExtraProperties = table.Column(nullable: true), + ConcurrencyStamp = table.Column(nullable: true), + CreationTime = table.Column(nullable: false), + CreatorId = table.Column(nullable: true), + LastModificationTime = table.Column(nullable: true), + LastModifierId = table.Column(nullable: true), + IsDeleted = table.Column(nullable: false, defaultValue: false), + DeleterId = table.Column(nullable: true), + DeletionTime = table.Column(nullable: true), + TenantId = table.Column(nullable: true), + ParentId = table.Column(nullable: true), + Code = table.Column(maxLength: 95, nullable: false), + DisplayName = table.Column(maxLength: 128, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpOrganizationUnits", x => x.Id); + table.ForeignKey( + name: "FK_AbpOrganizationUnits_AbpOrganizationUnits_ParentId", + column: x => x.ParentId, + principalTable: "AbpOrganizationUnits", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + migrationBuilder.CreateTable( name: "AbpPermissionGrants", columns: table => new @@ -163,6 +193,33 @@ namespace VoloDocs.EntityFrameworkCore.Migrations table.PrimaryKey("PK_DocsProjects", x => x.Id); }); + migrationBuilder.CreateTable( + name: "AbpOrganizationUnitRoles", + columns: table => new + { + RoleId = table.Column(nullable: false), + OrganizationUnitId = table.Column(nullable: false), + CreationTime = table.Column(nullable: false), + CreatorId = table.Column(nullable: true), + TenantId = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpOrganizationUnitRoles", x => new { x.OrganizationUnitId, x.RoleId }); + table.ForeignKey( + name: "FK_AbpOrganizationUnitRoles_AbpOrganizationUnits_OrganizationUnitId", + column: x => x.OrganizationUnitId, + principalTable: "AbpOrganizationUnits", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AbpOrganizationUnitRoles_AbpRoles_RoleId", + column: x => x.RoleId, + principalTable: "AbpRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "AbpRoleClaims", columns: table => new @@ -226,6 +283,33 @@ namespace VoloDocs.EntityFrameworkCore.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "AbpUserOrganizationUnits", + columns: table => new + { + UserId = table.Column(nullable: false), + OrganizationUnitId = table.Column(nullable: false), + CreationTime = table.Column(nullable: false), + CreatorId = table.Column(nullable: true), + TenantId = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUserOrganizationUnits", x => new { x.OrganizationUnitId, x.UserId }); + table.ForeignKey( + name: "FK_AbpUserOrganizationUnits_AbpOrganizationUnits_OrganizationUnitId", + column: x => x.OrganizationUnitId, + principalTable: "AbpOrganizationUnits", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AbpUserOrganizationUnits_AbpUsers_UserId", + column: x => x.UserId, + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + migrationBuilder.CreateTable( name: "AbpUserRoles", columns: table => new @@ -292,6 +376,21 @@ namespace VoloDocs.EntityFrameworkCore.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateIndex( + name: "IX_AbpOrganizationUnitRoles_RoleId_OrganizationUnitId", + table: "AbpOrganizationUnitRoles", + columns: new[] { "RoleId", "OrganizationUnitId" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpOrganizationUnits_Code", + table: "AbpOrganizationUnits", + column: "Code"); + + migrationBuilder.CreateIndex( + name: "IX_AbpOrganizationUnits_ParentId", + table: "AbpOrganizationUnits", + column: "ParentId"); + migrationBuilder.CreateIndex( name: "IX_AbpPermissionGrants_Name_ProviderName_ProviderKey", table: "AbpPermissionGrants", @@ -322,6 +421,11 @@ namespace VoloDocs.EntityFrameworkCore.Migrations table: "AbpUserLogins", columns: new[] { "LoginProvider", "ProviderKey" }); + migrationBuilder.CreateIndex( + name: "IX_AbpUserOrganizationUnits_UserId_OrganizationUnitId", + table: "AbpUserOrganizationUnits", + columns: new[] { "UserId", "OrganizationUnitId" }); + migrationBuilder.CreateIndex( name: "IX_AbpUserRoles_RoleId_UserId", table: "AbpUserRoles", @@ -353,6 +457,9 @@ namespace VoloDocs.EntityFrameworkCore.Migrations migrationBuilder.DropTable( name: "AbpClaimTypes"); + migrationBuilder.DropTable( + name: "AbpOrganizationUnitRoles"); + migrationBuilder.DropTable( name: "AbpPermissionGrants"); @@ -368,6 +475,9 @@ namespace VoloDocs.EntityFrameworkCore.Migrations migrationBuilder.DropTable( name: "AbpUserLogins"); + migrationBuilder.DropTable( + name: "AbpUserOrganizationUnits"); + migrationBuilder.DropTable( name: "AbpUserRoles"); @@ -380,6 +490,9 @@ namespace VoloDocs.EntityFrameworkCore.Migrations migrationBuilder.DropTable( name: "DocsProjects"); + migrationBuilder.DropTable( + name: "AbpOrganizationUnits"); + migrationBuilder.DropTable( name: "AbpRoles"); diff --git a/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/VoloDocsDbContextModelSnapshot.cs b/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/VoloDocsDbContextModelSnapshot.cs index 165f0428cc..216a9a06f4 100644 --- a/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/VoloDocsDbContextModelSnapshot.cs +++ b/modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/VoloDocsDbContextModelSnapshot.cs @@ -15,7 +15,7 @@ namespace VoloDocs.EntityFrameworkCore.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "3.1.1") + .HasAnnotation("ProductVersion", "3.1.2") .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); @@ -107,6 +107,7 @@ namespace VoloDocs.EntityFrameworkCore.Migrations .HasMaxLength(256); b.Property("TenantId") + .HasColumnName("TenantId") .HasColumnType("uniqueidentifier"); b.HasKey("Id"); @@ -134,6 +135,7 @@ namespace VoloDocs.EntityFrameworkCore.Migrations .HasColumnType("uniqueidentifier"); b.Property("TenantId") + .HasColumnName("TenantId") .HasColumnType("uniqueidentifier"); b.HasKey("Id"); @@ -303,6 +305,7 @@ namespace VoloDocs.EntityFrameworkCore.Migrations .HasMaxLength(1024); b.Property("TenantId") + .HasColumnName("TenantId") .HasColumnType("uniqueidentifier"); b.Property("UserId") @@ -334,6 +337,7 @@ namespace VoloDocs.EntityFrameworkCore.Migrations .HasMaxLength(196); b.Property("TenantId") + .HasColumnName("TenantId") .HasColumnType("uniqueidentifier"); b.HasKey("UserId", "LoginProvider"); @@ -343,6 +347,33 @@ namespace VoloDocs.EntityFrameworkCore.Migrations b.ToTable("AbpUserLogins"); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnName("CreationTime") + .HasColumnType("datetime2"); + + b.Property("CreatorId") + .HasColumnName("CreatorId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnName("TenantId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("OrganizationUnitId", "UserId"); + + b.HasIndex("UserId", "OrganizationUnitId"); + + b.ToTable("AbpUserOrganizationUnits"); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => { b.Property("UserId") @@ -352,6 +383,7 @@ namespace VoloDocs.EntityFrameworkCore.Migrations .HasColumnType("uniqueidentifier"); b.Property("TenantId") + .HasColumnName("TenantId") .HasColumnType("uniqueidentifier"); b.HasKey("UserId", "RoleId"); @@ -375,6 +407,7 @@ namespace VoloDocs.EntityFrameworkCore.Migrations .HasMaxLength(128); b.Property("TenantId") + .HasColumnName("TenantId") .HasColumnType("uniqueidentifier"); b.Property("Value") @@ -385,6 +418,106 @@ namespace VoloDocs.EntityFrameworkCore.Migrations b.ToTable("AbpUserTokens"); }); + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Code") + .IsRequired() + .HasColumnName("Code") + .HasColumnType("nvarchar(95)") + .HasMaxLength(95); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("CreationTime") + .HasColumnName("CreationTime") + .HasColumnType("datetime2"); + + b.Property("CreatorId") + .HasColumnName("CreatorId") + .HasColumnType("uniqueidentifier"); + + b.Property("DeleterId") + .HasColumnName("DeleterId") + .HasColumnType("uniqueidentifier"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime") + .HasColumnType("datetime2"); + + b.Property("DisplayName") + .IsRequired() + .HasColumnName("DisplayName") + .HasColumnType("nvarchar(128)") + .HasMaxLength(128); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime") + .HasColumnType("datetime2"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId") + .HasColumnType("uniqueidentifier"); + + b.Property("ParentId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnName("TenantId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("Code"); + + b.HasIndex("ParentId"); + + b.ToTable("AbpOrganizationUnits"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreationTime") + .HasColumnName("CreationTime") + .HasColumnType("datetime2"); + + b.Property("CreatorId") + .HasColumnName("CreatorId") + .HasColumnType("uniqueidentifier"); + + b.Property("TenantId") + .HasColumnName("TenantId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("OrganizationUnitId", "RoleId"); + + b.HasIndex("RoleId", "OrganizationUnitId"); + + b.ToTable("AbpOrganizationUnitRoles"); + }); + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => { b.Property("Id") @@ -407,6 +540,7 @@ namespace VoloDocs.EntityFrameworkCore.Migrations .HasMaxLength(64); b.Property("TenantId") + .HasColumnName("TenantId") .HasColumnType("uniqueidentifier"); b.HasKey("Id"); @@ -633,6 +767,21 @@ namespace VoloDocs.EntityFrameworkCore.Migrations .IsRequired(); }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("OrganizationUnits") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => { b.HasOne("Volo.Abp.Identity.IdentityRole", null) @@ -657,6 +806,28 @@ namespace VoloDocs.EntityFrameworkCore.Migrations .IsRequired(); }); + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("ParentId"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany("Roles") + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + modelBuilder.Entity("Volo.Docs.Documents.DocumentContributor", b => { b.HasOne("Volo.Docs.Documents.Document", null) diff --git a/modules/docs/app/VoloDocs.Web/Localization/Resources/VoloDocs/Web/cs.json b/modules/docs/app/VoloDocs.Web/Localization/Resources/VoloDocs/Web/cs.json index 54e544e43e..3ffad5d85a 100644 --- a/modules/docs/app/VoloDocs.Web/Localization/Resources/VoloDocs/Web/cs.json +++ b/modules/docs/app/VoloDocs.Web/Localization/Resources/VoloDocs/Web/cs.json @@ -7,4 +7,4 @@ "CreateYourFirstProject": "Klikněte zde k vytvoření vašeho prvního projektu", "NoProject": "Žádný projekt!" } -} \ No newline at end of file +} diff --git a/modules/docs/app/VoloDocs.Web/VoloDocs.Web.csproj b/modules/docs/app/VoloDocs.Web/VoloDocs.Web.csproj index b7b70d8b03..3fb341be49 100644 --- a/modules/docs/app/VoloDocs.Web/VoloDocs.Web.csproj +++ b/modules/docs/app/VoloDocs.Web/VoloDocs.Web.csproj @@ -20,7 +20,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/cs.json b/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/cs.json index 9c44d1d10f..64ecaa2f47 100644 --- a/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/cs.json +++ b/modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/cs.json @@ -6,11 +6,13 @@ "Permission:Edit": "Upravit", "Permission:Delete": "Smazat", "Permission:Create": "Vytvořit", + "Permission:Documents": "Dokumenty", "Menu:DocumentManagement": "Dokumenty", "Menu:ProjectManagement": "Projekty", "CreateANewProject": "Vytvořit nový projekt", "Edit": "Upravit", "Create": "Vytvořit", + "Pull": "Pull", "Projects": "Projekty", "Name": "Název", "ShortName": "Krátký název", @@ -27,6 +29,9 @@ "DisplayName:LatestVersionBranchName": "Název poslední verze odvětví", "DisplayName:GitHubRootUrl": "GitHub kořenové URL", "DisplayName:GitHubAccessToken": "GitHub přístupový token", - "DisplayName:GitHubUserAgent": "GitHub user agent" + "DisplayName:GitHubUserAgent": "GitHub user agent", + "DisplayName:All": "Pull všeho", + "DisplayName:LanguageCode": "Kód jazyka", + "DisplayName:Version": "Verze" } -} \ No newline at end of file +} diff --git a/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Documents/DocumentAdminAppService.cs b/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Documents/DocumentAdminAppService.cs index 92b809bf87..744ccac5b6 100644 --- a/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Documents/DocumentAdminAppService.cs +++ b/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Documents/DocumentAdminAppService.cs @@ -90,6 +90,12 @@ namespace Volo.Docs.Admin.Documents var documents = new List(); foreach (var leaf in leafs) { + if (leaf.Path.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || + leaf.Path.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + var sourceDocument = await source.GetDocumentAsync(project, leaf.Path, input.LanguageCode, input.Version); documents.Add(sourceDocument); diff --git a/modules/docs/src/Volo.Docs.Admin.Web/Navigation/DocsMenuContributor.cs b/modules/docs/src/Volo.Docs.Admin.Web/Navigation/DocsMenuContributor.cs index 4097470c60..51ad4e79b9 100644 --- a/modules/docs/src/Volo.Docs.Admin.Web/Navigation/DocsMenuContributor.cs +++ b/modules/docs/src/Volo.Docs.Admin.Web/Navigation/DocsMenuContributor.cs @@ -19,17 +19,15 @@ namespace Volo.Docs.Admin.Navigation private async Task ConfigureMainMenu(MenuConfigurationContext context) { - var administrationMenu = context.Menu.GetAdministration(); - var authorizationService = context.ServiceProvider.GetRequiredService(); - var l = context.ServiceProvider.GetRequiredService>(); + var l = context.GetLocalizer(); var rootMenuItem = new ApplicationMenuItem(DocsMenuNames.GroupName, l["Menu:DocumentManagement"], icon: "fa fa-book"); administrationMenu.AddItem(rootMenuItem); - if (await authorizationService.IsGrantedAsync(DocsAdminPermissions.Projects.Default)) + if (await context.IsGrantedAsync(DocsAdminPermissions.Projects.Default)) { rootMenuItem.AddItem(new ApplicationMenuItem(DocsMenuNames.Projects, l["Menu:ProjectManagement"], "/Docs/Admin/Projects")); } diff --git a/modules/docs/src/Volo.Docs.Admin.Web/Volo.Docs.Admin.Web.csproj b/modules/docs/src/Volo.Docs.Admin.Web/Volo.Docs.Admin.Web.csproj index 4cad3d9e71..14aa5d167d 100644 --- a/modules/docs/src/Volo.Docs.Admin.Web/Volo.Docs.Admin.Web.csproj +++ b/modules/docs/src/Volo.Docs.Admin.Web/Volo.Docs.Admin.Web.csproj @@ -23,10 +23,8 @@ - - diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/ElasticDocumentFullSearch.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/ElasticDocumentFullSearch.cs index 854032ab2c..cbe5905d3a 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/ElasticDocumentFullSearch.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/ElasticDocumentFullSearch.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Elasticsearch.Net; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Nest; using Volo.Abp; @@ -14,10 +15,14 @@ namespace Volo.Docs.Documents.FullSearch.Elastic { private readonly IElasticClientProvider _clientProvider; private readonly DocsElasticSearchOptions _options; + private readonly ILogger _logger; - public ElasticDocumentFullSearch(IElasticClientProvider clientProvider, IOptions options) + public ElasticDocumentFullSearch(IElasticClientProvider clientProvider, + IOptions options, + ILogger logger) { _clientProvider = clientProvider; + _logger = logger; _options = options.Value; } @@ -215,7 +220,8 @@ namespace Volo.Docs.Documents.FullSearch.Elastic { if (!response.ApiCall.Success) { - throw response.ApiCall.OriginalException; + _logger.LogError(response.ApiCall.OriginalException, + "An error occurred in the elastic search api call."); } } diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/cs.json b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/cs.json index 743218aeba..d3c4238d6f 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/cs.json +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/cs.json @@ -7,15 +7,32 @@ "ShareOn": "Sdílet na", "Version": "Verze", "Edit": "Upravit", + "LastEditTime": "Naposled upraveno", "Delete": "Smazat", + "ClearCache": "Vymazat mezipaměť", + "ClearCacheConfirmationMessage": "Jste si jistí že chcete vymazat veškerou mezipaměť projektu \"{0}\"", + "ReIndexAllProjects": "ReIndexovat všechny projekty", + "ReIndexProject": "ReIndexovat projekt", + "ReIndexProjectConfirmationMessage": "Opravdu reindexovat projekt \"{0}\"", + "SuccessfullyReIndexProject": "Úspěšně reindexován projekt \"{0}\"", + "ReIndexAllProjectConfirmationMessage": "Opravdu reindexovat všechny projekty?", + "SuccessfullyReIndexAllProject": "Úspěšně reindexovány všechny projekty", "InThisDocument": "V tomto dokumentu", "GoToTop": "Přejít nahoru", "Projects": "Projekt(y)", "NoProjectWarning": "Zatím zde není žádný projekt!", - "DocumentNotFound": "Ups, vyžádaný dokument neexistuje!", + "DocumentNotFound": "Ups, vyžádaný dokument nebyl nalezen!", + "ProjectNotFound": "Ups, vyžádaný projekt nebyl nalezen!", "NavigationDocumentNotFound": "Tato verze nemá navigační dokument!", "DocumentNotFoundInSelectedLanguage": "Tento dokument není dostupný ve vybraném jazyce. Zobrazen dokument ve výchozím jazyce.", "FilterTopics": "Filtrovat témata", - "MultipleVersionDocumentInfo": "Tento dokument má více verzí. Vyberte možnosti, které vám nejlépe vyhovují." + "FullSearch": "Hledat v dokumentech", + "Volo.Docs.Domain:010001": "Elastické vyhledávání není povoleno.", + "MultipleVersionDocumentInfo": "Tento dokument má více verzí. Vyberte možnosti, které vám nejlépe vyhovují.", + "New": "Nové", + "Upd": "Akt", + "NewExplanation": "Vytvořeno v posledních dvou týdnech.", + "UpdatedExplanation": "Aktualizováno v posledních dvou týdnech.", + "Volo.Docs.Domain:010002": "ShortName {ShortName} už existuje." } -} \ No newline at end of file +} diff --git a/modules/docs/src/Volo.Docs.Web/Volo.Docs.Web.csproj b/modules/docs/src/Volo.Docs.Web/Volo.Docs.Web.csproj index afec611259..013435afda 100644 --- a/modules/docs/src/Volo.Docs.Web/Volo.Docs.Web.csproj +++ b/modules/docs/src/Volo.Docs.Web/Volo.Docs.Web.csproj @@ -24,14 +24,12 @@ - - + + - - diff --git a/modules/docs/test/Volo.Docs.EntityFrameworkCore.Tests/Volo.Docs.EntityFrameworkCore.Tests.csproj b/modules/docs/test/Volo.Docs.EntityFrameworkCore.Tests/Volo.Docs.EntityFrameworkCore.Tests.csproj index 7f8c963c0c..b41999464c 100644 --- a/modules/docs/test/Volo.Docs.EntityFrameworkCore.Tests/Volo.Docs.EntityFrameworkCore.Tests.csproj +++ b/modules/docs/test/Volo.Docs.EntityFrameworkCore.Tests/Volo.Docs.EntityFrameworkCore.Tests.csproj @@ -7,14 +7,13 @@ - - - + + diff --git a/modules/docs/test/Volo.Docs.EntityFrameworkCore.Tests/Volo/Docs/EntityFrameworkCore/DocsEntityFrameworkCoreTestModule.cs b/modules/docs/test/Volo.Docs.EntityFrameworkCore.Tests/Volo/Docs/EntityFrameworkCore/DocsEntityFrameworkCoreTestModule.cs index a4d5eb5a25..eb3a5ae5bd 100644 --- a/modules/docs/test/Volo.Docs.EntityFrameworkCore.Tests/Volo/Docs/EntityFrameworkCore/DocsEntityFrameworkCoreTestModule.cs +++ b/modules/docs/test/Volo.Docs.EntityFrameworkCore.Tests/Volo/Docs/EntityFrameworkCore/DocsEntityFrameworkCoreTestModule.cs @@ -3,13 +3,15 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore.Sqlite; using Volo.Abp.Modularity; namespace Volo.Docs.EntityFrameworkCore { [DependsOn( typeof(DocsTestBaseModule), - typeof(DocsEntityFrameworkCoreModule) + typeof(DocsEntityFrameworkCoreModule), + typeof(AbpEntityFrameworkCoreSqliteModule) )] public class DocsEntityFrameworkCoreTestModule : AbpModule { diff --git a/modules/docs/test/Volo.Docs.MongoDB.Tests/Volo/Docs/MongoDB/MongoDbFixture.cs b/modules/docs/test/Volo.Docs.MongoDB.Tests/Volo/Docs/MongoDB/MongoDbFixture.cs index fc920813ab..9a40d79492 100644 --- a/modules/docs/test/Volo.Docs.MongoDB.Tests/Volo/Docs/MongoDB/MongoDbFixture.cs +++ b/modules/docs/test/Volo.Docs.MongoDB.Tests/Volo/Docs/MongoDB/MongoDbFixture.cs @@ -5,12 +5,18 @@ namespace Volo.Docs.MongoDB { public class MongoDbFixture : IDisposable { - private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); - public static readonly string ConnectionString = MongoDbRunner.ConnectionString; + private static readonly MongoDbRunner MongoDbRunner; + public static readonly string ConnectionString; + + static MongoDbFixture() + { + MongoDbRunner = MongoDbRunner.Start(); + ConnectionString = MongoDbRunner.ConnectionString; + } public void Dispose() { MongoDbRunner?.Dispose(); } } -} \ No newline at end of file +} diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/Domain/cs.json b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/Domain/cs.json index 47f49bbc50..466d626391 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/Domain/cs.json +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/Domain/cs.json @@ -4,4 +4,4 @@ "Features": "Funkce", "NoFeatureFoundMessage": "Nejsou zde dostupné žádné funkce." } -} \ No newline at end of file +} diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Volo.Abp.FeatureManagement.Web.csproj b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Volo.Abp.FeatureManagement.Web.csproj index b4917d34d6..a60c57040e 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Volo.Abp.FeatureManagement.Web.csproj +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Volo.Abp.FeatureManagement.Web.csproj @@ -12,10 +12,8 @@ - - diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests.csproj b/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests.csproj index ef8877a1f1..31d4a3065f 100644 --- a/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests.csproj +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests.csproj @@ -10,13 +10,12 @@ + - - - + diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo/Abp/FeatureManagement/EntityFrameworkCore/AbpFeatureManagementEntityFrameworkCoreTestModule.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo/Abp/FeatureManagement/EntityFrameworkCore/AbpFeatureManagementEntityFrameworkCoreTestModule.cs index d5c573a360..45c53d90ab 100644 --- a/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo/Abp/FeatureManagement/EntityFrameworkCore/AbpFeatureManagementEntityFrameworkCoreTestModule.cs +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo/Abp/FeatureManagement/EntityFrameworkCore/AbpFeatureManagementEntityFrameworkCoreTestModule.cs @@ -3,13 +3,15 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore.Sqlite; using Volo.Abp.Modularity; namespace Volo.Abp.FeatureManagement.EntityFrameworkCore { [DependsOn( typeof(FeatureManagementTestBaseModule), - typeof(AbpFeatureManagementEntityFrameworkCoreModule) + typeof(AbpFeatureManagementEntityFrameworkCoreModule), + typeof(AbpEntityFrameworkCoreSqliteModule) )] public class AbpFeatureManagementEntityFrameworkCoreTestModule : AbpModule { diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/MongoDbFixture.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/MongoDbFixture.cs index bfd7fa9c04..bb89a83f76 100644 --- a/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/MongoDbFixture.cs +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/MongoDbFixture.cs @@ -5,12 +5,18 @@ namespace Volo.Abp.FeatureManagement.MongoDB { public class MongoDbFixture : IDisposable { - private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); - public static readonly string ConnectionString = MongoDbRunner.ConnectionString; + private static readonly MongoDbRunner MongoDbRunner; + public static readonly string ConnectionString; + + static MongoDbFixture() + { + MongoDbRunner = MongoDbRunner.Start(); + ConnectionString = MongoDbRunner.ConnectionString; + } public void Dispose() { MongoDbRunner?.Dispose(); } } -} \ No newline at end of file +} diff --git a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateOrUpdateDtoBase.cs b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateOrUpdateDtoBase.cs index ec8b4ef213..c18f2c16b5 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateOrUpdateDtoBase.cs +++ b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateOrUpdateDtoBase.cs @@ -1,4 +1,5 @@ -using System.ComponentModel.DataAnnotations; +using System; +using System.ComponentModel.DataAnnotations; using JetBrains.Annotations; using Volo.Abp.ObjectExtending; diff --git a/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityUserAppService.cs b/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityUserAppService.cs index 9d231b18d1..166d71fa5e 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityUserAppService.cs +++ b/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityUserAppService.cs @@ -45,6 +45,8 @@ namespace Volo.Abp.Identity [Authorize(IdentityPermissions.Users.Default)] public virtual async Task> GetRolesAsync(Guid id) { + //TODO: Should also include roles of the related OUs. + var roles = await UserRepository.GetRolesAsync(id); return new ListResultDto( diff --git a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityErrorCodes.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityErrorCodes.cs similarity index 53% rename from modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityErrorCodes.cs rename to modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityErrorCodes.cs index 4bdfc5d8e9..d77ee9f5a9 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityErrorCodes.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentityErrorCodes.cs @@ -1,11 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Volo.Abp.Identity +namespace Volo.Abp.Identity { public static class IdentityErrorCodes { public const string UserSelfDeletion = "Volo.Abp.Identity:010001"; + public const string MaxAllowedOuMembership = "Volo.Abp.Identity:010002"; } } \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/cs.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/cs.json index 8517adcf12..04c85027a6 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/cs.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/cs.json @@ -63,6 +63,8 @@ "Identity.PasswordConfirmationFailed": "Heslo nesouhlasí s potvrzovacím heslem.", "Identity.StaticRoleRenamingErrorMessage": "Statické role nemohou být přejmenovány.", "Identity.StaticRoleDeletionErrorMessage": "Statické role nemohou být smazány.", + "Identity.OrganizationUnit.DuplicateDisplayNameWarning": "Organizační jednotka s názvem {0} již existuje. Na stejné úrovni nelze vytvořit více jednotek se stejným názvem.", + "Identity.OrganizationUnit.MaxUserMembershipCount": "Maximální povolený počet členů organizační jednotky pro uživatele", "Volo.Abp.Identity:010001": "Nemůžete smazat svůj vlastní účet!", "Permission:IdentityManagement": "Správa identit", "Permission:RoleManagement": "Správa rolí", @@ -82,6 +84,7 @@ "DisplayName:Abp.Identity.Lockout.LockoutDuration": "Délka blokování (sekundy)", "DisplayName:Abp.Identity.Lockout.MaxFailedAccessAttempts": "Maximální počet neúspěšných pokusů o přístup", "DisplayName:Abp.Identity.SignIn.RequireConfirmedEmail": "Požadovat potvrzený email", + "DisplayName:Abp.Identity.SignIn.EnablePhoneNumberConfirmation": "Povolit potvrzování telefonního čísla", "DisplayName:Abp.Identity.SignIn.RequireConfirmedPhoneNumber": "Požadovat potvrzené telefonní číslo", "DisplayName:Abp.Identity.User.IsUserNameUpdateEnabled": "Je povolena změna uživatelského jména", "DisplayName:Abp.Identity.User.IsEmailUpdateEnabled": "Je povolena změna emailu", @@ -95,8 +98,10 @@ "Description:Abp.Identity.Lockout.LockoutDuration": "Doba, po kterou je uživatel zablokován, když dojde k zablokování.", "Description:Abp.Identity.Lockout.MaxFailedAccessAttempts": "Počet neúspěšných pokusů o přístup než je uživatel uzamčen, za předpokladu, že je uzamčení povoleno.", "Description:Abp.Identity.SignIn.RequireConfirmedEmail": "Zda je k přihlášení vyžadována potvrzená emailová adresa.", + "Description:Abp.Identity.SignIn.EnablePhoneNumberConfirmation": "Zda telefonní číslo může být potvrzeno uživatelem.", "Description:Abp.Identity.SignIn.RequireConfirmedPhoneNumber": "Zda je pro přihlášení vyžadováno potvrzené telefonní číslo.", "Description:Abp.Identity.User.IsUserNameUpdateEnabled": "Zda může uživatel změnit uživatelské jméno.", - "Description:Abp.Identity.User.IsEmailUpdateEnabled": "Zda může uživatel změnit email." + "Description:Abp.Identity.User.IsEmailUpdateEnabled": "Zda může uživatel změnit email.", + "Volo.Abp.Identity:010002": "Nelze nastavit více než {MaxUserMembershipCount} organizačních jednotek na uživatele!" } -} \ No newline at end of file +} diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json index d6d6ca3983..8305ca9589 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/en.json @@ -63,6 +63,8 @@ "Identity.PasswordConfirmationFailed": "Password does not match the confirm password.", "Identity.StaticRoleRenamingErrorMessage": "Static roles can not be renamed.", "Identity.StaticRoleDeletionErrorMessage": "Static roles can not be deleted.", + "Identity.OrganizationUnit.DuplicateDisplayNameWarning": "There is already an organization unit with name {0}. Two units with same name can not be created in same level.", + "Identity.OrganizationUnit.MaxUserMembershipCount": "Maximum allowed organization unit membership count for a user", "Volo.Abp.Identity:010001": "You can not delete your own account!", "Permission:IdentityManagement": "Identity management", "Permission:RoleManagement": "Role management", @@ -99,6 +101,7 @@ "Description:Abp.Identity.SignIn.EnablePhoneNumberConfirmation": "Whether the phoneNumber can be confirmed by the user.", "Description:Abp.Identity.SignIn.RequireConfirmedPhoneNumber": "Whether a confirmed telephone number is required to sign in.", "Description:Abp.Identity.User.IsUserNameUpdateEnabled": "Whether the username can be updated by the user.", - "Description:Abp.Identity.User.IsEmailUpdateEnabled": "Whether the email can be updated by the user." + "Description:Abp.Identity.User.IsEmailUpdateEnabled": "Whether the email can be updated by the user.", + "Volo.Abp.Identity:010002": "Can not set more than {MaxUserMembershipCount} organization unit for a user!" } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/pt-BR.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/pt-BR.json index 4df719fffe..1dda38b2a5 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/pt-BR.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/pt-BR.json @@ -63,6 +63,7 @@ "Identity.PasswordConfirmationFailed": "A senha não confere com a confirmação de senha.", "Identity.StaticRoleRenamingErrorMessage": "Perfis estáticos não podem ser renomeados.", "Identity.StaticRoleDeletionErrorMessage": "Perfis estáticos não podem ser excluídos.", + "Identity.OrganizationUnit.DuplicateDisplayNameWarning": "Já existe uma unidade organizacional com o nome {0}. Duas unidades com o mesmo nome não pode ser criada no mesmo nível.", "Volo.Abp.Identity:010001": "Você não pode deletar sua própria conta!", "Permission:IdentityManagement": "Acessos", "Permission:RoleManagement": "Perfis", diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/tr.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/tr.json index 82efcf8f8a..0b17e51276 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/tr.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/tr.json @@ -63,6 +63,8 @@ "Identity.UserNameNotFound": "{0} kullanıcısı bulunamadı.", "Identity.UserNotInRole": "Kullanıcı '{0}' rolünde değil.", "Identity.PasswordConfirmationFailed": "Yeni şifre ile onay şifresi uyuşmuyor.", + "Identity.OrganizationUnit.DuplicateDisplayNameWarning": "{0} isminde bir birim zaten var. Aynı seviyede aynı isimli iki birim olamaz.", + "Identity.OrganizationUnit.MaxUserMembershipCount": "Bir kullanıcı için izin verilen en fazla organizasyon birimi sayısı", "Volo.Abp.Identity:010001": "Kendi hesabınızı silemezsiniz!", "Permission:IdentityManagement": "Kimlik yönetimi", "Permission:RoleManagement": "Rol yönetimi", @@ -72,7 +74,8 @@ "Permission:ChangePermissions": "İzinleri değiştirme", "Permission:UserManagement": "Kullanıcı yönetimi", "Permission:UserLookup": "Kullanıcı sorgulama", - "DisplayName:Abp.Identity.Password.RequiredLength": "Uzunluk gerekli", + "Volo.Abp.Identity:010002": "Bir kullanıcı en fazla {MaxUserMembershipCount} organizasyon birimine üye olabilir!", + "DisplayName:Abp.Identity.Password.RequiredLength": "Uzunluk gerekli", "DisplayName:Abp.Identity.Password.RequiredUniqueChars": "Tekil karakter gerekli", "DisplayName:Abp.Identity.Password.RequireNonAlphanumeric": "Alfasayısal olmayan karakter gerekli", "DisplayName:Abp.Identity.Password.RequireLowercase": "Küçük harf gerekli", diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hans.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hans.json index 4908f170fa..ffeb4d0d40 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hans.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hans.json @@ -63,6 +63,7 @@ "Identity.PasswordConfirmationFailed": "密码或确认密码不一致.", "Identity.StaticRoleRenamingErrorMessage": "无法重命名静态角色.", "Identity.StaticRoleDeletionErrorMessage": "无法删除静态角色.", + "Identity.OrganizationUnit.DuplicateDisplayNameWarning": "已存在名为 {0} 的组织单位. 无法在同一级别创建相同名称的组织单位.", "Volo.Abp.Identity:010001": "您无法删除自己的帐户!", "Permission:IdentityManagement": "身份标识管理", "Permission:RoleManagement": "角色管理", diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/OrganizationUnitConsts.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/OrganizationUnitConsts.cs new file mode 100644 index 0000000000..ad6091a44e --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/OrganizationUnitConsts.cs @@ -0,0 +1,25 @@ +namespace Volo.Abp.Identity +{ + public static class OrganizationUnitConsts + { + /// + /// Maximum length of the property. + /// + public const int MaxDisplayNameLength = 128; + + /// + /// Maximum depth of an OU hierarchy. + /// + public const int MaxDepth = 16; + + /// + /// Length of a code unit between dots. + /// + public const int CodeUnitLength = 5; + + /// + /// Maximum length of the property. + /// + public const int MaxCodeLength = MaxDepth * (CodeUnitLength + 1) - 1; + } +} diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Settings/IdentitySettingNames.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Settings/IdentitySettingNames.cs index 9c07f6c55b..eb7a88407f 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Settings/IdentitySettingNames.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Settings/IdentitySettingNames.cs @@ -41,5 +41,12 @@ public const string IsUserNameUpdateEnabled = UserPrefix + ".IsUserNameUpdateEnabled"; public const string IsEmailUpdateEnabled = UserPrefix + ".IsEmailUpdateEnabled"; } + + public static class OrganizationUnit + { + private const string OrganizationUnitPrefix = Prefix + ".OrganizationUnit"; + + public const string MaxUserMembershipCount = OrganizationUnitPrefix + ".MaxUserMembershipCount"; + } } } \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentitySettingDefinitionProvider.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentitySettingDefinitionProvider.cs index 92847004ea..1fdd2dd451 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentitySettingDefinitionProvider.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentitySettingDefinitionProvider.cs @@ -11,88 +11,106 @@ namespace Volo.Abp.Identity { context.Add( new SettingDefinition( - IdentitySettingNames.Password.RequiredLength, - 6.ToString(), - L("DisplayName:Abp.Identity.Password.RequiredLength"), - L("Description:Abp.Identity.Password.RequiredLength"), + IdentitySettingNames.Password.RequiredLength, + 6.ToString(), + L("DisplayName:Abp.Identity.Password.RequiredLength"), + L("Description:Abp.Identity.Password.RequiredLength"), true), new SettingDefinition( - IdentitySettingNames.Password.RequiredUniqueChars, - 1.ToString(), - L("DisplayName:Abp.Identity.Password.RequiredUniqueChars"), - L("Description:Abp.Identity.Password.RequiredUniqueChars"), + IdentitySettingNames.Password.RequiredUniqueChars, + 1.ToString(), + L("DisplayName:Abp.Identity.Password.RequiredUniqueChars"), + L("Description:Abp.Identity.Password.RequiredUniqueChars"), true), new SettingDefinition( - IdentitySettingNames.Password.RequireNonAlphanumeric, - true.ToString(), - L("DisplayName:Abp.Identity.Password.RequireNonAlphanumeric"), - L("Description:Abp.Identity.Password.RequireNonAlphanumeric"), + IdentitySettingNames.Password.RequireNonAlphanumeric, + true.ToString(), + L("DisplayName:Abp.Identity.Password.RequireNonAlphanumeric"), + L("Description:Abp.Identity.Password.RequireNonAlphanumeric"), true), new SettingDefinition( - IdentitySettingNames.Password.RequireLowercase, - true.ToString(), L("DisplayName:Abp.Identity.Password.RequireLowercase"), - L("Description:Abp.Identity.Password.RequireLowercase"), + IdentitySettingNames.Password.RequireLowercase, + true.ToString(), + L("DisplayName:Abp.Identity.Password.RequireLowercase"), + L("Description:Abp.Identity.Password.RequireLowercase"), true), new SettingDefinition( - IdentitySettingNames.Password.RequireUppercase, - true.ToString(), L("DisplayName:Abp.Identity.Password.RequireUppercase"), - L("Description:Abp.Identity.Password.RequireUppercase"), + IdentitySettingNames.Password.RequireUppercase, + true.ToString(), + L("DisplayName:Abp.Identity.Password.RequireUppercase"), + L("Description:Abp.Identity.Password.RequireUppercase"), true), new SettingDefinition( - IdentitySettingNames.Password.RequireDigit, - true.ToString(), L("DisplayName:Abp.Identity.Password.RequireDigit"), - L("Description:Abp.Identity.Password.RequireDigit"), + IdentitySettingNames.Password.RequireDigit, + true.ToString(), + L("DisplayName:Abp.Identity.Password.RequireDigit"), + L("Description:Abp.Identity.Password.RequireDigit"), true), new SettingDefinition( - IdentitySettingNames.Lockout.AllowedForNewUsers, - true.ToString(), L("DisplayName:Abp.Identity.Lockout.AllowedForNewUsers"), - L("Description:Abp.Identity.Lockout.AllowedForNewUsers"), + IdentitySettingNames.Lockout.AllowedForNewUsers, + true.ToString(), + L("DisplayName:Abp.Identity.Lockout.AllowedForNewUsers"), + L("Description:Abp.Identity.Lockout.AllowedForNewUsers"), true), - + new SettingDefinition( - IdentitySettingNames.Lockout.LockoutDuration, - (5*60).ToString(), L("DisplayName:Abp.Identity.Lockout.LockoutDuration"), - L("Description:Abp.Identity.Lockout.LockoutDuration"), + IdentitySettingNames.Lockout.LockoutDuration, + (5 * 60).ToString(), + L("DisplayName:Abp.Identity.Lockout.LockoutDuration"), + L("Description:Abp.Identity.Lockout.LockoutDuration"), true), new SettingDefinition( - IdentitySettingNames.Lockout.MaxFailedAccessAttempts, - 5.ToString(), L("DisplayName:Abp.Identity.Lockout.MaxFailedAccessAttempts"), - L("Description:Abp.Identity.Lockout.MaxFailedAccessAttempts"), + IdentitySettingNames.Lockout.MaxFailedAccessAttempts, + 5.ToString(), + L("DisplayName:Abp.Identity.Lockout.MaxFailedAccessAttempts"), + L("Description:Abp.Identity.Lockout.MaxFailedAccessAttempts"), true), new SettingDefinition( - IdentitySettingNames.SignIn.RequireConfirmedEmail, - false.ToString(), L("DisplayName:Abp.Identity.SignIn.RequireConfirmedEmail"), - L("Description:Abp.Identity.SignIn.RequireConfirmedEmail"), + IdentitySettingNames.SignIn.RequireConfirmedEmail, + false.ToString(), + L("DisplayName:Abp.Identity.SignIn.RequireConfirmedEmail"), + L("Description:Abp.Identity.SignIn.RequireConfirmedEmail"), true), new SettingDefinition( IdentitySettingNames.SignIn.EnablePhoneNumberConfirmation, - true.ToString(), L("DisplayName:Abp.Identity.SignIn.EnablePhoneNumberConfirmation"), + true.ToString(), + L("DisplayName:Abp.Identity.SignIn.EnablePhoneNumberConfirmation"), L("Description:Abp.Identity.SignIn.EnablePhoneNumberConfirmation"), true), new SettingDefinition( IdentitySettingNames.SignIn.RequireConfirmedPhoneNumber, - false.ToString(), L("DisplayName:Abp.Identity.SignIn.RequireConfirmedPhoneNumber"), + false.ToString(), + L("DisplayName:Abp.Identity.SignIn.RequireConfirmedPhoneNumber"), L("Description:Abp.Identity.SignIn.RequireConfirmedPhoneNumber"), true), new SettingDefinition( - IdentitySettingNames.User.IsUserNameUpdateEnabled, - true.ToString(), L("DisplayName:Abp.Identity.User.IsUserNameUpdateEnabled"), - L("Description:Abp.Identity.User.IsUserNameUpdateEnabled"), + IdentitySettingNames.User.IsUserNameUpdateEnabled, + true.ToString(), + L("DisplayName:Abp.Identity.User.IsUserNameUpdateEnabled"), + L("Description:Abp.Identity.User.IsUserNameUpdateEnabled"), true), new SettingDefinition( - IdentitySettingNames.User.IsEmailUpdateEnabled, - true.ToString(), L("DisplayName:Abp.Identity.User.IsEmailUpdateEnabled"), - L("Description:Abp.Identity.User.IsEmailUpdateEnabled"), + IdentitySettingNames.User.IsEmailUpdateEnabled, + true.ToString(), + L("DisplayName:Abp.Identity.User.IsEmailUpdateEnabled"), + L("Description:Abp.Identity.User.IsEmailUpdateEnabled"), + true), + + new SettingDefinition( + IdentitySettingNames.OrganizationUnit.MaxUserMembershipCount, + int.MaxValue.ToString(), + L("Identity.OrganizationUnit.MaxUserMembershipCount"), + L("Identity.OrganizationUnit.MaxUserMembershipCount"), true) ); } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityRoleRepository.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityRoleRepository.cs index 3a344b3140..590003ca7d 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityRoleRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityRoleRepository.cs @@ -18,8 +18,13 @@ namespace Volo.Abp.Identity string sorting = null, int maxResultCount = int.MaxValue, int skipCount = 0, + string filter = null, bool includeDetails = false, CancellationToken cancellationToken = default + ); + Task> GetListAsync( + IEnumerable ids, + CancellationToken cancellationToken = default ); Task> GetDefaultOnesAsync( diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityUserRepository.cs index 98269da005..3814ab96e3 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityUserRepository.cs @@ -21,6 +21,10 @@ namespace Volo.Abp.Identity CancellationToken cancellationToken = default ); + Task> GetRoleNamesInOrganizationUnitAsync( + Guid id, + CancellationToken cancellationToken = default); + Task FindByLoginAsync( [NotNull] string loginProvider, [NotNull] string providerKey, @@ -29,19 +33,19 @@ namespace Volo.Abp.Identity ); Task FindByNormalizedEmailAsync( - [NotNull] string normalizedEmail, + [NotNull] string normalizedEmail, bool includeDetails = true, CancellationToken cancellationToken = default ); Task> GetListByClaimAsync( - Claim claim, + Claim claim, bool includeDetails = false, CancellationToken cancellationToken = default ); Task> GetListByNormalizedRoleNameAsync( - string normalizedRoleName, + string normalizedRoleName, bool includeDetails = false, CancellationToken cancellationToken = default ); @@ -61,6 +65,25 @@ namespace Volo.Abp.Identity CancellationToken cancellationToken = default ); + Task> GetOrganizationUnitsAsync( + Guid id, + bool includeDetails = false, + CancellationToken cancellationToken = default); + + Task> GetUsersInOrganizationUnitAsync( + Guid organizationUnitId, + CancellationToken cancellationToken = default + ); + Task> GetUsersInOrganizationsListAsync( + List organizationUnitIds, + CancellationToken cancellationToken = default + ); + + Task> GetUsersInOrganizationUnitWithChildrenAsync( + string code, + CancellationToken cancellationToken = default + ); + Task GetCountAsync( string filter = null, CancellationToken cancellationToken = default diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IOrganizationUnitRepository.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IOrganizationUnitRepository.cs new file mode 100644 index 0000000000..bf698ca54f --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IOrganizationUnitRepository.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories; + +namespace Volo.Abp.Identity +{ + public interface IOrganizationUnitRepository : IBasicRepository + { + Task> GetChildrenAsync( + Guid? parentId, + bool includeDetails = false, + CancellationToken cancellationToken = default + ); + + Task> GetAllChildrenWithParentCodeAsync( + string code, + Guid? parentId, + bool includeDetails = false, + CancellationToken cancellationToken = default + ); + + Task GetAsync( + string displayName, + bool includeDetails = true, + CancellationToken cancellationToken = default + ); + + Task> GetListAsync( + string sorting = null, + int maxResultCount = int.MaxValue, + int skipCount = 0, + bool includeDetails = false, + CancellationToken cancellationToken = default + ); + + Task> GetListAsync( + IEnumerable ids, + bool includeDetails = false, + CancellationToken cancellationToken = default + ); + + Task> GetRolesAsync( + OrganizationUnit organizationUnit, + string sorting = null, + int maxResultCount = int.MaxValue, + int skipCount = 0, + bool includeDetails = false, + CancellationToken cancellationToken = default + ); + + Task GetRolesCountAsync( + OrganizationUnit organizationUnit, + CancellationToken cancellationToken = default + ); + + Task> GetMembersAsync( + OrganizationUnit organizationUnit, + string sorting = null, + int maxResultCount = int.MaxValue, + int skipCount = 0, + string filter = null, + bool includeDetails = false, + CancellationToken cancellationToken = default + ); + + Task GetMembersCountAsync( + OrganizationUnit organizationUnit, + CancellationToken cancellationToken = default + ); + + Task RemoveAllRolesAsync( + OrganizationUnit organizationUnit, + CancellationToken cancellationToken = default + ); + + Task RemoveAllMembersAsync( + OrganizationUnit organizationUnit, + CancellationToken cancellationToken = default + ); + } +} diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUser.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUser.cs index 51e15339ee..ea1f5ccf72 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUser.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUser.cs @@ -124,9 +124,13 @@ namespace Volo.Abp.Identity /// public virtual ICollection Tokens { get; protected set; } + /// + /// Navigation property for this organization units. + /// + public virtual ICollection OrganizationUnits { get; protected set; } + protected IdentityUser() { - ExtraProperties = new Dictionary(); } public IdentityUser(Guid id, [NotNull] string userName, [NotNull] string email, Guid? tenantId = null) @@ -147,6 +151,7 @@ namespace Volo.Abp.Identity Claims = new Collection(); Logins = new Collection(); Tokens = new Collection(); + OrganizationUnits = new Collection(); ExtraProperties = new Dictionary(); } @@ -276,6 +281,41 @@ namespace Volo.Abp.Identity Tokens.RemoveAll(t => t.LoginProvider == loginProvider && t.Name == name); } + public virtual void AddOrganizationUnit(Guid organizationUnitId) + { + if (IsInOrganizationUnit(organizationUnitId)) + { + return; + } + + OrganizationUnits.Add( + new IdentityUserOrganizationUnit( + Id, + organizationUnitId, + TenantId + ) + ); + } + + public virtual void RemoveOrganizationUnit(Guid organizationUnitId) + { + if (!IsInOrganizationUnit(organizationUnitId)) + { + return; + } + + OrganizationUnits.RemoveAll( + ou => ou.OrganizationUnitId == organizationUnitId + ); + } + + public virtual bool IsInOrganizationUnit(Guid organizationUnitId) + { + return OrganizationUnits.Any( + ou => ou.OrganizationUnitId == organizationUnitId + ); + } + public override string ToString() { return $"{base.ToString()}, UserName = {UserName}"; diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs index 430a4822b4..2400743ab3 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs @@ -11,6 +11,9 @@ using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Services; using Volo.Abp.Threading; +using Volo.Abp.Uow; +using Volo.Abp.Settings; +using Volo.Abp.Identity.Settings; namespace Volo.Abp.Identity { @@ -18,11 +21,13 @@ namespace Volo.Abp.Identity { protected IIdentityRoleRepository RoleRepository { get; } protected IIdentityUserRepository UserRepository { get; } + protected IOrganizationUnitRepository OrganizationUnitRepository { get; } + protected IIdentityUserRepository IdentityUserRepository { get; } + protected ISettingProvider SettingProvider { get; } + protected ICancellationTokenProvider CancellationTokenProvider { get; } protected override CancellationToken CancellationToken => CancellationTokenProvider.Token; - protected ICancellationTokenProvider CancellationTokenProvider { get; } - public IdentityUserManager( IdentityUserStore store, IIdentityRoleRepository roleRepository, @@ -35,7 +40,10 @@ namespace Volo.Abp.Identity IdentityErrorDescriber errors, IServiceProvider services, ILogger logger, - ICancellationTokenProvider cancellationTokenProvider) + ICancellationTokenProvider cancellationTokenProvider, + IOrganizationUnitRepository organizationUnitRepository, + IIdentityUserRepository identityUserRepository, + ISettingProvider settingProvider) : base( store, optionsAccessor, @@ -47,6 +55,9 @@ namespace Volo.Abp.Identity services, logger) { + OrganizationUnitRepository = organizationUnitRepository; + IdentityUserRepository = identityUserRepository; + SettingProvider = settingProvider; RoleRepository = roleRepository; UserRepository = userRepository; CancellationTokenProvider = cancellationTokenProvider; @@ -67,7 +78,7 @@ namespace Volo.Abp.Identity { Check.NotNull(user, nameof(user)); Check.NotNull(roleNames, nameof(roleNames)); - + var currentRoleNames = await GetRolesAsync(user); var result = await RemoveFromRolesAsync(user, currentRoleNames.Except(roleNames).Distinct()); @@ -85,10 +96,132 @@ namespace Volo.Abp.Identity return IdentityResult.Success; } + public virtual async Task IsInOrganizationUnitAsync(Guid userId, Guid ouId) + { + var user = await IdentityUserRepository.GetAsync(userId, cancellationToken: CancellationToken); + return user.IsInOrganizationUnit(ouId); + } + + public virtual async Task IsInOrganizationUnitAsync(IdentityUser user, OrganizationUnit ou) + { + await IdentityUserRepository.EnsureCollectionLoadedAsync(user, u => u.OrganizationUnits, CancellationTokenProvider.Token); + return user.IsInOrganizationUnit(ou.Id); + } + + public virtual async Task AddToOrganizationUnitAsync(Guid userId, Guid ouId) + { + await AddToOrganizationUnitAsync( + await IdentityUserRepository.GetAsync(userId, cancellationToken: CancellationToken), + await OrganizationUnitRepository.GetAsync(ouId, cancellationToken: CancellationToken) + ); + } + + public virtual async Task AddToOrganizationUnitAsync(IdentityUser user, OrganizationUnit ou) + { + await IdentityUserRepository.EnsureCollectionLoadedAsync(user, u => u.OrganizationUnits, CancellationTokenProvider.Token); + + if (user.OrganizationUnits.Any(cou => cou.OrganizationUnitId == ou.Id)) + { + return; + } + + await CheckMaxUserOrganizationUnitMembershipCountAsync(user.OrganizationUnits.Count + 1); + + user.AddOrganizationUnit(ou.Id); + } + + public virtual async Task RemoveFromOrganizationUnitAsync(Guid userId, Guid ouId) + { + var user = await IdentityUserRepository.GetAsync(userId, cancellationToken: CancellationToken); + user.RemoveOrganizationUnit(ouId); + } + + public virtual async Task RemoveFromOrganizationUnitAsync(IdentityUser user, OrganizationUnit ou) + { + await IdentityUserRepository.EnsureCollectionLoadedAsync(user, u => u.OrganizationUnits, CancellationTokenProvider.Token); + + user.RemoveOrganizationUnit(ou.Id); + } + + public virtual async Task SetOrganizationUnitsAsync(Guid userId, params Guid[] organizationUnitIds) + { + await SetOrganizationUnitsAsync( + await IdentityUserRepository.GetAsync(userId, cancellationToken: CancellationToken), + organizationUnitIds + ); + } + + public virtual async Task SetOrganizationUnitsAsync(IdentityUser user, params Guid[] organizationUnitIds) + { + Check.NotNull(user, nameof(user)); + Check.NotNull(organizationUnitIds, nameof(organizationUnitIds)); + + await CheckMaxUserOrganizationUnitMembershipCountAsync(organizationUnitIds.Length); + + await IdentityUserRepository.EnsureCollectionLoadedAsync(user, u => u.OrganizationUnits, CancellationTokenProvider.Token); + + //Remove from removed OUs + foreach (var ouId in user.OrganizationUnits.Select(uou => uou.OrganizationUnitId).ToArray()) + { + if (!organizationUnitIds.Contains(ouId)) + { + user.RemoveOrganizationUnit(ouId); + } + } + + //Add to added OUs + foreach (var organizationUnitId in organizationUnitIds) + { + if (!user.IsInOrganizationUnit(organizationUnitId)) + { + user.AddOrganizationUnit(organizationUnitId); + } + } + } + + private async Task CheckMaxUserOrganizationUnitMembershipCountAsync(int requestedCount) + { + var maxCount = await SettingProvider.GetAsync(IdentitySettingNames.OrganizationUnit.MaxUserMembershipCount); + if (requestedCount > maxCount) + { + throw new BusinessException(IdentityErrorCodes.MaxAllowedOuMembership) + .WithData("MaxUserMembershipCount", maxCount); + } + } + + [UnitOfWork] + public virtual async Task> GetOrganizationUnitsAsync(IdentityUser user, bool includeDetails = false) + { + await IdentityUserRepository.EnsureCollectionLoadedAsync(user, u => u.OrganizationUnits, CancellationTokenProvider.Token); + + return await OrganizationUnitRepository.GetListAsync( + user.OrganizationUnits.Select(t => t.OrganizationUnitId), + includeDetails, + cancellationToken: CancellationToken + ); + } + + [UnitOfWork] + public virtual async Task> GetUsersInOrganizationUnitAsync( + OrganizationUnit organizationUnit, + bool includeChildren = false) + { + if (includeChildren) + { + return await IdentityUserRepository + .GetUsersInOrganizationUnitWithChildrenAsync(organizationUnit.Code, CancellationToken); + } + else + { + return await IdentityUserRepository + .GetUsersInOrganizationUnitAsync(organizationUnit.Id, CancellationToken); + } + } + public virtual async Task AddDefaultRolesAsync([NotNull] IdentityUser user) { await UserRepository.EnsureCollectionLoadedAsync(user, u => u.Roles, CancellationToken); - + foreach (var role in await RoleRepository.GetDefaultOnesAsync(cancellationToken: CancellationToken)) { if (!user.IsInRole(role.Id)) @@ -96,7 +229,7 @@ namespace Volo.Abp.Identity user.AddRole(role.Id); } } - + return await UpdateUserAsync(user); } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserOrganizationUnit.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserOrganizationUnit.cs new file mode 100644 index 0000000000..5bb0ed790d --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserOrganizationUnit.cs @@ -0,0 +1,44 @@ +using System; +using Volo.Abp.Domain.Entities.Auditing; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.Identity +{ + /// + /// Represents membership of a User to an OU. + /// + public class IdentityUserOrganizationUnit : CreationAuditedEntity, IMultiTenant + { + /// + /// TenantId of this entity. + /// + public virtual Guid? TenantId { get; protected set; } + + /// + /// Id of the User. + /// + public virtual Guid UserId { get; protected set; } + + /// + /// Id of the related . + /// + public virtual Guid OrganizationUnitId { get; protected set; } + + protected IdentityUserOrganizationUnit() + { + + } + + public IdentityUserOrganizationUnit(Guid userId, Guid organizationUnitId, Guid? tenantId = null) + { + UserId = userId; + OrganizationUnitId = organizationUnitId; + TenantId = tenantId; + } + + public override object[] GetKeys() + { + return new object[] { UserId, OrganizationUnitId }; + } + } +} diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserStore.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserStore.cs index ddf2dd4f4b..81269adf5a 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserStore.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserStore.cs @@ -53,6 +53,7 @@ namespace Volo.Abp.Identity protected IIdentityRoleRepository RoleRepository { get; } protected IGuidGenerator GuidGenerator { get; } protected ILogger Logger { get; } + protected ILookupNormalizer LookupNormalizer { get; } protected IIdentityUserRepository UserRepository { get; } public IdentityUserStore( @@ -60,12 +61,14 @@ namespace Volo.Abp.Identity IIdentityRoleRepository roleRepository, IGuidGenerator guidGenerator, ILogger logger, + ILookupNormalizer lookupNormalizer, IdentityErrorDescriber describer = null) { UserRepository = userRepository; RoleRepository = roleRepository; GuidGenerator = guidGenerator; Logger = logger; + LookupNormalizer = lookupNormalizer; ErrorDescriber = describer ?? new IdentityErrorDescriber(); } @@ -313,13 +316,17 @@ namespace Volo.Abp.Identity Check.NotNull(user, nameof(user)); Check.NotNull(normalizedRoleName, nameof(normalizedRoleName)); - var role = await RoleRepository.FindByNormalizedNameAsync(normalizedRoleName, cancellationToken: cancellationToken); + if (await IsInRoleAsync(user, normalizedRoleName, cancellationToken)) + { + return; + } + var role = await RoleRepository.FindByNormalizedNameAsync(normalizedRoleName, cancellationToken: cancellationToken); if (role == null) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Role {0} does not exist!", normalizedRoleName)); } - + await UserRepository.EnsureCollectionLoadedAsync(user, u => u.Roles, cancellationToken); user.AddRole(role.Id); @@ -337,11 +344,7 @@ namespace Volo.Abp.Identity cancellationToken.ThrowIfCancellationRequested(); Check.NotNull(user, nameof(user)); - - if (string.IsNullOrWhiteSpace(normalizedRoleName)) - { - throw new ArgumentException(nameof(normalizedRoleName) + " can not be null or whitespace"); - } + Check.NotNullOrWhiteSpace(normalizedRoleName, nameof(normalizedRoleName)); var role = await RoleRepository.FindByNormalizedNameAsync(normalizedRoleName, cancellationToken: cancellationToken); if (role == null) @@ -366,7 +369,13 @@ namespace Volo.Abp.Identity Check.NotNull(user, nameof(user)); - return await UserRepository.GetRoleNamesAsync(user.Id, cancellationToken: cancellationToken); + var userRoles = await UserRepository + .GetRoleNamesAsync(user.Id, cancellationToken: cancellationToken); + + var userOrganizationUnitRoles = await UserRepository + .GetRoleNamesInOrganizationUnitAsync(user.Id, cancellationToken: cancellationToken); + + return userRoles.Union(userOrganizationUnitRoles).ToList(); } /// @@ -377,26 +386,21 @@ namespace Volo.Abp.Identity /// The used to propagate notifications that the operation should be canceled. /// A containing a flag indicating if the specified user is a member of the given group. If the /// user is a member of the group the returned value with be true, otherwise it will be false. - public virtual async Task IsInRoleAsync([NotNull] IdentityUser user, [NotNull] string normalizedRoleName, CancellationToken cancellationToken = default) + public virtual async Task IsInRoleAsync( + [NotNull] IdentityUser user, + [NotNull] string normalizedRoleName, + CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); - + Check.NotNull(user, nameof(user)); + Check.NotNullOrWhiteSpace(normalizedRoleName, nameof(normalizedRoleName)); - if (string.IsNullOrWhiteSpace(normalizedRoleName)) - { - throw new ArgumentException(nameof(normalizedRoleName) + " can not be null or whitespace"); - } - - var role = await RoleRepository.FindByNormalizedNameAsync(normalizedRoleName, cancellationToken: cancellationToken); - if (role == null) - { - return false; - } - - await UserRepository.EnsureCollectionLoadedAsync(user, u => u.Roles, cancellationToken); + var roles = await GetRolesAsync(user, cancellationToken); - return user.IsInRole(role.Id); + return roles + .Select(r => LookupNormalizer.NormalizeName(r)) + .Contains(normalizedRoleName); } /// diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/OrganizationUnit.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/OrganizationUnit.cs new file mode 100644 index 0000000000..3d1bbb0531 --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/OrganizationUnit.cs @@ -0,0 +1,212 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using Volo.Abp.Domain.Entities.Auditing; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.Identity +{ + /// + /// Represents an organization unit (OU). + /// + public class OrganizationUnit : FullAuditedAggregateRoot, IMultiTenant + { + public virtual Guid? TenantId { get; protected set; } + + /// + /// Parent Id. + /// Null, if this OU is a root. + /// + public virtual Guid? ParentId { get; internal set; } + + /// + /// Hierarchical Code of this organization unit. + /// Example: "00001.00042.00005". + /// This is a unique code for a Tenant. + /// It's changeable if OU hierarchy is changed. + /// + public virtual string Code { get; internal set; } + + /// + /// Display name of this role. + /// + public virtual string DisplayName { get; set; } + + /// + /// Roles of this OU. + /// + public virtual ICollection Roles { get; protected set; } + + /// + /// Initializes a new instance of the class. + /// + public OrganizationUnit() + { + + } + + /// + /// Initializes a new instance of the class. + /// + /// Tenant's Id or null for host. + /// Display name. + /// Parent's Id or null if OU is a root. + public OrganizationUnit(Guid id, string displayName, Guid? parentId = null, Guid? tenantId = null) + { + Id = id; + TenantId = tenantId; + DisplayName = displayName; + ParentId = parentId; + Roles = new Collection(); + } + + /// + /// Creates code for given numbers. + /// Example: if numbers are 4,2 then returns "00004.00002"; + /// + /// Numbers + public static string CreateCode(params int[] numbers) + { + if (numbers.IsNullOrEmpty()) + { + return null; + } + + return numbers.Select(number => number.ToString(new string('0', OrganizationUnitConsts.CodeUnitLength))).JoinAsString("."); + } + + /// + /// Appends a child code to a parent code. + /// Example: if parentCode = "00001", childCode = "00042" then returns "00001.00042". + /// + /// Parent code. Can be null or empty if parent is a root. + /// Child code. + public static string AppendCode(string parentCode, string childCode) + { + if (childCode.IsNullOrEmpty()) + { + throw new ArgumentNullException(nameof(childCode), "childCode can not be null or empty."); + } + + if (parentCode.IsNullOrEmpty()) + { + return childCode; + } + + return parentCode + "." + childCode; + } + + /// + /// Gets relative code to the parent. + /// Example: if code = "00019.00055.00001" and parentCode = "00019" then returns "00055.00001". + /// + /// The code. + /// The parent code. + public static string GetRelativeCode(string code, string parentCode) + { + if (code.IsNullOrEmpty()) + { + throw new ArgumentNullException(nameof(code), "code can not be null or empty."); + } + + if (parentCode.IsNullOrEmpty()) + { + return code; + } + + if (code.Length == parentCode.Length) + { + return null; + } + + return code.Substring(parentCode.Length + 1); + } + + /// + /// Calculates next code for given code. + /// Example: if code = "00019.00055.00001" returns "00019.00055.00002". + /// + /// The code. + public static string CalculateNextCode(string code) + { + if (code.IsNullOrEmpty()) + { + throw new ArgumentNullException(nameof(code), "code can not be null or empty."); + } + + var parentCode = GetParentCode(code); + var lastUnitCode = GetLastUnitCode(code); + + return AppendCode(parentCode, CreateCode(Convert.ToInt32(lastUnitCode) + 1)); + } + + /// + /// Gets the last unit code. + /// Example: if code = "00019.00055.00001" returns "00001". + /// + /// The code. + public static string GetLastUnitCode(string code) + { + if (code.IsNullOrEmpty()) + { + throw new ArgumentNullException(nameof(code), "code can not be null or empty."); + } + + var splittedCode = code.Split('.'); + return splittedCode[splittedCode.Length - 1]; + } + + /// + /// Gets parent code. + /// Example: if code = "00019.00055.00001" returns "00019.00055". + /// + /// The code. + public static string GetParentCode(string code) + { + if (code.IsNullOrEmpty()) + { + throw new ArgumentNullException(nameof(code), "code can not be null or empty."); + } + + var splittedCode = code.Split('.'); + if (splittedCode.Length == 1) + { + return null; + } + + return splittedCode.Take(splittedCode.Length - 1).JoinAsString("."); + } + + public virtual void AddRole(Guid roleId) + { + Check.NotNull(roleId, nameof(roleId)); + + if (IsInRole(roleId)) + { + return; + } + + Roles.Add(new OrganizationUnitRole(roleId, Id, TenantId)); + } + + public virtual void RemoveRole(Guid roleId) + { + Check.NotNull(roleId, nameof(roleId)); + + if (!IsInRole(roleId)) + { + return; + } + + Roles.RemoveAll(r => r.RoleId == roleId); + } + + public virtual bool IsInRole(Guid roleId) + { + Check.NotNull(roleId, nameof(roleId)); + + return Roles.Any(r => r.RoleId == roleId); + } + } +} diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/OrganizationUnitManager.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/OrganizationUnitManager.cs new file mode 100644 index 0000000000..778bde3802 --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/OrganizationUnitManager.cs @@ -0,0 +1,194 @@ +using Microsoft.Extensions.Localization; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.Domain.Services; +using Volo.Abp.Identity.Localization; +using Volo.Abp.Threading; +using Volo.Abp.Uow; + +namespace Volo.Abp.Identity +{ + /// + /// Performs domain logic for Organization Units. + /// + public class OrganizationUnitManager : DomainService + { + protected IOrganizationUnitRepository OrganizationUnitRepository { get; } + protected IStringLocalizer Localizer { get; } + protected IIdentityRoleRepository IdentityRoleRepository { get; } + protected ICancellationTokenProvider CancellationTokenProvider { get; } + + public OrganizationUnitManager( + IOrganizationUnitRepository organizationUnitRepository, + IStringLocalizer localizer, + IIdentityRoleRepository identityRoleRepository, + ICancellationTokenProvider cancellationTokenProvider) + { + OrganizationUnitRepository = organizationUnitRepository; + Localizer = localizer; + IdentityRoleRepository = identityRoleRepository; + CancellationTokenProvider = cancellationTokenProvider; + } + + [UnitOfWork] + public virtual async Task CreateAsync(OrganizationUnit organizationUnit) + { + organizationUnit.Code = await GetNextChildCodeAsync(organizationUnit.ParentId); + await ValidateOrganizationUnitAsync(organizationUnit); + await OrganizationUnitRepository.InsertAsync(organizationUnit); + } + + public virtual async Task UpdateAsync(OrganizationUnit organizationUnit) + { + await ValidateOrganizationUnitAsync(organizationUnit); + await OrganizationUnitRepository.UpdateAsync(organizationUnit); + } + + public virtual async Task GetNextChildCodeAsync(Guid? parentId) + { + var lastChild = await GetLastChildOrNullAsync(parentId); + if (lastChild != null) + { + return OrganizationUnit.CalculateNextCode(lastChild.Code); + } + + var parentCode = parentId != null + ? await GetCodeOrDefaultAsync(parentId.Value) + : null; + + return OrganizationUnit.AppendCode( + parentCode, + OrganizationUnit.CreateCode(1) + ); + } + + public virtual async Task GetLastChildOrNullAsync(Guid? parentId) + { + var children = await OrganizationUnitRepository.GetChildrenAsync(parentId); + return children.OrderBy(c => c.Code).LastOrDefault(); + } + + [UnitOfWork] + public virtual async Task DeleteAsync(Guid id) + { + var children = await FindChildrenAsync(id, true); + + foreach (var child in children) + { + await OrganizationUnitRepository.RemoveAllMembersAsync(child); + await OrganizationUnitRepository.RemoveAllRolesAsync(child); + await OrganizationUnitRepository.DeleteAsync(child); + } + + var organizationUnit = await OrganizationUnitRepository.GetAsync(id); + + await OrganizationUnitRepository.RemoveAllMembersAsync(organizationUnit); + await OrganizationUnitRepository.RemoveAllRolesAsync(organizationUnit); + await OrganizationUnitRepository.DeleteAsync(id); + } + + [UnitOfWork] + public virtual async Task MoveAsync(Guid id, Guid? parentId) + { + var organizationUnit = await OrganizationUnitRepository.GetAsync(id); + if (organizationUnit.ParentId == parentId) + { + return; + } + + //Should find children before Code change + var children = await FindChildrenAsync(id, true); + + //Store old code of OU + var oldCode = organizationUnit.Code; + + //Move OU + organizationUnit.Code = await GetNextChildCodeAsync(parentId); + organizationUnit.ParentId = parentId; + + await ValidateOrganizationUnitAsync(organizationUnit); + + //Update Children Codes + foreach (var child in children) + { + child.Code = OrganizationUnit.AppendCode(organizationUnit.Code, OrganizationUnit.GetRelativeCode(child.Code, oldCode)); + } + } + + public virtual async Task GetCodeOrDefaultAsync(Guid id) + { + var ou = await OrganizationUnitRepository.GetAsync(id); + return ou?.Code; + } + + protected virtual async Task ValidateOrganizationUnitAsync(OrganizationUnit organizationUnit) + { + var siblings = (await FindChildrenAsync(organizationUnit.ParentId)) + .Where(ou => ou.Id != organizationUnit.Id) + .ToList(); + + if (siblings.Any(ou => ou.DisplayName == organizationUnit.DisplayName)) + { + throw new UserFriendlyException(Localizer["OrganizationUnitDuplicateDisplayNameWarning", organizationUnit.DisplayName]); + } + } + + public async Task> FindChildrenAsync(Guid? parentId, bool recursive = false) + { + if (!recursive) + { + return await OrganizationUnitRepository.GetChildrenAsync(parentId, includeDetails: true); + } + + if (!parentId.HasValue) + { + return await OrganizationUnitRepository.GetListAsync(includeDetails: true); + } + + var code = await GetCodeOrDefaultAsync(parentId.Value); + + return await OrganizationUnitRepository.GetAllChildrenWithParentCodeAsync(code, parentId, includeDetails: true); + } + + public virtual Task IsInOrganizationUnitAsync(IdentityUser user, OrganizationUnit ou) + { + return Task.FromResult(user.IsInOrganizationUnit(ou.Id)); + } + + public virtual async Task AddRoleToOrganizationUnitAsync(Guid roleId, Guid ouId) + { + await AddRoleToOrganizationUnitAsync( + await IdentityRoleRepository.GetAsync(roleId), + await OrganizationUnitRepository.GetAsync(ouId, true) + ); + } + + public virtual Task AddRoleToOrganizationUnitAsync(IdentityRole role, OrganizationUnit ou) + { + var currentRoles = ou.Roles; + + if (currentRoles.Any(r => r.OrganizationUnitId == ou.Id && r.RoleId == role.Id)) + { + return Task.FromResult(0); + } + ou.AddRole(role.Id); + return Task.FromResult(0); + } + + public virtual async Task RemoveRoleFromOrganizationUnitAsync(Guid roleId, Guid ouId) + { + await RemoveRoleFromOrganizationUnitAsync( + await IdentityRoleRepository.GetAsync(roleId), + await OrganizationUnitRepository.GetAsync(ouId, true) + ); + } + + public virtual Task RemoveRoleFromOrganizationUnitAsync(IdentityRole role, OrganizationUnit organizationUnit) + { + organizationUnit.RemoveRole(role.Id); + return Task.FromResult(0); + } + } +} diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/OrganizationUnitRole.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/OrganizationUnitRole.cs new file mode 100644 index 0000000000..43aca1e272 --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/OrganizationUnitRole.cs @@ -0,0 +1,53 @@ +using System; +using Volo.Abp.Domain.Entities.Auditing; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.Identity +{ + /// + /// Represents membership of a User to an OU. + /// + public class OrganizationUnitRole : CreationAuditedEntity, IMultiTenant + { + /// + /// TenantId of this entity. + /// + public virtual Guid? TenantId { get; protected set; } + + /// + /// Id of the Role. + /// + public virtual Guid RoleId { get; protected set; } + + /// + /// Id of the . + /// + public virtual Guid OrganizationUnitId { get; protected set; } + + /// + /// Initializes a new instance of the class. + /// + protected OrganizationUnitRole() + { + + } + + /// + /// Initializes a new instance of the class. + /// + /// TenantId + /// Id of the User. + /// Id of the . + public OrganizationUnitRole(Guid roleId, Guid organizationUnitId, Guid? tenantId = null) + { + RoleId = roleId; + OrganizationUnitId = organizationUnitId; + TenantId = tenantId; + } + + public override object[] GetKeys() + { + return new object[] { OrganizationUnitId, RoleId }; + } + } +} diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo.Abp.Identity.EntityFrameworkCore.csproj b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo.Abp.Identity.EntityFrameworkCore.csproj index fc647ad159..0f1e0d1c7e 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo.Abp.Identity.EntityFrameworkCore.csproj +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo.Abp.Identity.EntityFrameworkCore.csproj @@ -15,8 +15,8 @@ - + diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/AbpIdentityEntityFrameworkCoreModule.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/AbpIdentityEntityFrameworkCoreModule.cs index b3ff5c368b..f46d3b6e0b 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/AbpIdentityEntityFrameworkCoreModule.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/AbpIdentityEntityFrameworkCoreModule.cs @@ -16,6 +16,7 @@ namespace Volo.Abp.Identity.EntityFrameworkCore options.AddRepository(); options.AddRepository(); options.AddRepository(); + options.AddRepository(); }); } } diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityRoleRepository.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityRoleRepository.cs index dfaaf51d91..2814abd891 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityRoleRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityRoleRepository.cs @@ -7,7 +7,6 @@ using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Volo.Abp.Domain.Repositories.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore; -using Volo.Abp.Guids; namespace Volo.Abp.Identity.EntityFrameworkCore { @@ -19,7 +18,7 @@ namespace Volo.Abp.Identity.EntityFrameworkCore } public virtual async Task FindByNormalizedNameAsync( - string normalizedRoleName, + string normalizedRoleName, bool includeDetails = true, CancellationToken cancellationToken = default) { @@ -29,19 +28,32 @@ namespace Volo.Abp.Identity.EntityFrameworkCore } public virtual async Task> GetListAsync( - string sorting = null, - int maxResultCount = int.MaxValue, - int skipCount = 0, + string sorting = null, + int maxResultCount = int.MaxValue, + int skipCount = 0, + string filter = null, bool includeDetails = true, CancellationToken cancellationToken = default) { return await DbSet .IncludeDetails(includeDetails) + .WhereIf(!filter.IsNullOrWhiteSpace(), + x => x.Name.Contains(filter) || + x.NormalizedName.Contains(filter)) .OrderBy(sorting ?? nameof(IdentityRole.Name)) .PageBy(skipCount, maxResultCount) .ToListAsync(GetCancellationToken(cancellationToken)); } + public virtual async Task> GetListAsync( + IEnumerable ids, + CancellationToken cancellationToken = default) + { + return await DbSet + .Where(t => ids.Contains(t.Id)) + .ToListAsync(GetCancellationToken(cancellationToken)); + } + public virtual async Task> GetDefaultOnesAsync( bool includeDetails = false, CancellationToken cancellationToken = default) { diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs index 97c2ebe779..9ad8effa8a 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs @@ -6,6 +6,7 @@ using System.Security.Claims; using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Internal; using Volo.Abp.Domain.Repositories.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore; @@ -39,8 +40,34 @@ namespace Volo.Abp.Identity.EntityFrameworkCore join role in DbContext.Roles on userRole.RoleId equals role.Id where userRole.UserId == id select role.Name; + var organizationUnitIds = DbContext.Set().Where(q => q.UserId == id).Select(q => q.OrganizationUnitId).ToArray(); + + var organizationRoleIds = await ( + from ouRole in DbContext.Set() + join ou in DbContext.Set() on ouRole.OrganizationUnitId equals ou.Id + where organizationUnitIds.Contains(ouRole.OrganizationUnitId) + select ouRole.RoleId + ).ToListAsync(GetCancellationToken(cancellationToken)); + + var orgUnitRoleNameQuery = DbContext.Roles.Where(r => organizationRoleIds.Contains(r.Id)).Select(n => n.Name); + var resultQuery = query.Union(orgUnitRoleNameQuery); + return await resultQuery.ToListAsync(GetCancellationToken(cancellationToken)); + } - return await query.ToListAsync(GetCancellationToken(cancellationToken)); + public virtual async Task> GetRoleNamesInOrganizationUnitAsync( + Guid id, + CancellationToken cancellationToken = default) + { + var query = from userOu in DbContext.Set() + join roleOu in DbContext.Set() on userOu.OrganizationUnitId equals roleOu.OrganizationUnitId + join ou in DbContext.Set() on roleOu.OrganizationUnitId equals ou.Id + join userOuRoles in DbContext.Roles on roleOu.RoleId equals userOuRoles.Id + where userOu.UserId == id + select userOuRoles.Name; + + var result = await query.ToListAsync(GetCancellationToken(cancellationToken)); + + return result; } public virtual async Task FindByLoginAsync( @@ -129,7 +156,22 @@ namespace Volo.Abp.Identity.EntityFrameworkCore where userRole.UserId == id select role; - return await query.ToListAsync(GetCancellationToken(cancellationToken)); + //TODO: Needs improvement + var userOrganizationsQuery = from userOrg in DbContext.Set() + join ou in DbContext.OrganizationUnits.IncludeDetails(includeDetails) on userOrg.OrganizationUnitId equals ou.Id + where userOrg.UserId == id + select ou; + + var orgUserRoleQuery = DbContext.Set() + .Where(q => userOrganizationsQuery + .Select(t => t.Id) + .Contains(q.OrganizationUnitId)) + .Select(t => t.RoleId); + + var orgRoles = DbContext.Roles.Where(q => orgUserRoleQuery.Contains(q.Id)); + var resultQuery = query.Union(orgRoles); + + return await resultQuery.ToListAsync(GetCancellationToken(cancellationToken)); } public virtual async Task GetCountAsync( @@ -147,6 +189,56 @@ namespace Volo.Abp.Identity.EntityFrameworkCore .LongCountAsync(GetCancellationToken(cancellationToken)); } + public virtual async Task> GetOrganizationUnitsAsync( + Guid id, + bool includeDetails = false, + CancellationToken cancellationToken = default) + { + var query = from userOU in DbContext.Set() + join ou in DbContext.OrganizationUnits.IncludeDetails(includeDetails) on userOU.OrganizationUnitId equals ou.Id + where userOU.UserId == id + select ou; + + return await query.ToListAsync(GetCancellationToken(cancellationToken)); + } + + public virtual async Task> GetUsersInOrganizationUnitAsync( + Guid organizationUnitId, + CancellationToken cancellationToken = default + ) + { + var query = from userOu in DbContext.Set() + join user in DbSet on userOu.UserId equals user.Id + where userOu.OrganizationUnitId == organizationUnitId + select user; + return await query.ToListAsync(GetCancellationToken(cancellationToken)); + } + + public async Task> GetUsersInOrganizationsListAsync( + List organizationUnitIds, + CancellationToken cancellationToken = default + ) + { + var query = from userOu in DbContext.Set() + join user in DbSet on userOu.UserId equals user.Id + where organizationUnitIds.Contains(userOu.OrganizationUnitId) + select user; + return await query.ToListAsync(GetCancellationToken(cancellationToken)); + } + + public virtual async Task> GetUsersInOrganizationUnitWithChildrenAsync( + string code, + CancellationToken cancellationToken = default + ) + { + var query = from userOu in DbContext.Set() + join user in DbSet on userOu.UserId equals user.Id + join ou in DbContext.Set() on userOu.OrganizationUnitId equals ou.Id + where ou.Code.StartsWith(code) + select user; + return await query.ToListAsync(GetCancellationToken(cancellationToken)); + } + public override IQueryable WithDetails() { return GetQueryable().IncludeDetails(); diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreOrganizationUnitRepository.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreOrganizationUnitRepository.cs new file mode 100644 index 0000000000..06b2084707 --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreOrganizationUnitRepository.cs @@ -0,0 +1,179 @@ +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq.Dynamic.Core; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; + +namespace Volo.Abp.Identity.EntityFrameworkCore +{ + public class EfCoreOrganizationUnitRepository + : EfCoreRepository, + IOrganizationUnitRepository + { + public EfCoreOrganizationUnitRepository( + IDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + } + + public virtual async Task> GetChildrenAsync( + Guid? parentId, + bool includeDetails = false, + CancellationToken cancellationToken = default) + { + return await DbSet + .IncludeDetails(includeDetails) + .Where(x => x.ParentId == parentId) + .ToListAsync(GetCancellationToken(cancellationToken)); + } + + public virtual async Task> GetAllChildrenWithParentCodeAsync( + string code, + Guid? parentId, + bool includeDetails = false, + CancellationToken cancellationToken = default) + { + return await DbSet + .IncludeDetails(includeDetails) + .Where(ou => ou.Code.StartsWith(code) && ou.Id != parentId.Value) + .ToListAsync(GetCancellationToken(cancellationToken)); + } + + public virtual async Task> GetListAsync( + string sorting = null, + int maxResultCount = int.MaxValue, + int skipCount = 0, + bool includeDetails = true, + CancellationToken cancellationToken = default) + { + return await DbSet + .IncludeDetails(includeDetails) + .OrderBy(sorting ?? nameof(OrganizationUnit.DisplayName)) + .PageBy(skipCount, maxResultCount) + .ToListAsync(GetCancellationToken(cancellationToken)); + } + public virtual async Task> GetListAsync( + IEnumerable ids, + bool includeDetails = false, + CancellationToken cancellationToken = default) + { + return await DbSet + .IncludeDetails(includeDetails) + .Where(t => ids.Contains(t.Id)) + .ToListAsync(GetCancellationToken(cancellationToken)); + } + + public virtual async Task GetAsync( + string displayName, + bool includeDetails = true, + CancellationToken cancellationToken = default) + { + return await DbSet + .IncludeDetails(includeDetails) + .FirstOrDefaultAsync( + ou => ou.DisplayName == displayName, + GetCancellationToken(cancellationToken) + ); + } + + public virtual async Task> GetRolesAsync( + OrganizationUnit organizationUnit, + string sorting = null, + int maxResultCount = int.MaxValue, + int skipCount = 0, + bool includeDetails = false, + CancellationToken cancellationToken = default) + { + var query = from organizationRole in DbContext.Set() + join role in DbContext.Roles.IncludeDetails(includeDetails) on organizationRole.RoleId equals role.Id + where organizationRole.OrganizationUnitId == organizationUnit.Id + select role; + query = query + .OrderBy(sorting ?? nameof(IdentityRole.Name)) + .PageBy(skipCount, maxResultCount); + + return await query.ToListAsync(GetCancellationToken(cancellationToken)); + } + + public virtual async Task GetRolesCountAsync( + OrganizationUnit organizationUnit, + CancellationToken cancellationToken = default) + { + var query = from organizationRole in DbContext.Set() + join role in DbContext.Roles on organizationRole.RoleId equals role.Id + where organizationRole.OrganizationUnitId == organizationUnit.Id + select role; + + return await query.CountAsync(GetCancellationToken(cancellationToken)); + } + + public virtual async Task> GetMembersAsync( + OrganizationUnit organizationUnit, + string sorting = null, + int maxResultCount = int.MaxValue, + int skipCount = 0, + string filter = null, + bool includeDetails = false, + CancellationToken cancellationToken = default + ) + { + var query = from userOu in DbContext.Set() + join user in DbContext.Users.IncludeDetails(includeDetails) on userOu.UserId equals user.Id + where userOu.OrganizationUnitId == organizationUnit.Id + select user; + + if (!filter.IsNullOrWhiteSpace()) + { + query = query.Where(u => + u.UserName.Contains(filter) || + u.Email.Contains(filter) || + (u.PhoneNumber != null && u.PhoneNumber.Contains(filter)) + ); + } + + return await query.OrderBy(sorting ?? nameof(IdentityUser.UserName)) + .PageBy(skipCount, maxResultCount) + .ToListAsync(GetCancellationToken(cancellationToken)); + } + + public virtual async Task GetMembersCountAsync( + OrganizationUnit organizationUnit, + CancellationToken cancellationToken = default) + { + var query = from userOu in DbContext.Set() + join user in DbContext.Users on userOu.UserId equals user.Id + where userOu.OrganizationUnitId == organizationUnit.Id + select user; + + return await query.CountAsync(GetCancellationToken(cancellationToken)); + } + + public override IQueryable WithDetails() + { + return GetQueryable().IncludeDetails(); + } + + public virtual Task RemoveAllRolesAsync( + OrganizationUnit organizationUnit, + CancellationToken cancellationToken = default) + { + organizationUnit.Roles.Clear(); + return Task.CompletedTask; + } + + public virtual async Task RemoveAllMembersAsync( + OrganizationUnit organizationUnit, + CancellationToken cancellationToken = default) + { + var ouMembersQuery = await DbContext.Set() + .Where(q => q.OrganizationUnitId == organizationUnit.Id) + .ToListAsync(GetCancellationToken(cancellationToken)); + + DbContext.Set().RemoveRange(ouMembersQuery); + } + } +} diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IIdentityDbContext.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IIdentityDbContext.cs index d3c560dca2..f27c9b534a 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IIdentityDbContext.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IIdentityDbContext.cs @@ -12,5 +12,7 @@ namespace Volo.Abp.Identity.EntityFrameworkCore DbSet Roles { get; set; } DbSet ClaimTypes { get; set; } + + DbSet OrganizationUnits { get; set; } } -} \ No newline at end of file +} diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContext.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContext.cs index 05050159bc..811280de7b 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContext.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContext.cs @@ -16,6 +16,8 @@ namespace Volo.Abp.Identity.EntityFrameworkCore public DbSet ClaimTypes { get; set; } + public DbSet OrganizationUnits { get; set; } + public IdentityDbContext(DbContextOptions options) : base(options) { diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextModelBuilderExtensions.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextModelBuilderExtensions.cs index 05b99e767e..74cc2ce995 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextModelBuilderExtensions.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextModelBuilderExtensions.cs @@ -28,19 +28,31 @@ namespace Volo.Abp.Identity.EntityFrameworkCore b.ConfigureByConvention(); b.ConfigureAbpUser(); - b.Property(u => u.NormalizedUserName).IsRequired().HasMaxLength(IdentityUserConsts.MaxNormalizedUserNameLength).HasColumnName(nameof(IdentityUser.NormalizedUserName)); - b.Property(u => u.NormalizedEmail).IsRequired().HasMaxLength(IdentityUserConsts.MaxNormalizedEmailLength).HasColumnName(nameof(IdentityUser.NormalizedEmail)); - b.Property(u => u.PasswordHash).HasMaxLength(IdentityUserConsts.MaxPasswordHashLength).HasColumnName(nameof(IdentityUser.PasswordHash)); - b.Property(u => u.SecurityStamp).IsRequired().HasMaxLength(IdentityUserConsts.MaxSecurityStampLength).HasColumnName(nameof(IdentityUser.SecurityStamp)); - b.Property(u => u.TwoFactorEnabled).HasDefaultValue(false).HasColumnName(nameof(IdentityUser.TwoFactorEnabled)); - b.Property(u => u.LockoutEnabled).HasDefaultValue(false).HasColumnName(nameof(IdentityUser.LockoutEnabled)); - b.Property(u => u.AccessFailedCount).HasDefaultValue(0).HasColumnName(nameof(IdentityUser.AccessFailedCount)); - + b.Property(u => u.NormalizedUserName).IsRequired() + .HasMaxLength(IdentityUserConsts.MaxNormalizedUserNameLength) + .HasColumnName(nameof(IdentityUser.NormalizedUserName)); + b.Property(u => u.NormalizedEmail).IsRequired() + .HasMaxLength(IdentityUserConsts.MaxNormalizedEmailLength) + .HasColumnName(nameof(IdentityUser.NormalizedEmail)); + b.Property(u => u.PasswordHash).HasMaxLength(IdentityUserConsts.MaxPasswordHashLength) + .HasColumnName(nameof(IdentityUser.PasswordHash)); + b.Property(u => u.SecurityStamp).IsRequired().HasMaxLength(IdentityUserConsts.MaxSecurityStampLength) + .HasColumnName(nameof(IdentityUser.SecurityStamp)); + b.Property(u => u.TwoFactorEnabled).HasDefaultValue(false) + .HasColumnName(nameof(IdentityUser.TwoFactorEnabled)); + b.Property(u => u.LockoutEnabled).HasDefaultValue(false) + .HasColumnName(nameof(IdentityUser.LockoutEnabled)); + + b.Property(u => u.AccessFailedCount) + .If(!builder.IsUsingOracle(), p => p.HasDefaultValue(0)) + .HasColumnName(nameof(IdentityUser.AccessFailedCount)); + b.HasMany(u => u.Claims).WithOne().HasForeignKey(uc => uc.UserId).IsRequired(); b.HasMany(u => u.Logins).WithOne().HasForeignKey(ul => ul.UserId).IsRequired(); b.HasMany(u => u.Roles).WithOne().HasForeignKey(ur => ur.UserId).IsRequired(); b.HasMany(u => u.Tokens).WithOne().HasForeignKey(ur => ur.UserId).IsRequired(); - + b.HasMany(u => u.OrganizationUnits).WithOne().HasForeignKey(ur => ur.UserId).IsRequired(); + b.HasIndex(u => u.NormalizedUserName); b.HasIndex(u => u.NormalizedEmail); b.HasIndex(u => u.UserName); @@ -54,7 +66,7 @@ namespace Volo.Abp.Identity.EntityFrameworkCore b.ConfigureByConvention(); b.Property(x => x.Id).ValueGeneratedNever(); - + b.Property(uc => uc.ClaimType).HasMaxLength(IdentityUserClaimConsts.MaxClaimTypeLength).IsRequired(); b.Property(uc => uc.ClaimValue).HasMaxLength(IdentityUserClaimConsts.MaxClaimValueLength); @@ -67,12 +79,12 @@ namespace Volo.Abp.Identity.EntityFrameworkCore b.ConfigureByConvention(); - b.HasKey(ur => new { ur.UserId, ur.RoleId }); + b.HasKey(ur => new {ur.UserId, ur.RoleId}); b.HasOne().WithMany().HasForeignKey(ur => ur.RoleId).IsRequired(); b.HasOne().WithMany(u => u.Roles).HasForeignKey(ur => ur.UserId).IsRequired(); - b.HasIndex(ur => new { ur.RoleId, ur.UserId }); + b.HasIndex(ur => new {ur.RoleId, ur.UserId}); }); builder.Entity(b => @@ -81,13 +93,16 @@ namespace Volo.Abp.Identity.EntityFrameworkCore b.ConfigureByConvention(); - b.HasKey(x => new { x.UserId, x.LoginProvider }); + b.HasKey(x => new {x.UserId, x.LoginProvider}); - b.Property(ul => ul.LoginProvider).HasMaxLength(IdentityUserLoginConsts.MaxLoginProviderLength).IsRequired(); - b.Property(ul => ul.ProviderKey).HasMaxLength(IdentityUserLoginConsts.MaxProviderKeyLength).IsRequired(); - b.Property(ul => ul.ProviderDisplayName).HasMaxLength(IdentityUserLoginConsts.MaxProviderDisplayNameLength); + b.Property(ul => ul.LoginProvider).HasMaxLength(IdentityUserLoginConsts.MaxLoginProviderLength) + .IsRequired(); + b.Property(ul => ul.ProviderKey).HasMaxLength(IdentityUserLoginConsts.MaxProviderKeyLength) + .IsRequired(); + b.Property(ul => ul.ProviderDisplayName) + .HasMaxLength(IdentityUserLoginConsts.MaxProviderDisplayNameLength); - b.HasIndex(l => new { l.LoginProvider, l.ProviderKey }); + b.HasIndex(l => new {l.LoginProvider, l.ProviderKey}); }); builder.Entity(b => @@ -96,9 +111,10 @@ namespace Volo.Abp.Identity.EntityFrameworkCore b.ConfigureByConvention(); - b.HasKey(l => new { l.UserId, l.LoginProvider, l.Name }); + b.HasKey(l => new {l.UserId, l.LoginProvider, l.Name}); - b.Property(ul => ul.LoginProvider).HasMaxLength(IdentityUserTokenConsts.MaxLoginProviderLength).IsRequired(); + b.Property(ul => ul.LoginProvider).HasMaxLength(IdentityUserTokenConsts.MaxLoginProviderLength) + .IsRequired(); b.Property(ul => ul.Name).HasMaxLength(IdentityUserTokenConsts.MaxNameLength).IsRequired(); }); @@ -110,7 +126,9 @@ namespace Volo.Abp.Identity.EntityFrameworkCore b.Property(r => r.Name).IsRequired().HasMaxLength(IdentityRoleConsts.MaxNameLength); b.Property(r => r.NormalizedName).IsRequired().HasMaxLength(IdentityRoleConsts.MaxNormalizedNameLength); - b.Property(u => u.ConcurrencyStamp).IsRequired().IsConcurrencyToken().HasMaxLength(IdentityRoleConsts.MaxConcurrencyStampLength).HasColumnName(nameof(IdentityRole.ConcurrencyStamp)); + b.Property(u => u.ConcurrencyStamp).IsRequired().IsConcurrencyToken() + .HasMaxLength(IdentityRoleConsts.MaxConcurrencyStampLength) + .HasColumnName(nameof(IdentityRole.ConcurrencyStamp)); b.Property(r => r.IsDefault).HasColumnName(nameof(IdentityRole.IsDefault)); b.Property(r => r.IsStatic).HasColumnName(nameof(IdentityRole.IsStatic)); b.Property(r => r.IsPublic).HasColumnName(nameof(IdentityRole.IsPublic)); @@ -140,12 +158,58 @@ namespace Volo.Abp.Identity.EntityFrameworkCore b.ConfigureByConvention(); - b.Property(uc => uc.Name).HasMaxLength(IdentityClaimTypeConsts.MaxNameLength).IsRequired(); // make unique + b.Property(uc => uc.Name).HasMaxLength(IdentityClaimTypeConsts.MaxNameLength) + .IsRequired(); // make unique b.Property(uc => uc.Regex).HasMaxLength(IdentityClaimTypeConsts.MaxRegexLength); b.Property(uc => uc.RegexDescription).HasMaxLength(IdentityClaimTypeConsts.MaxRegexDescriptionLength); b.Property(uc => uc.Description).HasMaxLength(IdentityClaimTypeConsts.MaxDescriptionLength); - b.Property(uc => uc.ConcurrencyStamp).IsRequired().IsConcurrencyToken().HasMaxLength(IdentityClaimTypeConsts.MaxConcurrencyStampLength).HasColumnName(nameof(IdentityClaimType.ConcurrencyStamp)); + b.Property(uc => uc.ConcurrencyStamp).IsRequired().IsConcurrencyToken() + .HasMaxLength(IdentityClaimTypeConsts.MaxConcurrencyStampLength) + .HasColumnName(nameof(IdentityClaimType.ConcurrencyStamp)); + }); + + builder.Entity(b => + { + b.ToTable(options.TablePrefix + "OrganizationUnits", options.Schema); + + b.ConfigureByConvention(); + + b.Property(ou => ou.Code).IsRequired().HasMaxLength(OrganizationUnitConsts.MaxCodeLength) + .HasColumnName(nameof(OrganizationUnit.Code)); + b.Property(ou => ou.DisplayName).IsRequired().HasMaxLength(OrganizationUnitConsts.MaxDisplayNameLength) + .HasColumnName(nameof(OrganizationUnit.DisplayName)); + + b.HasMany().WithOne().HasForeignKey(ou => ou.ParentId); + b.HasMany(ou => ou.Roles).WithOne().HasForeignKey(our => our.OrganizationUnitId).IsRequired(); + + b.HasIndex(ou => ou.Code); + }); + + builder.Entity(b => + { + b.ToTable(options.TablePrefix + "OrganizationUnitRoles", options.Schema); + + b.ConfigureByConvention(); + + b.HasKey(ou => new {ou.OrganizationUnitId, ou.RoleId}); + + b.HasOne().WithMany().HasForeignKey(ou => ou.RoleId).IsRequired(); + + b.HasIndex(ou => new {ou.RoleId, ou.OrganizationUnitId}); + }); + + builder.Entity(b => + { + b.ToTable(options.TablePrefix + "UserOrganizationUnits", options.Schema); + + b.ConfigureByConvention(); + + b.HasKey(ou => new {ou.OrganizationUnitId, ou.UserId}); + + b.HasOne().WithMany().HasForeignKey(ou => ou.OrganizationUnitId).IsRequired(); + + b.HasIndex(ou => new {ou.UserId, ou.OrganizationUnitId}); }); } } -} +} \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityEfCoreQueryableExtensions.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityEfCoreQueryableExtensions.cs index 8cd598c673..5e4a0a2876 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityEfCoreQueryableExtensions.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityEfCoreQueryableExtensions.cs @@ -16,7 +16,8 @@ namespace Volo.Abp.Identity.EntityFrameworkCore .Include(x => x.Roles) .Include(x => x.Logins) .Include(x => x.Claims) - .Include(x => x.Tokens); + .Include(x => x.Tokens) + .Include(x => x.OrganizationUnits); } public static IQueryable IncludeDetails(this IQueryable queryable, bool include = true) @@ -29,5 +30,16 @@ namespace Volo.Abp.Identity.EntityFrameworkCore return queryable .Include(x => x.Claims); } + + public static IQueryable IncludeDetails(this IQueryable queryable, bool include = true) + { + if (!include) + { + return queryable; + } + + return queryable + .Include(x => x.Roles); + } } } \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/AbpIdentityMongoDbContext.cs b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/AbpIdentityMongoDbContext.cs index 3db35866f5..3240ce1454 100644 --- a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/AbpIdentityMongoDbContext.cs +++ b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/AbpIdentityMongoDbContext.cs @@ -13,6 +13,8 @@ namespace Volo.Abp.Identity.MongoDB public IMongoCollection ClaimTypes => Collection(); + public IMongoCollection OrganizationUnits => Collection(); + protected override void CreateModel(IMongoModelBuilder modelBuilder) { base.CreateModel(modelBuilder); diff --git a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/AbpIdentityMongoDbContextExtensions.cs b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/AbpIdentityMongoDbContextExtensions.cs index dd0cc8d42e..bc303e0eb5 100644 --- a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/AbpIdentityMongoDbContextExtensions.cs +++ b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/AbpIdentityMongoDbContextExtensions.cs @@ -31,6 +31,11 @@ namespace Volo.Abp.Identity.MongoDB { b.CollectionName = options.CollectionPrefix + "ClaimTypes"; }); + + builder.Entity(b => + { + b.CollectionName = options.CollectionPrefix + "OrganizationUnits"; + }); } } } \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/AbpIdentityMongoDbModule.cs b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/AbpIdentityMongoDbModule.cs index cfbc10adba..268c718b12 100644 --- a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/AbpIdentityMongoDbModule.cs +++ b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/AbpIdentityMongoDbModule.cs @@ -17,6 +17,7 @@ namespace Volo.Abp.Identity.MongoDB options.AddRepository(); options.AddRepository(); options.AddRepository(); + options.AddRepository(); }); } } diff --git a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/IAbpIdentityMongoDbContext.cs b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/IAbpIdentityMongoDbContext.cs index 36cdcbec3e..c903c5d96d 100644 --- a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/IAbpIdentityMongoDbContext.cs +++ b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/IAbpIdentityMongoDbContext.cs @@ -12,5 +12,7 @@ namespace Volo.Abp.Identity.MongoDB IMongoCollection Roles { get; } IMongoCollection ClaimTypes { get; } + + IMongoCollection OrganizationUnits { get; } } } \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityRoleRepository.cs b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityRoleRepository.cs index 7acb9b4e4b..2651fbfa6c 100644 --- a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityRoleRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityRoleRepository.cs @@ -7,20 +7,19 @@ using System.Linq.Dynamic.Core; using MongoDB.Driver; using MongoDB.Driver.Linq; using Volo.Abp.Domain.Repositories.MongoDB; -using Volo.Abp.Guids; using Volo.Abp.MongoDB; namespace Volo.Abp.Identity.MongoDB { public class MongoIdentityRoleRepository : MongoDbRepository, IIdentityRoleRepository { - public MongoIdentityRoleRepository(IMongoDbContextProvider dbContextProvider) + public MongoIdentityRoleRepository(IMongoDbContextProvider dbContextProvider) : base(dbContextProvider) { } public async Task FindByNormalizedNameAsync( - string normalizedRoleName, + string normalizedRoleName, bool includeDetails = true, CancellationToken cancellationToken = default) { @@ -28,19 +27,32 @@ namespace Volo.Abp.Identity.MongoDB } public async Task> GetListAsync( - string sorting = null, - int maxResultCount = int.MaxValue, - int skipCount = 0, + string sorting = null, + int maxResultCount = int.MaxValue, + int skipCount = 0, + string filter = null, bool includeDetails = false, CancellationToken cancellationToken = default) { return await GetMongoQueryable() + .WhereIf(!filter.IsNullOrWhiteSpace(), + x => x.Name.Contains(filter) || + x.NormalizedName.Contains(filter)) .OrderBy(sorting ?? nameof(IdentityRole.Name)) .As>() .PageBy>(skipCount, maxResultCount) .ToListAsync(GetCancellationToken(cancellationToken)); } + public virtual async Task> GetListAsync( + IEnumerable ids, + CancellationToken cancellationToken = default) + { + return await GetMongoQueryable() + .Where(t => ids.Contains(t.Id)) + .ToListAsync(GetCancellationToken(cancellationToken)); + } + public virtual async Task> GetDefaultOnesAsync( bool includeDetails = false, CancellationToken cancellationToken = default) { diff --git a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs index e87d6aacfc..b82fdf987c 100644 --- a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs @@ -8,7 +8,6 @@ using System.Threading.Tasks; using MongoDB.Driver; using MongoDB.Driver.Linq; using Volo.Abp.Domain.Repositories.MongoDB; -using Volo.Abp.Guids; using Volo.Abp.MongoDB; namespace Volo.Abp.Identity.MongoDB @@ -37,8 +36,41 @@ namespace Volo.Abp.Identity.MongoDB CancellationToken cancellationToken = default) { var user = await GetAsync(id, cancellationToken: GetCancellationToken(cancellationToken)); + var organizationUnitIds = user.OrganizationUnits + .Select(r => r.OrganizationUnitId) + .ToArray(); + var organizationUnits = DbContext.OrganizationUnits + .AsQueryable() + .Where(ou => organizationUnitIds.Contains(ou.Id)) + .ToArray(); + var orgUnitRoleIds = organizationUnits.SelectMany(x => x.Roles.Select(r => r.RoleId)).ToArray(); var roleIds = user.Roles.Select(r => r.RoleId).ToArray(); - return await DbContext.Roles.AsQueryable().Where(r => roleIds.Contains(r.Id)).Select(r => r.Name).ToListAsync(GetCancellationToken(cancellationToken)); + var allRoleIds = orgUnitRoleIds.Union(roleIds); + return await DbContext.Roles.AsQueryable().Where(r => allRoleIds.Contains(r.Id)).Select(r => r.Name).ToListAsync(GetCancellationToken(cancellationToken)); + } + + public async Task> GetRoleNamesInOrganizationUnitAsync( + Guid id, + CancellationToken cancellationToken = default) + { + var user = await GetAsync(id, cancellationToken: GetCancellationToken(cancellationToken)); + + var organizationUnitIds = user.OrganizationUnits + .Select(r => r.OrganizationUnitId) + .ToArray(); + + var organizationUnits = DbContext.OrganizationUnits + .AsQueryable() + .Where(ou => organizationUnitIds.Contains(ou.Id)) + .ToArray(); + + var roleIds = organizationUnits.SelectMany(x => x.Roles.Select(r => r.RoleId)).ToArray(); + + return await DbContext.Roles //TODO: Such usage suppress filters! + .AsQueryable() + .Where(r => roleIds.Contains(r.Id)) + .Select(r => r.Name) + .ToListAsync(GetCancellationToken(cancellationToken)); } public virtual async Task FindByLoginAsync( @@ -116,8 +148,30 @@ namespace Volo.Abp.Identity.MongoDB CancellationToken cancellationToken = default) { var user = await GetAsync(id, cancellationToken: GetCancellationToken(cancellationToken)); + var organizationUnitIds = user.OrganizationUnits + .Select(r => r.OrganizationUnitId) + .ToArray(); + var organizationUnits = DbContext.OrganizationUnits + .AsQueryable() + .Where(ou => organizationUnitIds.Contains(ou.Id)) + .ToArray(); + var orgUnitRoleIds = organizationUnits.SelectMany(x => x.Roles.Select(r => r.RoleId)).ToArray(); var roleIds = user.Roles.Select(r => r.RoleId).ToArray(); - return await DbContext.Roles.AsQueryable().Where(r => roleIds.Contains(r.Id)).ToListAsync(GetCancellationToken(cancellationToken)); + var allRoleIds = orgUnitRoleIds.Union(roleIds); + return await DbContext.Roles.AsQueryable().Where(r => allRoleIds.Contains(r.Id)).ToListAsync(GetCancellationToken(cancellationToken)); + } + + public async Task> GetOrganizationUnitsAsync( + Guid id, + bool includeDetails = false, + CancellationToken cancellationToken = default) + { + var user = await GetAsync(id, cancellationToken: GetCancellationToken(cancellationToken)); + var organizationUnitIds = user.OrganizationUnits.Select(r => r.OrganizationUnitId); + return await DbContext.OrganizationUnits.AsQueryable() + .Where(ou => organizationUnitIds.Contains(ou.Id)) + .ToListAsync(GetCancellationToken(cancellationToken)) + ; } public virtual async Task GetCountAsync( @@ -135,5 +189,43 @@ namespace Volo.Abp.Identity.MongoDB ) .LongCountAsync(GetCancellationToken(cancellationToken)); } + + public async Task> GetUsersInOrganizationUnitAsync( + Guid organizationUnitId, + CancellationToken cancellationToken = default) + { + var result = await GetMongoQueryable() + .Where(u => u.OrganizationUnits.Any(uou => uou.OrganizationUnitId == organizationUnitId)) + .ToListAsync(GetCancellationToken(cancellationToken)) + ; + return result; + } + + public async Task> GetUsersInOrganizationsListAsync( + List organizationUnitIds, + CancellationToken cancellationToken = default) + { + var result = await GetMongoQueryable() + .Where(u => u.OrganizationUnits.Any(uou => organizationUnitIds.Contains(uou.OrganizationUnitId))) + .ToListAsync(GetCancellationToken(cancellationToken)) + ; + return result; + } + + public async Task> GetUsersInOrganizationUnitWithChildrenAsync( + string code, + CancellationToken cancellationToken = default) + { + var organizationUnitIds = await DbContext.OrganizationUnits.AsQueryable() + .Where(ou => ou.Code.StartsWith(code)) + .Select(ou => ou.Id) + .ToListAsync(GetCancellationToken(cancellationToken)) + ; + + return await GetMongoQueryable() + .Where(u => u.OrganizationUnits.Any(uou => organizationUnitIds.Contains(uou.OrganizationUnitId))) + .ToListAsync(GetCancellationToken(cancellationToken)) + ; + } } } diff --git a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoOrganizationUnitRepository.cs b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoOrganizationUnitRepository.cs new file mode 100644 index 0000000000..943d570153 --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoOrganizationUnitRepository.cs @@ -0,0 +1,163 @@ +using MongoDB.Bson; +using MongoDB.Driver; +using MongoDB.Driver.Linq; +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories.MongoDB; +using Volo.Abp.MongoDB; +using Volo.Abp.Uow; + +namespace Volo.Abp.Identity.MongoDB +{ + public class MongoOrganizationUnitRepository + : MongoDbRepository, + IOrganizationUnitRepository + { + public MongoOrganizationUnitRepository( + IMongoDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + } + + public virtual async Task> GetChildrenAsync( + Guid? parentId, + bool includeDetails = false, + CancellationToken cancellationToken = default) + { + return await GetMongoQueryable() + .Where(ou => ou.ParentId == parentId) + .ToListAsync(GetCancellationToken(cancellationToken)); + } + + public virtual async Task> GetAllChildrenWithParentCodeAsync( + string code, + Guid? parentId, + bool includeDetails = false, + CancellationToken cancellationToken = default) + { + return await GetMongoQueryable() + .Where(ou => ou.Code.StartsWith(code) && ou.Id != parentId.Value) + .ToListAsync(GetCancellationToken(cancellationToken)); + } + + public virtual async Task> GetListAsync( + IEnumerable ids, + bool includeDetails = false, + CancellationToken cancellationToken = default) + { + return await GetMongoQueryable() + .Where(t => ids.Contains(t.Id)) + .ToListAsync(GetCancellationToken(cancellationToken)); + } + + public virtual async Task> GetListAsync( + string sorting = null, + int maxResultCount = int.MaxValue, + int skipCount = 0, + bool includeDetails = false, + CancellationToken cancellationToken = default) + { + return await GetMongoQueryable() + .OrderBy(sorting ?? nameof(OrganizationUnit.DisplayName)) + .As>() + .PageBy>(skipCount, maxResultCount) + .ToListAsync(GetCancellationToken(cancellationToken)); + } + + public virtual async Task GetAsync( + string displayName, + bool includeDetails = true, + CancellationToken cancellationToken = default) + { + return await GetMongoQueryable() + .FirstOrDefaultAsync( + ou => ou.DisplayName == displayName, + GetCancellationToken(cancellationToken) + ); + } + + public virtual async Task> GetRolesAsync( + OrganizationUnit organizationUnit, + string sorting = null, + int maxResultCount = int.MaxValue, + int skipCount = 0, + bool includeDetails = false, + CancellationToken cancellationToken = default) + { + var roleIds = organizationUnit.Roles.Select(r => r.RoleId).ToArray(); + return await DbContext.Roles.AsQueryable().Where(r => roleIds.Contains(r.Id)) + .OrderBy(sorting ?? nameof(IdentityRole.Name)) + .As>() + .PageBy>(skipCount, maxResultCount) + .ToListAsync(cancellationToken); + } + + public virtual async Task GetRolesCountAsync( + OrganizationUnit organizationUnit, + CancellationToken cancellationToken = default) + { + var roleIds = organizationUnit.Roles.Select(r => r.RoleId).ToArray(); + return await DbContext.Roles.AsQueryable().Where(r => roleIds.Contains(r.Id)) + .As>() + .CountAsync(cancellationToken); + } + + public virtual async Task> GetMembersAsync( + OrganizationUnit organizationUnit, + string sorting = null, + int maxResultCount = int.MaxValue, + int skipCount = 0, + string filter = null, + bool includeDetails = false, + CancellationToken cancellationToken = default) + { + return await DbContext.Users.AsQueryable() + .Where(u => u.OrganizationUnits.Any(uou => uou.OrganizationUnitId == organizationUnit.Id)) + .WhereIf>( + !filter.IsNullOrWhiteSpace(), + u => + u.UserName.Contains(filter) || + u.Email.Contains(filter) + ) + .OrderBy(sorting ?? nameof(IdentityUser.UserName)) + .As>() + .PageBy>(skipCount, maxResultCount) + .ToListAsync(GetCancellationToken(cancellationToken)); + } + + public virtual async Task GetMembersCountAsync( + OrganizationUnit organizationUnit, + CancellationToken cancellationToken = default) + { + return await DbContext.Users.AsQueryable() + .Where(u => u.OrganizationUnits.Any(uou => uou.OrganizationUnitId == organizationUnit.Id)) + .As>() + .CountAsync(GetCancellationToken(cancellationToken)); + } + + public virtual Task RemoveAllRolesAsync(OrganizationUnit organizationUnit, CancellationToken cancellationToken = default) + { + organizationUnit.Roles.Clear(); + return Task.FromResult(0); + } + + public virtual async Task RemoveAllMembersAsync(OrganizationUnit organizationUnit, CancellationToken cancellationToken = default) + { + var users = await DbContext.Users.AsQueryable() + .Where(u => u.OrganizationUnits.Any(uou => uou.OrganizationUnitId == organizationUnit.Id)) + .As>() + .ToListAsync(GetCancellationToken(cancellationToken)); + + foreach (var user in users) + { + user.RemoveOrganizationUnit(organizationUnit.Id); + DbContext.Users.ReplaceOne(u => u.Id == user.Id, user); + } + } + } +} diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Navigation/AbpIdentityWebMainMenuContributor.cs b/modules/identity/src/Volo.Abp.Identity.Web/Navigation/AbpIdentityWebMainMenuContributor.cs index b06f590066..784ea35c18 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Navigation/AbpIdentityWebMainMenuContributor.cs +++ b/modules/identity/src/Volo.Abp.Identity.Web/Navigation/AbpIdentityWebMainMenuContributor.cs @@ -16,17 +16,14 @@ namespace Volo.Abp.Identity.Web.Navigation return; } - var authorizationService = context.ServiceProvider.GetRequiredService(); - - var hasRolePermission = await authorizationService.IsGrantedAsync(IdentityPermissions.Roles.Default); - var hasUserPermission = await authorizationService.IsGrantedAsync(IdentityPermissions.Users.Default); + var hasRolePermission = await context.IsGrantedAsync(IdentityPermissions.Roles.Default); + var hasUserPermission = await context.IsGrantedAsync(IdentityPermissions.Users.Default); if (hasRolePermission || hasUserPermission) { var administrationMenu = context.Menu.GetAdministration(); - - var l = context.ServiceProvider.GetRequiredService>(); + var l = context.GetLocalizer(); var identityMenuItem = new ApplicationMenuItem(IdentityMenuNames.GroupName, l["Menu:IdentityManagement"], icon: "fa fa-id-card-o"); administrationMenu.AddItem(identityMenuItem); @@ -43,4 +40,4 @@ namespace Volo.Abp.Identity.Web.Navigation } } } -} \ No newline at end of file +} diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Volo.Abp.Identity.Web.csproj b/modules/identity/src/Volo.Abp.Identity.Web/Volo.Abp.Identity.Web.csproj index 98cdf702d4..f5c9ffc3d4 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Volo.Abp.Identity.Web.csproj +++ b/modules/identity/src/Volo.Abp.Identity.Web/Volo.Abp.Identity.Web.csproj @@ -16,13 +16,11 @@ - - diff --git a/modules/identity/test/Volo.Abp.Identity.Application.Tests/Volo/Abp/Identity/IdentityUserAppService_Tests.cs b/modules/identity/test/Volo.Abp.Identity.Application.Tests/Volo/Abp/Identity/IdentityUserAppService_Tests.cs index 6fbf23ed73..a56631b2ae 100644 --- a/modules/identity/test/Volo.Abp.Identity.Application.Tests/Volo/Abp/Identity/IdentityUserAppService_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.Application.Tests/Volo/Abp/Identity/IdentityUserAppService_Tests.cs @@ -189,9 +189,10 @@ namespace Volo.Abp.Identity //Assert - result.Items.Count.ShouldBe(2); + result.Items.Count.ShouldBe(3); result.Items.ShouldContain(r => r.Name == "moderator"); result.Items.ShouldContain(r => r.Name == "supporter"); + result.Items.ShouldContain(r => r.Name == "manager"); } [Fact] @@ -214,9 +215,10 @@ namespace Volo.Abp.Identity //Assert var roleNames = await _userRepository.GetRoleNamesAsync(johnNash.Id); - roleNames.Count.ShouldBe(2); + roleNames.Count.ShouldBe(3); roleNames.ShouldContain("admin"); roleNames.ShouldContain("moderator"); + roleNames.ShouldContain("manager"); } private static string CreateRandomEmail() diff --git a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityUserManager_Tests.cs b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityUserManager_Tests.cs index 32f344a32e..04f08ed266 100644 --- a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityUserManager_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityUserManager_Tests.cs @@ -1,11 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; using Shouldly; -using Volo.Abp.Castle.DynamicProxy; using Volo.Abp.Uow; using Xunit; @@ -16,6 +14,7 @@ namespace Volo.Abp.Identity private readonly IdentityUserManager _identityUserManager; private readonly IIdentityUserRepository _identityUserRepository; private readonly IIdentityRoleRepository _identityRoleRepository; + private readonly IOrganizationUnitRepository _organizationUnitRepository; private readonly ILookupNormalizer _lookupNormalizer; private readonly IUnitOfWorkManager _unitOfWorkManager; private readonly IdentityTestData _testData; @@ -25,6 +24,7 @@ namespace Volo.Abp.Identity _identityUserManager = GetRequiredService(); _identityUserRepository = GetRequiredService(); _identityRoleRepository = GetRequiredService(); + _organizationUnitRepository = GetRequiredService(); _lookupNormalizer = GetRequiredService(); _testData = GetRequiredService(); _unitOfWorkManager = GetRequiredService(); @@ -88,6 +88,35 @@ namespace Volo.Abp.Identity } } + [Fact] + public async Task SetOrganizationUnitsAsync() + { + using (var uow = _unitOfWorkManager.Begin()) + { + var user = await _identityUserRepository.FindByNormalizedUserNameAsync( + _lookupNormalizer.NormalizeName("david")); + user.ShouldNotBeNull(); + + var ou = await _organizationUnitRepository.GetAsync( + _lookupNormalizer.NormalizeName("OU11")); + ou.ShouldNotBeNull(); + + await _identityUserManager.SetOrganizationUnitsAsync(user, new Guid[] + { + ou.Id + }); + + user = await _identityUserRepository.FindByNormalizedUserNameAsync( + _lookupNormalizer.NormalizeName("david")); + user.OrganizationUnits.Count.ShouldBeGreaterThan(0); + user.OrganizationUnits.FirstOrDefault(uou => uou.OrganizationUnitId == ou.Id).ShouldNotBeNull(); + + await uow.CompleteAsync(); + + + } + } + [Fact] public async Task AddDefaultRolesAsync_In_Same_Uow() { @@ -111,6 +140,35 @@ namespace Volo.Abp.Identity role.IsDefault.ShouldBe(true); } + await uow.CompleteAsync(); + + } + } + + [Fact] + public async Task SetOrganizationUnits_Should_Remove() + { + using (var uow = _unitOfWorkManager.Begin()) + { + var ou = await _organizationUnitRepository.GetAsync( + _lookupNormalizer.NormalizeName("OU111")); + ou.ShouldNotBeNull(); + + var user = await _identityUserRepository.FindByNormalizedUserNameAsync( + _lookupNormalizer.NormalizeName("john.nash")); + user.ShouldNotBeNull(); + + var ouNew = await _organizationUnitRepository.GetAsync( + _lookupNormalizer.NormalizeName("OU2")); + ouNew.ShouldNotBeNull(); + + await _identityUserManager.SetOrganizationUnitsAsync(user, new Guid[] + { + ouNew.Id + }); + + user.OrganizationUnits.ShouldNotContain(x => x.OrganizationUnitId == ou.Id); + await uow.CompleteAsync(); } } diff --git a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/OrganizationUnitManager_Tests.cs b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/OrganizationUnitManager_Tests.cs new file mode 100644 index 0000000000..2f9ac794ac --- /dev/null +++ b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/OrganizationUnitManager_Tests.cs @@ -0,0 +1,121 @@ +using Microsoft.AspNetCore.Identity; +using Shouldly; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.Guids; +using Volo.Abp.Uow; +using Xunit; + +namespace Volo.Abp.Identity +{ + public class OrganizationUnitManager_Tests : AbpIdentityDomainTestBase + { + private readonly OrganizationUnitManager _organizationUnitManager; + private readonly IOrganizationUnitRepository _organizationUnitRepository; + private readonly IdentityTestData _testData; + private readonly IIdentityRoleRepository _identityRoleRepository; + private readonly ILookupNormalizer _lookupNormalizer; + private readonly IUnitOfWorkManager _unitOfWorkManager; + private readonly IGuidGenerator _guidGenerator; + public OrganizationUnitManager_Tests() + { + _organizationUnitManager = GetRequiredService(); + _organizationUnitRepository = GetRequiredService(); + _identityRoleRepository = GetRequiredService(); + _lookupNormalizer = GetRequiredService(); + _testData = GetRequiredService(); + _unitOfWorkManager = GetRequiredService(); + _guidGenerator = GetService(); + } + + [Fact] + public async Task CreateAsnyc() + { + await _organizationUnitManager.CreateAsync(new OrganizationUnit(_guidGenerator.Create(), "Root 1")); + + var root1 = await _organizationUnitRepository.GetAsync("Root 1"); + root1.ShouldNotBeNull(); + } + + [Fact] + public async Task UpdateAsync() + { + var ou = await _organizationUnitRepository.GetAsync("OU111"); + ou.DisplayName = "OU111 Updated"; + await _organizationUnitManager.UpdateAsync(ou); + + var ouAfterChange = await _organizationUnitRepository.GetAsync("OU111 Updated"); + ouAfterChange.DisplayName.ShouldContain("OU111 Updated"); + } + + [Fact] + public async Task DeleteAsync() + { + var ou = await _organizationUnitRepository.GetAsync("OU11"); + await _organizationUnitManager.DeleteAsync(ou.Id); + + (await _organizationUnitRepository.GetAsync("OU11")).ShouldBeNull(); + } + + [Fact] + public async Task MoveAsync() + { + var ou1 = await _organizationUnitRepository.GetAsync("OU1"); + var ou2 = await _organizationUnitRepository.GetAsync("OU2"); + + await _organizationUnitManager.MoveAsync(ou1.Id, ou2.Id); + + ou1 = await _organizationUnitRepository.GetAsync("OU1"); + ou1.ParentId.ShouldBe(ou2.Id); + ou1.Code.ShouldBe(OrganizationUnit.CreateCode(2, 2)); + + var ou11 = await _organizationUnitRepository.GetAsync("OU11"); + ou11.ParentId.ShouldBe(ou1.Id); + ou11.Code.ShouldBe(OrganizationUnit.CreateCode(2, 2, 1)); + + var ou111 = await _organizationUnitRepository.GetAsync("OU111"); + ou111.ParentId.ShouldBe(ou11.Id); + ou111.Code.ShouldBe(OrganizationUnit.CreateCode(2, 2, 1, 1)); + + var ou112 = await _organizationUnitRepository.GetAsync("OU112"); + ou112.ParentId.ShouldBe(ou11.Id); + ou112.Code.ShouldBe(OrganizationUnit.CreateCode(2, 2, 1, 2)); + + var ou12 = await _organizationUnitRepository.GetAsync("OU12"); + ou12.ParentId.ShouldBe(ou1.Id); + ou12.Code.ShouldBe(OrganizationUnit.CreateCode(2, 2, 2)); + } + + [Fact] + public async Task AddRoleToOrganizationUnitAsync() + { + OrganizationUnit ou = null; + IdentityRole adminRole = null; + + using (var uow = _unitOfWorkManager.Begin()) + { + ou = await _organizationUnitRepository.GetAsync("OU1", true); + adminRole = await _identityRoleRepository.FindByNormalizedNameAsync(_lookupNormalizer.NormalizeName("admin")); + await _organizationUnitManager.AddRoleToOrganizationUnitAsync(adminRole, ou); + await _organizationUnitRepository.UpdateAsync(ou); + + await uow.CompleteAsync(); + } + + ou = await _organizationUnitRepository.GetAsync("OU1", includeDetails: true); + ou.Roles.First().RoleId.ShouldBe(adminRole.Id); + } + + [Fact] + public async Task RemoveRoleFromOrganizationUnitAsync() + { + var ou = await _organizationUnitRepository.GetAsync("OU1", true); + var adminRole = await _identityRoleRepository.FindByNormalizedNameAsync(_lookupNormalizer.NormalizeName("admin")); + await _organizationUnitManager.AddRoleToOrganizationUnitAsync(adminRole.Id, ou.Id); + + await _organizationUnitManager.RemoveRoleFromOrganizationUnitAsync(adminRole.Id, ou.Id); + ou = await _organizationUnitRepository.GetAsync("OU1", includeDetails: true); + ou.Roles.FirstOrDefault(r => r.RoleId == adminRole.Id).ShouldBeNull(); + } + } +} diff --git a/modules/identity/test/Volo.Abp.Identity.EntityFrameworkCore.Tests/Volo.Abp.Identity.EntityFrameworkCore.Tests.csproj b/modules/identity/test/Volo.Abp.Identity.EntityFrameworkCore.Tests/Volo.Abp.Identity.EntityFrameworkCore.Tests.csproj index f4eb09517d..81f0f63e94 100644 --- a/modules/identity/test/Volo.Abp.Identity.EntityFrameworkCore.Tests/Volo.Abp.Identity.EntityFrameworkCore.Tests.csproj +++ b/modules/identity/test/Volo.Abp.Identity.EntityFrameworkCore.Tests/Volo.Abp.Identity.EntityFrameworkCore.Tests.csproj @@ -15,13 +15,12 @@ - + + - - - + diff --git a/modules/identity/test/Volo.Abp.Identity.EntityFrameworkCore.Tests/Volo/Abp/Identity/EntityFrameworkCore/AbpIdentityEntityFrameworkCoreTestModule.cs b/modules/identity/test/Volo.Abp.Identity.EntityFrameworkCore.Tests/Volo/Abp/Identity/EntityFrameworkCore/AbpIdentityEntityFrameworkCoreTestModule.cs index 322f07e995..8a2778d6c2 100644 --- a/modules/identity/test/Volo.Abp.Identity.EntityFrameworkCore.Tests/Volo/Abp/Identity/EntityFrameworkCore/AbpIdentityEntityFrameworkCoreTestModule.cs +++ b/modules/identity/test/Volo.Abp.Identity.EntityFrameworkCore.Tests/Volo/Abp/Identity/EntityFrameworkCore/AbpIdentityEntityFrameworkCoreTestModule.cs @@ -3,6 +3,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore.Sqlite; using Volo.Abp.Modularity; using Volo.Abp.PermissionManagement.EntityFrameworkCore; @@ -11,7 +12,8 @@ namespace Volo.Abp.Identity.EntityFrameworkCore [DependsOn( typeof(AbpIdentityTestBaseModule), typeof(AbpPermissionManagementEntityFrameworkCoreModule), - typeof(AbpIdentityEntityFrameworkCoreModule) + typeof(AbpIdentityEntityFrameworkCoreModule), + typeof(AbpEntityFrameworkCoreSqliteModule) )] public class AbpIdentityEntityFrameworkCoreTestModule : AbpModule { diff --git a/modules/identity/test/Volo.Abp.Identity.EntityFrameworkCore.Tests/Volo/Abp/Identity/EntityFrameworkCore/OrganizationUnitRepository_Tests.cs b/modules/identity/test/Volo.Abp.Identity.EntityFrameworkCore.Tests/Volo/Abp/Identity/EntityFrameworkCore/OrganizationUnitRepository_Tests.cs new file mode 100644 index 0000000000..3e8de4f511 --- /dev/null +++ b/modules/identity/test/Volo.Abp.Identity.EntityFrameworkCore.Tests/Volo/Abp/Identity/EntityFrameworkCore/OrganizationUnitRepository_Tests.cs @@ -0,0 +1,6 @@ +namespace Volo.Abp.Identity.EntityFrameworkCore +{ + public class OrganizationUnitRepository_Tests : OrganizationUnitRepository_Tests + { + } +} diff --git a/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/MongoDbFixture.cs b/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/MongoDbFixture.cs index 26de0a9399..09581c13d5 100644 --- a/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/MongoDbFixture.cs +++ b/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/MongoDbFixture.cs @@ -5,12 +5,18 @@ namespace Volo.Abp.Identity.MongoDB { public class MongoDbFixture : IDisposable { - private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); - public static readonly string ConnectionString = MongoDbRunner.ConnectionString; + private static readonly MongoDbRunner MongoDbRunner; + public static readonly string ConnectionString; + + static MongoDbFixture() + { + MongoDbRunner = MongoDbRunner.Start(); + ConnectionString = MongoDbRunner.ConnectionString; + } public void Dispose() { MongoDbRunner?.Dispose(); } } -} \ No newline at end of file +} diff --git a/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/OrganizationUnitRepository_Tests.cs b/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/OrganizationUnitRepository_Tests.cs new file mode 100644 index 0000000000..d60611b2b6 --- /dev/null +++ b/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/OrganizationUnitRepository_Tests.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Volo.Abp.Identity.MongoDB +{ + public class OrganizationUnitRepository_Tests : OrganizationUnitRepository_Tests + { + } +} diff --git a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/AbpIdentityTestDataBuilder.cs b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/AbpIdentityTestDataBuilder.cs index 69361ff5a8..9258be16af 100644 --- a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/AbpIdentityTestDataBuilder.cs +++ b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/AbpIdentityTestDataBuilder.cs @@ -1,4 +1,5 @@ -using System.Security.Claims; +using System; +using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; using Volo.Abp.DependencyInjection; @@ -12,20 +13,27 @@ namespace Volo.Abp.Identity private readonly IIdentityUserRepository _userRepository; private readonly IIdentityClaimTypeRepository _identityClaimTypeRepository; private readonly IIdentityRoleRepository _roleRepository; + private readonly IOrganizationUnitRepository _organizationUnitRepository; private readonly ILookupNormalizer _lookupNormalizer; private readonly IdentityTestData _testData; + private readonly OrganizationUnitManager _organizationUnitManager; private IdentityRole _adminRole; - private IdentityRole _moderator; + private IdentityRole _moderatorRole; private IdentityRole _supporterRole; + private IdentityRole _managerRole; + private OrganizationUnit _ou111; + private OrganizationUnit _ou112; public AbpIdentityTestDataBuilder( IGuidGenerator guidGenerator, IIdentityUserRepository userRepository, IIdentityClaimTypeRepository identityClaimTypeRepository, IIdentityRoleRepository roleRepository, + IOrganizationUnitRepository organizationUnitRepository, ILookupNormalizer lookupNormalizer, - IdentityTestData testData) + IdentityTestData testData, + OrganizationUnitManager organizationUnitManager) { _guidGenerator = guidGenerator; _userRepository = userRepository; @@ -33,11 +41,14 @@ namespace Volo.Abp.Identity _roleRepository = roleRepository; _lookupNormalizer = lookupNormalizer; _testData = testData; + _organizationUnitRepository = organizationUnitRepository; + _organizationUnitManager = organizationUnitManager; } public async Task Build() { await AddRoles(); + await AddOrganizationUnits(); await AddUsers(); await AddClaimTypes(); } @@ -46,12 +57,41 @@ namespace Volo.Abp.Identity { _adminRole = await _roleRepository.FindByNormalizedNameAsync(_lookupNormalizer.NormalizeName("admin")); - _moderator = new IdentityRole(_testData.RoleModeratorId, "moderator"); - _moderator.AddClaim(_guidGenerator, new Claim("test-claim", "test-value")); - await _roleRepository.InsertAsync(_moderator); + _moderatorRole = new IdentityRole(_testData.RoleModeratorId, "moderator"); + _moderatorRole.AddClaim(_guidGenerator, new Claim("test-claim", "test-value")); + await _roleRepository.InsertAsync(_moderatorRole); _supporterRole = new IdentityRole(_guidGenerator.Create(), "supporter"); await _roleRepository.InsertAsync(_supporterRole); + + _managerRole = new IdentityRole(_guidGenerator.Create(), "manager"); + await _roleRepository.InsertAsync(_managerRole); + } + + /* Creates OU tree as shown below: + * + * - OU1 + * - OU11 + * - OU111 + * - OU112 + * - OU12 + * - OU2 + * - OU21 + */ + private async Task AddOrganizationUnits() + { + var ou1 = await CreateOU("OU1", OrganizationUnit.CreateCode(1)); + var ou11 = await CreateOU("OU11", OrganizationUnit.CreateCode(1, 1), ou1.Id); + _ou112 = await CreateOU("OU112", OrganizationUnit.CreateCode(1, 1, 2), ou11.Id); + var ou12 = await CreateOU("OU12", OrganizationUnit.CreateCode(1, 2), ou1.Id); + var ou2 = await CreateOU("OU2", OrganizationUnit.CreateCode(2)); + var ou21 = await CreateOU("OU21", OrganizationUnit.CreateCode(2, 1), ou2.Id); + + _ou111 = new OrganizationUnit(_guidGenerator.Create(), "OU111", ou11.Id); + _ou111.Code = OrganizationUnit.CreateCode(1, 1, 1); + _ou111.AddRole(_moderatorRole.Id); + _ou111.AddRole(_managerRole.Id); + await _organizationUnitRepository.InsertAsync(_ou111); } private async Task AddUsers() @@ -62,8 +102,10 @@ namespace Volo.Abp.Identity await _userRepository.InsertAsync(adminUser); var john = new IdentityUser(_testData.UserJohnId, "john.nash", "john.nash@abp.io"); - john.AddRole(_moderator.Id); + john.AddRole(_moderatorRole.Id); john.AddRole(_supporterRole.Id); + john.AddOrganizationUnit(_ou111.Id); + john.AddOrganizationUnit(_ou112.Id); john.AddLogin(new UserLoginInfo("github", "john", "John Nash")); john.AddLogin(new UserLoginInfo("twitter", "johnx", "John Nash")); john.AddClaim(_guidGenerator, new Claim("TestClaimType", "42")); @@ -71,20 +113,30 @@ namespace Volo.Abp.Identity await _userRepository.InsertAsync(john); var david = new IdentityUser(_testData.UserDavidId, "david", "david@abp.io"); + david.AddOrganizationUnit(_ou112.Id); await _userRepository.InsertAsync(david); var neo = new IdentityUser(_testData.UserNeoId, "neo", "neo@abp.io"); neo.AddRole(_supporterRole.Id); neo.AddClaim(_guidGenerator, new Claim("TestClaimType", "43")); + neo.AddOrganizationUnit(_ou111.Id); await _userRepository.InsertAsync(neo); } + private async Task AddClaimTypes() { - var ageClaim = new IdentityClaimType(_testData.AgeClaimId, "Age", false, false, null, null, null,IdentityClaimValueType.Int); + var ageClaim = new IdentityClaimType(_testData.AgeClaimId, "Age", false, false, null, null, null, IdentityClaimValueType.Int); await _identityClaimTypeRepository.InsertAsync(ageClaim); + var educationClaim = new IdentityClaimType(_testData.EducationClaimId, "Education", true, false, null, null, null); await _identityClaimTypeRepository.InsertAsync(educationClaim); } + + private async Task CreateOU(string displayName, string code, Guid? parentId = null) + { + var ou = await _organizationUnitRepository.InsertAsync(new OrganizationUnit(_guidGenerator.Create(), displayName, parentId) { Code = code }); + return ou; + } } } \ No newline at end of file diff --git a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityUserRepository_Tests.cs b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityUserRepository_Tests.cs index 4fa407a512..1d6728c0ae 100644 --- a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityUserRepository_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/IdentityUserRepository_Tests.cs @@ -15,11 +15,13 @@ namespace Volo.Abp.Identity { protected IIdentityUserRepository UserRepository { get; } protected ILookupNormalizer LookupNormalizer { get; } + protected IOrganizationUnitRepository OrganizationUnitRepository { get; } protected IdentityUserRepository_Tests() { UserRepository = ServiceProvider.GetRequiredService(); LookupNormalizer = ServiceProvider.GetRequiredService(); + OrganizationUnitRepository = ServiceProvider.GetRequiredService(); } [Fact] @@ -42,9 +44,10 @@ namespace Volo.Abp.Identity { var john = await UserRepository.FindByNormalizedUserNameAsync(LookupNormalizer.NormalizeName("john.nash")); var roles = await UserRepository.GetRoleNamesAsync(john.Id); - roles.Count.ShouldBe(2); + roles.Count.ShouldBe(3); roles.ShouldContain("moderator"); roles.ShouldContain("supporter"); + roles.ShouldContain("manager"); } [Fact] @@ -116,9 +119,10 @@ namespace Volo.Abp.Identity { var john = await UserRepository.FindByNormalizedUserNameAsync(LookupNormalizer.NormalizeName("john.nash")); var roles = await UserRepository.GetRolesAsync(john.Id); - roles.Count.ShouldBe(2); + roles.Count.ShouldBe(3); roles.ShouldContain(r => r.Name == "moderator"); roles.ShouldContain(r => r.Name == "supporter"); + roles.ShouldContain(r => r.Name == "manager"); } [Fact] @@ -128,6 +132,22 @@ namespace Volo.Abp.Identity (await UserRepository.GetCountAsync("undefined-username")).ShouldBe(0); } + [Fact] + public async Task GetUsersInOrganizationUnitAsync() + { + var users = await UserRepository.GetUsersInOrganizationUnitAsync((await GetOU("OU111")).Id); + users.ShouldNotBeNull(); + users.Count.ShouldBeGreaterThan(0); + } + + [Fact] + public async Task GetUsersInOrganizationUnitWithChildrenAsync() + { + var users = await UserRepository.GetUsersInOrganizationUnitWithChildrenAsync((await GetOU("OU111")).Code); + users.ShouldNotBeNull(); + users.Count.ShouldBeGreaterThan(0); + } + [Fact] public async Task Should_Eager_Load_User_Collections() { @@ -144,6 +164,16 @@ namespace Volo.Abp.Identity john.Tokens.ShouldNotBeNull(); john.Tokens.Any().ShouldBeTrue(); + + john.OrganizationUnits.ShouldNotBeNull(); + john.OrganizationUnits.Any().ShouldBeTrue(); + } + + private async Task GetOU(string diplayName) + { + var organizationUnit = await OrganizationUnitRepository.GetAsync(diplayName); + organizationUnit.ShouldNotBeNull(); + return organizationUnit; } } } diff --git a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/Identity_Repository_Resolve_Tests.cs b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/Identity_Repository_Resolve_Tests.cs index 8b748f10b7..6d541d5987 100644 --- a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/Identity_Repository_Resolve_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/Identity_Repository_Resolve_Tests.cs @@ -20,6 +20,8 @@ namespace Volo.Abp.Identity ServiceProvider.GetService>().ShouldNotBeNull(); ServiceProvider.GetService>().ShouldNotBeNull(); ServiceProvider.GetService().ShouldNotBeNull(); + + ServiceProvider.GetService().ShouldNotBeNull(); } } } diff --git a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/LazyLoading_Tests.cs b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/LazyLoading_Tests.cs index 1ae671dd4f..eae5ee5c1d 100644 --- a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/LazyLoading_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/LazyLoading_Tests.cs @@ -14,6 +14,7 @@ namespace Volo.Abp.Identity { protected IIdentityUserRepository UserRepository { get; } protected IIdentityRoleRepository RoleRepository { get; } + protected IOrganizationUnitRepository OrganizationUnitRepository { get; } protected ILookupNormalizer LookupNormalizer { get; } protected LazyLoading_Tests() @@ -21,6 +22,7 @@ namespace Volo.Abp.Identity UserRepository = ServiceProvider.GetRequiredService(); RoleRepository = ServiceProvider.GetRequiredService(); LookupNormalizer = ServiceProvider.GetRequiredService(); + OrganizationUnitRepository = ServiceProvider.GetRequiredService(); } [Fact] @@ -55,6 +57,22 @@ namespace Volo.Abp.Identity john.Tokens.ShouldNotBeNull(); john.Tokens.Any().ShouldBeTrue(); + john.OrganizationUnits.ShouldNotBeNull(); + john.OrganizationUnits.Any().ShouldBeTrue(); + + await uow.CompleteAsync(); + } + } + + [Fact] + public async Task Should_Lazy_Load_OrganizationUnit_Collections() + { + using (var uow = GetRequiredService().Begin()) + { + var ou = await OrganizationUnitRepository.GetAsync(LookupNormalizer.NormalizeName("OU111"), includeDetails: false); + ou.Roles.ShouldNotBeNull(); //? + ou.Roles.Any().ShouldBeTrue(); + await uow.CompleteAsync(); } } diff --git a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/OrganizationUnitRepository_Tests.cs b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/OrganizationUnitRepository_Tests.cs new file mode 100644 index 0000000000..bbb7d649c9 --- /dev/null +++ b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/OrganizationUnitRepository_Tests.cs @@ -0,0 +1,179 @@ +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.Guids; +using Volo.Abp.Modularity; +using Volo.Abp.Uow; +using Xunit; + +namespace Volo.Abp.Identity +{ + public abstract class OrganizationUnitRepository_Tests : AbpIdentityTestBase + where TStartupModule : IAbpModule + { + private readonly IOrganizationUnitRepository _organizationUnitRepository; + private readonly ILookupNormalizer _lookupNormalizer; + private readonly IdentityTestData _testData; + private readonly IGuidGenerator _guidGenerator; + private readonly OrganizationUnitManager _organizationUnitManager; + private readonly IIdentityRoleRepository _identityRoleRepository; + private readonly IUnitOfWorkManager _unitOfWorkManager; + private readonly IIdentityUserRepository _identityUserRepository; + + public OrganizationUnitRepository_Tests() + { + _organizationUnitRepository = ServiceProvider.GetRequiredService(); + _lookupNormalizer = ServiceProvider.GetRequiredService(); + _testData = GetRequiredService(); + _guidGenerator = GetRequiredService(); + _organizationUnitManager = GetRequiredService(); + _identityRoleRepository = GetRequiredService(); + _unitOfWorkManager = GetRequiredService(); + _identityUserRepository = GetRequiredService(); + } + + [Fact] + public async Task GetChildrenAsync() + { + (await _organizationUnitRepository.GetChildrenAsync(_testData.RoleModeratorId)).ShouldNotBeNull(); + } + + [Fact] + public async Task GetAllChildrenWithParentCodeAsync() + { + (await _organizationUnitRepository.GetAllChildrenWithParentCodeAsync(OrganizationUnit.CreateCode(0), _guidGenerator.Create())).ShouldNotBeNull(); + } + + [Fact] + public async Task GetListAsync() + { + var ouIds = (await _organizationUnitRepository.GetListAsync(includeDetails: true)) + .Select(ou => ou.Id).Take(2); + var ous = await _organizationUnitRepository.GetListAsync(ouIds); + ous.Count.ShouldBe(2); + ous.ShouldContain(ou => ou.Id == ouIds.First()); + } + + [Fact] + public async Task GetOrganizationUnitAsync() + { + var organizationUnit = await _organizationUnitRepository.GetAsync("OU111"); + organizationUnit.ShouldNotBeNull(); + } + + [Fact] + public async Task GetCountAsync() + { + (await _organizationUnitRepository.GetCountAsync()).ShouldBeGreaterThan(0); + } + + [Fact] + public async Task Should_Eager_Load_OrganizationUnit_Collections() + { + var ou = (await _organizationUnitRepository.GetListAsync(includeDetails: true)) + .FirstOrDefault(ou => ou.DisplayName == "OU111"); + ou.Roles.ShouldNotBeNull(); + ou.Roles.Any().ShouldBeTrue(); + } + [Fact] + public async Task GetOrganizationUnitRolesAsync() + { + OrganizationUnit ou = await _organizationUnitRepository.GetAsync("OU111", includeDetails: true); + + var ou111Roles = await _organizationUnitRepository.GetRolesAsync(ou, includeDetails: true); + ou111Roles.Count.ShouldBe(2); + ou111Roles.ShouldContain(n => n.Name == "manager"); + ou111Roles.ShouldContain(n => n.Name == "moderator"); + } + + [Fact] + public async Task GetOrganizationUnitRolesWithPagingAsync() + { + OrganizationUnit ou = await _organizationUnitRepository.GetAsync("OU111", includeDetails: true); + + var ou111Roles = await _organizationUnitRepository.GetRolesAsync(ou, sorting: "name desc", maxResultCount: 1, includeDetails: true); + ou111Roles.Count.ShouldBe(1); + ou111Roles.ShouldContain(n => n.Name == "moderator"); + } + + [Fact] + public async Task GetMembersInOrganizationUnitListAsync() + { + OrganizationUnit ou1 = await _organizationUnitRepository.GetAsync("OU111", true); + OrganizationUnit ou2 = await _organizationUnitRepository.GetAsync("OU112", true); + var users = await _identityUserRepository.GetUsersInOrganizationsListAsync(new List { ou1.Id, ou2.Id }); + users.Count.ShouldBeGreaterThan(0); + } + [Fact] + public async Task GetMembersInOrganizationUnitWithParamsAsync() + { + OrganizationUnit ou = await _organizationUnitRepository.GetAsync("OU111", true); + var users = await _organizationUnitRepository.GetMembersAsync(ou, "UserName DESC", 5, 0, "n"); + + users.Count.ShouldBeGreaterThan(1); + users.Count.ShouldBeLessThanOrEqualTo(5); + + //Filter check + users.ShouldAllBe(u => u.UserName.Contains("ne") || u.Email.Contains("n")); + + //Order check + for (var i = 0; i < users.Count - 1; i++) + { + string.Compare( + users[i].UserName, + users[i + 1].UserName, + StringComparison.OrdinalIgnoreCase + ).ShouldBeGreaterThan(0); + } + + users = await _organizationUnitRepository.GetMembersAsync(ou, null, 999, 0, "undefined-username"); + users.Count.ShouldBe(0); + } + + [Fact] + public async Task GetMembersCountOfOrganizationUnit() + { + OrganizationUnit ou = await _organizationUnitRepository.GetAsync("OU111", true); + var usersCount = await _organizationUnitRepository.GetMembersCountAsync(ou); + + usersCount.ShouldBeGreaterThan(1); + } + + [Fact] + public async Task GetRolesCountOfOrganizationUnit() + { + OrganizationUnit ou = await _organizationUnitRepository.GetAsync("OU111", true); + var rolesCount = await _organizationUnitRepository.GetRolesCountAsync(ou); + + rolesCount.ShouldBeGreaterThan(1); + } + + [Fact] + public async Task RemoveAllMembersOfOrganizationUnit() + { + OrganizationUnit ou = await _organizationUnitRepository.GetAsync("OU111", true); + var membersCount = await _organizationUnitRepository.GetMembersCountAsync(ou); + membersCount.ShouldBeGreaterThan(1); + + await _organizationUnitRepository.RemoveAllMembersAsync(ou); + var newCount = await _organizationUnitRepository.GetMembersCountAsync(ou); + newCount.ShouldBe(0); + } + + [Fact] + public async Task RemoveAllRolesOfOrganizationUnit() + { + OrganizationUnit ou = await _organizationUnitRepository.GetAsync("OU111", true); + var rolesCount = await _organizationUnitRepository.GetRolesCountAsync(ou); + rolesCount.ShouldBeGreaterThan(1); + + await _organizationUnitRepository.RemoveAllRolesAsync(ou); + var newCount = await _organizationUnitRepository.GetRolesCountAsync(ou); + newCount.ShouldBe(0); + } + } +} diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientPostLogoutRedirectUriConsts.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientPostLogoutRedirectUriConsts.cs index 8fe55ba018..3092232d47 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientPostLogoutRedirectUriConsts.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientPostLogoutRedirectUriConsts.cs @@ -3,5 +3,6 @@ public class ClientPostLogoutRedirectUriConsts { public const int PostLogoutRedirectUriMaxLength = 2000; + public static int PostLogoutRedirectUriMaxLengthValue { get; set; } = 2000; } } \ No newline at end of file diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientRedirectUriConsts.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientRedirectUriConsts.cs index d67bbea509..e972388487 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientRedirectUriConsts.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Clients/ClientRedirectUriConsts.cs @@ -3,5 +3,6 @@ public class ClientRedirectUriConsts { public const int RedirectUriMaxLength = 2000; + public static int RedirectUriMaxLengthValue { get; set; } = 2000; } } \ No newline at end of file diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Grants/PersistedGrantConsts.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Grants/PersistedGrantConsts.cs index ab46d03ece..6d0836ab17 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Grants/PersistedGrantConsts.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Grants/PersistedGrantConsts.cs @@ -6,9 +6,7 @@ public const int TypeMaxLength = 50; public const int SubjectIdMaxLength = 200; public const int ClientIdMaxLength = 200; - - // 50000 chosen to be explicit to allow enough size to avoid truncation, yet stay beneath the MySql row size limit of ~65K - // apparently anything over 4K converts to nvarchar(max) on SqlServer public const int DataMaxLength = 50000; + public static int DataMaxLengthValue { get; set; }= 50000; } } \ No newline at end of file diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/cs.json b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/cs.json index 5b30e44dad..bee3e74fd9 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/cs.json +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/Localization/Resources/cs.json @@ -3,6 +3,10 @@ "texts": { "Volo.IdentityServer:DuplicateIdentityResourceName": "Název Identity Resource již existuje: {Name}", "Volo.IdentityServer:DuplicateApiResourceName": "Název Api Resource již existuje: {Name}", - "Volo.IdentityServer:DuplicateClientId": "ClientId již existuje: {ClientId}" + "Volo.IdentityServer:DuplicateClientId": "ClientId již existuje: {ClientId}", + "UserLockedOut": "Tento uživatelský účet byl zablokován kvůli neplatným pokusům o přihlášení. Chvilku počkejte a zkuste to znovu.", + "InvalidUserNameOrPassword": "Neplatné uživatelské jméno či heslo!", + "LoginIsNotAllowed": "Nemáte oprávnění se přihlásit! Musíte potvrdit svůj email/telefonní číslo.", + "InvalidUsername": "Neplatné uživatelské jméno či heslo!" } -} \ No newline at end of file +} diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/SecretConsts.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/SecretConsts.cs index b21f901f25..76635e322e 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/SecretConsts.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain.Shared/Volo/Abp/IdentityServer/SecretConsts.cs @@ -4,6 +4,7 @@ { public const int TypeMaxLength = 250; public const int ValueMaxLength = 4000; + public static int ValueMaxLengthValue { get; set; } = ValueMaxLength; public const int DescriptionMaxLength = 2000; } } \ No newline at end of file diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/IApiResourceRepository.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/IApiResourceRepository.cs index 5b9b8a2e26..988cd8c4a6 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/IApiResourceRepository.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/IApiResourceRepository.cs @@ -24,6 +24,7 @@ namespace Volo.Abp.IdentityServer.ApiResources string sorting, int skipCount, int maxResultCount, + string filter = null, bool includeDetails = false, CancellationToken cancellationToken = default ); diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/IClientRepository.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/IClientRepository.cs index fb21202d9c..606b0086bd 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/IClientRepository.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/IClientRepository.cs @@ -19,6 +19,7 @@ namespace Volo.Abp.IdentityServer.Clients string sorting, int skipCount, int maxResultCount, + string filter = null, bool includeDetails = false, CancellationToken cancellationToken = default ); diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/IdentityResources/IIdentityResourceRepository.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/IdentityResources/IIdentityResourceRepository.cs index 7fcf64255d..c8b350ce1a 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/IdentityResources/IIdentityResourceRepository.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/IdentityResources/IIdentityResourceRepository.cs @@ -18,6 +18,7 @@ namespace Volo.Abp.IdentityServer.IdentityResources string sorting, int skipCount, int maxResultCount, + string filter = null, bool includeDetails = false, CancellationToken cancellationToken = default ); diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/ApiResources/ApiResourceRepository.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/ApiResources/ApiResourceRepository.cs index 384db34364..d4fc77ede2 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/ApiResources/ApiResourceRepository.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/ApiResources/ApiResourceRepository.cs @@ -8,6 +8,7 @@ using Volo.Abp.Domain.Repositories.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore; using Volo.Abp.IdentityServer.EntityFrameworkCore; using System.Linq.Dynamic.Core; +using System.Runtime.InteropServices.ComTypes; namespace Volo.Abp.IdentityServer.ApiResources { @@ -24,30 +25,35 @@ namespace Volo.Abp.IdentityServer.ApiResources CancellationToken cancellationToken = default) { var query = from apiResource in DbSet.IncludeDetails(includeDetails) - where apiResource.Name == name - select apiResource; + where apiResource.Name == name + select apiResource; return await query .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); } public virtual async Task> GetListByScopesAsync( - string[] scopeNames, + string[] scopeNames, bool includeDetails = false, CancellationToken cancellationToken = default) { var query = from api in DbSet.IncludeDetails(includeDetails) - where api.Scopes.Any(x => scopeNames.Contains(x.Name)) - select api; + where api.Scopes.Any(x => scopeNames.Contains(x.Name)) + select api; return await query.ToListAsync(GetCancellationToken(cancellationToken)); } - public virtual async Task> GetListAsync(string sorting, int skipCount, int maxResultCount, bool includeDetails = false, + public virtual async Task> GetListAsync( + string sorting, int skipCount, int maxResultCount, string filter, bool includeDetails = false, CancellationToken cancellationToken = default) { return await DbSet - .IncludeDetails(includeDetails).OrderBy(sorting ?? "name desc") + .IncludeDetails(includeDetails) + .WhereIf(!filter.IsNullOrWhiteSpace(), x => x.Name.Contains(filter) || + x.Description.Contains(filter) || + x.DisplayName.Contains(filter)) + .OrderBy(sorting ?? "name desc") .PageBy(skipCount, maxResultCount) .ToListAsync(GetCancellationToken(cancellationToken)); } diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/Clients/ClientRepository.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/Clients/ClientRepository.cs index de2ff83bc0..5b350d0365 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/Clients/ClientRepository.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/Clients/ClientRepository.cs @@ -19,7 +19,7 @@ namespace Volo.Abp.IdentityServer.Clients } public virtual async Task FindByCliendIdAsync( - string clientId, + string clientId, bool includeDetails = true, CancellationToken cancellationToken = default) { @@ -28,11 +28,14 @@ namespace Volo.Abp.IdentityServer.Clients .FirstOrDefaultAsync(x => x.ClientId == clientId, GetCancellationToken(cancellationToken)); } - public virtual async Task> GetListAsync(string sorting, int skipCount, int maxResultCount, bool includeDetails = false, + public virtual async Task> GetListAsync( + string sorting, int skipCount, int maxResultCount, string filter, bool includeDetails = false, CancellationToken cancellationToken = default) { return await DbSet - .IncludeDetails(includeDetails).OrderBy(sorting ?? nameof(Client.ClientName) + " desc") + .IncludeDetails(includeDetails) + .WhereIf(!filter.IsNullOrWhiteSpace(), x => x.ClientId.Contains(filter)) + .OrderBy(sorting ?? nameof(Client.ClientName) + " desc") .PageBy(skipCount, maxResultCount) .ToListAsync(GetCancellationToken(cancellationToken)); } diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs index 277084e13d..8acb8a9539 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs @@ -65,7 +65,7 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore b.ConfigureByConvention(); - b.HasKey(x => new { x.ClientId, x.GrantType }); + b.HasKey(x => new {x.ClientId, x.GrantType}); b.Property(x => x.GrantType).HasMaxLength(ClientGrantTypeConsts.GrantTypeMaxLength).IsRequired(); }); @@ -76,16 +76,14 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore b.ConfigureByConvention(); - b.HasKey(x => new { x.ClientId, x.RedirectUri }); + b.HasKey(x => new {x.ClientId, x.RedirectUri}); - if (options.DatabaseProvider == EfCoreDatabaseProvider.MySql) + if (IsDatabaseProvider(builder, options, EfCoreDatabaseProvider.MySql)) { - b.Property(x => x.RedirectUri).HasMaxLength(300).IsRequired(); - } - else - { - b.Property(x => x.RedirectUri).HasMaxLength(ClientRedirectUriConsts.RedirectUriMaxLength).IsRequired(); - } + ClientRedirectUriConsts.RedirectUriMaxLengthValue = 300; + } + + b.Property(x => x.RedirectUri).HasMaxLength(ClientRedirectUriConsts.RedirectUriMaxLengthValue).IsRequired(); }); builder.Entity(b => @@ -94,16 +92,16 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore b.ConfigureByConvention(); - b.HasKey(x => new { x.ClientId, x.PostLogoutRedirectUri }); + b.HasKey(x => new {x.ClientId, x.PostLogoutRedirectUri}); - if (options.DatabaseProvider == EfCoreDatabaseProvider.MySql) - { - b.Property(x => x.PostLogoutRedirectUri).HasMaxLength(300).IsRequired(); - } - else + if (IsDatabaseProvider(builder, options, EfCoreDatabaseProvider.MySql)) { - b.Property(x => x.PostLogoutRedirectUri).HasMaxLength(ClientPostLogoutRedirectUriConsts.PostLogoutRedirectUriMaxLength).IsRequired(); - } + ClientPostLogoutRedirectUriConsts.PostLogoutRedirectUriMaxLengthValue = 300; + } + + b.Property(x => x.PostLogoutRedirectUri) + .HasMaxLength(ClientPostLogoutRedirectUriConsts.PostLogoutRedirectUriMaxLengthValue) + .IsRequired(); }); builder.Entity(b => @@ -112,7 +110,7 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore b.ConfigureByConvention(); - b.HasKey(x => new { x.ClientId, x.Scope }); + b.HasKey(x => new {x.ClientId, x.Scope}); b.Property(x => x.Scope).HasMaxLength(ClientScopeConsts.ScopeMaxLength).IsRequired(); }); @@ -123,18 +121,16 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore b.ConfigureByConvention(); - b.HasKey(x => new { x.ClientId, x.Type, x.Value }); + b.HasKey(x => new {x.ClientId, x.Type, x.Value}); b.Property(x => x.Type).HasMaxLength(SecretConsts.TypeMaxLength).IsRequired(); - if (options.DatabaseProvider == EfCoreDatabaseProvider.MySql) - { - b.Property(x => x.Value).HasMaxLength(300).IsRequired(); - } - else + if (IsDatabaseProvider(builder, options, EfCoreDatabaseProvider.MySql, EfCoreDatabaseProvider.Oracle)) { - b.Property(x => x.Value).HasMaxLength(SecretConsts.ValueMaxLength).IsRequired(); + SecretConsts.ValueMaxLengthValue = 300; } + + b.Property(x => x.Value).HasMaxLength(SecretConsts.ValueMaxLengthValue).IsRequired(); b.Property(x => x.Description).HasMaxLength(SecretConsts.DescriptionMaxLength); }); @@ -145,7 +141,7 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore b.ConfigureByConvention(); - b.HasKey(x => new { x.ClientId, x.Type, x.Value }); + b.HasKey(x => new {x.ClientId, x.Type, x.Value}); b.Property(x => x.Type).HasMaxLength(ClientClaimConsts.TypeMaxLength).IsRequired(); b.Property(x => x.Value).HasMaxLength(ClientClaimConsts.ValueMaxLength).IsRequired(); @@ -157,7 +153,7 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore b.ConfigureByConvention(); - b.HasKey(x => new { x.ClientId, x.Provider }); + b.HasKey(x => new {x.ClientId, x.Provider}); b.Property(x => x.Provider).HasMaxLength(ClientIdPRestrictionConsts.ProviderMaxLength).IsRequired(); }); @@ -168,7 +164,7 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore b.ConfigureByConvention(); - b.HasKey(x => new { x.ClientId, x.Origin }); + b.HasKey(x => new {x.ClientId, x.Origin}); b.Property(x => x.Origin).HasMaxLength(ClientCorsOriginConsts.OriginMaxLength).IsRequired(); }); @@ -179,7 +175,7 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore b.ConfigureByConvention(); - b.HasKey(x => new { x.ClientId, x.Key }); + b.HasKey(x => new {x.ClientId, x.Key}); b.Property(x => x.Key).HasMaxLength(ClientPropertyConsts.KeyMaxLength).IsRequired(); b.Property(x => x.Value).HasMaxLength(ClientPropertyConsts.ValueMaxLength).IsRequired(); @@ -197,18 +193,16 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore b.Property(x => x.ClientId).HasMaxLength(PersistedGrantConsts.ClientIdMaxLength).IsRequired(); b.Property(x => x.CreationTime).IsRequired(); - if (options.DatabaseProvider == EfCoreDatabaseProvider.MySql) - { - b.Property(x => x.Data).HasMaxLength(10000).IsRequired(); - } - else + if (IsDatabaseProvider(builder, options, EfCoreDatabaseProvider.MySql)) { - b.Property(x => x.Data).HasMaxLength(PersistedGrantConsts.DataMaxLength).IsRequired(); + PersistedGrantConsts.DataMaxLengthValue = 10000; //TODO: MySQL accepts 20.000. We can consider to change in v3.0. } + + b.Property(x => x.Data).HasMaxLength(PersistedGrantConsts.DataMaxLengthValue).IsRequired(); b.HasKey(x => x.Key); //TODO: What about Id!!! - b.HasIndex(x => new { x.SubjectId, x.ClientId, x.Type }); + b.HasIndex(x => new {x.SubjectId, x.ClientId, x.Type}); b.HasIndex(x => x.Expiration); }); @@ -234,7 +228,7 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore b.ConfigureByConvention(); - b.HasKey(x => new { x.IdentityResourceId, x.Type }); + b.HasKey(x => new {x.IdentityResourceId, x.Type}); b.Property(x => x.Type).HasMaxLength(UserClaimConsts.TypeMaxLength).IsRequired(); }); @@ -263,19 +257,17 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore b.ConfigureByConvention(); - b.HasKey(x => new { x.ApiResourceId, x.Type, x.Value }); + b.HasKey(x => new {x.ApiResourceId, x.Type, x.Value}); b.Property(x => x.Type).HasMaxLength(SecretConsts.TypeMaxLength).IsRequired(); b.Property(x => x.Description).HasMaxLength(SecretConsts.DescriptionMaxLength); - if (options.DatabaseProvider == EfCoreDatabaseProvider.MySql) + if (IsDatabaseProvider(builder, options, EfCoreDatabaseProvider.MySql, EfCoreDatabaseProvider.Oracle)) { - b.Property(x => x.Value).HasMaxLength(300).IsRequired(); - } - else - { - b.Property(x => x.Value).HasMaxLength(SecretConsts.ValueMaxLength).IsRequired(); - } + SecretConsts.ValueMaxLengthValue = 300; + } + + b.Property(x => x.Value).HasMaxLength(SecretConsts.ValueMaxLengthValue).IsRequired(); }); builder.Entity(b => @@ -284,7 +276,7 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore b.ConfigureByConvention(); - b.HasKey(x => new { x.ApiResourceId, x.Type }); + b.HasKey(x => new {x.ApiResourceId, x.Type}); b.Property(x => x.Type).HasMaxLength(UserClaimConsts.TypeMaxLength).IsRequired(); }); @@ -295,13 +287,13 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore b.ConfigureByConvention(); - b.HasKey(x => new { x.ApiResourceId, x.Name }); + b.HasKey(x => new {x.ApiResourceId, x.Name}); b.Property(x => x.Name).HasMaxLength(ApiScopeConsts.NameMaxLength).IsRequired(); b.Property(x => x.DisplayName).HasMaxLength(ApiScopeConsts.DisplayNameMaxLength); b.Property(x => x.Description).HasMaxLength(ApiScopeConsts.DescriptionMaxLength); - b.HasMany(x => x.UserClaims).WithOne().HasForeignKey(x => new { x.ApiResourceId, x.Name }).IsRequired(); + b.HasMany(x => x.UserClaims).WithOne().HasForeignKey(x => new {x.ApiResourceId, x.Name}).IsRequired(); }); builder.Entity(b => @@ -310,7 +302,7 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore b.ConfigureByConvention(); - b.HasKey(x => new { x.ApiResourceId, x.Name, x.Type }); + b.HasKey(x => new {x.ApiResourceId, x.Name, x.Type}); b.Property(x => x.Type).HasMaxLength(UserClaimConsts.TypeMaxLength).IsRequired(); b.Property(x => x.Name).HasMaxLength(ApiScopeConsts.NameMaxLength).IsRequired(); @@ -329,10 +321,27 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore b.Property(x => x.Expiration).IsRequired(); b.Property(x => x.Data).HasMaxLength(50000).IsRequired(); - b.HasIndex(x => new { x.UserCode }).IsUnique(); + b.HasIndex(x => new {x.UserCode}).IsUnique(); b.HasIndex(x => x.DeviceCode).IsUnique(); b.HasIndex(x => x.Expiration); }); } + + private static bool IsDatabaseProvider( + ModelBuilder modelBuilder, + IdentityServerModelBuilderConfigurationOptions options, + params EfCoreDatabaseProvider[] providers) + { + foreach (var provider in providers) + { + if (options.DatabaseProvider == EfCoreDatabaseProvider.MySql || + modelBuilder.GetDatabaseProvider() == provider) + { + return true; + } + } + + return false; + } } -} +} \ No newline at end of file diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerModelBuilderConfigurationOptions.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerModelBuilderConfigurationOptions.cs index 53b2bf0d8f..41a645bcd6 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerModelBuilderConfigurationOptions.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerModelBuilderConfigurationOptions.cs @@ -1,4 +1,5 @@ -using JetBrains.Annotations; +using System; +using JetBrains.Annotations; using Volo.Abp.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore.Modeling; @@ -6,6 +7,7 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore { public class IdentityServerModelBuilderConfigurationOptions : AbpModelBuilderConfigurationOptions { + [Obsolete("No need to manually set database provider after v2.9+. If it doesn't set automatically, use modelBuilder.UseXXX() in the OnModelCreating method of your DbContext (XXX is your database provider name).")] public EfCoreDatabaseProvider? DatabaseProvider { get; set; } public IdentityServerModelBuilderConfigurationOptions( diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/IdentityResources/IdentityResourceRepository.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/IdentityResources/IdentityResourceRepository.cs index 49ca386502..faed5fb67a 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/IdentityResources/IdentityResourceRepository.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/IdentityResources/IdentityResourceRepository.cs @@ -13,7 +13,7 @@ namespace Volo.Abp.IdentityServer.IdentityResources { public class IdentityResourceRepository : EfCoreRepository, IIdentityResourceRepository { - public IdentityResourceRepository(IDbContextProvider dbContextProvider) + public IdentityResourceRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider) { @@ -36,19 +36,22 @@ namespace Volo.Abp.IdentityServer.IdentityResources return GetQueryable().IncludeDetails(); } - public virtual async Task> GetListAsync(string sorting, int skipCount, int maxResultCount, - bool includeDetails = false, CancellationToken cancellationToken = default) + public virtual async Task> GetListAsync(string sorting, int skipCount, int maxResultCount, + string filter, bool includeDetails = false, CancellationToken cancellationToken = default) { return await DbSet .IncludeDetails(includeDetails) + .WhereIf(!filter.IsNullOrWhiteSpace(), x => x.Name.Contains(filter) || + x.Description.Contains(filter) || + x.DisplayName.Contains(filter)) .OrderBy(sorting ?? "name desc") .PageBy(skipCount, maxResultCount) .ToListAsync(GetCancellationToken(cancellationToken)); } public virtual async Task FindByNameAsync( - string name, - bool includeDetails = true, + string name, + bool includeDetails = true, CancellationToken cancellationToken = default) { return await DbSet diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoApiResourceRepository.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoApiResourceRepository.cs index 760e150853..d2c89f193c 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoApiResourceRepository.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoApiResourceRepository.cs @@ -33,10 +33,14 @@ namespace Volo.Abp.IdentityServer.MongoDB .ToListAsync(GetCancellationToken(cancellationToken)); } - public virtual async Task> GetListAsync(string sorting, int skipCount, int maxResultCount, bool includeDetails = false, + public virtual async Task> GetListAsync(string sorting, int skipCount, int maxResultCount, string filter, bool includeDetails = false, CancellationToken cancellationToken = default) { return await GetMongoQueryable() + .WhereIf(!filter.IsNullOrWhiteSpace(), + x => x.Name.Contains(filter) || + x.Description.Contains(filter) || + x.DisplayName.Contains(filter)) .OrderBy(sorting ?? nameof(ApiResource.Name)) .As>() .PageBy>(skipCount, maxResultCount) diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoClientRepository.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoClientRepository.cs index ae71273f56..5855c42513 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoClientRepository.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoClientRepository.cs @@ -22,7 +22,7 @@ namespace Volo.Abp.IdentityServer.MongoDB } public virtual async Task FindByCliendIdAsync( - string clientId, + string clientId, bool includeDetails = true, CancellationToken cancellationToken = default) { @@ -30,13 +30,15 @@ namespace Volo.Abp.IdentityServer.MongoDB } public virtual async Task> GetListAsync( - string sorting, - int skipCount, - int maxResultCount, + string sorting, + int skipCount, + int maxResultCount, + string filter = null, bool includeDetails = false, CancellationToken cancellationToken = default) { return await GetMongoQueryable() + .WhereIf(!filter.IsNullOrWhiteSpace(), x=>x.ClientId.Contains(filter)) .OrderBy(sorting ?? nameof(Client.ClientName)) .As>() .PageBy>(skipCount, maxResultCount) diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoIdentityResourceRepository.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoIdentityResourceRepository.cs index 519db606ee..e166ee9ec5 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoIdentityResourceRepository.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoIdentityResourceRepository.cs @@ -18,9 +18,12 @@ namespace Volo.Abp.IdentityServer.MongoDB { } - public virtual async Task> GetListAsync(string sorting, int skipCount, int maxResultCount, bool includeDetails = false, CancellationToken cancellationToken = default) + public virtual async Task> GetListAsync(string sorting, int skipCount, int maxResultCount, string filter, bool includeDetails = false, CancellationToken cancellationToken = default) { return await GetMongoQueryable() + .WhereIf(!filter.IsNullOrWhiteSpace(), x => x.Name.Contains(filter) || + x.Description.Contains(filter) || + x.DisplayName.Contains(filter)) .OrderBy(sorting ?? nameof(IdentityResource.Name)) .As>() .PageBy>(skipCount, maxResultCount) diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests.csproj b/modules/identityserver/test/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests.csproj index f9cdb67d4c..74e1c5867f 100644 --- a/modules/identityserver/test/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests.csproj +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests.csproj @@ -15,11 +15,11 @@ + - diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests/Volo/Abp/IdentityServer/AbpIdentityServerTestEntityFrameworkCoreModule.cs b/modules/identityserver/test/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests/Volo/Abp/IdentityServer/AbpIdentityServerTestEntityFrameworkCoreModule.cs index 450bd922f8..5abc8ad2a2 100644 --- a/modules/identityserver/test/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests/Volo/Abp/IdentityServer/AbpIdentityServerTestEntityFrameworkCoreModule.cs +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests/Volo/Abp/IdentityServer/AbpIdentityServerTestEntityFrameworkCoreModule.cs @@ -4,6 +4,7 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore.Sqlite; using Volo.Abp.Identity.EntityFrameworkCore; using Volo.Abp.IdentityServer.EntityFrameworkCore; using Volo.Abp.Modularity; @@ -14,7 +15,8 @@ namespace Volo.Abp.IdentityServer [DependsOn( typeof(AbpIdentityEntityFrameworkCoreModule), typeof(AbpIdentityServerEntityFrameworkCoreModule), - typeof(AbpIdentityServerTestBaseModule) + typeof(AbpIdentityServerTestBaseModule), + typeof(AbpEntityFrameworkCoreSqliteModule) )] public class AbpIdentityServerTestEntityFrameworkCoreModule : AbpModule { diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/MongoDbFixture.cs b/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/MongoDbFixture.cs index 22bbf08ef4..2c2f120acd 100644 --- a/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/MongoDbFixture.cs +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/MongoDbFixture.cs @@ -5,12 +5,18 @@ namespace Volo.Abp.IdentityServer { public class MongoDbFixture : IDisposable { - private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); - public static readonly string ConnectionString = MongoDbRunner.ConnectionString; + private static readonly MongoDbRunner MongoDbRunner; + public static readonly string ConnectionString; + + static MongoDbFixture() + { + MongoDbRunner = MongoDbRunner.Start(); + ConnectionString = MongoDbRunner.ConnectionString; + } public void Dispose() { MongoDbRunner?.Dispose(); } } -} \ No newline at end of file +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/cs.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/cs.json index 4c500d06eb..f7ebdc1386 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/cs.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain.Shared/Volo/Abp/PermissionManagement/Localization/Domain/cs.json @@ -7,4 +7,4 @@ "SelectAllInAllTabs": "Dát veškerá oprávnění", "SelectAllInThisTab": "Vybrat vše" } -} \ No newline at end of file +} diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Volo.Abp.PermissionManagement.Web.csproj b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Volo.Abp.PermissionManagement.Web.csproj index 2c1e0b290d..3209ee2e20 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Volo.Abp.PermissionManagement.Web.csproj +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Volo.Abp.PermissionManagement.Web.csproj @@ -13,10 +13,8 @@ - - diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo.Abp.PermissionManagement.Domain.Tests.csproj b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo.Abp.PermissionManagement.Domain.Tests.csproj index 4177b1b4dd..db520434e9 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo.Abp.PermissionManagement.Domain.Tests.csproj +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo.Abp.PermissionManagement.Domain.Tests.csproj @@ -17,7 +17,7 @@ - + diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests.csproj b/modules/permission-management/test/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests.csproj index 3736c5651e..a668fb9d6f 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests.csproj +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests/Volo.Abp.PermissionManagement.EntityFrameworkCore.Tests.csproj @@ -17,7 +17,7 @@ - + diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo/Abp/PermissionManagement/MongoDb/MongoDbFixture.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo/Abp/PermissionManagement/MongoDb/MongoDbFixture.cs index 3a09e6e1a4..97799fc03b 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo/Abp/PermissionManagement/MongoDb/MongoDbFixture.cs +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo/Abp/PermissionManagement/MongoDb/MongoDbFixture.cs @@ -5,12 +5,18 @@ namespace Volo.Abp.PermissionManagement.MongoDB { public class MongoDbFixture : IDisposable { - private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); - public static readonly string ConnectionString = MongoDbRunner.ConnectionString; + private static readonly MongoDbRunner MongoDbRunner; + public static readonly string ConnectionString; + + static MongoDbFixture() + { + MongoDbRunner = MongoDbRunner.Start(); + ConnectionString = MongoDbRunner.ConnectionString; + } public void Dispose() { MongoDbRunner?.Dispose(); } } -} \ No newline at end of file +} diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/cs.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/cs.json index 0cdb16d2d4..a0dc156e88 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/cs.json +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/Localization/Resources/AbpSettingManagement/cs.json @@ -4,4 +4,4 @@ "Settings": "Nastavení", "SuccessfullySaved": "Úspěšně uloženo" } -} \ No newline at end of file +} diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/SettingConsts.cs b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/SettingConsts.cs index 0b92e24ece..44a21f3ccb 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/SettingConsts.cs +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain.Shared/Volo/Abp/SettingManagement/SettingConsts.cs @@ -4,6 +4,7 @@ { public const int MaxNameLength = 128; public const int MaxValueLength = 2048; + public static int MaxValueLengthValue { get; set; } = MaxValueLength; public const int MaxProviderNameLength = 64; public const int MaxProviderKeyLength = 64; } diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.EntityFrameworkCore/Volo/Abp/SettingManagement/EntityFrameworkCore/SettingManagementDbContextModelBuilderExtensions.cs b/modules/setting-management/src/Volo.Abp.SettingManagement.EntityFrameworkCore/Volo/Abp/SettingManagement/EntityFrameworkCore/SettingManagementDbContextModelBuilderExtensions.cs index 715d7abfd0..6f0bd5c3f8 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.EntityFrameworkCore/Volo/Abp/SettingManagement/EntityFrameworkCore/SettingManagementDbContextModelBuilderExtensions.cs +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.EntityFrameworkCore/Volo/Abp/SettingManagement/EntityFrameworkCore/SettingManagementDbContextModelBuilderExtensions.cs @@ -41,7 +41,10 @@ namespace Volo.Abp.SettingManagement.EntityFrameworkCore b.ConfigureByConvention(); b.Property(x => x.Name).HasMaxLength(SettingConsts.MaxNameLength).IsRequired(); - b.Property(x => x.Value).HasMaxLength(SettingConsts.MaxValueLength).IsRequired(); + + if (builder.IsUsingOracle()) { SettingConsts.MaxValueLengthValue = 2000; } + b.Property(x => x.Value).HasMaxLength(SettingConsts.MaxValueLengthValue).IsRequired(); + b.Property(x => x.ProviderName).HasMaxLength(SettingConsts.MaxProviderNameLength); b.Property(x => x.ProviderKey).HasMaxLength(SettingConsts.MaxProviderKeyLength); diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Navigation/SettingManagementMainMenuContributor.cs b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Navigation/SettingManagementMainMenuContributor.cs index 59c0518d3c..2c9f555f09 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Navigation/SettingManagementMainMenuContributor.cs +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Navigation/SettingManagementMainMenuContributor.cs @@ -29,7 +29,7 @@ namespace Volo.Abp.SettingManagement.Web.Navigation return; } - var l = context.ServiceProvider.GetRequiredService>(); + var l = context.GetLocalizer(); context.Menu .GetAdministration() diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Volo.Abp.SettingManagement.Web.csproj b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Volo.Abp.SettingManagement.Web.csproj index cbf526e751..b37ef515df 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Volo.Abp.SettingManagement.Web.csproj +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Volo.Abp.SettingManagement.Web.csproj @@ -20,10 +20,8 @@ - - diff --git a/modules/setting-management/test/Volo.Abp.SettingManagement.EntityFrameworkCore.Tests/Volo.Abp.SettingManagement.EntityFrameworkCore.Tests.csproj b/modules/setting-management/test/Volo.Abp.SettingManagement.EntityFrameworkCore.Tests/Volo.Abp.SettingManagement.EntityFrameworkCore.Tests.csproj index 5cfcd5f3ff..83ab36608b 100644 --- a/modules/setting-management/test/Volo.Abp.SettingManagement.EntityFrameworkCore.Tests/Volo.Abp.SettingManagement.EntityFrameworkCore.Tests.csproj +++ b/modules/setting-management/test/Volo.Abp.SettingManagement.EntityFrameworkCore.Tests/Volo.Abp.SettingManagement.EntityFrameworkCore.Tests.csproj @@ -14,12 +14,11 @@ + - - diff --git a/modules/setting-management/test/Volo.Abp.SettingManagement.EntityFrameworkCore.Tests/Volo/Abp/SettingManagement/EntityFrameworkCore/AbpSettingManagementEntityFrameworkCoreTestModule.cs b/modules/setting-management/test/Volo.Abp.SettingManagement.EntityFrameworkCore.Tests/Volo/Abp/SettingManagement/EntityFrameworkCore/AbpSettingManagementEntityFrameworkCoreTestModule.cs index 1a234961bc..a885e806b7 100644 --- a/modules/setting-management/test/Volo.Abp.SettingManagement.EntityFrameworkCore.Tests/Volo/Abp/SettingManagement/EntityFrameworkCore/AbpSettingManagementEntityFrameworkCoreTestModule.cs +++ b/modules/setting-management/test/Volo.Abp.SettingManagement.EntityFrameworkCore.Tests/Volo/Abp/SettingManagement/EntityFrameworkCore/AbpSettingManagementEntityFrameworkCoreTestModule.cs @@ -4,13 +4,15 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore.Sqlite; using Volo.Abp.Modularity; namespace Volo.Abp.SettingManagement.EntityFrameworkCore { [DependsOn( typeof(AbpSettingManagementTestBaseModule), - typeof(AbpSettingManagementEntityFrameworkCoreModule) + typeof(AbpSettingManagementEntityFrameworkCoreModule), + typeof(AbpEntityFrameworkCoreSqliteModule) )] public class AbpSettingManagementEntityFrameworkCoreTestModule : AbpModule { diff --git a/modules/setting-management/test/Volo.Abp.SettingManagement.MongoDB.Tests/Volo/Abp/SettingManagement/MongoDB/MongoDbFixture.cs b/modules/setting-management/test/Volo.Abp.SettingManagement.MongoDB.Tests/Volo/Abp/SettingManagement/MongoDB/MongoDbFixture.cs index 31ccdcc4f7..4f9142a829 100644 --- a/modules/setting-management/test/Volo.Abp.SettingManagement.MongoDB.Tests/Volo/Abp/SettingManagement/MongoDB/MongoDbFixture.cs +++ b/modules/setting-management/test/Volo.Abp.SettingManagement.MongoDB.Tests/Volo/Abp/SettingManagement/MongoDB/MongoDbFixture.cs @@ -5,12 +5,18 @@ namespace Volo.Abp.SettingManagement.MongoDB { public class MongoDbFixture : IDisposable { - private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); - public static readonly string ConnectionString = MongoDbRunner.ConnectionString; + private static readonly MongoDbRunner MongoDbRunner; + public static readonly string ConnectionString; + + static MongoDbFixture() + { + MongoDbRunner = MongoDbRunner.Start(); + ConnectionString = MongoDbRunner.ConnectionString; + } public void Dispose() { MongoDbRunner?.Dispose(); } } -} \ No newline at end of file +} diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/cs.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/cs.json index 16f967139f..86a0461dd5 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/cs.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/cs.json @@ -15,6 +15,8 @@ "Permission:Edit": "Upravit", "Permission:Delete": "Smazat", "Permission:ManageConnectionStrings": "Spravovat connection stringy", - "Permission:ManageFeatures": "Spravovat funkce" + "Permission:ManageFeatures": "Spravovat funkce", + "DisplayName:AdminEmailAddress": "Email adresa správce", + "DisplayName:AdminPassword": "Heslo správce" } -} \ No newline at end of file +} diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Navigation/AbpTenantManagementWebMainMenuContributor.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Navigation/AbpTenantManagementWebMainMenuContributor.cs index e021538a5c..fe5b403ed7 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Navigation/AbpTenantManagementWebMainMenuContributor.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Navigation/AbpTenantManagementWebMainMenuContributor.cs @@ -18,13 +18,12 @@ namespace Volo.Abp.TenantManagement.Web.Navigation var administrationMenu = context.Menu.GetAdministration(); - var authorizationService = context.ServiceProvider.GetRequiredService(); - var l = context.ServiceProvider.GetRequiredService>(); + var l = context.GetLocalizer(); var tenantManagementMenuItem = new ApplicationMenuItem(TenantManagementMenuNames.GroupName, l["Menu:TenantManagement"], icon: "fa fa-users"); administrationMenu.AddItem(tenantManagementMenuItem); - if (await authorizationService.IsGrantedAsync(TenantManagementPermissions.Tenants.Default)) + if (await context.IsGrantedAsync(TenantManagementPermissions.Tenants.Default)) { tenantManagementMenuItem.AddItem(new ApplicationMenuItem(TenantManagementMenuNames.Tenants, l["Tenants"], url: "/TenantManagement/Tenants")); } diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/_ViewImports.cshtml b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/_ViewImports.cshtml index d1ac64721f..c1da1f5f10 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/_ViewImports.cshtml +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/_ViewImports.cshtml @@ -1,2 +1,4 @@ @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers -@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap \ No newline at end of file +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling \ No newline at end of file diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Volo.Abp.TenantManagement.Web.csproj b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Volo.Abp.TenantManagement.Web.csproj index 56e6d3cae7..c558ea9637 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Volo.Abp.TenantManagement.Web.csproj +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Volo.Abp.TenantManagement.Web.csproj @@ -13,10 +13,8 @@ - - diff --git a/modules/tenant-management/test/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests.csproj b/modules/tenant-management/test/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests.csproj index 7786d493df..5030a26cfb 100644 --- a/modules/tenant-management/test/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests.csproj +++ b/modules/tenant-management/test/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests.csproj @@ -14,12 +14,12 @@ + - - + diff --git a/modules/tenant-management/test/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests/Volo/Abp/TenantManagement/EntityFrameworkCore/AbpTenantManagementEntityFrameworkCoreTestModule.cs b/modules/tenant-management/test/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests/Volo/Abp/TenantManagement/EntityFrameworkCore/AbpTenantManagementEntityFrameworkCoreTestModule.cs index 2328c4e302..ac50564a87 100644 --- a/modules/tenant-management/test/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests/Volo/Abp/TenantManagement/EntityFrameworkCore/AbpTenantManagementEntityFrameworkCoreTestModule.cs +++ b/modules/tenant-management/test/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests/Volo/Abp/TenantManagement/EntityFrameworkCore/AbpTenantManagementEntityFrameworkCoreTestModule.cs @@ -4,6 +4,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore.Sqlite; using Volo.Abp.Modularity; using Volo.Abp.Uow; @@ -11,7 +12,8 @@ namespace Volo.Abp.TenantManagement.EntityFrameworkCore { [DependsOn( typeof(AbpTenantManagementEntityFrameworkCoreModule), - typeof(AbpTenantManagementTestBaseModule) + typeof(AbpTenantManagementTestBaseModule), + typeof(AbpEntityFrameworkCoreSqliteModule) )] public class AbpTenantManagementEntityFrameworkCoreTestModule : AbpModule { diff --git a/modules/tenant-management/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo/Abp/TenantManagement/MongoDb/MongoDbFixture.cs b/modules/tenant-management/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo/Abp/TenantManagement/MongoDb/MongoDbFixture.cs index be115d9dff..1b3777f6a8 100644 --- a/modules/tenant-management/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo/Abp/TenantManagement/MongoDb/MongoDbFixture.cs +++ b/modules/tenant-management/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo/Abp/TenantManagement/MongoDb/MongoDbFixture.cs @@ -5,12 +5,18 @@ namespace Volo.Abp.TenantManagement.MongoDB { public class MongoDbFixture : IDisposable { - private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); - public static readonly string ConnectionString = MongoDbRunner.ConnectionString; + private static readonly MongoDbRunner MongoDbRunner; + public static readonly string ConnectionString; + + static MongoDbFixture() + { + MongoDbRunner = MongoDbRunner.Start(); + ConnectionString = MongoDbRunner.ConnectionString; + } public void Dispose() { MongoDbRunner?.Dispose(); } } -} \ No newline at end of file +} diff --git a/modules/users/test/Volo.Abp.Users.EntityFrameworkCore.Tests/Volo.Abp.Users.EntityFrameworkCore.Tests.csproj b/modules/users/test/Volo.Abp.Users.EntityFrameworkCore.Tests/Volo.Abp.Users.EntityFrameworkCore.Tests.csproj index a63193fbff..912daad74d 100644 --- a/modules/users/test/Volo.Abp.Users.EntityFrameworkCore.Tests/Volo.Abp.Users.EntityFrameworkCore.Tests.csproj +++ b/modules/users/test/Volo.Abp.Users.EntityFrameworkCore.Tests/Volo.Abp.Users.EntityFrameworkCore.Tests.csproj @@ -19,7 +19,7 @@ - + diff --git a/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/AbpUserRepository_Tests.cs b/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/AbpUserRepository_Tests.cs index 7f0f5620db..a54790410e 100644 --- a/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/AbpUserRepository_Tests.cs +++ b/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/AbpUserRepository_Tests.cs @@ -1,5 +1,6 @@ namespace Volo.Abp.Users.MongoDB { + [Collection(MongoTestCollection.Name)] public class AbpUserRepository_Tests : AbpUserRepository_Tests { diff --git a/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/AbpUsersMongoDbTestModule.cs b/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/AbpUsersMongoDbTestModule.cs index a2235ee03e..560d6287d7 100644 --- a/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/AbpUsersMongoDbTestModule.cs +++ b/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/AbpUsersMongoDbTestModule.cs @@ -16,9 +16,9 @@ namespace Volo.Abp.Users.MongoDB public override void ConfigureServices(IServiceCollection services) { - var connectionString = MongoDbRunner.ConnectionString.EnsureEndsWith('/') + + var connectionString = MongoDbFixture.ConnectionString.EnsureEndsWith('/') + "Db_" + - Guid.NewGuid().ToString("N"); + Guid.NewGuid().ToString("N"); Configure(options => { diff --git a/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/ExternalUserLookupService_Tests.cs b/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/ExternalUserLookupService_Tests.cs index 451ef8baae..764cadedf4 100644 --- a/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/ExternalUserLookupService_Tests.cs +++ b/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/ExternalUserLookupService_Tests.cs @@ -1,7 +1,8 @@ namespace Volo.Abp.Users.MongoDB { + [Collection(MongoTestCollection.Name)] public class ExternalUserLookupService_Tests : ExternalUserLookupService_Tests { - + } -} \ No newline at end of file +} diff --git a/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/LocalUserLookupService_Tests.cs b/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/LocalUserLookupService_Tests.cs index 26f9aa1ac5..b52af21d3e 100644 --- a/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/LocalUserLookupService_Tests.cs +++ b/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/LocalUserLookupService_Tests.cs @@ -1,7 +1,8 @@ namespace Volo.Abp.Users.MongoDB { + [Collection(MongoTestCollection.Name)] public class LocalUserLookupService_Tests : LocalUserLookupService_Tests { - + } -} \ No newline at end of file +} diff --git a/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/MongoDbFixture.cs b/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/MongoDbFixture.cs new file mode 100644 index 0000000000..4f8c6eea2f --- /dev/null +++ b/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/MongoDbFixture.cs @@ -0,0 +1,22 @@ +using System; +using Mongo2Go; + +namespace Volo.Abp.Users.MongoDB +{ + public class MongoDbFixture : IDisposable + { + private static readonly MongoDbRunner MongoDbRunner; + public static readonly string ConnectionString; + + static MongoDbFixture() + { + MongoDbRunner = MongoDbRunner.Start(); + ConnectionString = MongoDbRunner.ConnectionString; + } + + public void Dispose() + { + MongoDbRunner?.Dispose(); + } + } +} diff --git a/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/MongoTestCollection.cs b/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/MongoTestCollection.cs new file mode 100644 index 0000000000..d8b6a718d8 --- /dev/null +++ b/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/MongoTestCollection.cs @@ -0,0 +1,10 @@ +using Xunit; + +namespace Volo.Abp.Users.MongoDB +{ + [CollectionDefinition(Name)] + public class MongoTestCollection : ICollectionFixture + { + public const string Name = "MongoDB Collection"; + } +} diff --git a/modules/virtual-file-explorer/Volo.Abp.VirtualFileExplorer.sln b/modules/virtual-file-explorer/Volo.Abp.VirtualFileExplorer.sln new file mode 100644 index 0000000000..240117b5b7 --- /dev/null +++ b/modules/virtual-file-explorer/Volo.Abp.VirtualFileExplorer.sln @@ -0,0 +1,39 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29001.49 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{649A3FFA-182F-4E56-9717-E6A9A2BEC545}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.VirtualFileExplorer.Web", "src\Volo.Abp.VirtualFileExplorer.Web\Volo.Abp.VirtualFileExplorer.Web.csproj", "{3B7B6317-1B85-4164-8E11-75574F80AE17}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "app", "app", "{0489DA85-A27F-4D98-9DBD-950F5F2ECD11}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.VirtualFileExplorer.DemoApp", "app\Volo.Abp.VirtualFileExplorer.DemoApp\Volo.Abp.VirtualFileExplorer.DemoApp.csproj", "{6DAA2A8D-5422-4C3B-BA70-B54D949B1678}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3B7B6317-1B85-4164-8E11-75574F80AE17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3B7B6317-1B85-4164-8E11-75574F80AE17}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3B7B6317-1B85-4164-8E11-75574F80AE17}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3B7B6317-1B85-4164-8E11-75574F80AE17}.Release|Any CPU.Build.0 = Release|Any CPU + {6DAA2A8D-5422-4C3B-BA70-B54D949B1678}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6DAA2A8D-5422-4C3B-BA70-B54D949B1678}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6DAA2A8D-5422-4C3B-BA70-B54D949B1678}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6DAA2A8D-5422-4C3B-BA70-B54D949B1678}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {3B7B6317-1B85-4164-8E11-75574F80AE17} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545} + {6DAA2A8D-5422-4C3B-BA70-B54D949B1678} = {0489DA85-A27F-4D98-9DBD-950F5F2ECD11} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {4324B3B4-B60B-4E3C-91D8-59576B4E26DD} + EndGlobalSection +EndGlobal diff --git a/modules/virtual-file-explorer/Volo.Abp.VirtualFileExplorer.sln.DotSettings b/modules/virtual-file-explorer/Volo.Abp.VirtualFileExplorer.sln.DotSettings new file mode 100644 index 0000000000..cb0b2c919f --- /dev/null +++ b/modules/virtual-file-explorer/Volo.Abp.VirtualFileExplorer.sln.DotSettings @@ -0,0 +1,23 @@ + + True + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + WARNING + Required + Required + Required + Required + False + True + False + False + True + False + False + SQL + \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/AbpVirtualFileExplorerDemoAppModule.cs b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/AbpVirtualFileExplorerDemoAppModule.cs new file mode 100644 index 0000000000..b1b0d2945c --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/AbpVirtualFileExplorerDemoAppModule.cs @@ -0,0 +1,38 @@ +using Microsoft.AspNetCore.Builder; +using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic; +using Volo.Abp.Autofac; +using Volo.Abp.Localization; +using Volo.Abp.Modularity; +using Volo.Abp.VirtualFileExplorer.Web; + +namespace Volo.Abp.VirtualFileExplorer.DemoApp +{ + [DependsOn( + typeof(AbpAutofacModule), + typeof(AbpAspNetCoreMvcUiBasicThemeModule), + typeof(AbpVirtualFileExplorerWebModule) + )] + public class AbpVirtualFileExplorerDemoAppModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.Languages.Add(new LanguageInfo("cs", "cs", "Čeština")); + options.Languages.Add(new LanguageInfo("en", "en", "English")); + options.Languages.Add(new LanguageInfo("tr", "tr", "Türkçe")); + options.Languages.Add(new LanguageInfo("zh-Hans", "zh-Hans", "简体中文")); + }); + } + + public override void OnApplicationInitialization(ApplicationInitializationContext context) + { + var app = context.GetApplicationBuilder(); + + app.UseVirtualFiles(); + app.UseRouting(); + app.UseAbpRequestLocalization(); + app.UseConfiguredEndpoints(); + } + } +} diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/Branding/AbpVirtualFileExplorerDemoAppBrandingProvider.cs b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/Branding/AbpVirtualFileExplorerDemoAppBrandingProvider.cs new file mode 100644 index 0000000000..369e4fde1e --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/Branding/AbpVirtualFileExplorerDemoAppBrandingProvider.cs @@ -0,0 +1,20 @@ +using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Components; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.VirtualFileExplorer.DemoApp.Branding +{ + [Dependency(ReplaceServices = true)] + public class AbpVirtualFileExplorerDemoAppBrandingProvider : DefaultBrandingProvider + { + public AbpVirtualFileExplorerDemoAppBrandingProvider() + { + AppName = "Virtual file explorer demo app"; + + + } + + public override string AppName { get; } + + public override string LogoUrl { get; } + } +} diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/FodyWeavers.xml b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/FodyWeavers.xml new file mode 100644 index 0000000000..fe11c68dc9 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/FodyWeavers.xsd b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/FodyWeavers.xsd new file mode 100644 index 0000000000..3f3946e282 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/Pages/Index.cshtml b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/Pages/Index.cshtml new file mode 100644 index 0000000000..fb6ccb3305 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/Pages/Index.cshtml @@ -0,0 +1,14 @@ +@page + +@model Volo.Abp.VirtualFileExplorer.DemoApp.Pages.IndexModel + +
+
+
+

Virtual file explprer demo application

+
+
+
+ abp.io + +
diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/Pages/Index.cshtml.cs b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/Pages/Index.cshtml.cs new file mode 100644 index 0000000000..27f305a740 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/Pages/Index.cshtml.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Logging; + +namespace Volo.Abp.VirtualFileExplorer.DemoApp.Pages +{ + public class IndexModel : PageModel + { + private readonly ILogger _logger; + + public IndexModel(ILogger logger) + { + _logger = logger; + } + + public void OnGet() + { + } + } +} \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/Pages/_ViewImports.cshtml b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/Pages/_ViewImports.cshtml new file mode 100644 index 0000000000..231948b339 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/Pages/_ViewImports.cshtml @@ -0,0 +1,4 @@ +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/Program.cs b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/Program.cs new file mode 100644 index 0000000000..60996fc2f0 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/Program.cs @@ -0,0 +1,43 @@ +using System; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using Serilog; +using Serilog.Events; + +namespace Volo.Abp.VirtualFileExplorer.DemoApp +{ + public class Program + { + public static int Main(string[] args) + { + Log.Logger = new LoggerConfiguration() + .MinimumLevel.Debug() + .MinimumLevel.Override("Microsoft", LogEventLevel.Information) + .Enrich.FromLogContext() + .WriteTo.File("Logs/logs.txt") + .CreateLogger(); + + try + { + Log.Information("Starting web host."); + CreateHostBuilder(args).Build().Run(); + return 0; + } + catch (Exception ex) + { + Log.Fatal(ex, "Host terminated unexpectedly!"); + return 1; + } + finally + { + Log.CloseAndFlush(); + } + } + + internal static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }) + .UseAutofac() + .UseSerilog(); + } +} diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/Properties/launchSettings.json b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/Properties/launchSettings.json new file mode 100644 index 0000000000..11e34a6fc8 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:51233", + "sslPort": 44377 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Volo.Abp.VirtualFileExplorer.DemoApp": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/Startup.cs b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/Startup.cs new file mode 100644 index 0000000000..a60467c712 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/Startup.cs @@ -0,0 +1,20 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Volo.Abp.VirtualFileExplorer.DemoApp +{ + public class Startup + { + public void ConfigureServices(IServiceCollection services) + { + services.AddApplication(); + } + + public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) + { + app.InitializeApplication(); + } + } +} diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/Volo.Abp.VirtualFileExplorer.DemoApp.csproj b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/Volo.Abp.VirtualFileExplorer.DemoApp.csproj new file mode 100644 index 0000000000..b1a9bbb18c --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/Volo.Abp.VirtualFileExplorer.DemoApp.csproj @@ -0,0 +1,35 @@ + + + + + + netcoreapp3.1 + aspnet-Volo.Abp.VirtualFileExplorer.DemoApp-234AF9E1-C3E0-4F8F-BD7D-840627CC8E46 + + + + + + + + + + + + + + + + + true + PreserveNewest + PreserveNewest + + + + + + + + + diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/abp.resourcemapping.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/abp.resourcemapping.js new file mode 100644 index 0000000000..57d6897c7a --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/abp.resourcemapping.js @@ -0,0 +1,12 @@ +module.exports = { + aliases: { + "@node_modules": "./node_modules", + "@libs": "./wwwroot/libs" + }, + clean: [ + "@libs" + ], + mappings: { + + } +}; diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/gulpfile.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/gulpfile.js new file mode 100644 index 0000000000..f7ebc78f23 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/gulpfile.js @@ -0,0 +1,9 @@ +"use strict"; + +var gulp = require("gulp"), + path = require('path'), + copyResources = require('./node_modules/@abp/aspnetcore.mvc.ui/gulp/copy-resources.js'); + +exports.default = function(){ + return copyResources(path.resolve('./')); +}; \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/package.json b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/package.json new file mode 100644 index 0000000000..2fccd8b3bc --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/package.json @@ -0,0 +1,11 @@ +{ + "name": "volo.virtualfileexplorer.dempapp", + "version": "1.0.0", + "private": true, + "dependencies": { + "@abp/aspnetcore.mvc.ui.theme.basic": "^2.7.0", + "@abp/virtual-file-explorer": "^2.7.0", + "@abp/clipboard": "^2.7.0", + "@abp/prismjs": "^2.7.0" + } +} diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/css/all.css b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/css/all.css new file mode 100644 index 0000000000..8ebd25ff0c --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/css/all.css @@ -0,0 +1,4556 @@ +/*! + * Font Awesome Free 5.13.0 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + */ +.fa, +.fas, +.far, +.fal, +.fad, +.fab { + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + display: inline-block; + font-style: normal; + font-variant: normal; + text-rendering: auto; + line-height: 1; } + +.fa-lg { + font-size: 1.33333em; + line-height: 0.75em; + vertical-align: -.0667em; } + +.fa-xs { + font-size: .75em; } + +.fa-sm { + font-size: .875em; } + +.fa-1x { + font-size: 1em; } + +.fa-2x { + font-size: 2em; } + +.fa-3x { + font-size: 3em; } + +.fa-4x { + font-size: 4em; } + +.fa-5x { + font-size: 5em; } + +.fa-6x { + font-size: 6em; } + +.fa-7x { + font-size: 7em; } + +.fa-8x { + font-size: 8em; } + +.fa-9x { + font-size: 9em; } + +.fa-10x { + font-size: 10em; } + +.fa-fw { + text-align: center; + width: 1.25em; } + +.fa-ul { + list-style-type: none; + margin-left: 2.5em; + padding-left: 0; } + .fa-ul > li { + position: relative; } + +.fa-li { + left: -2em; + position: absolute; + text-align: center; + width: 2em; + line-height: inherit; } + +.fa-border { + border: solid 0.08em #eee; + border-radius: .1em; + padding: .2em .25em .15em; } + +.fa-pull-left { + float: left; } + +.fa-pull-right { + float: right; } + +.fa.fa-pull-left, +.fas.fa-pull-left, +.far.fa-pull-left, +.fal.fa-pull-left, +.fab.fa-pull-left { + margin-right: .3em; } + +.fa.fa-pull-right, +.fas.fa-pull-right, +.far.fa-pull-right, +.fal.fa-pull-right, +.fab.fa-pull-right { + margin-left: .3em; } + +.fa-spin { + -webkit-animation: fa-spin 2s infinite linear; + animation: fa-spin 2s infinite linear; } + +.fa-pulse { + -webkit-animation: fa-spin 1s infinite steps(8); + animation: fa-spin 1s infinite steps(8); } + +@-webkit-keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); } } + +@keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); } } + +.fa-rotate-90 { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)"; + -webkit-transform: rotate(90deg); + transform: rotate(90deg); } + +.fa-rotate-180 { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)"; + -webkit-transform: rotate(180deg); + transform: rotate(180deg); } + +.fa-rotate-270 { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)"; + -webkit-transform: rotate(270deg); + transform: rotate(270deg); } + +.fa-flip-horizontal { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)"; + -webkit-transform: scale(-1, 1); + transform: scale(-1, 1); } + +.fa-flip-vertical { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"; + -webkit-transform: scale(1, -1); + transform: scale(1, -1); } + +.fa-flip-both, .fa-flip-horizontal.fa-flip-vertical { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"; + -webkit-transform: scale(-1, -1); + transform: scale(-1, -1); } + +:root .fa-rotate-90, +:root .fa-rotate-180, +:root .fa-rotate-270, +:root .fa-flip-horizontal, +:root .fa-flip-vertical, +:root .fa-flip-both { + -webkit-filter: none; + filter: none; } + +.fa-stack { + display: inline-block; + height: 2em; + line-height: 2em; + position: relative; + vertical-align: middle; + width: 2.5em; } + +.fa-stack-1x, +.fa-stack-2x { + left: 0; + position: absolute; + text-align: center; + width: 100%; } + +.fa-stack-1x { + line-height: inherit; } + +.fa-stack-2x { + font-size: 2em; } + +.fa-inverse { + color: #fff; } + +/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen +readers do not read off random characters that represent icons */ +.fa-500px:before { + content: "\f26e"; } + +.fa-accessible-icon:before { + content: "\f368"; } + +.fa-accusoft:before { + content: "\f369"; } + +.fa-acquisitions-incorporated:before { + content: "\f6af"; } + +.fa-ad:before { + content: "\f641"; } + +.fa-address-book:before { + content: "\f2b9"; } + +.fa-address-card:before { + content: "\f2bb"; } + +.fa-adjust:before { + content: "\f042"; } + +.fa-adn:before { + content: "\f170"; } + +.fa-adobe:before { + content: "\f778"; } + +.fa-adversal:before { + content: "\f36a"; } + +.fa-affiliatetheme:before { + content: "\f36b"; } + +.fa-air-freshener:before { + content: "\f5d0"; } + +.fa-airbnb:before { + content: "\f834"; } + +.fa-algolia:before { + content: "\f36c"; } + +.fa-align-center:before { + content: "\f037"; } + +.fa-align-justify:before { + content: "\f039"; } + +.fa-align-left:before { + content: "\f036"; } + +.fa-align-right:before { + content: "\f038"; } + +.fa-alipay:before { + content: "\f642"; } + +.fa-allergies:before { + content: "\f461"; } + +.fa-amazon:before { + content: "\f270"; } + +.fa-amazon-pay:before { + content: "\f42c"; } + +.fa-ambulance:before { + content: "\f0f9"; } + +.fa-american-sign-language-interpreting:before { + content: "\f2a3"; } + +.fa-amilia:before { + content: "\f36d"; } + +.fa-anchor:before { + content: "\f13d"; } + +.fa-android:before { + content: "\f17b"; } + +.fa-angellist:before { + content: "\f209"; } + +.fa-angle-double-down:before { + content: "\f103"; } + +.fa-angle-double-left:before { + content: "\f100"; } + +.fa-angle-double-right:before { + content: "\f101"; } + +.fa-angle-double-up:before { + content: "\f102"; } + +.fa-angle-down:before { + content: "\f107"; } + +.fa-angle-left:before { + content: "\f104"; } + +.fa-angle-right:before { + content: "\f105"; } + +.fa-angle-up:before { + content: "\f106"; } + +.fa-angry:before { + content: "\f556"; } + +.fa-angrycreative:before { + content: "\f36e"; } + +.fa-angular:before { + content: "\f420"; } + +.fa-ankh:before { + content: "\f644"; } + +.fa-app-store:before { + content: "\f36f"; } + +.fa-app-store-ios:before { + content: "\f370"; } + +.fa-apper:before { + content: "\f371"; } + +.fa-apple:before { + content: "\f179"; } + +.fa-apple-alt:before { + content: "\f5d1"; } + +.fa-apple-pay:before { + content: "\f415"; } + +.fa-archive:before { + content: "\f187"; } + +.fa-archway:before { + content: "\f557"; } + +.fa-arrow-alt-circle-down:before { + content: "\f358"; } + +.fa-arrow-alt-circle-left:before { + content: "\f359"; } + +.fa-arrow-alt-circle-right:before { + content: "\f35a"; } + +.fa-arrow-alt-circle-up:before { + content: "\f35b"; } + +.fa-arrow-circle-down:before { + content: "\f0ab"; } + +.fa-arrow-circle-left:before { + content: "\f0a8"; } + +.fa-arrow-circle-right:before { + content: "\f0a9"; } + +.fa-arrow-circle-up:before { + content: "\f0aa"; } + +.fa-arrow-down:before { + content: "\f063"; } + +.fa-arrow-left:before { + content: "\f060"; } + +.fa-arrow-right:before { + content: "\f061"; } + +.fa-arrow-up:before { + content: "\f062"; } + +.fa-arrows-alt:before { + content: "\f0b2"; } + +.fa-arrows-alt-h:before { + content: "\f337"; } + +.fa-arrows-alt-v:before { + content: "\f338"; } + +.fa-artstation:before { + content: "\f77a"; } + +.fa-assistive-listening-systems:before { + content: "\f2a2"; } + +.fa-asterisk:before { + content: "\f069"; } + +.fa-asymmetrik:before { + content: "\f372"; } + +.fa-at:before { + content: "\f1fa"; } + +.fa-atlas:before { + content: "\f558"; } + +.fa-atlassian:before { + content: "\f77b"; } + +.fa-atom:before { + content: "\f5d2"; } + +.fa-audible:before { + content: "\f373"; } + +.fa-audio-description:before { + content: "\f29e"; } + +.fa-autoprefixer:before { + content: "\f41c"; } + +.fa-avianex:before { + content: "\f374"; } + +.fa-aviato:before { + content: "\f421"; } + +.fa-award:before { + content: "\f559"; } + +.fa-aws:before { + content: "\f375"; } + +.fa-baby:before { + content: "\f77c"; } + +.fa-baby-carriage:before { + content: "\f77d"; } + +.fa-backspace:before { + content: "\f55a"; } + +.fa-backward:before { + content: "\f04a"; } + +.fa-bacon:before { + content: "\f7e5"; } + +.fa-bahai:before { + content: "\f666"; } + +.fa-balance-scale:before { + content: "\f24e"; } + +.fa-balance-scale-left:before { + content: "\f515"; } + +.fa-balance-scale-right:before { + content: "\f516"; } + +.fa-ban:before { + content: "\f05e"; } + +.fa-band-aid:before { + content: "\f462"; } + +.fa-bandcamp:before { + content: "\f2d5"; } + +.fa-barcode:before { + content: "\f02a"; } + +.fa-bars:before { + content: "\f0c9"; } + +.fa-baseball-ball:before { + content: "\f433"; } + +.fa-basketball-ball:before { + content: "\f434"; } + +.fa-bath:before { + content: "\f2cd"; } + +.fa-battery-empty:before { + content: "\f244"; } + +.fa-battery-full:before { + content: "\f240"; } + +.fa-battery-half:before { + content: "\f242"; } + +.fa-battery-quarter:before { + content: "\f243"; } + +.fa-battery-three-quarters:before { + content: "\f241"; } + +.fa-battle-net:before { + content: "\f835"; } + +.fa-bed:before { + content: "\f236"; } + +.fa-beer:before { + content: "\f0fc"; } + +.fa-behance:before { + content: "\f1b4"; } + +.fa-behance-square:before { + content: "\f1b5"; } + +.fa-bell:before { + content: "\f0f3"; } + +.fa-bell-slash:before { + content: "\f1f6"; } + +.fa-bezier-curve:before { + content: "\f55b"; } + +.fa-bible:before { + content: "\f647"; } + +.fa-bicycle:before { + content: "\f206"; } + +.fa-biking:before { + content: "\f84a"; } + +.fa-bimobject:before { + content: "\f378"; } + +.fa-binoculars:before { + content: "\f1e5"; } + +.fa-biohazard:before { + content: "\f780"; } + +.fa-birthday-cake:before { + content: "\f1fd"; } + +.fa-bitbucket:before { + content: "\f171"; } + +.fa-bitcoin:before { + content: "\f379"; } + +.fa-bity:before { + content: "\f37a"; } + +.fa-black-tie:before { + content: "\f27e"; } + +.fa-blackberry:before { + content: "\f37b"; } + +.fa-blender:before { + content: "\f517"; } + +.fa-blender-phone:before { + content: "\f6b6"; } + +.fa-blind:before { + content: "\f29d"; } + +.fa-blog:before { + content: "\f781"; } + +.fa-blogger:before { + content: "\f37c"; } + +.fa-blogger-b:before { + content: "\f37d"; } + +.fa-bluetooth:before { + content: "\f293"; } + +.fa-bluetooth-b:before { + content: "\f294"; } + +.fa-bold:before { + content: "\f032"; } + +.fa-bolt:before { + content: "\f0e7"; } + +.fa-bomb:before { + content: "\f1e2"; } + +.fa-bone:before { + content: "\f5d7"; } + +.fa-bong:before { + content: "\f55c"; } + +.fa-book:before { + content: "\f02d"; } + +.fa-book-dead:before { + content: "\f6b7"; } + +.fa-book-medical:before { + content: "\f7e6"; } + +.fa-book-open:before { + content: "\f518"; } + +.fa-book-reader:before { + content: "\f5da"; } + +.fa-bookmark:before { + content: "\f02e"; } + +.fa-bootstrap:before { + content: "\f836"; } + +.fa-border-all:before { + content: "\f84c"; } + +.fa-border-none:before { + content: "\f850"; } + +.fa-border-style:before { + content: "\f853"; } + +.fa-bowling-ball:before { + content: "\f436"; } + +.fa-box:before { + content: "\f466"; } + +.fa-box-open:before { + content: "\f49e"; } + +.fa-box-tissue:before { + content: "\f95b"; } + +.fa-boxes:before { + content: "\f468"; } + +.fa-braille:before { + content: "\f2a1"; } + +.fa-brain:before { + content: "\f5dc"; } + +.fa-bread-slice:before { + content: "\f7ec"; } + +.fa-briefcase:before { + content: "\f0b1"; } + +.fa-briefcase-medical:before { + content: "\f469"; } + +.fa-broadcast-tower:before { + content: "\f519"; } + +.fa-broom:before { + content: "\f51a"; } + +.fa-brush:before { + content: "\f55d"; } + +.fa-btc:before { + content: "\f15a"; } + +.fa-buffer:before { + content: "\f837"; } + +.fa-bug:before { + content: "\f188"; } + +.fa-building:before { + content: "\f1ad"; } + +.fa-bullhorn:before { + content: "\f0a1"; } + +.fa-bullseye:before { + content: "\f140"; } + +.fa-burn:before { + content: "\f46a"; } + +.fa-buromobelexperte:before { + content: "\f37f"; } + +.fa-bus:before { + content: "\f207"; } + +.fa-bus-alt:before { + content: "\f55e"; } + +.fa-business-time:before { + content: "\f64a"; } + +.fa-buy-n-large:before { + content: "\f8a6"; } + +.fa-buysellads:before { + content: "\f20d"; } + +.fa-calculator:before { + content: "\f1ec"; } + +.fa-calendar:before { + content: "\f133"; } + +.fa-calendar-alt:before { + content: "\f073"; } + +.fa-calendar-check:before { + content: "\f274"; } + +.fa-calendar-day:before { + content: "\f783"; } + +.fa-calendar-minus:before { + content: "\f272"; } + +.fa-calendar-plus:before { + content: "\f271"; } + +.fa-calendar-times:before { + content: "\f273"; } + +.fa-calendar-week:before { + content: "\f784"; } + +.fa-camera:before { + content: "\f030"; } + +.fa-camera-retro:before { + content: "\f083"; } + +.fa-campground:before { + content: "\f6bb"; } + +.fa-canadian-maple-leaf:before { + content: "\f785"; } + +.fa-candy-cane:before { + content: "\f786"; } + +.fa-cannabis:before { + content: "\f55f"; } + +.fa-capsules:before { + content: "\f46b"; } + +.fa-car:before { + content: "\f1b9"; } + +.fa-car-alt:before { + content: "\f5de"; } + +.fa-car-battery:before { + content: "\f5df"; } + +.fa-car-crash:before { + content: "\f5e1"; } + +.fa-car-side:before { + content: "\f5e4"; } + +.fa-caravan:before { + content: "\f8ff"; } + +.fa-caret-down:before { + content: "\f0d7"; } + +.fa-caret-left:before { + content: "\f0d9"; } + +.fa-caret-right:before { + content: "\f0da"; } + +.fa-caret-square-down:before { + content: "\f150"; } + +.fa-caret-square-left:before { + content: "\f191"; } + +.fa-caret-square-right:before { + content: "\f152"; } + +.fa-caret-square-up:before { + content: "\f151"; } + +.fa-caret-up:before { + content: "\f0d8"; } + +.fa-carrot:before { + content: "\f787"; } + +.fa-cart-arrow-down:before { + content: "\f218"; } + +.fa-cart-plus:before { + content: "\f217"; } + +.fa-cash-register:before { + content: "\f788"; } + +.fa-cat:before { + content: "\f6be"; } + +.fa-cc-amazon-pay:before { + content: "\f42d"; } + +.fa-cc-amex:before { + content: "\f1f3"; } + +.fa-cc-apple-pay:before { + content: "\f416"; } + +.fa-cc-diners-club:before { + content: "\f24c"; } + +.fa-cc-discover:before { + content: "\f1f2"; } + +.fa-cc-jcb:before { + content: "\f24b"; } + +.fa-cc-mastercard:before { + content: "\f1f1"; } + +.fa-cc-paypal:before { + content: "\f1f4"; } + +.fa-cc-stripe:before { + content: "\f1f5"; } + +.fa-cc-visa:before { + content: "\f1f0"; } + +.fa-centercode:before { + content: "\f380"; } + +.fa-centos:before { + content: "\f789"; } + +.fa-certificate:before { + content: "\f0a3"; } + +.fa-chair:before { + content: "\f6c0"; } + +.fa-chalkboard:before { + content: "\f51b"; } + +.fa-chalkboard-teacher:before { + content: "\f51c"; } + +.fa-charging-station:before { + content: "\f5e7"; } + +.fa-chart-area:before { + content: "\f1fe"; } + +.fa-chart-bar:before { + content: "\f080"; } + +.fa-chart-line:before { + content: "\f201"; } + +.fa-chart-pie:before { + content: "\f200"; } + +.fa-check:before { + content: "\f00c"; } + +.fa-check-circle:before { + content: "\f058"; } + +.fa-check-double:before { + content: "\f560"; } + +.fa-check-square:before { + content: "\f14a"; } + +.fa-cheese:before { + content: "\f7ef"; } + +.fa-chess:before { + content: "\f439"; } + +.fa-chess-bishop:before { + content: "\f43a"; } + +.fa-chess-board:before { + content: "\f43c"; } + +.fa-chess-king:before { + content: "\f43f"; } + +.fa-chess-knight:before { + content: "\f441"; } + +.fa-chess-pawn:before { + content: "\f443"; } + +.fa-chess-queen:before { + content: "\f445"; } + +.fa-chess-rook:before { + content: "\f447"; } + +.fa-chevron-circle-down:before { + content: "\f13a"; } + +.fa-chevron-circle-left:before { + content: "\f137"; } + +.fa-chevron-circle-right:before { + content: "\f138"; } + +.fa-chevron-circle-up:before { + content: "\f139"; } + +.fa-chevron-down:before { + content: "\f078"; } + +.fa-chevron-left:before { + content: "\f053"; } + +.fa-chevron-right:before { + content: "\f054"; } + +.fa-chevron-up:before { + content: "\f077"; } + +.fa-child:before { + content: "\f1ae"; } + +.fa-chrome:before { + content: "\f268"; } + +.fa-chromecast:before { + content: "\f838"; } + +.fa-church:before { + content: "\f51d"; } + +.fa-circle:before { + content: "\f111"; } + +.fa-circle-notch:before { + content: "\f1ce"; } + +.fa-city:before { + content: "\f64f"; } + +.fa-clinic-medical:before { + content: "\f7f2"; } + +.fa-clipboard:before { + content: "\f328"; } + +.fa-clipboard-check:before { + content: "\f46c"; } + +.fa-clipboard-list:before { + content: "\f46d"; } + +.fa-clock:before { + content: "\f017"; } + +.fa-clone:before { + content: "\f24d"; } + +.fa-closed-captioning:before { + content: "\f20a"; } + +.fa-cloud:before { + content: "\f0c2"; } + +.fa-cloud-download-alt:before { + content: "\f381"; } + +.fa-cloud-meatball:before { + content: "\f73b"; } + +.fa-cloud-moon:before { + content: "\f6c3"; } + +.fa-cloud-moon-rain:before { + content: "\f73c"; } + +.fa-cloud-rain:before { + content: "\f73d"; } + +.fa-cloud-showers-heavy:before { + content: "\f740"; } + +.fa-cloud-sun:before { + content: "\f6c4"; } + +.fa-cloud-sun-rain:before { + content: "\f743"; } + +.fa-cloud-upload-alt:before { + content: "\f382"; } + +.fa-cloudscale:before { + content: "\f383"; } + +.fa-cloudsmith:before { + content: "\f384"; } + +.fa-cloudversify:before { + content: "\f385"; } + +.fa-cocktail:before { + content: "\f561"; } + +.fa-code:before { + content: "\f121"; } + +.fa-code-branch:before { + content: "\f126"; } + +.fa-codepen:before { + content: "\f1cb"; } + +.fa-codiepie:before { + content: "\f284"; } + +.fa-coffee:before { + content: "\f0f4"; } + +.fa-cog:before { + content: "\f013"; } + +.fa-cogs:before { + content: "\f085"; } + +.fa-coins:before { + content: "\f51e"; } + +.fa-columns:before { + content: "\f0db"; } + +.fa-comment:before { + content: "\f075"; } + +.fa-comment-alt:before { + content: "\f27a"; } + +.fa-comment-dollar:before { + content: "\f651"; } + +.fa-comment-dots:before { + content: "\f4ad"; } + +.fa-comment-medical:before { + content: "\f7f5"; } + +.fa-comment-slash:before { + content: "\f4b3"; } + +.fa-comments:before { + content: "\f086"; } + +.fa-comments-dollar:before { + content: "\f653"; } + +.fa-compact-disc:before { + content: "\f51f"; } + +.fa-compass:before { + content: "\f14e"; } + +.fa-compress:before { + content: "\f066"; } + +.fa-compress-alt:before { + content: "\f422"; } + +.fa-compress-arrows-alt:before { + content: "\f78c"; } + +.fa-concierge-bell:before { + content: "\f562"; } + +.fa-confluence:before { + content: "\f78d"; } + +.fa-connectdevelop:before { + content: "\f20e"; } + +.fa-contao:before { + content: "\f26d"; } + +.fa-cookie:before { + content: "\f563"; } + +.fa-cookie-bite:before { + content: "\f564"; } + +.fa-copy:before { + content: "\f0c5"; } + +.fa-copyright:before { + content: "\f1f9"; } + +.fa-cotton-bureau:before { + content: "\f89e"; } + +.fa-couch:before { + content: "\f4b8"; } + +.fa-cpanel:before { + content: "\f388"; } + +.fa-creative-commons:before { + content: "\f25e"; } + +.fa-creative-commons-by:before { + content: "\f4e7"; } + +.fa-creative-commons-nc:before { + content: "\f4e8"; } + +.fa-creative-commons-nc-eu:before { + content: "\f4e9"; } + +.fa-creative-commons-nc-jp:before { + content: "\f4ea"; } + +.fa-creative-commons-nd:before { + content: "\f4eb"; } + +.fa-creative-commons-pd:before { + content: "\f4ec"; } + +.fa-creative-commons-pd-alt:before { + content: "\f4ed"; } + +.fa-creative-commons-remix:before { + content: "\f4ee"; } + +.fa-creative-commons-sa:before { + content: "\f4ef"; } + +.fa-creative-commons-sampling:before { + content: "\f4f0"; } + +.fa-creative-commons-sampling-plus:before { + content: "\f4f1"; } + +.fa-creative-commons-share:before { + content: "\f4f2"; } + +.fa-creative-commons-zero:before { + content: "\f4f3"; } + +.fa-credit-card:before { + content: "\f09d"; } + +.fa-critical-role:before { + content: "\f6c9"; } + +.fa-crop:before { + content: "\f125"; } + +.fa-crop-alt:before { + content: "\f565"; } + +.fa-cross:before { + content: "\f654"; } + +.fa-crosshairs:before { + content: "\f05b"; } + +.fa-crow:before { + content: "\f520"; } + +.fa-crown:before { + content: "\f521"; } + +.fa-crutch:before { + content: "\f7f7"; } + +.fa-css3:before { + content: "\f13c"; } + +.fa-css3-alt:before { + content: "\f38b"; } + +.fa-cube:before { + content: "\f1b2"; } + +.fa-cubes:before { + content: "\f1b3"; } + +.fa-cut:before { + content: "\f0c4"; } + +.fa-cuttlefish:before { + content: "\f38c"; } + +.fa-d-and-d:before { + content: "\f38d"; } + +.fa-d-and-d-beyond:before { + content: "\f6ca"; } + +.fa-dailymotion:before { + content: "\f952"; } + +.fa-dashcube:before { + content: "\f210"; } + +.fa-database:before { + content: "\f1c0"; } + +.fa-deaf:before { + content: "\f2a4"; } + +.fa-delicious:before { + content: "\f1a5"; } + +.fa-democrat:before { + content: "\f747"; } + +.fa-deploydog:before { + content: "\f38e"; } + +.fa-deskpro:before { + content: "\f38f"; } + +.fa-desktop:before { + content: "\f108"; } + +.fa-dev:before { + content: "\f6cc"; } + +.fa-deviantart:before { + content: "\f1bd"; } + +.fa-dharmachakra:before { + content: "\f655"; } + +.fa-dhl:before { + content: "\f790"; } + +.fa-diagnoses:before { + content: "\f470"; } + +.fa-diaspora:before { + content: "\f791"; } + +.fa-dice:before { + content: "\f522"; } + +.fa-dice-d20:before { + content: "\f6cf"; } + +.fa-dice-d6:before { + content: "\f6d1"; } + +.fa-dice-five:before { + content: "\f523"; } + +.fa-dice-four:before { + content: "\f524"; } + +.fa-dice-one:before { + content: "\f525"; } + +.fa-dice-six:before { + content: "\f526"; } + +.fa-dice-three:before { + content: "\f527"; } + +.fa-dice-two:before { + content: "\f528"; } + +.fa-digg:before { + content: "\f1a6"; } + +.fa-digital-ocean:before { + content: "\f391"; } + +.fa-digital-tachograph:before { + content: "\f566"; } + +.fa-directions:before { + content: "\f5eb"; } + +.fa-discord:before { + content: "\f392"; } + +.fa-discourse:before { + content: "\f393"; } + +.fa-disease:before { + content: "\f7fa"; } + +.fa-divide:before { + content: "\f529"; } + +.fa-dizzy:before { + content: "\f567"; } + +.fa-dna:before { + content: "\f471"; } + +.fa-dochub:before { + content: "\f394"; } + +.fa-docker:before { + content: "\f395"; } + +.fa-dog:before { + content: "\f6d3"; } + +.fa-dollar-sign:before { + content: "\f155"; } + +.fa-dolly:before { + content: "\f472"; } + +.fa-dolly-flatbed:before { + content: "\f474"; } + +.fa-donate:before { + content: "\f4b9"; } + +.fa-door-closed:before { + content: "\f52a"; } + +.fa-door-open:before { + content: "\f52b"; } + +.fa-dot-circle:before { + content: "\f192"; } + +.fa-dove:before { + content: "\f4ba"; } + +.fa-download:before { + content: "\f019"; } + +.fa-draft2digital:before { + content: "\f396"; } + +.fa-drafting-compass:before { + content: "\f568"; } + +.fa-dragon:before { + content: "\f6d5"; } + +.fa-draw-polygon:before { + content: "\f5ee"; } + +.fa-dribbble:before { + content: "\f17d"; } + +.fa-dribbble-square:before { + content: "\f397"; } + +.fa-dropbox:before { + content: "\f16b"; } + +.fa-drum:before { + content: "\f569"; } + +.fa-drum-steelpan:before { + content: "\f56a"; } + +.fa-drumstick-bite:before { + content: "\f6d7"; } + +.fa-drupal:before { + content: "\f1a9"; } + +.fa-dumbbell:before { + content: "\f44b"; } + +.fa-dumpster:before { + content: "\f793"; } + +.fa-dumpster-fire:before { + content: "\f794"; } + +.fa-dungeon:before { + content: "\f6d9"; } + +.fa-dyalog:before { + content: "\f399"; } + +.fa-earlybirds:before { + content: "\f39a"; } + +.fa-ebay:before { + content: "\f4f4"; } + +.fa-edge:before { + content: "\f282"; } + +.fa-edit:before { + content: "\f044"; } + +.fa-egg:before { + content: "\f7fb"; } + +.fa-eject:before { + content: "\f052"; } + +.fa-elementor:before { + content: "\f430"; } + +.fa-ellipsis-h:before { + content: "\f141"; } + +.fa-ellipsis-v:before { + content: "\f142"; } + +.fa-ello:before { + content: "\f5f1"; } + +.fa-ember:before { + content: "\f423"; } + +.fa-empire:before { + content: "\f1d1"; } + +.fa-envelope:before { + content: "\f0e0"; } + +.fa-envelope-open:before { + content: "\f2b6"; } + +.fa-envelope-open-text:before { + content: "\f658"; } + +.fa-envelope-square:before { + content: "\f199"; } + +.fa-envira:before { + content: "\f299"; } + +.fa-equals:before { + content: "\f52c"; } + +.fa-eraser:before { + content: "\f12d"; } + +.fa-erlang:before { + content: "\f39d"; } + +.fa-ethereum:before { + content: "\f42e"; } + +.fa-ethernet:before { + content: "\f796"; } + +.fa-etsy:before { + content: "\f2d7"; } + +.fa-euro-sign:before { + content: "\f153"; } + +.fa-evernote:before { + content: "\f839"; } + +.fa-exchange-alt:before { + content: "\f362"; } + +.fa-exclamation:before { + content: "\f12a"; } + +.fa-exclamation-circle:before { + content: "\f06a"; } + +.fa-exclamation-triangle:before { + content: "\f071"; } + +.fa-expand:before { + content: "\f065"; } + +.fa-expand-alt:before { + content: "\f424"; } + +.fa-expand-arrows-alt:before { + content: "\f31e"; } + +.fa-expeditedssl:before { + content: "\f23e"; } + +.fa-external-link-alt:before { + content: "\f35d"; } + +.fa-external-link-square-alt:before { + content: "\f360"; } + +.fa-eye:before { + content: "\f06e"; } + +.fa-eye-dropper:before { + content: "\f1fb"; } + +.fa-eye-slash:before { + content: "\f070"; } + +.fa-facebook:before { + content: "\f09a"; } + +.fa-facebook-f:before { + content: "\f39e"; } + +.fa-facebook-messenger:before { + content: "\f39f"; } + +.fa-facebook-square:before { + content: "\f082"; } + +.fa-fan:before { + content: "\f863"; } + +.fa-fantasy-flight-games:before { + content: "\f6dc"; } + +.fa-fast-backward:before { + content: "\f049"; } + +.fa-fast-forward:before { + content: "\f050"; } + +.fa-faucet:before { + content: "\f905"; } + +.fa-fax:before { + content: "\f1ac"; } + +.fa-feather:before { + content: "\f52d"; } + +.fa-feather-alt:before { + content: "\f56b"; } + +.fa-fedex:before { + content: "\f797"; } + +.fa-fedora:before { + content: "\f798"; } + +.fa-female:before { + content: "\f182"; } + +.fa-fighter-jet:before { + content: "\f0fb"; } + +.fa-figma:before { + content: "\f799"; } + +.fa-file:before { + content: "\f15b"; } + +.fa-file-alt:before { + content: "\f15c"; } + +.fa-file-archive:before { + content: "\f1c6"; } + +.fa-file-audio:before { + content: "\f1c7"; } + +.fa-file-code:before { + content: "\f1c9"; } + +.fa-file-contract:before { + content: "\f56c"; } + +.fa-file-csv:before { + content: "\f6dd"; } + +.fa-file-download:before { + content: "\f56d"; } + +.fa-file-excel:before { + content: "\f1c3"; } + +.fa-file-export:before { + content: "\f56e"; } + +.fa-file-image:before { + content: "\f1c5"; } + +.fa-file-import:before { + content: "\f56f"; } + +.fa-file-invoice:before { + content: "\f570"; } + +.fa-file-invoice-dollar:before { + content: "\f571"; } + +.fa-file-medical:before { + content: "\f477"; } + +.fa-file-medical-alt:before { + content: "\f478"; } + +.fa-file-pdf:before { + content: "\f1c1"; } + +.fa-file-powerpoint:before { + content: "\f1c4"; } + +.fa-file-prescription:before { + content: "\f572"; } + +.fa-file-signature:before { + content: "\f573"; } + +.fa-file-upload:before { + content: "\f574"; } + +.fa-file-video:before { + content: "\f1c8"; } + +.fa-file-word:before { + content: "\f1c2"; } + +.fa-fill:before { + content: "\f575"; } + +.fa-fill-drip:before { + content: "\f576"; } + +.fa-film:before { + content: "\f008"; } + +.fa-filter:before { + content: "\f0b0"; } + +.fa-fingerprint:before { + content: "\f577"; } + +.fa-fire:before { + content: "\f06d"; } + +.fa-fire-alt:before { + content: "\f7e4"; } + +.fa-fire-extinguisher:before { + content: "\f134"; } + +.fa-firefox:before { + content: "\f269"; } + +.fa-firefox-browser:before { + content: "\f907"; } + +.fa-first-aid:before { + content: "\f479"; } + +.fa-first-order:before { + content: "\f2b0"; } + +.fa-first-order-alt:before { + content: "\f50a"; } + +.fa-firstdraft:before { + content: "\f3a1"; } + +.fa-fish:before { + content: "\f578"; } + +.fa-fist-raised:before { + content: "\f6de"; } + +.fa-flag:before { + content: "\f024"; } + +.fa-flag-checkered:before { + content: "\f11e"; } + +.fa-flag-usa:before { + content: "\f74d"; } + +.fa-flask:before { + content: "\f0c3"; } + +.fa-flickr:before { + content: "\f16e"; } + +.fa-flipboard:before { + content: "\f44d"; } + +.fa-flushed:before { + content: "\f579"; } + +.fa-fly:before { + content: "\f417"; } + +.fa-folder:before { + content: "\f07b"; } + +.fa-folder-minus:before { + content: "\f65d"; } + +.fa-folder-open:before { + content: "\f07c"; } + +.fa-folder-plus:before { + content: "\f65e"; } + +.fa-font:before { + content: "\f031"; } + +.fa-font-awesome:before { + content: "\f2b4"; } + +.fa-font-awesome-alt:before { + content: "\f35c"; } + +.fa-font-awesome-flag:before { + content: "\f425"; } + +.fa-font-awesome-logo-full:before { + content: "\f4e6"; } + +.fa-fonticons:before { + content: "\f280"; } + +.fa-fonticons-fi:before { + content: "\f3a2"; } + +.fa-football-ball:before { + content: "\f44e"; } + +.fa-fort-awesome:before { + content: "\f286"; } + +.fa-fort-awesome-alt:before { + content: "\f3a3"; } + +.fa-forumbee:before { + content: "\f211"; } + +.fa-forward:before { + content: "\f04e"; } + +.fa-foursquare:before { + content: "\f180"; } + +.fa-free-code-camp:before { + content: "\f2c5"; } + +.fa-freebsd:before { + content: "\f3a4"; } + +.fa-frog:before { + content: "\f52e"; } + +.fa-frown:before { + content: "\f119"; } + +.fa-frown-open:before { + content: "\f57a"; } + +.fa-fulcrum:before { + content: "\f50b"; } + +.fa-funnel-dollar:before { + content: "\f662"; } + +.fa-futbol:before { + content: "\f1e3"; } + +.fa-galactic-republic:before { + content: "\f50c"; } + +.fa-galactic-senate:before { + content: "\f50d"; } + +.fa-gamepad:before { + content: "\f11b"; } + +.fa-gas-pump:before { + content: "\f52f"; } + +.fa-gavel:before { + content: "\f0e3"; } + +.fa-gem:before { + content: "\f3a5"; } + +.fa-genderless:before { + content: "\f22d"; } + +.fa-get-pocket:before { + content: "\f265"; } + +.fa-gg:before { + content: "\f260"; } + +.fa-gg-circle:before { + content: "\f261"; } + +.fa-ghost:before { + content: "\f6e2"; } + +.fa-gift:before { + content: "\f06b"; } + +.fa-gifts:before { + content: "\f79c"; } + +.fa-git:before { + content: "\f1d3"; } + +.fa-git-alt:before { + content: "\f841"; } + +.fa-git-square:before { + content: "\f1d2"; } + +.fa-github:before { + content: "\f09b"; } + +.fa-github-alt:before { + content: "\f113"; } + +.fa-github-square:before { + content: "\f092"; } + +.fa-gitkraken:before { + content: "\f3a6"; } + +.fa-gitlab:before { + content: "\f296"; } + +.fa-gitter:before { + content: "\f426"; } + +.fa-glass-cheers:before { + content: "\f79f"; } + +.fa-glass-martini:before { + content: "\f000"; } + +.fa-glass-martini-alt:before { + content: "\f57b"; } + +.fa-glass-whiskey:before { + content: "\f7a0"; } + +.fa-glasses:before { + content: "\f530"; } + +.fa-glide:before { + content: "\f2a5"; } + +.fa-glide-g:before { + content: "\f2a6"; } + +.fa-globe:before { + content: "\f0ac"; } + +.fa-globe-africa:before { + content: "\f57c"; } + +.fa-globe-americas:before { + content: "\f57d"; } + +.fa-globe-asia:before { + content: "\f57e"; } + +.fa-globe-europe:before { + content: "\f7a2"; } + +.fa-gofore:before { + content: "\f3a7"; } + +.fa-golf-ball:before { + content: "\f450"; } + +.fa-goodreads:before { + content: "\f3a8"; } + +.fa-goodreads-g:before { + content: "\f3a9"; } + +.fa-google:before { + content: "\f1a0"; } + +.fa-google-drive:before { + content: "\f3aa"; } + +.fa-google-play:before { + content: "\f3ab"; } + +.fa-google-plus:before { + content: "\f2b3"; } + +.fa-google-plus-g:before { + content: "\f0d5"; } + +.fa-google-plus-square:before { + content: "\f0d4"; } + +.fa-google-wallet:before { + content: "\f1ee"; } + +.fa-gopuram:before { + content: "\f664"; } + +.fa-graduation-cap:before { + content: "\f19d"; } + +.fa-gratipay:before { + content: "\f184"; } + +.fa-grav:before { + content: "\f2d6"; } + +.fa-greater-than:before { + content: "\f531"; } + +.fa-greater-than-equal:before { + content: "\f532"; } + +.fa-grimace:before { + content: "\f57f"; } + +.fa-grin:before { + content: "\f580"; } + +.fa-grin-alt:before { + content: "\f581"; } + +.fa-grin-beam:before { + content: "\f582"; } + +.fa-grin-beam-sweat:before { + content: "\f583"; } + +.fa-grin-hearts:before { + content: "\f584"; } + +.fa-grin-squint:before { + content: "\f585"; } + +.fa-grin-squint-tears:before { + content: "\f586"; } + +.fa-grin-stars:before { + content: "\f587"; } + +.fa-grin-tears:before { + content: "\f588"; } + +.fa-grin-tongue:before { + content: "\f589"; } + +.fa-grin-tongue-squint:before { + content: "\f58a"; } + +.fa-grin-tongue-wink:before { + content: "\f58b"; } + +.fa-grin-wink:before { + content: "\f58c"; } + +.fa-grip-horizontal:before { + content: "\f58d"; } + +.fa-grip-lines:before { + content: "\f7a4"; } + +.fa-grip-lines-vertical:before { + content: "\f7a5"; } + +.fa-grip-vertical:before { + content: "\f58e"; } + +.fa-gripfire:before { + content: "\f3ac"; } + +.fa-grunt:before { + content: "\f3ad"; } + +.fa-guitar:before { + content: "\f7a6"; } + +.fa-gulp:before { + content: "\f3ae"; } + +.fa-h-square:before { + content: "\f0fd"; } + +.fa-hacker-news:before { + content: "\f1d4"; } + +.fa-hacker-news-square:before { + content: "\f3af"; } + +.fa-hackerrank:before { + content: "\f5f7"; } + +.fa-hamburger:before { + content: "\f805"; } + +.fa-hammer:before { + content: "\f6e3"; } + +.fa-hamsa:before { + content: "\f665"; } + +.fa-hand-holding:before { + content: "\f4bd"; } + +.fa-hand-holding-heart:before { + content: "\f4be"; } + +.fa-hand-holding-medical:before { + content: "\f95c"; } + +.fa-hand-holding-usd:before { + content: "\f4c0"; } + +.fa-hand-holding-water:before { + content: "\f4c1"; } + +.fa-hand-lizard:before { + content: "\f258"; } + +.fa-hand-middle-finger:before { + content: "\f806"; } + +.fa-hand-paper:before { + content: "\f256"; } + +.fa-hand-peace:before { + content: "\f25b"; } + +.fa-hand-point-down:before { + content: "\f0a7"; } + +.fa-hand-point-left:before { + content: "\f0a5"; } + +.fa-hand-point-right:before { + content: "\f0a4"; } + +.fa-hand-point-up:before { + content: "\f0a6"; } + +.fa-hand-pointer:before { + content: "\f25a"; } + +.fa-hand-rock:before { + content: "\f255"; } + +.fa-hand-scissors:before { + content: "\f257"; } + +.fa-hand-sparkles:before { + content: "\f95d"; } + +.fa-hand-spock:before { + content: "\f259"; } + +.fa-hands:before { + content: "\f4c2"; } + +.fa-hands-helping:before { + content: "\f4c4"; } + +.fa-hands-wash:before { + content: "\f95e"; } + +.fa-handshake:before { + content: "\f2b5"; } + +.fa-handshake-alt-slash:before { + content: "\f95f"; } + +.fa-handshake-slash:before { + content: "\f960"; } + +.fa-hanukiah:before { + content: "\f6e6"; } + +.fa-hard-hat:before { + content: "\f807"; } + +.fa-hashtag:before { + content: "\f292"; } + +.fa-hat-cowboy:before { + content: "\f8c0"; } + +.fa-hat-cowboy-side:before { + content: "\f8c1"; } + +.fa-hat-wizard:before { + content: "\f6e8"; } + +.fa-hdd:before { + content: "\f0a0"; } + +.fa-head-side-cough:before { + content: "\f961"; } + +.fa-head-side-cough-slash:before { + content: "\f962"; } + +.fa-head-side-mask:before { + content: "\f963"; } + +.fa-head-side-virus:before { + content: "\f964"; } + +.fa-heading:before { + content: "\f1dc"; } + +.fa-headphones:before { + content: "\f025"; } + +.fa-headphones-alt:before { + content: "\f58f"; } + +.fa-headset:before { + content: "\f590"; } + +.fa-heart:before { + content: "\f004"; } + +.fa-heart-broken:before { + content: "\f7a9"; } + +.fa-heartbeat:before { + content: "\f21e"; } + +.fa-helicopter:before { + content: "\f533"; } + +.fa-highlighter:before { + content: "\f591"; } + +.fa-hiking:before { + content: "\f6ec"; } + +.fa-hippo:before { + content: "\f6ed"; } + +.fa-hips:before { + content: "\f452"; } + +.fa-hire-a-helper:before { + content: "\f3b0"; } + +.fa-history:before { + content: "\f1da"; } + +.fa-hockey-puck:before { + content: "\f453"; } + +.fa-holly-berry:before { + content: "\f7aa"; } + +.fa-home:before { + content: "\f015"; } + +.fa-hooli:before { + content: "\f427"; } + +.fa-hornbill:before { + content: "\f592"; } + +.fa-horse:before { + content: "\f6f0"; } + +.fa-horse-head:before { + content: "\f7ab"; } + +.fa-hospital:before { + content: "\f0f8"; } + +.fa-hospital-alt:before { + content: "\f47d"; } + +.fa-hospital-symbol:before { + content: "\f47e"; } + +.fa-hospital-user:before { + content: "\f80d"; } + +.fa-hot-tub:before { + content: "\f593"; } + +.fa-hotdog:before { + content: "\f80f"; } + +.fa-hotel:before { + content: "\f594"; } + +.fa-hotjar:before { + content: "\f3b1"; } + +.fa-hourglass:before { + content: "\f254"; } + +.fa-hourglass-end:before { + content: "\f253"; } + +.fa-hourglass-half:before { + content: "\f252"; } + +.fa-hourglass-start:before { + content: "\f251"; } + +.fa-house-damage:before { + content: "\f6f1"; } + +.fa-house-user:before { + content: "\f965"; } + +.fa-houzz:before { + content: "\f27c"; } + +.fa-hryvnia:before { + content: "\f6f2"; } + +.fa-html5:before { + content: "\f13b"; } + +.fa-hubspot:before { + content: "\f3b2"; } + +.fa-i-cursor:before { + content: "\f246"; } + +.fa-ice-cream:before { + content: "\f810"; } + +.fa-icicles:before { + content: "\f7ad"; } + +.fa-icons:before { + content: "\f86d"; } + +.fa-id-badge:before { + content: "\f2c1"; } + +.fa-id-card:before { + content: "\f2c2"; } + +.fa-id-card-alt:before { + content: "\f47f"; } + +.fa-ideal:before { + content: "\f913"; } + +.fa-igloo:before { + content: "\f7ae"; } + +.fa-image:before { + content: "\f03e"; } + +.fa-images:before { + content: "\f302"; } + +.fa-imdb:before { + content: "\f2d8"; } + +.fa-inbox:before { + content: "\f01c"; } + +.fa-indent:before { + content: "\f03c"; } + +.fa-industry:before { + content: "\f275"; } + +.fa-infinity:before { + content: "\f534"; } + +.fa-info:before { + content: "\f129"; } + +.fa-info-circle:before { + content: "\f05a"; } + +.fa-instagram:before { + content: "\f16d"; } + +.fa-instagram-square:before { + content: "\f955"; } + +.fa-intercom:before { + content: "\f7af"; } + +.fa-internet-explorer:before { + content: "\f26b"; } + +.fa-invision:before { + content: "\f7b0"; } + +.fa-ioxhost:before { + content: "\f208"; } + +.fa-italic:before { + content: "\f033"; } + +.fa-itch-io:before { + content: "\f83a"; } + +.fa-itunes:before { + content: "\f3b4"; } + +.fa-itunes-note:before { + content: "\f3b5"; } + +.fa-java:before { + content: "\f4e4"; } + +.fa-jedi:before { + content: "\f669"; } + +.fa-jedi-order:before { + content: "\f50e"; } + +.fa-jenkins:before { + content: "\f3b6"; } + +.fa-jira:before { + content: "\f7b1"; } + +.fa-joget:before { + content: "\f3b7"; } + +.fa-joint:before { + content: "\f595"; } + +.fa-joomla:before { + content: "\f1aa"; } + +.fa-journal-whills:before { + content: "\f66a"; } + +.fa-js:before { + content: "\f3b8"; } + +.fa-js-square:before { + content: "\f3b9"; } + +.fa-jsfiddle:before { + content: "\f1cc"; } + +.fa-kaaba:before { + content: "\f66b"; } + +.fa-kaggle:before { + content: "\f5fa"; } + +.fa-key:before { + content: "\f084"; } + +.fa-keybase:before { + content: "\f4f5"; } + +.fa-keyboard:before { + content: "\f11c"; } + +.fa-keycdn:before { + content: "\f3ba"; } + +.fa-khanda:before { + content: "\f66d"; } + +.fa-kickstarter:before { + content: "\f3bb"; } + +.fa-kickstarter-k:before { + content: "\f3bc"; } + +.fa-kiss:before { + content: "\f596"; } + +.fa-kiss-beam:before { + content: "\f597"; } + +.fa-kiss-wink-heart:before { + content: "\f598"; } + +.fa-kiwi-bird:before { + content: "\f535"; } + +.fa-korvue:before { + content: "\f42f"; } + +.fa-landmark:before { + content: "\f66f"; } + +.fa-language:before { + content: "\f1ab"; } + +.fa-laptop:before { + content: "\f109"; } + +.fa-laptop-code:before { + content: "\f5fc"; } + +.fa-laptop-house:before { + content: "\f966"; } + +.fa-laptop-medical:before { + content: "\f812"; } + +.fa-laravel:before { + content: "\f3bd"; } + +.fa-lastfm:before { + content: "\f202"; } + +.fa-lastfm-square:before { + content: "\f203"; } + +.fa-laugh:before { + content: "\f599"; } + +.fa-laugh-beam:before { + content: "\f59a"; } + +.fa-laugh-squint:before { + content: "\f59b"; } + +.fa-laugh-wink:before { + content: "\f59c"; } + +.fa-layer-group:before { + content: "\f5fd"; } + +.fa-leaf:before { + content: "\f06c"; } + +.fa-leanpub:before { + content: "\f212"; } + +.fa-lemon:before { + content: "\f094"; } + +.fa-less:before { + content: "\f41d"; } + +.fa-less-than:before { + content: "\f536"; } + +.fa-less-than-equal:before { + content: "\f537"; } + +.fa-level-down-alt:before { + content: "\f3be"; } + +.fa-level-up-alt:before { + content: "\f3bf"; } + +.fa-life-ring:before { + content: "\f1cd"; } + +.fa-lightbulb:before { + content: "\f0eb"; } + +.fa-line:before { + content: "\f3c0"; } + +.fa-link:before { + content: "\f0c1"; } + +.fa-linkedin:before { + content: "\f08c"; } + +.fa-linkedin-in:before { + content: "\f0e1"; } + +.fa-linode:before { + content: "\f2b8"; } + +.fa-linux:before { + content: "\f17c"; } + +.fa-lira-sign:before { + content: "\f195"; } + +.fa-list:before { + content: "\f03a"; } + +.fa-list-alt:before { + content: "\f022"; } + +.fa-list-ol:before { + content: "\f0cb"; } + +.fa-list-ul:before { + content: "\f0ca"; } + +.fa-location-arrow:before { + content: "\f124"; } + +.fa-lock:before { + content: "\f023"; } + +.fa-lock-open:before { + content: "\f3c1"; } + +.fa-long-arrow-alt-down:before { + content: "\f309"; } + +.fa-long-arrow-alt-left:before { + content: "\f30a"; } + +.fa-long-arrow-alt-right:before { + content: "\f30b"; } + +.fa-long-arrow-alt-up:before { + content: "\f30c"; } + +.fa-low-vision:before { + content: "\f2a8"; } + +.fa-luggage-cart:before { + content: "\f59d"; } + +.fa-lungs:before { + content: "\f604"; } + +.fa-lungs-virus:before { + content: "\f967"; } + +.fa-lyft:before { + content: "\f3c3"; } + +.fa-magento:before { + content: "\f3c4"; } + +.fa-magic:before { + content: "\f0d0"; } + +.fa-magnet:before { + content: "\f076"; } + +.fa-mail-bulk:before { + content: "\f674"; } + +.fa-mailchimp:before { + content: "\f59e"; } + +.fa-male:before { + content: "\f183"; } + +.fa-mandalorian:before { + content: "\f50f"; } + +.fa-map:before { + content: "\f279"; } + +.fa-map-marked:before { + content: "\f59f"; } + +.fa-map-marked-alt:before { + content: "\f5a0"; } + +.fa-map-marker:before { + content: "\f041"; } + +.fa-map-marker-alt:before { + content: "\f3c5"; } + +.fa-map-pin:before { + content: "\f276"; } + +.fa-map-signs:before { + content: "\f277"; } + +.fa-markdown:before { + content: "\f60f"; } + +.fa-marker:before { + content: "\f5a1"; } + +.fa-mars:before { + content: "\f222"; } + +.fa-mars-double:before { + content: "\f227"; } + +.fa-mars-stroke:before { + content: "\f229"; } + +.fa-mars-stroke-h:before { + content: "\f22b"; } + +.fa-mars-stroke-v:before { + content: "\f22a"; } + +.fa-mask:before { + content: "\f6fa"; } + +.fa-mastodon:before { + content: "\f4f6"; } + +.fa-maxcdn:before { + content: "\f136"; } + +.fa-mdb:before { + content: "\f8ca"; } + +.fa-medal:before { + content: "\f5a2"; } + +.fa-medapps:before { + content: "\f3c6"; } + +.fa-medium:before { + content: "\f23a"; } + +.fa-medium-m:before { + content: "\f3c7"; } + +.fa-medkit:before { + content: "\f0fa"; } + +.fa-medrt:before { + content: "\f3c8"; } + +.fa-meetup:before { + content: "\f2e0"; } + +.fa-megaport:before { + content: "\f5a3"; } + +.fa-meh:before { + content: "\f11a"; } + +.fa-meh-blank:before { + content: "\f5a4"; } + +.fa-meh-rolling-eyes:before { + content: "\f5a5"; } + +.fa-memory:before { + content: "\f538"; } + +.fa-mendeley:before { + content: "\f7b3"; } + +.fa-menorah:before { + content: "\f676"; } + +.fa-mercury:before { + content: "\f223"; } + +.fa-meteor:before { + content: "\f753"; } + +.fa-microblog:before { + content: "\f91a"; } + +.fa-microchip:before { + content: "\f2db"; } + +.fa-microphone:before { + content: "\f130"; } + +.fa-microphone-alt:before { + content: "\f3c9"; } + +.fa-microphone-alt-slash:before { + content: "\f539"; } + +.fa-microphone-slash:before { + content: "\f131"; } + +.fa-microscope:before { + content: "\f610"; } + +.fa-microsoft:before { + content: "\f3ca"; } + +.fa-minus:before { + content: "\f068"; } + +.fa-minus-circle:before { + content: "\f056"; } + +.fa-minus-square:before { + content: "\f146"; } + +.fa-mitten:before { + content: "\f7b5"; } + +.fa-mix:before { + content: "\f3cb"; } + +.fa-mixcloud:before { + content: "\f289"; } + +.fa-mixer:before { + content: "\f956"; } + +.fa-mizuni:before { + content: "\f3cc"; } + +.fa-mobile:before { + content: "\f10b"; } + +.fa-mobile-alt:before { + content: "\f3cd"; } + +.fa-modx:before { + content: "\f285"; } + +.fa-monero:before { + content: "\f3d0"; } + +.fa-money-bill:before { + content: "\f0d6"; } + +.fa-money-bill-alt:before { + content: "\f3d1"; } + +.fa-money-bill-wave:before { + content: "\f53a"; } + +.fa-money-bill-wave-alt:before { + content: "\f53b"; } + +.fa-money-check:before { + content: "\f53c"; } + +.fa-money-check-alt:before { + content: "\f53d"; } + +.fa-monument:before { + content: "\f5a6"; } + +.fa-moon:before { + content: "\f186"; } + +.fa-mortar-pestle:before { + content: "\f5a7"; } + +.fa-mosque:before { + content: "\f678"; } + +.fa-motorcycle:before { + content: "\f21c"; } + +.fa-mountain:before { + content: "\f6fc"; } + +.fa-mouse:before { + content: "\f8cc"; } + +.fa-mouse-pointer:before { + content: "\f245"; } + +.fa-mug-hot:before { + content: "\f7b6"; } + +.fa-music:before { + content: "\f001"; } + +.fa-napster:before { + content: "\f3d2"; } + +.fa-neos:before { + content: "\f612"; } + +.fa-network-wired:before { + content: "\f6ff"; } + +.fa-neuter:before { + content: "\f22c"; } + +.fa-newspaper:before { + content: "\f1ea"; } + +.fa-nimblr:before { + content: "\f5a8"; } + +.fa-node:before { + content: "\f419"; } + +.fa-node-js:before { + content: "\f3d3"; } + +.fa-not-equal:before { + content: "\f53e"; } + +.fa-notes-medical:before { + content: "\f481"; } + +.fa-npm:before { + content: "\f3d4"; } + +.fa-ns8:before { + content: "\f3d5"; } + +.fa-nutritionix:before { + content: "\f3d6"; } + +.fa-object-group:before { + content: "\f247"; } + +.fa-object-ungroup:before { + content: "\f248"; } + +.fa-odnoklassniki:before { + content: "\f263"; } + +.fa-odnoklassniki-square:before { + content: "\f264"; } + +.fa-oil-can:before { + content: "\f613"; } + +.fa-old-republic:before { + content: "\f510"; } + +.fa-om:before { + content: "\f679"; } + +.fa-opencart:before { + content: "\f23d"; } + +.fa-openid:before { + content: "\f19b"; } + +.fa-opera:before { + content: "\f26a"; } + +.fa-optin-monster:before { + content: "\f23c"; } + +.fa-orcid:before { + content: "\f8d2"; } + +.fa-osi:before { + content: "\f41a"; } + +.fa-otter:before { + content: "\f700"; } + +.fa-outdent:before { + content: "\f03b"; } + +.fa-page4:before { + content: "\f3d7"; } + +.fa-pagelines:before { + content: "\f18c"; } + +.fa-pager:before { + content: "\f815"; } + +.fa-paint-brush:before { + content: "\f1fc"; } + +.fa-paint-roller:before { + content: "\f5aa"; } + +.fa-palette:before { + content: "\f53f"; } + +.fa-palfed:before { + content: "\f3d8"; } + +.fa-pallet:before { + content: "\f482"; } + +.fa-paper-plane:before { + content: "\f1d8"; } + +.fa-paperclip:before { + content: "\f0c6"; } + +.fa-parachute-box:before { + content: "\f4cd"; } + +.fa-paragraph:before { + content: "\f1dd"; } + +.fa-parking:before { + content: "\f540"; } + +.fa-passport:before { + content: "\f5ab"; } + +.fa-pastafarianism:before { + content: "\f67b"; } + +.fa-paste:before { + content: "\f0ea"; } + +.fa-patreon:before { + content: "\f3d9"; } + +.fa-pause:before { + content: "\f04c"; } + +.fa-pause-circle:before { + content: "\f28b"; } + +.fa-paw:before { + content: "\f1b0"; } + +.fa-paypal:before { + content: "\f1ed"; } + +.fa-peace:before { + content: "\f67c"; } + +.fa-pen:before { + content: "\f304"; } + +.fa-pen-alt:before { + content: "\f305"; } + +.fa-pen-fancy:before { + content: "\f5ac"; } + +.fa-pen-nib:before { + content: "\f5ad"; } + +.fa-pen-square:before { + content: "\f14b"; } + +.fa-pencil-alt:before { + content: "\f303"; } + +.fa-pencil-ruler:before { + content: "\f5ae"; } + +.fa-penny-arcade:before { + content: "\f704"; } + +.fa-people-arrows:before { + content: "\f968"; } + +.fa-people-carry:before { + content: "\f4ce"; } + +.fa-pepper-hot:before { + content: "\f816"; } + +.fa-percent:before { + content: "\f295"; } + +.fa-percentage:before { + content: "\f541"; } + +.fa-periscope:before { + content: "\f3da"; } + +.fa-person-booth:before { + content: "\f756"; } + +.fa-phabricator:before { + content: "\f3db"; } + +.fa-phoenix-framework:before { + content: "\f3dc"; } + +.fa-phoenix-squadron:before { + content: "\f511"; } + +.fa-phone:before { + content: "\f095"; } + +.fa-phone-alt:before { + content: "\f879"; } + +.fa-phone-slash:before { + content: "\f3dd"; } + +.fa-phone-square:before { + content: "\f098"; } + +.fa-phone-square-alt:before { + content: "\f87b"; } + +.fa-phone-volume:before { + content: "\f2a0"; } + +.fa-photo-video:before { + content: "\f87c"; } + +.fa-php:before { + content: "\f457"; } + +.fa-pied-piper:before { + content: "\f2ae"; } + +.fa-pied-piper-alt:before { + content: "\f1a8"; } + +.fa-pied-piper-hat:before { + content: "\f4e5"; } + +.fa-pied-piper-pp:before { + content: "\f1a7"; } + +.fa-pied-piper-square:before { + content: "\f91e"; } + +.fa-piggy-bank:before { + content: "\f4d3"; } + +.fa-pills:before { + content: "\f484"; } + +.fa-pinterest:before { + content: "\f0d2"; } + +.fa-pinterest-p:before { + content: "\f231"; } + +.fa-pinterest-square:before { + content: "\f0d3"; } + +.fa-pizza-slice:before { + content: "\f818"; } + +.fa-place-of-worship:before { + content: "\f67f"; } + +.fa-plane:before { + content: "\f072"; } + +.fa-plane-arrival:before { + content: "\f5af"; } + +.fa-plane-departure:before { + content: "\f5b0"; } + +.fa-plane-slash:before { + content: "\f969"; } + +.fa-play:before { + content: "\f04b"; } + +.fa-play-circle:before { + content: "\f144"; } + +.fa-playstation:before { + content: "\f3df"; } + +.fa-plug:before { + content: "\f1e6"; } + +.fa-plus:before { + content: "\f067"; } + +.fa-plus-circle:before { + content: "\f055"; } + +.fa-plus-square:before { + content: "\f0fe"; } + +.fa-podcast:before { + content: "\f2ce"; } + +.fa-poll:before { + content: "\f681"; } + +.fa-poll-h:before { + content: "\f682"; } + +.fa-poo:before { + content: "\f2fe"; } + +.fa-poo-storm:before { + content: "\f75a"; } + +.fa-poop:before { + content: "\f619"; } + +.fa-portrait:before { + content: "\f3e0"; } + +.fa-pound-sign:before { + content: "\f154"; } + +.fa-power-off:before { + content: "\f011"; } + +.fa-pray:before { + content: "\f683"; } + +.fa-praying-hands:before { + content: "\f684"; } + +.fa-prescription:before { + content: "\f5b1"; } + +.fa-prescription-bottle:before { + content: "\f485"; } + +.fa-prescription-bottle-alt:before { + content: "\f486"; } + +.fa-print:before { + content: "\f02f"; } + +.fa-procedures:before { + content: "\f487"; } + +.fa-product-hunt:before { + content: "\f288"; } + +.fa-project-diagram:before { + content: "\f542"; } + +.fa-pump-medical:before { + content: "\f96a"; } + +.fa-pump-soap:before { + content: "\f96b"; } + +.fa-pushed:before { + content: "\f3e1"; } + +.fa-puzzle-piece:before { + content: "\f12e"; } + +.fa-python:before { + content: "\f3e2"; } + +.fa-qq:before { + content: "\f1d6"; } + +.fa-qrcode:before { + content: "\f029"; } + +.fa-question:before { + content: "\f128"; } + +.fa-question-circle:before { + content: "\f059"; } + +.fa-quidditch:before { + content: "\f458"; } + +.fa-quinscape:before { + content: "\f459"; } + +.fa-quora:before { + content: "\f2c4"; } + +.fa-quote-left:before { + content: "\f10d"; } + +.fa-quote-right:before { + content: "\f10e"; } + +.fa-quran:before { + content: "\f687"; } + +.fa-r-project:before { + content: "\f4f7"; } + +.fa-radiation:before { + content: "\f7b9"; } + +.fa-radiation-alt:before { + content: "\f7ba"; } + +.fa-rainbow:before { + content: "\f75b"; } + +.fa-random:before { + content: "\f074"; } + +.fa-raspberry-pi:before { + content: "\f7bb"; } + +.fa-ravelry:before { + content: "\f2d9"; } + +.fa-react:before { + content: "\f41b"; } + +.fa-reacteurope:before { + content: "\f75d"; } + +.fa-readme:before { + content: "\f4d5"; } + +.fa-rebel:before { + content: "\f1d0"; } + +.fa-receipt:before { + content: "\f543"; } + +.fa-record-vinyl:before { + content: "\f8d9"; } + +.fa-recycle:before { + content: "\f1b8"; } + +.fa-red-river:before { + content: "\f3e3"; } + +.fa-reddit:before { + content: "\f1a1"; } + +.fa-reddit-alien:before { + content: "\f281"; } + +.fa-reddit-square:before { + content: "\f1a2"; } + +.fa-redhat:before { + content: "\f7bc"; } + +.fa-redo:before { + content: "\f01e"; } + +.fa-redo-alt:before { + content: "\f2f9"; } + +.fa-registered:before { + content: "\f25d"; } + +.fa-remove-format:before { + content: "\f87d"; } + +.fa-renren:before { + content: "\f18b"; } + +.fa-reply:before { + content: "\f3e5"; } + +.fa-reply-all:before { + content: "\f122"; } + +.fa-replyd:before { + content: "\f3e6"; } + +.fa-republican:before { + content: "\f75e"; } + +.fa-researchgate:before { + content: "\f4f8"; } + +.fa-resolving:before { + content: "\f3e7"; } + +.fa-restroom:before { + content: "\f7bd"; } + +.fa-retweet:before { + content: "\f079"; } + +.fa-rev:before { + content: "\f5b2"; } + +.fa-ribbon:before { + content: "\f4d6"; } + +.fa-ring:before { + content: "\f70b"; } + +.fa-road:before { + content: "\f018"; } + +.fa-robot:before { + content: "\f544"; } + +.fa-rocket:before { + content: "\f135"; } + +.fa-rocketchat:before { + content: "\f3e8"; } + +.fa-rockrms:before { + content: "\f3e9"; } + +.fa-route:before { + content: "\f4d7"; } + +.fa-rss:before { + content: "\f09e"; } + +.fa-rss-square:before { + content: "\f143"; } + +.fa-ruble-sign:before { + content: "\f158"; } + +.fa-ruler:before { + content: "\f545"; } + +.fa-ruler-combined:before { + content: "\f546"; } + +.fa-ruler-horizontal:before { + content: "\f547"; } + +.fa-ruler-vertical:before { + content: "\f548"; } + +.fa-running:before { + content: "\f70c"; } + +.fa-rupee-sign:before { + content: "\f156"; } + +.fa-sad-cry:before { + content: "\f5b3"; } + +.fa-sad-tear:before { + content: "\f5b4"; } + +.fa-safari:before { + content: "\f267"; } + +.fa-salesforce:before { + content: "\f83b"; } + +.fa-sass:before { + content: "\f41e"; } + +.fa-satellite:before { + content: "\f7bf"; } + +.fa-satellite-dish:before { + content: "\f7c0"; } + +.fa-save:before { + content: "\f0c7"; } + +.fa-schlix:before { + content: "\f3ea"; } + +.fa-school:before { + content: "\f549"; } + +.fa-screwdriver:before { + content: "\f54a"; } + +.fa-scribd:before { + content: "\f28a"; } + +.fa-scroll:before { + content: "\f70e"; } + +.fa-sd-card:before { + content: "\f7c2"; } + +.fa-search:before { + content: "\f002"; } + +.fa-search-dollar:before { + content: "\f688"; } + +.fa-search-location:before { + content: "\f689"; } + +.fa-search-minus:before { + content: "\f010"; } + +.fa-search-plus:before { + content: "\f00e"; } + +.fa-searchengin:before { + content: "\f3eb"; } + +.fa-seedling:before { + content: "\f4d8"; } + +.fa-sellcast:before { + content: "\f2da"; } + +.fa-sellsy:before { + content: "\f213"; } + +.fa-server:before { + content: "\f233"; } + +.fa-servicestack:before { + content: "\f3ec"; } + +.fa-shapes:before { + content: "\f61f"; } + +.fa-share:before { + content: "\f064"; } + +.fa-share-alt:before { + content: "\f1e0"; } + +.fa-share-alt-square:before { + content: "\f1e1"; } + +.fa-share-square:before { + content: "\f14d"; } + +.fa-shekel-sign:before { + content: "\f20b"; } + +.fa-shield-alt:before { + content: "\f3ed"; } + +.fa-shield-virus:before { + content: "\f96c"; } + +.fa-ship:before { + content: "\f21a"; } + +.fa-shipping-fast:before { + content: "\f48b"; } + +.fa-shirtsinbulk:before { + content: "\f214"; } + +.fa-shoe-prints:before { + content: "\f54b"; } + +.fa-shopify:before { + content: "\f957"; } + +.fa-shopping-bag:before { + content: "\f290"; } + +.fa-shopping-basket:before { + content: "\f291"; } + +.fa-shopping-cart:before { + content: "\f07a"; } + +.fa-shopware:before { + content: "\f5b5"; } + +.fa-shower:before { + content: "\f2cc"; } + +.fa-shuttle-van:before { + content: "\f5b6"; } + +.fa-sign:before { + content: "\f4d9"; } + +.fa-sign-in-alt:before { + content: "\f2f6"; } + +.fa-sign-language:before { + content: "\f2a7"; } + +.fa-sign-out-alt:before { + content: "\f2f5"; } + +.fa-signal:before { + content: "\f012"; } + +.fa-signature:before { + content: "\f5b7"; } + +.fa-sim-card:before { + content: "\f7c4"; } + +.fa-simplybuilt:before { + content: "\f215"; } + +.fa-sistrix:before { + content: "\f3ee"; } + +.fa-sitemap:before { + content: "\f0e8"; } + +.fa-sith:before { + content: "\f512"; } + +.fa-skating:before { + content: "\f7c5"; } + +.fa-sketch:before { + content: "\f7c6"; } + +.fa-skiing:before { + content: "\f7c9"; } + +.fa-skiing-nordic:before { + content: "\f7ca"; } + +.fa-skull:before { + content: "\f54c"; } + +.fa-skull-crossbones:before { + content: "\f714"; } + +.fa-skyatlas:before { + content: "\f216"; } + +.fa-skype:before { + content: "\f17e"; } + +.fa-slack:before { + content: "\f198"; } + +.fa-slack-hash:before { + content: "\f3ef"; } + +.fa-slash:before { + content: "\f715"; } + +.fa-sleigh:before { + content: "\f7cc"; } + +.fa-sliders-h:before { + content: "\f1de"; } + +.fa-slideshare:before { + content: "\f1e7"; } + +.fa-smile:before { + content: "\f118"; } + +.fa-smile-beam:before { + content: "\f5b8"; } + +.fa-smile-wink:before { + content: "\f4da"; } + +.fa-smog:before { + content: "\f75f"; } + +.fa-smoking:before { + content: "\f48d"; } + +.fa-smoking-ban:before { + content: "\f54d"; } + +.fa-sms:before { + content: "\f7cd"; } + +.fa-snapchat:before { + content: "\f2ab"; } + +.fa-snapchat-ghost:before { + content: "\f2ac"; } + +.fa-snapchat-square:before { + content: "\f2ad"; } + +.fa-snowboarding:before { + content: "\f7ce"; } + +.fa-snowflake:before { + content: "\f2dc"; } + +.fa-snowman:before { + content: "\f7d0"; } + +.fa-snowplow:before { + content: "\f7d2"; } + +.fa-soap:before { + content: "\f96e"; } + +.fa-socks:before { + content: "\f696"; } + +.fa-solar-panel:before { + content: "\f5ba"; } + +.fa-sort:before { + content: "\f0dc"; } + +.fa-sort-alpha-down:before { + content: "\f15d"; } + +.fa-sort-alpha-down-alt:before { + content: "\f881"; } + +.fa-sort-alpha-up:before { + content: "\f15e"; } + +.fa-sort-alpha-up-alt:before { + content: "\f882"; } + +.fa-sort-amount-down:before { + content: "\f160"; } + +.fa-sort-amount-down-alt:before { + content: "\f884"; } + +.fa-sort-amount-up:before { + content: "\f161"; } + +.fa-sort-amount-up-alt:before { + content: "\f885"; } + +.fa-sort-down:before { + content: "\f0dd"; } + +.fa-sort-numeric-down:before { + content: "\f162"; } + +.fa-sort-numeric-down-alt:before { + content: "\f886"; } + +.fa-sort-numeric-up:before { + content: "\f163"; } + +.fa-sort-numeric-up-alt:before { + content: "\f887"; } + +.fa-sort-up:before { + content: "\f0de"; } + +.fa-soundcloud:before { + content: "\f1be"; } + +.fa-sourcetree:before { + content: "\f7d3"; } + +.fa-spa:before { + content: "\f5bb"; } + +.fa-space-shuttle:before { + content: "\f197"; } + +.fa-speakap:before { + content: "\f3f3"; } + +.fa-speaker-deck:before { + content: "\f83c"; } + +.fa-spell-check:before { + content: "\f891"; } + +.fa-spider:before { + content: "\f717"; } + +.fa-spinner:before { + content: "\f110"; } + +.fa-splotch:before { + content: "\f5bc"; } + +.fa-spotify:before { + content: "\f1bc"; } + +.fa-spray-can:before { + content: "\f5bd"; } + +.fa-square:before { + content: "\f0c8"; } + +.fa-square-full:before { + content: "\f45c"; } + +.fa-square-root-alt:before { + content: "\f698"; } + +.fa-squarespace:before { + content: "\f5be"; } + +.fa-stack-exchange:before { + content: "\f18d"; } + +.fa-stack-overflow:before { + content: "\f16c"; } + +.fa-stackpath:before { + content: "\f842"; } + +.fa-stamp:before { + content: "\f5bf"; } + +.fa-star:before { + content: "\f005"; } + +.fa-star-and-crescent:before { + content: "\f699"; } + +.fa-star-half:before { + content: "\f089"; } + +.fa-star-half-alt:before { + content: "\f5c0"; } + +.fa-star-of-david:before { + content: "\f69a"; } + +.fa-star-of-life:before { + content: "\f621"; } + +.fa-staylinked:before { + content: "\f3f5"; } + +.fa-steam:before { + content: "\f1b6"; } + +.fa-steam-square:before { + content: "\f1b7"; } + +.fa-steam-symbol:before { + content: "\f3f6"; } + +.fa-step-backward:before { + content: "\f048"; } + +.fa-step-forward:before { + content: "\f051"; } + +.fa-stethoscope:before { + content: "\f0f1"; } + +.fa-sticker-mule:before { + content: "\f3f7"; } + +.fa-sticky-note:before { + content: "\f249"; } + +.fa-stop:before { + content: "\f04d"; } + +.fa-stop-circle:before { + content: "\f28d"; } + +.fa-stopwatch:before { + content: "\f2f2"; } + +.fa-stopwatch-20:before { + content: "\f96f"; } + +.fa-store:before { + content: "\f54e"; } + +.fa-store-alt:before { + content: "\f54f"; } + +.fa-store-alt-slash:before { + content: "\f970"; } + +.fa-store-slash:before { + content: "\f971"; } + +.fa-strava:before { + content: "\f428"; } + +.fa-stream:before { + content: "\f550"; } + +.fa-street-view:before { + content: "\f21d"; } + +.fa-strikethrough:before { + content: "\f0cc"; } + +.fa-stripe:before { + content: "\f429"; } + +.fa-stripe-s:before { + content: "\f42a"; } + +.fa-stroopwafel:before { + content: "\f551"; } + +.fa-studiovinari:before { + content: "\f3f8"; } + +.fa-stumbleupon:before { + content: "\f1a4"; } + +.fa-stumbleupon-circle:before { + content: "\f1a3"; } + +.fa-subscript:before { + content: "\f12c"; } + +.fa-subway:before { + content: "\f239"; } + +.fa-suitcase:before { + content: "\f0f2"; } + +.fa-suitcase-rolling:before { + content: "\f5c1"; } + +.fa-sun:before { + content: "\f185"; } + +.fa-superpowers:before { + content: "\f2dd"; } + +.fa-superscript:before { + content: "\f12b"; } + +.fa-supple:before { + content: "\f3f9"; } + +.fa-surprise:before { + content: "\f5c2"; } + +.fa-suse:before { + content: "\f7d6"; } + +.fa-swatchbook:before { + content: "\f5c3"; } + +.fa-swift:before { + content: "\f8e1"; } + +.fa-swimmer:before { + content: "\f5c4"; } + +.fa-swimming-pool:before { + content: "\f5c5"; } + +.fa-symfony:before { + content: "\f83d"; } + +.fa-synagogue:before { + content: "\f69b"; } + +.fa-sync:before { + content: "\f021"; } + +.fa-sync-alt:before { + content: "\f2f1"; } + +.fa-syringe:before { + content: "\f48e"; } + +.fa-table:before { + content: "\f0ce"; } + +.fa-table-tennis:before { + content: "\f45d"; } + +.fa-tablet:before { + content: "\f10a"; } + +.fa-tablet-alt:before { + content: "\f3fa"; } + +.fa-tablets:before { + content: "\f490"; } + +.fa-tachometer-alt:before { + content: "\f3fd"; } + +.fa-tag:before { + content: "\f02b"; } + +.fa-tags:before { + content: "\f02c"; } + +.fa-tape:before { + content: "\f4db"; } + +.fa-tasks:before { + content: "\f0ae"; } + +.fa-taxi:before { + content: "\f1ba"; } + +.fa-teamspeak:before { + content: "\f4f9"; } + +.fa-teeth:before { + content: "\f62e"; } + +.fa-teeth-open:before { + content: "\f62f"; } + +.fa-telegram:before { + content: "\f2c6"; } + +.fa-telegram-plane:before { + content: "\f3fe"; } + +.fa-temperature-high:before { + content: "\f769"; } + +.fa-temperature-low:before { + content: "\f76b"; } + +.fa-tencent-weibo:before { + content: "\f1d5"; } + +.fa-tenge:before { + content: "\f7d7"; } + +.fa-terminal:before { + content: "\f120"; } + +.fa-text-height:before { + content: "\f034"; } + +.fa-text-width:before { + content: "\f035"; } + +.fa-th:before { + content: "\f00a"; } + +.fa-th-large:before { + content: "\f009"; } + +.fa-th-list:before { + content: "\f00b"; } + +.fa-the-red-yeti:before { + content: "\f69d"; } + +.fa-theater-masks:before { + content: "\f630"; } + +.fa-themeco:before { + content: "\f5c6"; } + +.fa-themeisle:before { + content: "\f2b2"; } + +.fa-thermometer:before { + content: "\f491"; } + +.fa-thermometer-empty:before { + content: "\f2cb"; } + +.fa-thermometer-full:before { + content: "\f2c7"; } + +.fa-thermometer-half:before { + content: "\f2c9"; } + +.fa-thermometer-quarter:before { + content: "\f2ca"; } + +.fa-thermometer-three-quarters:before { + content: "\f2c8"; } + +.fa-think-peaks:before { + content: "\f731"; } + +.fa-thumbs-down:before { + content: "\f165"; } + +.fa-thumbs-up:before { + content: "\f164"; } + +.fa-thumbtack:before { + content: "\f08d"; } + +.fa-ticket-alt:before { + content: "\f3ff"; } + +.fa-times:before { + content: "\f00d"; } + +.fa-times-circle:before { + content: "\f057"; } + +.fa-tint:before { + content: "\f043"; } + +.fa-tint-slash:before { + content: "\f5c7"; } + +.fa-tired:before { + content: "\f5c8"; } + +.fa-toggle-off:before { + content: "\f204"; } + +.fa-toggle-on:before { + content: "\f205"; } + +.fa-toilet:before { + content: "\f7d8"; } + +.fa-toilet-paper:before { + content: "\f71e"; } + +.fa-toilet-paper-slash:before { + content: "\f972"; } + +.fa-toolbox:before { + content: "\f552"; } + +.fa-tools:before { + content: "\f7d9"; } + +.fa-tooth:before { + content: "\f5c9"; } + +.fa-torah:before { + content: "\f6a0"; } + +.fa-torii-gate:before { + content: "\f6a1"; } + +.fa-tractor:before { + content: "\f722"; } + +.fa-trade-federation:before { + content: "\f513"; } + +.fa-trademark:before { + content: "\f25c"; } + +.fa-traffic-light:before { + content: "\f637"; } + +.fa-trailer:before { + content: "\f941"; } + +.fa-train:before { + content: "\f238"; } + +.fa-tram:before { + content: "\f7da"; } + +.fa-transgender:before { + content: "\f224"; } + +.fa-transgender-alt:before { + content: "\f225"; } + +.fa-trash:before { + content: "\f1f8"; } + +.fa-trash-alt:before { + content: "\f2ed"; } + +.fa-trash-restore:before { + content: "\f829"; } + +.fa-trash-restore-alt:before { + content: "\f82a"; } + +.fa-tree:before { + content: "\f1bb"; } + +.fa-trello:before { + content: "\f181"; } + +.fa-tripadvisor:before { + content: "\f262"; } + +.fa-trophy:before { + content: "\f091"; } + +.fa-truck:before { + content: "\f0d1"; } + +.fa-truck-loading:before { + content: "\f4de"; } + +.fa-truck-monster:before { + content: "\f63b"; } + +.fa-truck-moving:before { + content: "\f4df"; } + +.fa-truck-pickup:before { + content: "\f63c"; } + +.fa-tshirt:before { + content: "\f553"; } + +.fa-tty:before { + content: "\f1e4"; } + +.fa-tumblr:before { + content: "\f173"; } + +.fa-tumblr-square:before { + content: "\f174"; } + +.fa-tv:before { + content: "\f26c"; } + +.fa-twitch:before { + content: "\f1e8"; } + +.fa-twitter:before { + content: "\f099"; } + +.fa-twitter-square:before { + content: "\f081"; } + +.fa-typo3:before { + content: "\f42b"; } + +.fa-uber:before { + content: "\f402"; } + +.fa-ubuntu:before { + content: "\f7df"; } + +.fa-uikit:before { + content: "\f403"; } + +.fa-umbraco:before { + content: "\f8e8"; } + +.fa-umbrella:before { + content: "\f0e9"; } + +.fa-umbrella-beach:before { + content: "\f5ca"; } + +.fa-underline:before { + content: "\f0cd"; } + +.fa-undo:before { + content: "\f0e2"; } + +.fa-undo-alt:before { + content: "\f2ea"; } + +.fa-uniregistry:before { + content: "\f404"; } + +.fa-unity:before { + content: "\f949"; } + +.fa-universal-access:before { + content: "\f29a"; } + +.fa-university:before { + content: "\f19c"; } + +.fa-unlink:before { + content: "\f127"; } + +.fa-unlock:before { + content: "\f09c"; } + +.fa-unlock-alt:before { + content: "\f13e"; } + +.fa-untappd:before { + content: "\f405"; } + +.fa-upload:before { + content: "\f093"; } + +.fa-ups:before { + content: "\f7e0"; } + +.fa-usb:before { + content: "\f287"; } + +.fa-user:before { + content: "\f007"; } + +.fa-user-alt:before { + content: "\f406"; } + +.fa-user-alt-slash:before { + content: "\f4fa"; } + +.fa-user-astronaut:before { + content: "\f4fb"; } + +.fa-user-check:before { + content: "\f4fc"; } + +.fa-user-circle:before { + content: "\f2bd"; } + +.fa-user-clock:before { + content: "\f4fd"; } + +.fa-user-cog:before { + content: "\f4fe"; } + +.fa-user-edit:before { + content: "\f4ff"; } + +.fa-user-friends:before { + content: "\f500"; } + +.fa-user-graduate:before { + content: "\f501"; } + +.fa-user-injured:before { + content: "\f728"; } + +.fa-user-lock:before { + content: "\f502"; } + +.fa-user-md:before { + content: "\f0f0"; } + +.fa-user-minus:before { + content: "\f503"; } + +.fa-user-ninja:before { + content: "\f504"; } + +.fa-user-nurse:before { + content: "\f82f"; } + +.fa-user-plus:before { + content: "\f234"; } + +.fa-user-secret:before { + content: "\f21b"; } + +.fa-user-shield:before { + content: "\f505"; } + +.fa-user-slash:before { + content: "\f506"; } + +.fa-user-tag:before { + content: "\f507"; } + +.fa-user-tie:before { + content: "\f508"; } + +.fa-user-times:before { + content: "\f235"; } + +.fa-users:before { + content: "\f0c0"; } + +.fa-users-cog:before { + content: "\f509"; } + +.fa-usps:before { + content: "\f7e1"; } + +.fa-ussunnah:before { + content: "\f407"; } + +.fa-utensil-spoon:before { + content: "\f2e5"; } + +.fa-utensils:before { + content: "\f2e7"; } + +.fa-vaadin:before { + content: "\f408"; } + +.fa-vector-square:before { + content: "\f5cb"; } + +.fa-venus:before { + content: "\f221"; } + +.fa-venus-double:before { + content: "\f226"; } + +.fa-venus-mars:before { + content: "\f228"; } + +.fa-viacoin:before { + content: "\f237"; } + +.fa-viadeo:before { + content: "\f2a9"; } + +.fa-viadeo-square:before { + content: "\f2aa"; } + +.fa-vial:before { + content: "\f492"; } + +.fa-vials:before { + content: "\f493"; } + +.fa-viber:before { + content: "\f409"; } + +.fa-video:before { + content: "\f03d"; } + +.fa-video-slash:before { + content: "\f4e2"; } + +.fa-vihara:before { + content: "\f6a7"; } + +.fa-vimeo:before { + content: "\f40a"; } + +.fa-vimeo-square:before { + content: "\f194"; } + +.fa-vimeo-v:before { + content: "\f27d"; } + +.fa-vine:before { + content: "\f1ca"; } + +.fa-virus:before { + content: "\f974"; } + +.fa-virus-slash:before { + content: "\f975"; } + +.fa-viruses:before { + content: "\f976"; } + +.fa-vk:before { + content: "\f189"; } + +.fa-vnv:before { + content: "\f40b"; } + +.fa-voicemail:before { + content: "\f897"; } + +.fa-volleyball-ball:before { + content: "\f45f"; } + +.fa-volume-down:before { + content: "\f027"; } + +.fa-volume-mute:before { + content: "\f6a9"; } + +.fa-volume-off:before { + content: "\f026"; } + +.fa-volume-up:before { + content: "\f028"; } + +.fa-vote-yea:before { + content: "\f772"; } + +.fa-vr-cardboard:before { + content: "\f729"; } + +.fa-vuejs:before { + content: "\f41f"; } + +.fa-walking:before { + content: "\f554"; } + +.fa-wallet:before { + content: "\f555"; } + +.fa-warehouse:before { + content: "\f494"; } + +.fa-water:before { + content: "\f773"; } + +.fa-wave-square:before { + content: "\f83e"; } + +.fa-waze:before { + content: "\f83f"; } + +.fa-weebly:before { + content: "\f5cc"; } + +.fa-weibo:before { + content: "\f18a"; } + +.fa-weight:before { + content: "\f496"; } + +.fa-weight-hanging:before { + content: "\f5cd"; } + +.fa-weixin:before { + content: "\f1d7"; } + +.fa-whatsapp:before { + content: "\f232"; } + +.fa-whatsapp-square:before { + content: "\f40c"; } + +.fa-wheelchair:before { + content: "\f193"; } + +.fa-whmcs:before { + content: "\f40d"; } + +.fa-wifi:before { + content: "\f1eb"; } + +.fa-wikipedia-w:before { + content: "\f266"; } + +.fa-wind:before { + content: "\f72e"; } + +.fa-window-close:before { + content: "\f410"; } + +.fa-window-maximize:before { + content: "\f2d0"; } + +.fa-window-minimize:before { + content: "\f2d1"; } + +.fa-window-restore:before { + content: "\f2d2"; } + +.fa-windows:before { + content: "\f17a"; } + +.fa-wine-bottle:before { + content: "\f72f"; } + +.fa-wine-glass:before { + content: "\f4e3"; } + +.fa-wine-glass-alt:before { + content: "\f5ce"; } + +.fa-wix:before { + content: "\f5cf"; } + +.fa-wizards-of-the-coast:before { + content: "\f730"; } + +.fa-wolf-pack-battalion:before { + content: "\f514"; } + +.fa-won-sign:before { + content: "\f159"; } + +.fa-wordpress:before { + content: "\f19a"; } + +.fa-wordpress-simple:before { + content: "\f411"; } + +.fa-wpbeginner:before { + content: "\f297"; } + +.fa-wpexplorer:before { + content: "\f2de"; } + +.fa-wpforms:before { + content: "\f298"; } + +.fa-wpressr:before { + content: "\f3e4"; } + +.fa-wrench:before { + content: "\f0ad"; } + +.fa-x-ray:before { + content: "\f497"; } + +.fa-xbox:before { + content: "\f412"; } + +.fa-xing:before { + content: "\f168"; } + +.fa-xing-square:before { + content: "\f169"; } + +.fa-y-combinator:before { + content: "\f23b"; } + +.fa-yahoo:before { + content: "\f19e"; } + +.fa-yammer:before { + content: "\f840"; } + +.fa-yandex:before { + content: "\f413"; } + +.fa-yandex-international:before { + content: "\f414"; } + +.fa-yarn:before { + content: "\f7e3"; } + +.fa-yelp:before { + content: "\f1e9"; } + +.fa-yen-sign:before { + content: "\f157"; } + +.fa-yin-yang:before { + content: "\f6ad"; } + +.fa-yoast:before { + content: "\f2b1"; } + +.fa-youtube:before { + content: "\f167"; } + +.fa-youtube-square:before { + content: "\f431"; } + +.fa-zhihu:before { + content: "\f63f"; } + +.sr-only { + border: 0; + clip: rect(0, 0, 0, 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; } + +.sr-only-focusable:active, .sr-only-focusable:focus { + clip: auto; + height: auto; + margin: 0; + overflow: visible; + position: static; + width: auto; } +@font-face { + font-family: 'Font Awesome 5 Brands'; + font-style: normal; + font-weight: 400; + font-display: block; + src: url("../webfonts/fa-brands-400.eot"); + src: url("../webfonts/fa-brands-400.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.woff") format("woff"), url("../webfonts/fa-brands-400.ttf") format("truetype"), url("../webfonts/fa-brands-400.svg#fontawesome") format("svg"); } + +.fab { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } +@font-face { + font-family: 'Font Awesome 5 Free'; + font-style: normal; + font-weight: 400; + font-display: block; + src: url("../webfonts/fa-regular-400.eot"); + src: url("../webfonts/fa-regular-400.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.woff") format("woff"), url("../webfonts/fa-regular-400.ttf") format("truetype"), url("../webfonts/fa-regular-400.svg#fontawesome") format("svg"); } + +.far { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } +@font-face { + font-family: 'Font Awesome 5 Free'; + font-style: normal; + font-weight: 900; + font-display: block; + src: url("../webfonts/fa-solid-900.eot"); + src: url("../webfonts/fa-solid-900.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.woff") format("woff"), url("../webfonts/fa-solid-900.ttf") format("truetype"), url("../webfonts/fa-solid-900.svg#fontawesome") format("svg"); } + +.fa, +.fas { + font-family: 'Font Awesome 5 Free'; + font-weight: 900; } diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/css/v4-shims.css b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/css/v4-shims.css new file mode 100644 index 0000000000..1070fbe73e --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/css/v4-shims.css @@ -0,0 +1,2172 @@ +/*! + * Font Awesome Free 5.13.0 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + */ +.fa.fa-glass:before { + content: "\f000"; } + +.fa.fa-meetup { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-star-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-star-o:before { + content: "\f005"; } + +.fa.fa-remove:before { + content: "\f00d"; } + +.fa.fa-close:before { + content: "\f00d"; } + +.fa.fa-gear:before { + content: "\f013"; } + +.fa.fa-trash-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-trash-o:before { + content: "\f2ed"; } + +.fa.fa-file-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-file-o:before { + content: "\f15b"; } + +.fa.fa-clock-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-clock-o:before { + content: "\f017"; } + +.fa.fa-arrow-circle-o-down { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-arrow-circle-o-down:before { + content: "\f358"; } + +.fa.fa-arrow-circle-o-up { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-arrow-circle-o-up:before { + content: "\f35b"; } + +.fa.fa-play-circle-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-play-circle-o:before { + content: "\f144"; } + +.fa.fa-repeat:before { + content: "\f01e"; } + +.fa.fa-rotate-right:before { + content: "\f01e"; } + +.fa.fa-refresh:before { + content: "\f021"; } + +.fa.fa-list-alt { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-dedent:before { + content: "\f03b"; } + +.fa.fa-video-camera:before { + content: "\f03d"; } + +.fa.fa-picture-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-picture-o:before { + content: "\f03e"; } + +.fa.fa-photo { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-photo:before { + content: "\f03e"; } + +.fa.fa-image { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-image:before { + content: "\f03e"; } + +.fa.fa-pencil:before { + content: "\f303"; } + +.fa.fa-map-marker:before { + content: "\f3c5"; } + +.fa.fa-pencil-square-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-pencil-square-o:before { + content: "\f044"; } + +.fa.fa-share-square-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-share-square-o:before { + content: "\f14d"; } + +.fa.fa-check-square-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-check-square-o:before { + content: "\f14a"; } + +.fa.fa-arrows:before { + content: "\f0b2"; } + +.fa.fa-times-circle-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-times-circle-o:before { + content: "\f057"; } + +.fa.fa-check-circle-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-check-circle-o:before { + content: "\f058"; } + +.fa.fa-mail-forward:before { + content: "\f064"; } + +.fa.fa-expand:before { + content: "\f424"; } + +.fa.fa-compress:before { + content: "\f422"; } + +.fa.fa-eye { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-eye-slash { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-warning:before { + content: "\f071"; } + +.fa.fa-calendar:before { + content: "\f073"; } + +.fa.fa-arrows-v:before { + content: "\f338"; } + +.fa.fa-arrows-h:before { + content: "\f337"; } + +.fa.fa-bar-chart { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-bar-chart:before { + content: "\f080"; } + +.fa.fa-bar-chart-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-bar-chart-o:before { + content: "\f080"; } + +.fa.fa-twitter-square { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-facebook-square { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-gears:before { + content: "\f085"; } + +.fa.fa-thumbs-o-up { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-thumbs-o-up:before { + content: "\f164"; } + +.fa.fa-thumbs-o-down { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-thumbs-o-down:before { + content: "\f165"; } + +.fa.fa-heart-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-heart-o:before { + content: "\f004"; } + +.fa.fa-sign-out:before { + content: "\f2f5"; } + +.fa.fa-linkedin-square { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-linkedin-square:before { + content: "\f08c"; } + +.fa.fa-thumb-tack:before { + content: "\f08d"; } + +.fa.fa-external-link:before { + content: "\f35d"; } + +.fa.fa-sign-in:before { + content: "\f2f6"; } + +.fa.fa-github-square { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-lemon-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-lemon-o:before { + content: "\f094"; } + +.fa.fa-square-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-square-o:before { + content: "\f0c8"; } + +.fa.fa-bookmark-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-bookmark-o:before { + content: "\f02e"; } + +.fa.fa-twitter { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-facebook { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-facebook:before { + content: "\f39e"; } + +.fa.fa-facebook-f { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-facebook-f:before { + content: "\f39e"; } + +.fa.fa-github { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-credit-card { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-feed:before { + content: "\f09e"; } + +.fa.fa-hdd-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-hdd-o:before { + content: "\f0a0"; } + +.fa.fa-hand-o-right { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-hand-o-right:before { + content: "\f0a4"; } + +.fa.fa-hand-o-left { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-hand-o-left:before { + content: "\f0a5"; } + +.fa.fa-hand-o-up { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-hand-o-up:before { + content: "\f0a6"; } + +.fa.fa-hand-o-down { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-hand-o-down:before { + content: "\f0a7"; } + +.fa.fa-arrows-alt:before { + content: "\f31e"; } + +.fa.fa-group:before { + content: "\f0c0"; } + +.fa.fa-chain:before { + content: "\f0c1"; } + +.fa.fa-scissors:before { + content: "\f0c4"; } + +.fa.fa-files-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-files-o:before { + content: "\f0c5"; } + +.fa.fa-floppy-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-floppy-o:before { + content: "\f0c7"; } + +.fa.fa-navicon:before { + content: "\f0c9"; } + +.fa.fa-reorder:before { + content: "\f0c9"; } + +.fa.fa-pinterest { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-pinterest-square { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-google-plus-square { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-google-plus { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-google-plus:before { + content: "\f0d5"; } + +.fa.fa-money { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-money:before { + content: "\f3d1"; } + +.fa.fa-unsorted:before { + content: "\f0dc"; } + +.fa.fa-sort-desc:before { + content: "\f0dd"; } + +.fa.fa-sort-asc:before { + content: "\f0de"; } + +.fa.fa-linkedin { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-linkedin:before { + content: "\f0e1"; } + +.fa.fa-rotate-left:before { + content: "\f0e2"; } + +.fa.fa-legal:before { + content: "\f0e3"; } + +.fa.fa-tachometer:before { + content: "\f3fd"; } + +.fa.fa-dashboard:before { + content: "\f3fd"; } + +.fa.fa-comment-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-comment-o:before { + content: "\f075"; } + +.fa.fa-comments-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-comments-o:before { + content: "\f086"; } + +.fa.fa-flash:before { + content: "\f0e7"; } + +.fa.fa-clipboard { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-paste { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-paste:before { + content: "\f328"; } + +.fa.fa-lightbulb-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-lightbulb-o:before { + content: "\f0eb"; } + +.fa.fa-exchange:before { + content: "\f362"; } + +.fa.fa-cloud-download:before { + content: "\f381"; } + +.fa.fa-cloud-upload:before { + content: "\f382"; } + +.fa.fa-bell-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-bell-o:before { + content: "\f0f3"; } + +.fa.fa-cutlery:before { + content: "\f2e7"; } + +.fa.fa-file-text-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-file-text-o:before { + content: "\f15c"; } + +.fa.fa-building-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-building-o:before { + content: "\f1ad"; } + +.fa.fa-hospital-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-hospital-o:before { + content: "\f0f8"; } + +.fa.fa-tablet:before { + content: "\f3fa"; } + +.fa.fa-mobile:before { + content: "\f3cd"; } + +.fa.fa-mobile-phone:before { + content: "\f3cd"; } + +.fa.fa-circle-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-circle-o:before { + content: "\f111"; } + +.fa.fa-mail-reply:before { + content: "\f3e5"; } + +.fa.fa-github-alt { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-folder-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-folder-o:before { + content: "\f07b"; } + +.fa.fa-folder-open-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-folder-open-o:before { + content: "\f07c"; } + +.fa.fa-smile-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-smile-o:before { + content: "\f118"; } + +.fa.fa-frown-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-frown-o:before { + content: "\f119"; } + +.fa.fa-meh-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-meh-o:before { + content: "\f11a"; } + +.fa.fa-keyboard-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-keyboard-o:before { + content: "\f11c"; } + +.fa.fa-flag-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-flag-o:before { + content: "\f024"; } + +.fa.fa-mail-reply-all:before { + content: "\f122"; } + +.fa.fa-star-half-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-star-half-o:before { + content: "\f089"; } + +.fa.fa-star-half-empty { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-star-half-empty:before { + content: "\f089"; } + +.fa.fa-star-half-full { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-star-half-full:before { + content: "\f089"; } + +.fa.fa-code-fork:before { + content: "\f126"; } + +.fa.fa-chain-broken:before { + content: "\f127"; } + +.fa.fa-shield:before { + content: "\f3ed"; } + +.fa.fa-calendar-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-calendar-o:before { + content: "\f133"; } + +.fa.fa-maxcdn { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-html5 { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-css3 { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-ticket:before { + content: "\f3ff"; } + +.fa.fa-minus-square-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-minus-square-o:before { + content: "\f146"; } + +.fa.fa-level-up:before { + content: "\f3bf"; } + +.fa.fa-level-down:before { + content: "\f3be"; } + +.fa.fa-pencil-square:before { + content: "\f14b"; } + +.fa.fa-external-link-square:before { + content: "\f360"; } + +.fa.fa-compass { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-caret-square-o-down { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-caret-square-o-down:before { + content: "\f150"; } + +.fa.fa-toggle-down { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-toggle-down:before { + content: "\f150"; } + +.fa.fa-caret-square-o-up { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-caret-square-o-up:before { + content: "\f151"; } + +.fa.fa-toggle-up { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-toggle-up:before { + content: "\f151"; } + +.fa.fa-caret-square-o-right { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-caret-square-o-right:before { + content: "\f152"; } + +.fa.fa-toggle-right { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-toggle-right:before { + content: "\f152"; } + +.fa.fa-eur:before { + content: "\f153"; } + +.fa.fa-euro:before { + content: "\f153"; } + +.fa.fa-gbp:before { + content: "\f154"; } + +.fa.fa-usd:before { + content: "\f155"; } + +.fa.fa-dollar:before { + content: "\f155"; } + +.fa.fa-inr:before { + content: "\f156"; } + +.fa.fa-rupee:before { + content: "\f156"; } + +.fa.fa-jpy:before { + content: "\f157"; } + +.fa.fa-cny:before { + content: "\f157"; } + +.fa.fa-rmb:before { + content: "\f157"; } + +.fa.fa-yen:before { + content: "\f157"; } + +.fa.fa-rub:before { + content: "\f158"; } + +.fa.fa-ruble:before { + content: "\f158"; } + +.fa.fa-rouble:before { + content: "\f158"; } + +.fa.fa-krw:before { + content: "\f159"; } + +.fa.fa-won:before { + content: "\f159"; } + +.fa.fa-btc { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-bitcoin { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-bitcoin:before { + content: "\f15a"; } + +.fa.fa-file-text:before { + content: "\f15c"; } + +.fa.fa-sort-alpha-asc:before { + content: "\f15d"; } + +.fa.fa-sort-alpha-desc:before { + content: "\f881"; } + +.fa.fa-sort-amount-asc:before { + content: "\f160"; } + +.fa.fa-sort-amount-desc:before { + content: "\f884"; } + +.fa.fa-sort-numeric-asc:before { + content: "\f162"; } + +.fa.fa-sort-numeric-desc:before { + content: "\f886"; } + +.fa.fa-youtube-square { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-youtube { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-xing { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-xing-square { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-youtube-play { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-youtube-play:before { + content: "\f167"; } + +.fa.fa-dropbox { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-stack-overflow { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-instagram { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-flickr { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-adn { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-bitbucket { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-bitbucket-square { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-bitbucket-square:before { + content: "\f171"; } + +.fa.fa-tumblr { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-tumblr-square { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-long-arrow-down:before { + content: "\f309"; } + +.fa.fa-long-arrow-up:before { + content: "\f30c"; } + +.fa.fa-long-arrow-left:before { + content: "\f30a"; } + +.fa.fa-long-arrow-right:before { + content: "\f30b"; } + +.fa.fa-apple { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-windows { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-android { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-linux { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-dribbble { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-skype { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-foursquare { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-trello { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-gratipay { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-gittip { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-gittip:before { + content: "\f184"; } + +.fa.fa-sun-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-sun-o:before { + content: "\f185"; } + +.fa.fa-moon-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-moon-o:before { + content: "\f186"; } + +.fa.fa-vk { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-weibo { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-renren { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-pagelines { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-stack-exchange { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-arrow-circle-o-right { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-arrow-circle-o-right:before { + content: "\f35a"; } + +.fa.fa-arrow-circle-o-left { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-arrow-circle-o-left:before { + content: "\f359"; } + +.fa.fa-caret-square-o-left { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-caret-square-o-left:before { + content: "\f191"; } + +.fa.fa-toggle-left { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-toggle-left:before { + content: "\f191"; } + +.fa.fa-dot-circle-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-dot-circle-o:before { + content: "\f192"; } + +.fa.fa-vimeo-square { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-try:before { + content: "\f195"; } + +.fa.fa-turkish-lira:before { + content: "\f195"; } + +.fa.fa-plus-square-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-plus-square-o:before { + content: "\f0fe"; } + +.fa.fa-slack { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-wordpress { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-openid { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-institution:before { + content: "\f19c"; } + +.fa.fa-bank:before { + content: "\f19c"; } + +.fa.fa-mortar-board:before { + content: "\f19d"; } + +.fa.fa-yahoo { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-google { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-reddit { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-reddit-square { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-stumbleupon-circle { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-stumbleupon { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-delicious { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-digg { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-pied-piper-pp { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-pied-piper-alt { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-drupal { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-joomla { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-spoon:before { + content: "\f2e5"; } + +.fa.fa-behance { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-behance-square { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-steam { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-steam-square { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-automobile:before { + content: "\f1b9"; } + +.fa.fa-envelope-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-envelope-o:before { + content: "\f0e0"; } + +.fa.fa-spotify { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-deviantart { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-soundcloud { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-file-pdf-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-file-pdf-o:before { + content: "\f1c1"; } + +.fa.fa-file-word-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-file-word-o:before { + content: "\f1c2"; } + +.fa.fa-file-excel-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-file-excel-o:before { + content: "\f1c3"; } + +.fa.fa-file-powerpoint-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-file-powerpoint-o:before { + content: "\f1c4"; } + +.fa.fa-file-image-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-file-image-o:before { + content: "\f1c5"; } + +.fa.fa-file-photo-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-file-photo-o:before { + content: "\f1c5"; } + +.fa.fa-file-picture-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-file-picture-o:before { + content: "\f1c5"; } + +.fa.fa-file-archive-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-file-archive-o:before { + content: "\f1c6"; } + +.fa.fa-file-zip-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-file-zip-o:before { + content: "\f1c6"; } + +.fa.fa-file-audio-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-file-audio-o:before { + content: "\f1c7"; } + +.fa.fa-file-sound-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-file-sound-o:before { + content: "\f1c7"; } + +.fa.fa-file-video-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-file-video-o:before { + content: "\f1c8"; } + +.fa.fa-file-movie-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-file-movie-o:before { + content: "\f1c8"; } + +.fa.fa-file-code-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-file-code-o:before { + content: "\f1c9"; } + +.fa.fa-vine { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-codepen { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-jsfiddle { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-life-ring { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-life-bouy { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-life-bouy:before { + content: "\f1cd"; } + +.fa.fa-life-buoy { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-life-buoy:before { + content: "\f1cd"; } + +.fa.fa-life-saver { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-life-saver:before { + content: "\f1cd"; } + +.fa.fa-support { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-support:before { + content: "\f1cd"; } + +.fa.fa-circle-o-notch:before { + content: "\f1ce"; } + +.fa.fa-rebel { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-ra { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-ra:before { + content: "\f1d0"; } + +.fa.fa-resistance { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-resistance:before { + content: "\f1d0"; } + +.fa.fa-empire { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-ge { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-ge:before { + content: "\f1d1"; } + +.fa.fa-git-square { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-git { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-hacker-news { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-y-combinator-square { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-y-combinator-square:before { + content: "\f1d4"; } + +.fa.fa-yc-square { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-yc-square:before { + content: "\f1d4"; } + +.fa.fa-tencent-weibo { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-qq { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-weixin { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-wechat { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-wechat:before { + content: "\f1d7"; } + +.fa.fa-send:before { + content: "\f1d8"; } + +.fa.fa-paper-plane-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-paper-plane-o:before { + content: "\f1d8"; } + +.fa.fa-send-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-send-o:before { + content: "\f1d8"; } + +.fa.fa-circle-thin { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-circle-thin:before { + content: "\f111"; } + +.fa.fa-header:before { + content: "\f1dc"; } + +.fa.fa-sliders:before { + content: "\f1de"; } + +.fa.fa-futbol-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-futbol-o:before { + content: "\f1e3"; } + +.fa.fa-soccer-ball-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-soccer-ball-o:before { + content: "\f1e3"; } + +.fa.fa-slideshare { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-twitch { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-yelp { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-newspaper-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-newspaper-o:before { + content: "\f1ea"; } + +.fa.fa-paypal { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-google-wallet { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-cc-visa { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-cc-mastercard { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-cc-discover { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-cc-amex { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-cc-paypal { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-cc-stripe { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-bell-slash-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-bell-slash-o:before { + content: "\f1f6"; } + +.fa.fa-trash:before { + content: "\f2ed"; } + +.fa.fa-copyright { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-eyedropper:before { + content: "\f1fb"; } + +.fa.fa-area-chart:before { + content: "\f1fe"; } + +.fa.fa-pie-chart:before { + content: "\f200"; } + +.fa.fa-line-chart:before { + content: "\f201"; } + +.fa.fa-lastfm { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-lastfm-square { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-ioxhost { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-angellist { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-cc { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-cc:before { + content: "\f20a"; } + +.fa.fa-ils:before { + content: "\f20b"; } + +.fa.fa-shekel:before { + content: "\f20b"; } + +.fa.fa-sheqel:before { + content: "\f20b"; } + +.fa.fa-meanpath { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-meanpath:before { + content: "\f2b4"; } + +.fa.fa-buysellads { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-connectdevelop { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-dashcube { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-forumbee { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-leanpub { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-sellsy { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-shirtsinbulk { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-simplybuilt { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-skyatlas { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-diamond { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-diamond:before { + content: "\f3a5"; } + +.fa.fa-intersex:before { + content: "\f224"; } + +.fa.fa-facebook-official { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-facebook-official:before { + content: "\f09a"; } + +.fa.fa-pinterest-p { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-whatsapp { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-hotel:before { + content: "\f236"; } + +.fa.fa-viacoin { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-medium { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-y-combinator { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-yc { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-yc:before { + content: "\f23b"; } + +.fa.fa-optin-monster { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-opencart { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-expeditedssl { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-battery-4:before { + content: "\f240"; } + +.fa.fa-battery:before { + content: "\f240"; } + +.fa.fa-battery-3:before { + content: "\f241"; } + +.fa.fa-battery-2:before { + content: "\f242"; } + +.fa.fa-battery-1:before { + content: "\f243"; } + +.fa.fa-battery-0:before { + content: "\f244"; } + +.fa.fa-object-group { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-object-ungroup { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-sticky-note-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-sticky-note-o:before { + content: "\f249"; } + +.fa.fa-cc-jcb { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-cc-diners-club { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-clone { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-hourglass-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-hourglass-o:before { + content: "\f254"; } + +.fa.fa-hourglass-1:before { + content: "\f251"; } + +.fa.fa-hourglass-2:before { + content: "\f252"; } + +.fa.fa-hourglass-3:before { + content: "\f253"; } + +.fa.fa-hand-rock-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-hand-rock-o:before { + content: "\f255"; } + +.fa.fa-hand-grab-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-hand-grab-o:before { + content: "\f255"; } + +.fa.fa-hand-paper-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-hand-paper-o:before { + content: "\f256"; } + +.fa.fa-hand-stop-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-hand-stop-o:before { + content: "\f256"; } + +.fa.fa-hand-scissors-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-hand-scissors-o:before { + content: "\f257"; } + +.fa.fa-hand-lizard-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-hand-lizard-o:before { + content: "\f258"; } + +.fa.fa-hand-spock-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-hand-spock-o:before { + content: "\f259"; } + +.fa.fa-hand-pointer-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-hand-pointer-o:before { + content: "\f25a"; } + +.fa.fa-hand-peace-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-hand-peace-o:before { + content: "\f25b"; } + +.fa.fa-registered { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-creative-commons { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-gg { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-gg-circle { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-tripadvisor { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-odnoklassniki { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-odnoklassniki-square { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-get-pocket { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-wikipedia-w { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-safari { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-chrome { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-firefox { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-opera { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-internet-explorer { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-television:before { + content: "\f26c"; } + +.fa.fa-contao { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-500px { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-amazon { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-calendar-plus-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-calendar-plus-o:before { + content: "\f271"; } + +.fa.fa-calendar-minus-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-calendar-minus-o:before { + content: "\f272"; } + +.fa.fa-calendar-times-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-calendar-times-o:before { + content: "\f273"; } + +.fa.fa-calendar-check-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-calendar-check-o:before { + content: "\f274"; } + +.fa.fa-map-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-map-o:before { + content: "\f279"; } + +.fa.fa-commenting:before { + content: "\f4ad"; } + +.fa.fa-commenting-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-commenting-o:before { + content: "\f4ad"; } + +.fa.fa-houzz { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-vimeo { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-vimeo:before { + content: "\f27d"; } + +.fa.fa-black-tie { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-fonticons { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-reddit-alien { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-edge { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-credit-card-alt:before { + content: "\f09d"; } + +.fa.fa-codiepie { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-modx { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-fort-awesome { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-usb { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-product-hunt { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-mixcloud { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-scribd { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-pause-circle-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-pause-circle-o:before { + content: "\f28b"; } + +.fa.fa-stop-circle-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-stop-circle-o:before { + content: "\f28d"; } + +.fa.fa-bluetooth { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-bluetooth-b { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-gitlab { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-wpbeginner { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-wpforms { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-envira { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-wheelchair-alt { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-wheelchair-alt:before { + content: "\f368"; } + +.fa.fa-question-circle-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-question-circle-o:before { + content: "\f059"; } + +.fa.fa-volume-control-phone:before { + content: "\f2a0"; } + +.fa.fa-asl-interpreting:before { + content: "\f2a3"; } + +.fa.fa-deafness:before { + content: "\f2a4"; } + +.fa.fa-hard-of-hearing:before { + content: "\f2a4"; } + +.fa.fa-glide { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-glide-g { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-signing:before { + content: "\f2a7"; } + +.fa.fa-viadeo { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-viadeo-square { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-snapchat { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-snapchat-ghost { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-snapchat-square { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-pied-piper { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-first-order { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-yoast { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-themeisle { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-google-plus-official { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-google-plus-official:before { + content: "\f2b3"; } + +.fa.fa-google-plus-circle { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-google-plus-circle:before { + content: "\f2b3"; } + +.fa.fa-font-awesome { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-fa { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-fa:before { + content: "\f2b4"; } + +.fa.fa-handshake-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-handshake-o:before { + content: "\f2b5"; } + +.fa.fa-envelope-open-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-envelope-open-o:before { + content: "\f2b6"; } + +.fa.fa-linode { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-address-book-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-address-book-o:before { + content: "\f2b9"; } + +.fa.fa-vcard:before { + content: "\f2bb"; } + +.fa.fa-address-card-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-address-card-o:before { + content: "\f2bb"; } + +.fa.fa-vcard-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-vcard-o:before { + content: "\f2bb"; } + +.fa.fa-user-circle-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-user-circle-o:before { + content: "\f2bd"; } + +.fa.fa-user-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-user-o:before { + content: "\f007"; } + +.fa.fa-id-badge { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-drivers-license:before { + content: "\f2c2"; } + +.fa.fa-id-card-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-id-card-o:before { + content: "\f2c2"; } + +.fa.fa-drivers-license-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-drivers-license-o:before { + content: "\f2c2"; } + +.fa.fa-quora { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-free-code-camp { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-telegram { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-thermometer-4:before { + content: "\f2c7"; } + +.fa.fa-thermometer:before { + content: "\f2c7"; } + +.fa.fa-thermometer-3:before { + content: "\f2c8"; } + +.fa.fa-thermometer-2:before { + content: "\f2c9"; } + +.fa.fa-thermometer-1:before { + content: "\f2ca"; } + +.fa.fa-thermometer-0:before { + content: "\f2cb"; } + +.fa.fa-bathtub:before { + content: "\f2cd"; } + +.fa.fa-s15:before { + content: "\f2cd"; } + +.fa.fa-window-maximize { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-window-restore { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-times-rectangle:before { + content: "\f410"; } + +.fa.fa-window-close-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-window-close-o:before { + content: "\f410"; } + +.fa.fa-times-rectangle-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-times-rectangle-o:before { + content: "\f410"; } + +.fa.fa-bandcamp { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-grav { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-etsy { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-imdb { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-ravelry { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-eercast { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-eercast:before { + content: "\f2da"; } + +.fa.fa-snowflake-o { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; } + +.fa.fa-snowflake-o:before { + content: "\f2dc"; } + +.fa.fa-superpowers { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-wpexplorer { + font-family: 'Font Awesome 5 Brands'; + font-weight: 400; } + +.fa.fa-cab:before { + content: "\f1ba"; } diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-brands-400.eot b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-brands-400.eot new file mode 100644 index 0000000000..a1bc094ab1 Binary files /dev/null and b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-brands-400.eot differ diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-brands-400.svg b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-brands-400.svg new file mode 100644 index 0000000000..46ad237a61 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-brands-400.svg @@ -0,0 +1,3570 @@ + + + + + +Created by FontForge 20190801 at Mon Mar 23 10:45:51 2020 + By Robert Madole +Copyright (c) Font Awesome + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-brands-400.ttf b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-brands-400.ttf new file mode 100644 index 0000000000..948a2a6cc7 Binary files /dev/null and b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-brands-400.ttf differ diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-brands-400.woff b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-brands-400.woff new file mode 100644 index 0000000000..2a89d521e3 Binary files /dev/null and b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-brands-400.woff differ diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-brands-400.woff2 b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-brands-400.woff2 new file mode 100644 index 0000000000..141a90a9e0 Binary files /dev/null and b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-brands-400.woff2 differ diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-regular-400.eot b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-regular-400.eot new file mode 100644 index 0000000000..38cf2517a4 Binary files /dev/null and b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-regular-400.eot differ diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-regular-400.svg b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-regular-400.svg new file mode 100644 index 0000000000..48634a9ab4 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-regular-400.svg @@ -0,0 +1,803 @@ + + + + + +Created by FontForge 20190801 at Mon Mar 23 10:45:51 2020 + By Robert Madole +Copyright (c) Font Awesome + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-regular-400.ttf b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-regular-400.ttf new file mode 100644 index 0000000000..abe99e20c3 Binary files /dev/null and b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-regular-400.ttf differ diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-regular-400.woff b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-regular-400.woff new file mode 100644 index 0000000000..24de566a5c Binary files /dev/null and b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-regular-400.woff differ diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-regular-400.woff2 b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-regular-400.woff2 new file mode 100644 index 0000000000..7e0118e526 Binary files /dev/null and b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-regular-400.woff2 differ diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-solid-900.eot b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-solid-900.eot new file mode 100644 index 0000000000..d3b77c223a Binary files /dev/null and b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-solid-900.eot differ diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-solid-900.svg b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-solid-900.svg new file mode 100644 index 0000000000..7742838b44 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-solid-900.svg @@ -0,0 +1,4938 @@ + + + + + +Created by FontForge 20190801 at Mon Mar 23 10:45:51 2020 + By Robert Madole +Copyright (c) Font Awesome + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-solid-900.ttf b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-solid-900.ttf new file mode 100644 index 0000000000..5b979039ab Binary files /dev/null and b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-solid-900.ttf differ diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff new file mode 100644 index 0000000000..beec791784 Binary files /dev/null and b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff differ diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff2 b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff2 new file mode 100644 index 0000000000..978a681a10 Binary files /dev/null and b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff2 differ diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/abp/core/abp.css b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/abp/core/abp.css new file mode 100644 index 0000000000..ddf9cae5b2 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/abp/core/abp.css @@ -0,0 +1,56 @@ +@keyframes spin { + 0% { + transform: translateZ(0) rotate(0deg); + } + + 100% { + transform: translateZ(0) rotate(360deg); + } +} + +.abp-block-area { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 102; + background-color: #fff; + opacity: .8; + transition: opacity .25s; +} + + .abp-block-area.abp-block-area-disappearing { + opacity: 0; + } + + .abp-block-area.abp-block-area-busy:after { + content: attr(data-text); + display: block; + max-width: 125px; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-size: 20px; + font-family: sans-serif; + color: #343a40; + text-align: center; + text-transform: uppercase; + } + + .abp-block-area.abp-block-area-busy:before { + content: ""; + display: block; + width: 150px; + height: 150px; + border-radius: 50%; + border-width: 2px; + border-style: solid; + border-color: transparent #228ae6 #228ae6 #228ae6; + position: absolute; + top: calc(50% - 75px); + left: calc(50% - 75px); + will-change: transform; + animation: spin .75s infinite ease-in-out; + } diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/abp/core/abp.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/abp/core/abp.js new file mode 100644 index 0000000000..fa42828306 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/abp/core/abp.js @@ -0,0 +1,666 @@ +var abp = abp || {}; +(function () { + + /* Application paths *****************************************/ + + //Current application root path (including virtual directory if exists). + abp.appPath = abp.appPath || '/'; + + abp.pageLoadTime = new Date(); + + //Converts given path to absolute path using abp.appPath variable. + abp.toAbsAppPath = function (path) { + if (path.indexOf('/') == 0) { + path = path.substring(1); + } + + return abp.appPath + path; + }; + + /* LOGGING ***************************************************/ + //Implements Logging API that provides secure & controlled usage of console.log + + abp.log = abp.log || {}; + + abp.log.levels = { + DEBUG: 1, + INFO: 2, + WARN: 3, + ERROR: 4, + FATAL: 5 + }; + + abp.log.level = abp.log.levels.DEBUG; + + abp.log.log = function (logObject, logLevel) { + if (!window.console || !window.console.log) { + return; + } + + if (logLevel != undefined && logLevel < abp.log.level) { + return; + } + + console.log(logObject); + }; + + abp.log.debug = function (logObject) { + abp.log.log("DEBUG: ", abp.log.levels.DEBUG); + abp.log.log(logObject, abp.log.levels.DEBUG); + }; + + abp.log.info = function (logObject) { + abp.log.log("INFO: ", abp.log.levels.INFO); + abp.log.log(logObject, abp.log.levels.INFO); + }; + + abp.log.warn = function (logObject) { + abp.log.log("WARN: ", abp.log.levels.WARN); + abp.log.log(logObject, abp.log.levels.WARN); + }; + + abp.log.error = function (logObject) { + abp.log.log("ERROR: ", abp.log.levels.ERROR); + abp.log.log(logObject, abp.log.levels.ERROR); + }; + + abp.log.fatal = function (logObject) { + abp.log.log("FATAL: ", abp.log.levels.FATAL); + abp.log.log(logObject, abp.log.levels.FATAL); + }; + + /* LOCALIZATION ***********************************************/ + + abp.localization = abp.localization || {}; + + abp.localization.values = {}; + + abp.localization.localize = function (key, sourceName) { + if (sourceName === '_') { //A convention to suppress the localization + return key; + } + + sourceName = sourceName || abp.localization.defaultResourceName; + if (!sourceName) { + abp.log.warn('Localization source name is not specified and the defaultResourceName was not defined!'); + return key; + } + + var source = abp.localization.values[sourceName]; + if (!source) { + abp.log.warn('Could not find localization source: ' + sourceName); + return key; + } + + var value = source[key]; + if (value == undefined) { + return key; + } + + var copiedArguments = Array.prototype.slice.call(arguments, 0); + copiedArguments.splice(1, 1); + copiedArguments[0] = value; + + return abp.utils.formatString.apply(this, copiedArguments); + }; + + abp.localization.isLocalized = function (key, sourceName) { + if (sourceName === '_') { //A convention to suppress the localization + return true; + } + + sourceName = sourceName || abp.localization.defaultResourceName; + if (!sourceName) { + return false; + } + + var source = abp.localization.values[sourceName]; + if (!source) { + return false; + } + + var value = source[key]; + if (value === undefined) { + return false; + } + + return true; + }; + + abp.localization.getResource = function (name) { + return function () { + var copiedArguments = Array.prototype.slice.call(arguments, 0); + copiedArguments.splice(1, 0, name); + return abp.localization.localize.apply(this, copiedArguments); + }; + }; + + abp.localization.defaultResourceName = undefined; + + /* AUTHORIZATION **********************************************/ + + abp.auth = abp.auth || {}; + + abp.auth.policies = abp.auth.policies || {}; + + abp.auth.grantedPolicies = abp.auth.grantedPolicies || {}; + + abp.auth.isGranted = function (policyName) { + return abp.auth.policies[policyName] != undefined && abp.auth.grantedPolicies[policyName] != undefined; + }; + + abp.auth.isAnyGranted = function () { + if (!arguments || arguments.length <= 0) { + return true; + } + + for (var i = 0; i < arguments.length; i++) { + if (abp.auth.isGranted(arguments[i])) { + return true; + } + } + + return false; + }; + + abp.auth.areAllGranted = function () { + if (!arguments || arguments.length <= 0) { + return true; + } + + for (var i = 0; i < arguments.length; i++) { + if (!abp.auth.isGranted(arguments[i])) { + return false; + } + } + + return true; + }; + + abp.auth.tokenCookieName = 'Abp.AuthToken'; + + abp.auth.setToken = function (authToken, expireDate) { + abp.utils.setCookieValue(abp.auth.tokenCookieName, authToken, expireDate, abp.appPath, abp.domain); + }; + + abp.auth.getToken = function () { + return abp.utils.getCookieValue(abp.auth.tokenCookieName); + } + + abp.auth.clearToken = function () { + abp.auth.setToken(); + } + + /* SETTINGS *************************************************/ + + abp.setting = abp.setting || {}; + + abp.setting.values = abp.setting.values || {}; + + abp.setting.get = function (name) { + return abp.setting.values[name]; + }; + + abp.setting.getBoolean = function (name) { + var value = abp.setting.get(name); + return value == 'true' || value == 'True'; + }; + + abp.setting.getInt = function (name) { + return parseInt(abp.setting.values[name]); + }; + + /* NOTIFICATION *********************************************/ + //Defines Notification API, not implements it + + abp.notify = abp.notify || {}; + + abp.notify.success = function (message, title, options) { + abp.log.warn('abp.notify.success is not implemented!'); + }; + + abp.notify.info = function (message, title, options) { + abp.log.warn('abp.notify.info is not implemented!'); + }; + + abp.notify.warn = function (message, title, options) { + abp.log.warn('abp.notify.warn is not implemented!'); + }; + + abp.notify.error = function (message, title, options) { + abp.log.warn('abp.notify.error is not implemented!'); + }; + + /* MESSAGE **************************************************/ + //Defines Message API, not implements it + + abp.message = abp.message || {}; + + abp.message._showMessage = function (message, title) { + alert((title || '') + ' ' + message); + }; + + abp.message.info = function (message, title) { + abp.log.warn('abp.message.info is not implemented!'); + return abp.message._showMessage(message, title); + }; + + abp.message.success = function (message, title) { + abp.log.warn('abp.message.success is not implemented!'); + return abp.message._showMessage(message, title); + }; + + abp.message.warn = function (message, title) { + abp.log.warn('abp.message.warn is not implemented!'); + return abp.message._showMessage(message, title); + }; + + abp.message.error = function (message, title) { + abp.log.warn('abp.message.error is not implemented!'); + return abp.message._showMessage(message, title); + }; + + abp.message.confirm = function (message, titleOrCallback, callback) { + abp.log.warn('abp.message.confirm is not properly implemented!'); + + if (titleOrCallback && !(typeof titleOrCallback == 'string')) { + callback = titleOrCallback; + } + + var result = confirm(message); + callback && callback(result); + }; + + /* UI *******************************************************/ + + abp.ui = abp.ui || {}; + + /* UI BLOCK */ + //Defines UI Block API and implements basically + + var $abpBlockArea = document.createElement('div'); + $abpBlockArea.classList.add('abp-block-area'); + + /* opts: { //Can be an object with options or a string for query a selector + * elm: a query selector (optional - default: document.body) + * busy: boolean (optional - default: false) + * promise: A promise with always or finally handler (optional - auto unblocks the ui if provided) + * } + */ + abp.ui.block = function (opts) { + if (!opts) { + opts = {}; + } else if (typeof opts == 'string') { + opts = { + elm: opts + }; + } + + var $elm = document.querySelector(opts.elm) || document.body; + + if (opts.busy) { + $abpBlockArea.classList.add('abp-block-area-busy'); + } else { + $abpBlockArea.classList.remove('abp-block-area-busy'); + } + + if (document.querySelector(opts.elm)) { + $abpBlockArea.style.position = 'absolute'; + } else { + $abpBlockArea.style.position = 'fixed'; + } + + $elm.appendChild($abpBlockArea); + + if (opts.promise) { + if (opts.promise.always) { //jQuery.Deferred style + opts.promise.always(function () { + abp.ui.unblock({ + $elm: opts.elm + }); + }); + } else if (opts.promise['finally']) { //Q style + opts.promise['finally'](function () { + abp.ui.unblock({ + $elm: opts.elm + }); + }); + } + } + }; + + /* opts: { + * + * } + */ + abp.ui.unblock = function (opts) { + var element = document.querySelector('.abp-block-area'); + if (element) { + element.classList.add('abp-block-area-disappearing'); + setTimeout(function () { + if (element) { + element.classList.remove('abp-block-area-disappearing'); + element.parentElement.removeChild(element); + } + }, 250); + } + }; + + /* UI BUSY */ + //Defines UI Busy API, not implements it + + abp.ui.setBusy = function (opts) { + if (!opts) { + opts = { + busy: true + }; + } else if (typeof opts == 'string') { + opts = { + elm: opts, + busy: true + }; + } + + abp.ui.block(opts); + }; + + abp.ui.clearBusy = function (opts) { + abp.ui.unblock(opts); + }; + + /* SIMPLE EVENT BUS *****************************************/ + + abp.event = (function () { + + var _callbacks = {}; + + var on = function (eventName, callback) { + if (!_callbacks[eventName]) { + _callbacks[eventName] = []; + } + + _callbacks[eventName].push(callback); + }; + + var off = function (eventName, callback) { + var callbacks = _callbacks[eventName]; + if (!callbacks) { + return; + } + + var index = -1; + for (var i = 0; i < callbacks.length; i++) { + if (callbacks[i] === callback) { + index = i; + break; + } + } + + if (index < 0) { + return; + } + + _callbacks[eventName].splice(index, 1); + }; + + var trigger = function (eventName) { + var callbacks = _callbacks[eventName]; + if (!callbacks || !callbacks.length) { + return; + } + + var args = Array.prototype.slice.call(arguments, 1); + for (var i = 0; i < callbacks.length; i++) { + callbacks[i].apply(this, args); + } + }; + + // Public interface /////////////////////////////////////////////////// + + return { + on: on, + off: off, + trigger: trigger + }; + })(); + + + /* UTILS ***************************************************/ + + abp.utils = abp.utils || {}; + + /* Creates a name namespace. + * Example: + * var taskService = abp.utils.createNamespace(abp, 'services.task'); + * taskService will be equal to abp.services.task + * first argument (root) must be defined first + ************************************************************/ + abp.utils.createNamespace = function (root, ns) { + var parts = ns.split('.'); + for (var i = 0; i < parts.length; i++) { + if (typeof root[parts[i]] == 'undefined') { + root[parts[i]] = {}; + } + + root = root[parts[i]]; + } + + return root; + }; + + /* Find and replaces a string (search) to another string (replacement) in + * given string (str). + * Example: + * abp.utils.replaceAll('This is a test string', 'is', 'X') = 'ThX X a test string' + ************************************************************/ + abp.utils.replaceAll = function (str, search, replacement) { + var fix = search.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + return str.replace(new RegExp(fix, 'g'), replacement); + }; + + /* Formats a string just like string.format in C#. + * Example: + * abp.utils.formatString('Hello {0}','Tuana') = 'Hello Tuana' + ************************************************************/ + abp.utils.formatString = function () { + if (arguments.length < 1) { + return null; + } + + var str = arguments[0]; + + for (var i = 1; i < arguments.length; i++) { + var placeHolder = '{' + (i - 1) + '}'; + str = abp.utils.replaceAll(str, placeHolder, arguments[i]); + } + + return str; + }; + + abp.utils.toPascalCase = function (str) { + if (!str || !str.length) { + return str; + } + + if (str.length === 1) { + return str.charAt(0).toUpperCase(); + } + + return str.charAt(0).toUpperCase() + str.substr(1); + } + + abp.utils.toCamelCase = function (str) { + if (!str || !str.length) { + return str; + } + + if (str.length === 1) { + return str.charAt(0).toLowerCase(); + } + + return str.charAt(0).toLowerCase() + str.substr(1); + } + + abp.utils.truncateString = function (str, maxLength) { + if (!str || !str.length || str.length <= maxLength) { + return str; + } + + return str.substr(0, maxLength); + }; + + abp.utils.truncateStringWithPostfix = function (str, maxLength, postfix) { + postfix = postfix || '...'; + + if (!str || !str.length || str.length <= maxLength) { + return str; + } + + if (maxLength <= postfix.length) { + return postfix.substr(0, maxLength); + } + + return str.substr(0, maxLength - postfix.length) + postfix; + }; + + abp.utils.isFunction = function (obj) { + return !!(obj && obj.constructor && obj.call && obj.apply); + }; + + /** + * parameterInfos should be an array of { name, value } objects + * where name is query string parameter name and value is it's value. + * includeQuestionMark is true by default. + */ + abp.utils.buildQueryString = function (parameterInfos, includeQuestionMark) { + if (includeQuestionMark === undefined) { + includeQuestionMark = true; + } + + var qs = ''; + + function addSeperator() { + if (!qs.length) { + if (includeQuestionMark) { + qs = qs + '?'; + } + } else { + qs = qs + '&'; + } + } + + for (var i = 0; i < parameterInfos.length; ++i) { + var parameterInfo = parameterInfos[i]; + if (parameterInfo.value === undefined) { + continue; + } + + if (parameterInfo.value === null) { + parameterInfo.value = ''; + } + + addSeperator(); + + if (parameterInfo.value.toJSON && typeof parameterInfo.value.toJSON === "function") { + qs = qs + parameterInfo.name + '=' + encodeURIComponent(parameterInfo.value.toJSON()); + } else if (Array.isArray(parameterInfo.value) && parameterInfo.value.length) { + for (var j = 0; j < parameterInfo.value.length; j++) { + if (j > 0) { + addSeperator(); + } + + qs = qs + parameterInfo.name + '[' + j + ']=' + encodeURIComponent(parameterInfo.value[j]); + } + } else { + qs = qs + parameterInfo.name + '=' + encodeURIComponent(parameterInfo.value); + } + } + + return qs; + } + + /** + * Sets a cookie value for given key. + * This is a simple implementation created to be used by ABP. + * Please use a complete cookie library if you need. + * @param {string} key + * @param {string} value + * @param {Date} expireDate (optional). If not specified the cookie will expire at the end of session. + * @param {string} path (optional) + */ + abp.utils.setCookieValue = function (key, value, expireDate, path) { + var cookieValue = encodeURIComponent(key) + '='; + + if (value) { + cookieValue = cookieValue + encodeURIComponent(value); + } + + if (expireDate) { + cookieValue = cookieValue + "; expires=" + expireDate.toUTCString(); + } + + if (path) { + cookieValue = cookieValue + "; path=" + path; + } + + document.cookie = cookieValue; + }; + + /** + * Gets a cookie with given key. + * This is a simple implementation created to be used by ABP. + * Please use a complete cookie library if you need. + * @param {string} key + * @returns {string} Cookie value or null + */ + abp.utils.getCookieValue = function (key) { + var equalities = document.cookie.split('; '); + for (var i = 0; i < equalities.length; i++) { + if (!equalities[i]) { + continue; + } + + var splitted = equalities[i].split('='); + if (splitted.length != 2) { + continue; + } + + if (decodeURIComponent(splitted[0]) === key) { + return decodeURIComponent(splitted[1] || ''); + } + } + + return null; + }; + + /** + * Deletes cookie for given key. + * This is a simple implementation created to be used by ABP. + * Please use a complete cookie library if you need. + * @param {string} key + * @param {string} path (optional) + */ + abp.utils.deleteCookie = function (key, path) { + var cookieValue = encodeURIComponent(key) + '='; + + cookieValue = cookieValue + "; expires=" + (new Date(new Date().getTime() - 86400000)).toUTCString(); + + if (path) { + cookieValue = cookieValue + "; path=" + path; + } + + document.cookie = cookieValue; + } + + /* SECURITY ***************************************/ + abp.security = abp.security || {}; + abp.security.antiForgery = abp.security.antiForgery || {}; + + abp.security.antiForgery.tokenCookieName = 'XSRF-TOKEN'; + abp.security.antiForgery.tokenHeaderName = 'X-XSRF-TOKEN'; + + abp.security.antiForgery.getToken = function () { + return abp.utils.getCookieValue(abp.security.antiForgery.tokenCookieName); + }; + +})(); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/abp/jquery/abp.jquery.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/abp/jquery/abp.jquery.js new file mode 100644 index 0000000000..e6aff047f7 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/abp/jquery/abp.jquery.js @@ -0,0 +1,393 @@ +var abp = abp || {}; +(function($) { + + if (!$) { + throw "abp/jquery library requires the jquery library included to the page!"; + } + + // ABP CORE OVERRIDES ///////////////////////////////////////////////////// + + abp.message._showMessage = function (message, title) { + alert((title || '') + ' ' + message); + + return $.Deferred(function ($dfd) { + $dfd.resolve(); + }); + }; + + abp.message.confirm = function (message, titleOrCallback, callback) { + if (titleOrCallback && !(typeof titleOrCallback == 'string')) { + callback = titleOrCallback; + } + + var result = confirm(message); + callback && callback(result); + + return $.Deferred(function ($dfd) { + $dfd.resolve(result); + }); + }; + + abp.utils.isFunction = function (obj) { + return $.isFunction(obj); + }; + + // JQUERY EXTENSIONS ////////////////////////////////////////////////////// + + $.fn.findWithSelf = function (selector) { + return this.filter(selector).add(this.find(selector)); + }; + + // DOM //////////////////////////////////////////////////////////////////// + + abp.dom = abp.dom || {}; + + abp.dom.onNodeAdded = function (callback) { + abp.event.on('abp.dom.nodeAdded', callback); + }; + + abp.dom.onNodeRemoved = function (callback) { + abp.event.on('abp.dom.nodeRemoved', callback); + }; + + var mutationObserverCallback = function (mutationsList) { + for (var i = 0; i < mutationsList.length; i++) { + var mutation = mutationsList[i]; + if (mutation.type === 'childList') { + if (mutation.addedNodes && mutation.removedNodes.length) { + for (var k = 0; k < mutation.removedNodes.length; k++) { + abp.event.trigger( + 'abp.dom.nodeRemoved', + { + $el: $(mutation.removedNodes[k]) + } + ); + } + } + + if (mutation.addedNodes && mutation.addedNodes.length) { + for (var j = 0; j < mutation.addedNodes.length; j++) { + abp.event.trigger( + 'abp.dom.nodeAdded', + { + $el: $(mutation.addedNodes[j]) + } + ); + } + } + } + } + }; + + new MutationObserver(mutationObserverCallback).observe( + $('body')[0], + { + subtree: true, + childList: true + } + ); + + // AJAX /////////////////////////////////////////////////////////////////// + + abp.ajax = function (userOptions) { + userOptions = userOptions || {}; + + var options = $.extend(true, {}, abp.ajax.defaultOpts, userOptions); + + options.success = undefined; + options.error = undefined; + + return $.Deferred(function ($dfd) { + $.ajax(options) + .done(function (data, textStatus, jqXHR) { + $dfd.resolve(data); + userOptions.success && userOptions.success(data); + }).fail(function (jqXHR) { + if (jqXHR.getResponseHeader('_AbpErrorFormat') === 'true') { + abp.ajax.handleAbpErrorResponse(jqXHR, userOptions, $dfd); + } else { + abp.ajax.handleNonAbpErrorResponse(jqXHR, userOptions, $dfd); + } + }); + }); + }; + + $.extend(abp.ajax, { + defaultOpts: { + dataType: 'json', + type: 'POST', + contentType: 'application/json', + headers: { + 'X-Requested-With': 'XMLHttpRequest' + } + }, + + defaultError: { + message: 'An error has occurred!', + details: 'Error detail not sent by server.' + }, + + defaultError401: { + message: 'You are not authenticated!', + details: 'You should be authenticated (sign in) in order to perform this operation.' + }, + + defaultError403: { + message: 'You are not authorized!', + details: 'You are not allowed to perform this operation.' + }, + + defaultError404: { + message: 'Resource not found!', + details: 'The resource requested could not found on the server.' + }, + + logError: function (error) { + abp.log.error(error); + }, + + showError: function (error) { + if (error.details) { + return abp.message.error(error.details, error.message); + } else { + return abp.message.error(error.message || abp.ajax.defaultError.message); + } + }, + + handleTargetUrl: function (targetUrl) { + if (!targetUrl) { + location.href = abp.appPath; + } else { + location.href = targetUrl; + } + }, + + handleErrorStatusCode: function (status) { + switch (status) { + case 401: + abp.ajax.handleUnAuthorizedRequest( + abp.ajax.showError(abp.ajax.defaultError401), + abp.appPath + ); + break; + case 403: + abp.ajax.showError(abp.ajax.defaultError403); + break; + case 404: + abp.ajax.showError(abp.ajax.defaultError404); + break; + default: + abp.ajax.showError(abp.ajax.defaultError); + break; + } + }, + + handleNonAbpErrorResponse: function (jqXHR, userOptions, $dfd) { + if (userOptions.abpHandleError !== false) { + abp.ajax.handleErrorStatusCode(jqXHR.status); + } + + $dfd.reject.apply(this, arguments); + userOptions.error && userOptions.error.apply(this, arguments); + }, + + handleAbpErrorResponse: function (jqXHR, userOptions, $dfd) { + var messagePromise = null; + + if (userOptions.abpHandleError !== false) { + messagePromise = abp.ajax.showError(jqXHR.responseJSON.error); + } + + abp.ajax.logError(jqXHR.responseJSON.error); + + $dfd && $dfd.reject(jqXHR.responseJSON.error, jqXHR); + userOptions.error && userOptions.error(jqXHR.responseJSON.error, jqXHR); + + if (jqXHR.status === 401 && userOptions.abpHandleError !== false) { + abp.ajax.handleUnAuthorizedRequest(messagePromise); + } + }, + + handleUnAuthorizedRequest: function (messagePromise, targetUrl) { + if (messagePromise) { + messagePromise.done(function () { + abp.ajax.handleTargetUrl(targetUrl); + }); + } else { + abp.ajax.handleTargetUrl(targetUrl); + } + }, + + blockUI: function (options) { + if (options.blockUI) { + if (options.blockUI === true) { //block whole page + abp.ui.setBusy(); + } else { //block an element + abp.ui.setBusy(options.blockUI); + } + } + }, + + unblockUI: function (options) { + if (options.blockUI) { + if (options.blockUI === true) { //unblock whole page + abp.ui.clearBusy(); + } else { //unblock an element + abp.ui.clearBusy(options.blockUI); + } + } + }, + + ajaxSendHandler: function (event, request, settings) { + var token = abp.security.antiForgery.getToken(); + if (!token) { + return; + } + + if (!settings.headers || settings.headers[abp.security.antiForgery.tokenHeaderName] === undefined) { + request.setRequestHeader(abp.security.antiForgery.tokenHeaderName, token); + } + } + }); + + $(document).ajaxSend(function (event, request, settings) { + return abp.ajax.ajaxSendHandler(event, request, settings); + }); + + abp.event.on('abp.configurationInitialized', function () { + var l = abp.localization.getResource('AbpUi'); + + abp.ajax.defaultError.message = l('DefaultErrorMessage'); + abp.ajax.defaultError.details = l('DefaultErrorMessageDetail'); + abp.ajax.defaultError401.message = l('DefaultErrorMessage401'); + abp.ajax.defaultError401.details = l('DefaultErrorMessage401Detail'); + abp.ajax.defaultError403.message = l('DefaultErrorMessage403'); + abp.ajax.defaultError403.details = l('DefaultErrorMessage403Detail'); + abp.ajax.defaultError404.message = l('DefaultErrorMessage404'); + abp.ajax.defaultError404.details = l('DefaultErrorMessage404Detail'); + }); + + // RESOURCE LOADER //////////////////////////////////////////////////////// + + /* UrlStates enum */ + var UrlStates = { + LOADING: 'LOADING', + LOADED: 'LOADED', + FAILED: 'FAILED' + }; + + /* UrlInfo class */ + function UrlInfo(url) { + this.url = url; + this.state = UrlStates.LOADING; + this.loadCallbacks = []; + this.failCallbacks = []; + } + + UrlInfo.prototype.succeed = function () { + this.state = UrlStates.LOADED; + for (var i = 0; i < this.loadCallbacks.length; i++) { + this.loadCallbacks[i](); + } + }; + + UrlInfo.prototype.failed = function () { + this.state = UrlStates.FAILED; + for (var i = 0; i < this.failCallbacks.length; i++) { + this.failCallbacks[i](); + } + }; + + UrlInfo.prototype.handleCallbacks = function (loadCallback, failCallback) { + switch (this.state) { + case UrlStates.LOADED: + loadCallback && loadCallback(); + break; + case UrlStates.FAILED: + failCallback && failCallback(); + break; + case UrlStates.LOADING: + this.addCallbacks(loadCallback, failCallback); + break; + } + }; + + UrlInfo.prototype.addCallbacks = function (loadCallback, failCallback) { + loadCallback && this.loadCallbacks.push(loadCallback); + failCallback && this.failCallbacks.push(failCallback); + }; + + /* ResourceLoader API */ + + abp.ResourceLoader = (function () { + + var _urlInfos = {}; + + function getCacheKey(url) { + return url; + } + + function appendTimeToUrl(url) { + + if (url.indexOf('?') < 0) { + url += '?'; + } else { + url += '&'; + } + + url += '_=' + new Date().getTime(); + + return url; + } + + var _loadFromUrl = function (url, loadCallback, failCallback, serverLoader) { + + var cacheKey = getCacheKey(url); + + var urlInfo = _urlInfos[cacheKey]; + + if (urlInfo) { + urlInfo.handleCallbacks(loadCallback, failCallback); + return; + } + + _urlInfos[cacheKey] = urlInfo = new UrlInfo(url); + urlInfo.addCallbacks(loadCallback, failCallback); + + serverLoader(urlInfo); + }; + + var _loadScript = function (url, loadCallback, failCallback) { + _loadFromUrl(url, loadCallback, failCallback, function (urlInfo) { + $.get({ + url: url, + dataType: 'text' + }) + .done(function (script) { + $.globalEval(script); + urlInfo.succeed(); + }) + .fail(function () { + urlInfo.failed(); + }); + }); + }; + + var _loadStyle = function (url) { + _loadFromUrl(url, undefined, undefined, function (urlInfo) { + + $('', { + rel: 'stylesheet', + type: 'text/css', + href: appendTimeToUrl(url) + }).appendTo('head'); + }); + }; + + return { + loadScript: _loadScript, + loadStyle: _loadStyle + } + })(); + +})(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/abp/utils/abp-utils.umd.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/abp/utils/abp-utils.umd.js new file mode 100644 index 0000000000..dbf03b6f17 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/abp/utils/abp-utils.umd.js @@ -0,0 +1,600 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('just-compare')) : + typeof define === 'function' && define.amd ? define('@abp/utils', ['exports', 'just-compare'], factory) : + (global = global || self, factory((global.abp = global.abp || {}, global.abp.utils = global.abp.utils || {}, global.abp.utils.common = {}), global.compare)); +}(this, (function (exports, compare) { 'use strict'; + + compare = compare && Object.prototype.hasOwnProperty.call(compare, 'default') ? compare['default'] : compare; + + /*! ***************************************************************************** + Copyright (c) Microsoft Corporation. All rights reserved. + Licensed under the Apache License, Version 2.0 (the "License"); you may not use + this file except in compliance with the License. You may obtain a copy of the + License at http://www.apache.org/licenses/LICENSE-2.0 + + THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED + WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, + MERCHANTABLITY OR NON-INFRINGEMENT. + + See the Apache Version 2.0 License for specific language governing permissions + and limitations under the License. + ***************************************************************************** */ + /* global Reflect, Promise */ + + var extendStatics = function(d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + + function __extends(d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + } + + var __assign = function() { + __assign = Object.assign || function __assign(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); + }; + + function __rest(s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; + } + + function __decorate(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + } + + function __param(paramIndex, decorator) { + return function (target, key) { decorator(target, key, paramIndex); } + } + + function __metadata(metadataKey, metadataValue) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue); + } + + function __awaiter(thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); + } + + function __generator(thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } + } + + function __exportStar(m, exports) { + for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; + } + + function __values(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); + } + + function __read(o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; + } + + function __spread() { + for (var ar = [], i = 0; i < arguments.length; i++) + ar = ar.concat(__read(arguments[i])); + return ar; + } + + function __spreadArrays() { + for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; + for (var r = Array(s), k = 0, i = 0; i < il; i++) + for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) + r[k] = a[j]; + return r; + }; + + function __await(v) { + return this instanceof __await ? (this.v = v, this) : new __await(v); + } + + function __asyncGenerator(thisArg, _arguments, generator) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var g = generator.apply(thisArg, _arguments || []), i, q = []; + return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i; + function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; } + function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } + function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } + function fulfill(value) { resume("next", value); } + function reject(value) { resume("throw", value); } + function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } + } + + function __asyncDelegator(o) { + var i, p; + return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; + function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; } : f; } + } + + function __asyncValues(o) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var m = o[Symbol.asyncIterator], i; + return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); + function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } + function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } + } + + function __makeTemplateObject(cooked, raw) { + if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } + return cooked; + }; + + function __importStar(mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; + result.default = mod; + return result; + } + + function __importDefault(mod) { + return (mod && mod.__esModule) ? mod : { default: mod }; + } + + function __classPrivateFieldGet(receiver, privateMap) { + if (!privateMap.has(receiver)) { + throw new TypeError("attempted to get private field on non-instance"); + } + return privateMap.get(receiver); + } + + function __classPrivateFieldSet(receiver, privateMap, value) { + if (!privateMap.has(receiver)) { + throw new TypeError("attempted to set private field on non-instance"); + } + privateMap.set(receiver, value); + return value; + } + + /* tslint:disable:no-non-null-assertion */ + var ListNode = /** @class */ (function () { + function ListNode(value) { + this.value = value; + } + return ListNode; + }()); + var LinkedList = /** @class */ (function () { + function LinkedList() { + this.size = 0; + } + Object.defineProperty(LinkedList.prototype, "head", { + get: function () { + return this.first; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(LinkedList.prototype, "tail", { + get: function () { + return this.last; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(LinkedList.prototype, "length", { + get: function () { + return this.size; + }, + enumerable: true, + configurable: true + }); + LinkedList.prototype.attach = function (value, previousNode, nextNode) { + if (!previousNode) + return this.addHead(value); + if (!nextNode) + return this.addTail(value); + var node = new ListNode(value); + node.previous = previousNode; + previousNode.next = node; + node.next = nextNode; + nextNode.previous = node; + this.size++; + return node; + }; + LinkedList.prototype.attachMany = function (values, previousNode, nextNode) { + if (!values.length) + return []; + if (!previousNode) + return this.addManyHead(values); + if (!nextNode) + return this.addManyTail(values); + var list = new LinkedList(); + list.addManyTail(values); + list.first.previous = previousNode; + previousNode.next = list.first; + list.last.next = nextNode; + nextNode.previous = list.last; + this.size += values.length; + return list.toNodeArray(); + }; + LinkedList.prototype.detach = function (node) { + if (!node.previous) + return this.dropHead(); + if (!node.next) + return this.dropTail(); + node.previous.next = node.next; + node.next.previous = node.previous; + this.size--; + return node; + }; + LinkedList.prototype.add = function (value) { + var _this = this; + return { + after: function () { + var _a; + var params = []; + for (var _i = 0; _i < arguments.length; _i++) { + params[_i] = arguments[_i]; + } + return (_a = _this.addAfter).call.apply(_a, __spread([_this, value], params)); + }, + before: function () { + var _a; + var params = []; + for (var _i = 0; _i < arguments.length; _i++) { + params[_i] = arguments[_i]; + } + return (_a = _this.addBefore).call.apply(_a, __spread([_this, value], params)); + }, + byIndex: function (position) { return _this.addByIndex(value, position); }, + head: function () { return _this.addHead(value); }, + tail: function () { return _this.addTail(value); }, + }; + }; + LinkedList.prototype.addMany = function (values) { + var _this = this; + return { + after: function () { + var _a; + var params = []; + for (var _i = 0; _i < arguments.length; _i++) { + params[_i] = arguments[_i]; + } + return (_a = _this.addManyAfter).call.apply(_a, __spread([_this, values], params)); + }, + before: function () { + var _a; + var params = []; + for (var _i = 0; _i < arguments.length; _i++) { + params[_i] = arguments[_i]; + } + return (_a = _this.addManyBefore).call.apply(_a, __spread([_this, values], params)); + }, + byIndex: function (position) { return _this.addManyByIndex(values, position); }, + head: function () { return _this.addManyHead(values); }, + tail: function () { return _this.addManyTail(values); }, + }; + }; + LinkedList.prototype.addAfter = function (value, previousValue, compareFn) { + if (compareFn === void 0) { compareFn = compare; } + var previous = this.find(function (node) { return compareFn(node.value, previousValue); }); + return previous ? this.attach(value, previous, previous.next) : this.addTail(value); + }; + LinkedList.prototype.addBefore = function (value, nextValue, compareFn) { + if (compareFn === void 0) { compareFn = compare; } + var next = this.find(function (node) { return compareFn(node.value, nextValue); }); + return next ? this.attach(value, next.previous, next) : this.addHead(value); + }; + LinkedList.prototype.addByIndex = function (value, position) { + if (position < 0) + position += this.size; + else if (position >= this.size) + return this.addTail(value); + if (position <= 0) + return this.addHead(value); + var next = this.get(position); + return this.attach(value, next.previous, next); + }; + LinkedList.prototype.addHead = function (value) { + var node = new ListNode(value); + node.next = this.first; + if (this.first) + this.first.previous = node; + else + this.last = node; + this.first = node; + this.size++; + return node; + }; + LinkedList.prototype.addTail = function (value) { + var node = new ListNode(value); + if (this.first) { + node.previous = this.last; + this.last.next = node; + this.last = node; + } + else { + this.first = node; + this.last = node; + } + this.size++; + return node; + }; + LinkedList.prototype.addManyAfter = function (values, previousValue, compareFn) { + if (compareFn === void 0) { compareFn = compare; } + var previous = this.find(function (node) { return compareFn(node.value, previousValue); }); + return previous ? this.attachMany(values, previous, previous.next) : this.addManyTail(values); + }; + LinkedList.prototype.addManyBefore = function (values, nextValue, compareFn) { + if (compareFn === void 0) { compareFn = compare; } + var next = this.find(function (node) { return compareFn(node.value, nextValue); }); + return next ? this.attachMany(values, next.previous, next) : this.addManyHead(values); + }; + LinkedList.prototype.addManyByIndex = function (values, position) { + if (position < 0) + position += this.size; + if (position <= 0) + return this.addManyHead(values); + if (position >= this.size) + return this.addManyTail(values); + var next = this.get(position); + return this.attachMany(values, next.previous, next); + }; + LinkedList.prototype.addManyHead = function (values) { + var _this = this; + return values.reduceRight(function (nodes, value) { + nodes.unshift(_this.addHead(value)); + return nodes; + }, []); + }; + LinkedList.prototype.addManyTail = function (values) { + var _this = this; + return values.map(function (value) { return _this.addTail(value); }); + }; + LinkedList.prototype.drop = function () { + var _this = this; + return { + byIndex: function (position) { return _this.dropByIndex(position); }, + byValue: function () { + var params = []; + for (var _i = 0; _i < arguments.length; _i++) { + params[_i] = arguments[_i]; + } + return _this.dropByValue.apply(_this, params); + }, + byValueAll: function () { + var params = []; + for (var _i = 0; _i < arguments.length; _i++) { + params[_i] = arguments[_i]; + } + return _this.dropByValueAll.apply(_this, params); + }, + head: function () { return _this.dropHead(); }, + tail: function () { return _this.dropTail(); }, + }; + }; + LinkedList.prototype.dropMany = function (count) { + var _this = this; + return { + byIndex: function (position) { return _this.dropManyByIndex(count, position); }, + head: function () { return _this.dropManyHead(count); }, + tail: function () { return _this.dropManyTail(count); }, + }; + }; + LinkedList.prototype.dropByIndex = function (position) { + if (position < 0) + position += this.size; + var current = this.get(position); + return current ? this.detach(current) : undefined; + }; + LinkedList.prototype.dropByValue = function (value, compareFn) { + if (compareFn === void 0) { compareFn = compare; } + var position = this.findIndex(function (node) { return compareFn(node.value, value); }); + return position < 0 ? undefined : this.dropByIndex(position); + }; + LinkedList.prototype.dropByValueAll = function (value, compareFn) { + if (compareFn === void 0) { compareFn = compare; } + var dropped = []; + for (var current = this.first, position = 0; current; position++, current = current.next) { + if (compareFn(current.value, value)) { + dropped.push(this.dropByIndex(position - dropped.length)); + } + } + return dropped; + }; + LinkedList.prototype.dropHead = function () { + var head = this.first; + if (head) { + this.first = head.next; + if (this.first) + this.first.previous = undefined; + else + this.last = undefined; + this.size--; + return head; + } + return undefined; + }; + LinkedList.prototype.dropTail = function () { + var tail = this.last; + if (tail) { + this.last = tail.previous; + if (this.last) + this.last.next = undefined; + else + this.first = undefined; + this.size--; + return tail; + } + return undefined; + }; + LinkedList.prototype.dropManyByIndex = function (count, position) { + if (count <= 0) + return []; + if (position < 0) + position = Math.max(position + this.size, 0); + else if (position >= this.size) + return []; + count = Math.min(count, this.size - position); + var dropped = []; + while (count--) { + var current = this.get(position); + dropped.push(this.detach(current)); + } + return dropped; + }; + LinkedList.prototype.dropManyHead = function (count) { + if (count <= 0) + return []; + count = Math.min(count, this.size); + var dropped = []; + while (count--) + dropped.unshift(this.dropHead()); + return dropped; + }; + LinkedList.prototype.dropManyTail = function (count) { + if (count <= 0) + return []; + count = Math.min(count, this.size); + var dropped = []; + while (count--) + dropped.push(this.dropTail()); + return dropped; + }; + LinkedList.prototype.find = function (predicate) { + for (var current = this.first, position = 0; current; position++, current = current.next) { + if (predicate(current, position, this)) + return current; + } + return undefined; + }; + LinkedList.prototype.findIndex = function (predicate) { + for (var current = this.first, position = 0; current; position++, current = current.next) { + if (predicate(current, position, this)) + return position; + } + return -1; + }; + LinkedList.prototype.forEach = function (iteratorFn) { + for (var node = this.first, position = 0; node; position++, node = node.next) { + iteratorFn(node, position, this); + } + }; + LinkedList.prototype.get = function (position) { + return this.find(function (_, index) { return position === index; }); + }; + LinkedList.prototype.indexOf = function (value, compareFn) { + if (compareFn === void 0) { compareFn = compare; } + return this.findIndex(function (node) { return compareFn(node.value, value); }); + }; + LinkedList.prototype.toArray = function () { + var array = new Array(this.size); + this.forEach(function (node, index) { return (array[index] = node.value); }); + return array; + }; + LinkedList.prototype.toNodeArray = function () { + var array = new Array(this.size); + this.forEach(function (node, index) { return (array[index] = node); }); + return array; + }; + LinkedList.prototype.toString = function (mapperFn) { + if (mapperFn === void 0) { mapperFn = JSON.stringify; } + return this.toArray() + .map(function (value) { return mapperFn(value); }) + .join(' <-> '); + }; + // Cannot use Generator type because of ng-packagr + LinkedList.prototype[Symbol.iterator] = function () { + var node, position; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + node = this.first, position = 0; + _a.label = 1; + case 1: + if (!node) return [3 /*break*/, 4]; + return [4 /*yield*/, node.value]; + case 2: + _a.sent(); + _a.label = 3; + case 3: + position++, node = node.next; + return [3 /*break*/, 1]; + case 4: return [2 /*return*/]; + } + }); + }; + return LinkedList; + }()); + + exports.LinkedList = LinkedList; + exports.ListNode = ListNode; + + Object.defineProperty(exports, '__esModule', { value: true }); + +}))); +//# sourceMappingURL=abp-utils.umd.js.map diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/abp/utils/abp-utils.umd.js.map b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/abp/utils/abp-utils.umd.js.map new file mode 100644 index 0000000000..18a9fb2df7 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/abp/utils/abp-utils.umd.js.map @@ -0,0 +1 @@ +{"version":3,"file":"abp-utils.umd.js","sources":["../../node_modules/tslib/tslib.es6.js","ng://@abp/utils/lib/linked-list.ts"],"sourcesContent":["/*! *****************************************************************************\r\nCopyright (c) Microsoft Corporation. All rights reserved.\r\nLicensed under the Apache License, Version 2.0 (the \"License\"); you may not use\r\nthis file except in compliance with the License. You may obtain a copy of the\r\nLicense at http://www.apache.org/licenses/LICENSE-2.0\r\n\r\nTHIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\r\nKIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED\r\nWARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,\r\nMERCHANTABLITY OR NON-INFRINGEMENT.\r\n\r\nSee the Apache Version 2.0 License for specific language governing permissions\r\nand limitations under the License.\r\n***************************************************************************** */\r\n/* global Reflect, Promise */\r\n\r\nvar extendStatics = function(d, b) {\r\n extendStatics = Object.setPrototypeOf ||\r\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\r\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\r\n return extendStatics(d, b);\r\n};\r\n\r\nexport function __extends(d, b) {\r\n extendStatics(d, b);\r\n function __() { this.constructor = d; }\r\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\r\n}\r\n\r\nexport var __assign = function() {\r\n __assign = Object.assign || function __assign(t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\r\n }\r\n return t;\r\n }\r\n return __assign.apply(this, arguments);\r\n}\r\n\r\nexport function __rest(s, e) {\r\n var t = {};\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\r\n t[p] = s[p];\r\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\r\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\r\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\r\n t[p[i]] = s[p[i]];\r\n }\r\n return t;\r\n}\r\n\r\nexport function __decorate(decorators, target, key, desc) {\r\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\r\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\r\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\r\n return c > 3 && r && Object.defineProperty(target, key, r), r;\r\n}\r\n\r\nexport function __param(paramIndex, decorator) {\r\n return function (target, key) { decorator(target, key, paramIndex); }\r\n}\r\n\r\nexport function __metadata(metadataKey, metadataValue) {\r\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\r\n}\r\n\r\nexport function __awaiter(thisArg, _arguments, P, generator) {\r\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\r\n return new (P || (P = Promise))(function (resolve, reject) {\r\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\r\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\r\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\r\n step((generator = generator.apply(thisArg, _arguments || [])).next());\r\n });\r\n}\r\n\r\nexport function __generator(thisArg, body) {\r\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;\r\n return g = { next: verb(0), \"throw\": verb(1), \"return\": verb(2) }, typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\r\n function verb(n) { return function (v) { return step([n, v]); }; }\r\n function step(op) {\r\n if (f) throw new TypeError(\"Generator is already executing.\");\r\n while (_) try {\r\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\r\n if (y = 0, t) op = [op[0] & 2, t.value];\r\n switch (op[0]) {\r\n case 0: case 1: t = op; break;\r\n case 4: _.label++; return { value: op[1], done: false };\r\n case 5: _.label++; y = op[1]; op = [0]; continue;\r\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\r\n default:\r\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\r\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\r\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\r\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\r\n if (t[2]) _.ops.pop();\r\n _.trys.pop(); continue;\r\n }\r\n op = body.call(thisArg, _);\r\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\r\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\r\n }\r\n}\r\n\r\nexport function __exportStar(m, exports) {\r\n for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];\r\n}\r\n\r\nexport function __values(o) {\r\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\r\n if (m) return m.call(o);\r\n if (o && typeof o.length === \"number\") return {\r\n next: function () {\r\n if (o && i >= o.length) o = void 0;\r\n return { value: o && o[i++], done: !o };\r\n }\r\n };\r\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\r\n}\r\n\r\nexport function __read(o, n) {\r\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\r\n if (!m) return o;\r\n var i = m.call(o), r, ar = [], e;\r\n try {\r\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\r\n }\r\n catch (error) { e = { error: error }; }\r\n finally {\r\n try {\r\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\r\n }\r\n finally { if (e) throw e.error; }\r\n }\r\n return ar;\r\n}\r\n\r\nexport function __spread() {\r\n for (var ar = [], i = 0; i < arguments.length; i++)\r\n ar = ar.concat(__read(arguments[i]));\r\n return ar;\r\n}\r\n\r\nexport function __spreadArrays() {\r\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\r\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\r\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\r\n r[k] = a[j];\r\n return r;\r\n};\r\n\r\nexport function __await(v) {\r\n return this instanceof __await ? (this.v = v, this) : new __await(v);\r\n}\r\n\r\nexport function __asyncGenerator(thisArg, _arguments, generator) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\r\n return i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i;\r\n function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }\r\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\r\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\r\n function fulfill(value) { resume(\"next\", value); }\r\n function reject(value) { resume(\"throw\", value); }\r\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\r\n}\r\n\r\nexport function __asyncDelegator(o) {\r\n var i, p;\r\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\r\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === \"return\" } : f ? f(v) : v; } : f; }\r\n}\r\n\r\nexport function __asyncValues(o) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var m = o[Symbol.asyncIterator], i;\r\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\r\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\r\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\r\n}\r\n\r\nexport function __makeTemplateObject(cooked, raw) {\r\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\r\n return cooked;\r\n};\r\n\r\nexport function __importStar(mod) {\r\n if (mod && mod.__esModule) return mod;\r\n var result = {};\r\n if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];\r\n result.default = mod;\r\n return result;\r\n}\r\n\r\nexport function __importDefault(mod) {\r\n return (mod && mod.__esModule) ? mod : { default: mod };\r\n}\r\n\r\nexport function __classPrivateFieldGet(receiver, privateMap) {\r\n if (!privateMap.has(receiver)) {\r\n throw new TypeError(\"attempted to get private field on non-instance\");\r\n }\r\n return privateMap.get(receiver);\r\n}\r\n\r\nexport function __classPrivateFieldSet(receiver, privateMap, value) {\r\n if (!privateMap.has(receiver)) {\r\n throw new TypeError(\"attempted to set private field on non-instance\");\r\n }\r\n privateMap.set(receiver, value);\r\n return value;\r\n}\r\n","/* tslint:disable:no-non-null-assertion */\r\n\r\nimport compare from 'just-compare';\r\n\r\nexport class ListNode {\r\n next: ListNode | undefined;\r\n previous: ListNode | undefined;\r\n constructor(public readonly value: T) {}\r\n}\r\n\r\nexport class LinkedList {\r\n private first: ListNode | undefined;\r\n private last: ListNode | undefined;\r\n private size = 0;\r\n\r\n get head(): ListNode | undefined {\r\n return this.first;\r\n }\r\n get tail(): ListNode | undefined {\r\n return this.last;\r\n }\r\n get length(): number {\r\n return this.size;\r\n }\r\n\r\n private attach(\r\n value: T,\r\n previousNode: ListNode | undefined,\r\n nextNode: ListNode | undefined,\r\n ): ListNode {\r\n if (!previousNode) return this.addHead(value);\r\n\r\n if (!nextNode) return this.addTail(value);\r\n\r\n const node = new ListNode(value);\r\n node.previous = previousNode;\r\n previousNode.next = node;\r\n node.next = nextNode;\r\n nextNode.previous = node;\r\n\r\n this.size++;\r\n\r\n return node;\r\n }\r\n\r\n private attachMany(\r\n values: T[],\r\n previousNode: ListNode | undefined,\r\n nextNode: ListNode | undefined,\r\n ): ListNode[] {\r\n if (!values.length) return [];\r\n\r\n if (!previousNode) return this.addManyHead(values);\r\n\r\n if (!nextNode) return this.addManyTail(values);\r\n\r\n const list = new LinkedList();\r\n list.addManyTail(values);\r\n list.first!.previous = previousNode;\r\n previousNode.next = list.first;\r\n list.last!.next = nextNode;\r\n nextNode.previous = list.last;\r\n\r\n this.size += values.length;\r\n\r\n return list.toNodeArray();\r\n }\r\n\r\n private detach(node: ListNode) {\r\n if (!node.previous) return this.dropHead();\r\n\r\n if (!node.next) return this.dropTail();\r\n\r\n node.previous.next = node.next;\r\n node.next.previous = node.previous;\r\n\r\n this.size--;\r\n\r\n return node;\r\n }\r\n\r\n add(value: T) {\r\n return {\r\n after: (...params: [T] | [any, ListComparisonFn]) =>\r\n this.addAfter.call(this, value, ...params),\r\n before: (...params: [T] | [any, ListComparisonFn]) =>\r\n this.addBefore.call(this, value, ...params),\r\n byIndex: (position: number) => this.addByIndex(value, position),\r\n head: () => this.addHead(value),\r\n tail: () => this.addTail(value),\r\n };\r\n }\r\n\r\n addMany(values: T[]) {\r\n return {\r\n after: (...params: [T] | [any, ListComparisonFn]) =>\r\n this.addManyAfter.call(this, values, ...params),\r\n before: (...params: [T] | [any, ListComparisonFn]) =>\r\n this.addManyBefore.call(this, values, ...params),\r\n byIndex: (position: number) => this.addManyByIndex(values, position),\r\n head: () => this.addManyHead(values),\r\n tail: () => this.addManyTail(values),\r\n };\r\n }\r\n\r\n addAfter(value: T, previousValue: T): ListNode;\r\n addAfter(value: T, previousValue: any, compareFn: ListComparisonFn): ListNode;\r\n addAfter(value: T, previousValue: any, compareFn: ListComparisonFn = compare): ListNode {\r\n const previous = this.find(node => compareFn(node.value, previousValue));\r\n\r\n return previous ? this.attach(value, previous, previous.next) : this.addTail(value);\r\n }\r\n\r\n addBefore(value: T, nextValue: T): ListNode;\r\n addBefore(value: T, nextValue: any, compareFn: ListComparisonFn): ListNode;\r\n addBefore(value: T, nextValue: any, compareFn: ListComparisonFn = compare): ListNode {\r\n const next = this.find(node => compareFn(node.value, nextValue));\r\n\r\n return next ? this.attach(value, next.previous, next) : this.addHead(value);\r\n }\r\n\r\n addByIndex(value: T, position: number): ListNode {\r\n if (position < 0) position += this.size;\r\n else if (position >= this.size) return this.addTail(value);\r\n\r\n if (position <= 0) return this.addHead(value);\r\n\r\n const next = this.get(position)!;\r\n\r\n return this.attach(value, next.previous, next);\r\n }\r\n\r\n addHead(value: T): ListNode {\r\n const node = new ListNode(value);\r\n\r\n node.next = this.first;\r\n\r\n if (this.first) this.first.previous = node;\r\n else this.last = node;\r\n\r\n this.first = node;\r\n this.size++;\r\n\r\n return node;\r\n }\r\n\r\n addTail(value: T): ListNode {\r\n const node = new ListNode(value);\r\n\r\n if (this.first) {\r\n node.previous = this.last;\r\n this.last!.next = node;\r\n this.last = node;\r\n } else {\r\n this.first = node;\r\n this.last = node;\r\n }\r\n\r\n this.size++;\r\n\r\n return node;\r\n }\r\n\r\n addManyAfter(values: T[], previousValue: T): ListNode[];\r\n addManyAfter(values: T[], previousValue: any, compareFn: ListComparisonFn): ListNode[];\r\n addManyAfter(\r\n values: T[],\r\n previousValue: any,\r\n compareFn: ListComparisonFn = compare,\r\n ): ListNode[] {\r\n const previous = this.find(node => compareFn(node.value, previousValue));\r\n\r\n return previous ? this.attachMany(values, previous, previous.next) : this.addManyTail(values);\r\n }\r\n\r\n addManyBefore(values: T[], nextValue: T): ListNode[];\r\n addManyBefore(values: T[], nextValue: any, compareFn: ListComparisonFn): ListNode[];\r\n addManyBefore(\r\n values: T[],\r\n nextValue: any,\r\n compareFn: ListComparisonFn = compare,\r\n ): ListNode[] {\r\n const next = this.find(node => compareFn(node.value, nextValue));\r\n\r\n return next ? this.attachMany(values, next.previous, next) : this.addManyHead(values);\r\n }\r\n\r\n addManyByIndex(values: T[], position: number): ListNode[] {\r\n if (position < 0) position += this.size;\r\n\r\n if (position <= 0) return this.addManyHead(values);\r\n\r\n if (position >= this.size) return this.addManyTail(values);\r\n\r\n const next = this.get(position)!;\r\n\r\n return this.attachMany(values, next.previous, next);\r\n }\r\n\r\n addManyHead(values: T[]): ListNode[] {\r\n return values.reduceRight[]>((nodes, value) => {\r\n nodes.unshift(this.addHead(value));\r\n return nodes;\r\n }, []);\r\n }\r\n\r\n addManyTail(values: T[]): ListNode[] {\r\n return values.map(value => this.addTail(value));\r\n }\r\n\r\n drop() {\r\n return {\r\n byIndex: (position: number) => this.dropByIndex(position),\r\n byValue: (...params: [T] | [any, ListComparisonFn]) =>\r\n this.dropByValue.apply(this, params),\r\n byValueAll: (...params: [T] | [any, ListComparisonFn]) =>\r\n this.dropByValueAll.apply(this, params),\r\n head: () => this.dropHead(),\r\n tail: () => this.dropTail(),\r\n };\r\n }\r\n\r\n dropMany(count: number) {\r\n return {\r\n byIndex: (position: number) => this.dropManyByIndex(count, position),\r\n head: () => this.dropManyHead(count),\r\n tail: () => this.dropManyTail(count),\r\n };\r\n }\r\n\r\n dropByIndex(position: number): ListNode | undefined {\r\n if (position < 0) position += this.size;\r\n\r\n const current = this.get(position);\r\n\r\n return current ? this.detach(current) : undefined;\r\n }\r\n\r\n dropByValue(value: T): ListNode | undefined;\r\n dropByValue(value: any, compareFn: ListComparisonFn): ListNode | undefined;\r\n dropByValue(value: any, compareFn: ListComparisonFn = compare): ListNode | undefined {\r\n const position = this.findIndex(node => compareFn(node.value, value));\r\n\r\n return position < 0 ? undefined : this.dropByIndex(position);\r\n }\r\n\r\n dropByValueAll(value: T): ListNode[];\r\n dropByValueAll(value: any, compareFn: ListComparisonFn): ListNode[];\r\n dropByValueAll(value: any, compareFn: ListComparisonFn = compare): ListNode[] {\r\n const dropped: ListNode[] = [];\r\n\r\n for (let current = this.first, position = 0; current; position++, current = current.next) {\r\n if (compareFn(current.value, value)) {\r\n dropped.push(this.dropByIndex(position - dropped.length)!);\r\n }\r\n }\r\n\r\n return dropped;\r\n }\r\n\r\n dropHead(): ListNode | undefined {\r\n const head = this.first;\r\n\r\n if (head) {\r\n this.first = head.next;\r\n\r\n if (this.first) this.first.previous = undefined;\r\n else this.last = undefined;\r\n\r\n this.size--;\r\n\r\n return head;\r\n }\r\n\r\n return undefined;\r\n }\r\n\r\n dropTail(): ListNode | undefined {\r\n const tail = this.last;\r\n\r\n if (tail) {\r\n this.last = tail.previous;\r\n\r\n if (this.last) this.last.next = undefined;\r\n else this.first = undefined;\r\n\r\n this.size--;\r\n\r\n return tail;\r\n }\r\n\r\n return undefined;\r\n }\r\n\r\n dropManyByIndex(count: number, position: number): ListNode[] {\r\n if (count <= 0) return [];\r\n\r\n if (position < 0) position = Math.max(position + this.size, 0);\r\n else if (position >= this.size) return [];\r\n\r\n count = Math.min(count, this.size - position);\r\n\r\n const dropped: ListNode[] = [];\r\n\r\n while (count--) {\r\n const current = this.get(position);\r\n dropped.push(this.detach(current!)!);\r\n }\r\n\r\n return dropped;\r\n }\r\n\r\n dropManyHead(count: Exclude): ListNode[] {\r\n if (count <= 0) return [];\r\n\r\n count = Math.min(count, this.size);\r\n\r\n const dropped: ListNode[] = [];\r\n\r\n while (count--) dropped.unshift(this.dropHead()!);\r\n\r\n return dropped;\r\n }\r\n\r\n dropManyTail(count: Exclude): ListNode[] {\r\n if (count <= 0) return [];\r\n\r\n count = Math.min(count, this.size);\r\n\r\n const dropped: ListNode[] = [];\r\n\r\n while (count--) dropped.push(this.dropTail()!);\r\n\r\n return dropped;\r\n }\r\n\r\n find(predicate: ListIteratorFn): ListNode | undefined {\r\n for (let current = this.first, position = 0; current; position++, current = current.next) {\r\n if (predicate(current, position, this)) return current;\r\n }\r\n\r\n return undefined;\r\n }\r\n\r\n findIndex(predicate: ListIteratorFn): number {\r\n for (let current = this.first, position = 0; current; position++, current = current.next) {\r\n if (predicate(current, position, this)) return position;\r\n }\r\n\r\n return -1;\r\n }\r\n\r\n forEach(iteratorFn: ListIteratorFn) {\r\n for (let node = this.first, position = 0; node; position++, node = node.next) {\r\n iteratorFn(node, position, this);\r\n }\r\n }\r\n\r\n get(position: number): ListNode | undefined {\r\n return this.find((_, index) => position === index);\r\n }\r\n\r\n indexOf(value: T): number;\r\n indexOf(value: any, compareFn: ListComparisonFn): number;\r\n indexOf(value: any, compareFn: ListComparisonFn = compare): number {\r\n return this.findIndex(node => compareFn(node.value, value));\r\n }\r\n\r\n toArray(): T[] {\r\n const array = new Array(this.size);\r\n\r\n this.forEach((node, index) => (array[index!] = node.value));\r\n\r\n return array;\r\n }\r\n\r\n toNodeArray(): ListNode[] {\r\n const array = new Array(this.size);\r\n\r\n this.forEach((node, index) => (array[index!] = node));\r\n\r\n return array;\r\n }\r\n\r\n toString(mapperFn: ListMapperFn = JSON.stringify): string {\r\n return this.toArray()\r\n .map(value => mapperFn(value))\r\n .join(' <-> ');\r\n }\r\n\r\n // Cannot use Generator type because of ng-packagr\r\n *[Symbol.iterator](): any {\r\n for (let node = this.first, position = 0; node; position++, node = node.next) {\r\n yield node.value;\r\n }\r\n }\r\n}\r\n\r\nexport type ListMapperFn = (value: T) => any;\r\n\r\nexport type ListComparisonFn = (value1: T, value2: any) => boolean;\r\n\r\nexport type ListIteratorFn = (\r\n node: ListNode,\r\n index?: number,\r\n list?: LinkedList,\r\n) => R;\r\n"],"names":[],"mappings":";;;;;;;;IAAA;IACA;IACA;IACA;IACA;AACA;IACA;IACA;IACA;IACA;AACA;IACA;IACA;IACA;IACA;AACA;IACA,IAAI,aAAa,GAAG,SAAS,CAAC,EAAE,CAAC,EAAE;IACnC,IAAI,aAAa,GAAG,MAAM,CAAC,cAAc;IACzC,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,YAAY,KAAK,IAAI,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,EAAE,CAAC;IACpF,QAAQ,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,KAAK,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACnF,IAAI,OAAO,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC/B,CAAC,CAAC;AACF;IACO,SAAS,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE;IAChC,IAAI,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACxB,IAAI,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC,EAAE;IAC3C,IAAI,CAAC,CAAC,SAAS,GAAG,CAAC,KAAK,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;IACzF,CAAC;AACD;IACO,IAAI,QAAQ,GAAG,WAAW;IACjC,IAAI,QAAQ,GAAG,MAAM,CAAC,MAAM,IAAI,SAAS,QAAQ,CAAC,CAAC,EAAE;IACrD,QAAQ,KAAK,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE;IAC7D,YAAY,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;IAC7B,YAAY,KAAK,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACzF,SAAS;IACT,QAAQ,OAAO,CAAC,CAAC;IACjB,MAAK;IACL,IAAI,OAAO,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAC3C,EAAC;AACD;IACO,SAAS,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE;IAC7B,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;IACf,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;IACvF,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACpB,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,OAAO,MAAM,CAAC,qBAAqB,KAAK,UAAU;IACvE,QAAQ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,qBAAqB,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;IAChF,YAAY,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,SAAS,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1F,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAClC,SAAS;IACT,IAAI,OAAO,CAAC,CAAC;IACb,CAAC;AACD;IACO,SAAS,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE;IAC1D,IAAI,IAAI,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,GAAG,IAAI,KAAK,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC,wBAAwB,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC;IACjI,IAAI,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,OAAO,CAAC,QAAQ,KAAK,UAAU,EAAE,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IACnI,SAAS,KAAK,IAAI,CAAC,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;IACtJ,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IAClE,CAAC;AACD;IACO,SAAS,OAAO,CAAC,UAAU,EAAE,SAAS,EAAE;IAC/C,IAAI,OAAO,UAAU,MAAM,EAAE,GAAG,EAAE,EAAE,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC,EAAE;IACzE,CAAC;AACD;IACO,SAAS,UAAU,CAAC,WAAW,EAAE,aAAa,EAAE;IACvD,IAAI,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,OAAO,CAAC,QAAQ,KAAK,UAAU,EAAE,OAAO,OAAO,CAAC,QAAQ,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;IACnI,CAAC;AACD;IACO,SAAS,SAAS,CAAC,OAAO,EAAE,UAAU,EAAE,CAAC,EAAE,SAAS,EAAE;IAC7D,IAAI,SAAS,KAAK,CAAC,KAAK,EAAE,EAAE,OAAO,KAAK,YAAY,CAAC,GAAG,KAAK,GAAG,IAAI,CAAC,CAAC,UAAU,OAAO,EAAE,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE;IAChH,IAAI,OAAO,KAAK,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,EAAE,UAAU,OAAO,EAAE,MAAM,EAAE;IAC/D,QAAQ,SAAS,SAAS,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE;IACnG,QAAQ,SAAS,QAAQ,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE;IACtG,QAAQ,SAAS,IAAI,CAAC,MAAM,EAAE,EAAE,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,EAAE;IACtH,QAAQ,IAAI,CAAC,CAAC,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,OAAO,EAAE,UAAU,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9E,KAAK,CAAC,CAAC;IACP,CAAC;AACD;IACO,SAAS,WAAW,CAAC,OAAO,EAAE,IAAI,EAAE;IAC3C,IAAI,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IACrH,IAAI,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,OAAO,MAAM,KAAK,UAAU,KAAK,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,WAAW,EAAE,OAAO,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IAC7J,IAAI,SAAS,IAAI,CAAC,CAAC,EAAE,EAAE,OAAO,UAAU,CAAC,EAAE,EAAE,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE;IACtE,IAAI,SAAS,IAAI,CAAC,EAAE,EAAE;IACtB,QAAQ,IAAI,CAAC,EAAE,MAAM,IAAI,SAAS,CAAC,iCAAiC,CAAC,CAAC;IACtE,QAAQ,OAAO,CAAC,EAAE,IAAI;IACtB,YAAY,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IACzK,YAAY,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;IACpD,YAAY,QAAQ,EAAE,CAAC,CAAC,CAAC;IACzB,gBAAgB,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,MAAM;IAC9C,gBAAgB,KAAK,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IACxE,gBAAgB,KAAK,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS;IACjE,gBAAgB,KAAK,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,SAAS;IACjE,gBAAgB;IAChB,oBAAoB,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE;IAChI,oBAAoB,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE;IAC1G,oBAAoB,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;IACzF,oBAAoB,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE;IACvF,oBAAoB,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;IAC1C,oBAAoB,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,SAAS;IAC3C,aAAa;IACb,YAAY,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACvC,SAAS,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE;IAClE,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACzF,KAAK;IACL,CAAC;AACD;IACO,SAAS,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE;IACzC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACvE,CAAC;AACD;IACO,SAAS,QAAQ,CAAC,CAAC,EAAE;IAC5B,IAAI,IAAI,CAAC,GAAG,OAAO,MAAM,KAAK,UAAU,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IAClF,IAAI,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC5B,IAAI,IAAI,CAAC,IAAI,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ,EAAE,OAAO;IAClD,QAAQ,IAAI,EAAE,YAAY;IAC1B,YAAY,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC;IAC/C,YAAY,OAAO,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;IACpD,SAAS;IACT,KAAK,CAAC;IACN,IAAI,MAAM,IAAI,SAAS,CAAC,CAAC,GAAG,yBAAyB,GAAG,iCAAiC,CAAC,CAAC;IAC3F,CAAC;AACD;IACO,SAAS,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE;IAC7B,IAAI,IAAI,CAAC,GAAG,OAAO,MAAM,KAAK,UAAU,IAAI,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC/D,IAAI,IAAI,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IACrB,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;IACrC,IAAI,IAAI;IACR,QAAQ,OAAO,CAAC,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IACnF,KAAK;IACL,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE;IAC3C,YAAY;IACZ,QAAQ,IAAI;IACZ,YAAY,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC7D,SAAS;IACT,gBAAgB,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,EAAE;IACzC,KAAK;IACL,IAAI,OAAO,EAAE,CAAC;IACd,CAAC;AACD;IACO,SAAS,QAAQ,GAAG;IAC3B,IAAI,KAAK,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE;IACtD,QAAQ,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7C,IAAI,OAAO,EAAE,CAAC;IACd,CAAC;AACD;IACO,SAAS,cAAc,GAAG;IACjC,IAAI,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IACxF,IAAI,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE;IACpD,QAAQ,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE;IACzE,YAAY,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACxB,IAAI,OAAO,CAAC,CAAC;IACb,CAAC,CAAC;AACF;IACO,SAAS,OAAO,CAAC,CAAC,EAAE;IAC3B,IAAI,OAAO,IAAI,YAAY,OAAO,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,IAAI,IAAI,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC;IACzE,CAAC;AACD;IACO,SAAS,gBAAgB,CAAC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE;IACjE,IAAI,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,IAAI,SAAS,CAAC,sCAAsC,CAAC,CAAC;IAC3F,IAAI,IAAI,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,OAAO,EAAE,UAAU,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC;IAClE,IAAI,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,YAAY,EAAE,OAAO,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;IAC1H,IAAI,SAAS,IAAI,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,EAAE,EAAE,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE;IAC9I,IAAI,SAAS,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE;IACtF,IAAI,SAAS,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,YAAY,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE;IAC5H,IAAI,SAAS,OAAO,CAAC,KAAK,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,EAAE;IACtD,IAAI,SAAS,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,EAAE;IACtD,IAAI,SAAS,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;IACtF,CAAC;AACD;IACO,SAAS,gBAAgB,CAAC,CAAC,EAAE;IACpC,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC;IACb,IAAI,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,YAAY,EAAE,OAAO,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;IAChJ,IAAI,SAAS,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,KAAK,QAAQ,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE;IACnJ,CAAC;AACD;IACO,SAAS,aAAa,CAAC,CAAC,EAAE;IACjC,IAAI,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,IAAI,SAAS,CAAC,sCAAsC,CAAC,CAAC;IAC3F,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;IACvC,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,QAAQ,KAAK,UAAU,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,YAAY,EAAE,OAAO,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IACrN,IAAI,SAAS,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,EAAE,EAAE,OAAO,IAAI,OAAO,CAAC,UAAU,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE;IACpK,IAAI,SAAS,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,EAAE;IAChI,CAAC;AACD;IACO,SAAS,oBAAoB,CAAC,MAAM,EAAE,GAAG,EAAE;IAClD,IAAI,IAAI,MAAM,CAAC,cAAc,EAAE,EAAE,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,GAAG,GAAG,CAAC,EAAE;IACnH,IAAI,OAAO,MAAM,CAAC;IAClB,CAAC,CAAC;AACF;IACO,SAAS,YAAY,CAAC,GAAG,EAAE;IAClC,IAAI,IAAI,GAAG,IAAI,GAAG,CAAC,UAAU,EAAE,OAAO,GAAG,CAAC;IAC1C,IAAI,IAAI,MAAM,GAAG,EAAE,CAAC;IACpB,IAAI,IAAI,GAAG,IAAI,IAAI,EAAE,KAAK,IAAI,CAAC,IAAI,GAAG,EAAE,IAAI,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;IACnG,IAAI,MAAM,CAAC,OAAO,GAAG,GAAG,CAAC;IACzB,IAAI,OAAO,MAAM,CAAC;IAClB,CAAC;AACD;IACO,SAAS,eAAe,CAAC,GAAG,EAAE;IACrC,IAAI,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,UAAU,IAAI,GAAG,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;IAC5D,CAAC;AACD;IACO,SAAS,sBAAsB,CAAC,QAAQ,EAAE,UAAU,EAAE;IAC7D,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE;IACnC,QAAQ,MAAM,IAAI,SAAS,CAAC,gDAAgD,CAAC,CAAC;IAC9E,KAAK;IACL,IAAI,OAAO,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;AACD;IACO,SAAS,sBAAsB,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE;IACpE,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE;IACnC,QAAQ,MAAM,IAAI,SAAS,CAAC,gDAAgD,CAAC,CAAC;IAC9E,KAAK;IACL,IAAI,UAAU,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IACpC,IAAI,OAAO,KAAK,CAAC;IACjB;;ICpNA;;QAOE,kBAA4B,KAAQ;YAAR,UAAK,GAAL,KAAK,CAAG;SAAI;QAC1C,eAAC;IAAD,CAAC,IAAA;;QAED;YAGU,SAAI,GAAG,CAAC,CAAC;SA+XlB;QA7XC,sBAAI,4BAAI;iBAAR;gBACE,OAAO,IAAI,CAAC,KAAK,CAAC;aACnB;;;WAAA;QACD,sBAAI,4BAAI;iBAAR;gBACE,OAAO,IAAI,CAAC,IAAI,CAAC;aAClB;;;WAAA;QACD,sBAAI,8BAAM;iBAAV;gBACE,OAAO,IAAI,CAAC,IAAI,CAAC;aAClB;;;WAAA;QAEO,2BAAM,GAAd,UACE,KAAQ,EACR,YAAqC,EACrC,QAAiC;YAEjC,IAAI,CAAC,YAAY;gBAAE,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YAE9C,IAAI,CAAC,QAAQ;gBAAE,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YAE1C,IAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC;YACjC,IAAI,CAAC,QAAQ,GAAG,YAAY,CAAC;YAC7B,YAAY,CAAC,IAAI,GAAG,IAAI,CAAC;YACzB,IAAI,CAAC,IAAI,GAAG,QAAQ,CAAC;YACrB,QAAQ,CAAC,QAAQ,GAAG,IAAI,CAAC;YAEzB,IAAI,CAAC,IAAI,EAAE,CAAC;YAEZ,OAAO,IAAI,CAAC;SACb;QAEO,+BAAU,GAAlB,UACE,MAAW,EACX,YAAqC,EACrC,QAAiC;YAEjC,IAAI,CAAC,MAAM,CAAC,MAAM;gBAAE,OAAO,EAAE,CAAC;YAE9B,IAAI,CAAC,YAAY;gBAAE,OAAO,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YAEnD,IAAI,CAAC,QAAQ;gBAAE,OAAO,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YAE/C,IAAM,IAAI,GAAG,IAAI,UAAU,EAAK,CAAC;YACjC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YACzB,IAAI,CAAC,KAAM,CAAC,QAAQ,GAAG,YAAY,CAAC;YACpC,YAAY,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC;YAC/B,IAAI,CAAC,IAAK,CAAC,IAAI,GAAG,QAAQ,CAAC;YAC3B,QAAQ,CAAC,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC;YAE9B,IAAI,CAAC,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC;YAE3B,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;SAC3B;QAEO,2BAAM,GAAd,UAAe,IAAiB;YAC9B,IAAI,CAAC,IAAI,CAAC,QAAQ;gBAAE,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC;YAE3C,IAAI,CAAC,IAAI,CAAC,IAAI;gBAAE,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC;YAEvC,IAAI,CAAC,QAAQ,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;YAC/B,IAAI,CAAC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;YAEnC,IAAI,CAAC,IAAI,EAAE,CAAC;YAEZ,OAAO,IAAI,CAAC;SACb;QAED,wBAAG,GAAH,UAAI,KAAQ;YAAZ,iBAUC;YATC,OAAO;gBACL,KAAK,EAAE;;oBAAC,gBAA2C;yBAA3C,UAA2C,EAA3C,qBAA2C,EAA3C,IAA2C;wBAA3C,2BAA2C;;oBACjD,OAAA,CAAA,KAAA,KAAI,CAAC,QAAQ,EAAC,IAAI,qBAAC,KAAI,EAAE,KAAK,GAAK,MAAM;iBAAC;gBAC5C,MAAM,EAAE;;oBAAC,gBAA2C;yBAA3C,UAA2C,EAA3C,qBAA2C,EAA3C,IAA2C;wBAA3C,2BAA2C;;oBAClD,OAAA,CAAA,KAAA,KAAI,CAAC,SAAS,EAAC,IAAI,qBAAC,KAAI,EAAE,KAAK,GAAK,MAAM;iBAAC;gBAC7C,OAAO,EAAE,UAAC,QAAgB,IAAK,OAAA,KAAI,CAAC,UAAU,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAA;gBAC/D,IAAI,EAAE,cAAM,OAAA,KAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAA;gBAC/B,IAAI,EAAE,cAAM,OAAA,KAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAA;aAChC,CAAC;SACH;QAED,4BAAO,GAAP,UAAQ,MAAW;YAAnB,iBAUC;YATC,OAAO;gBACL,KAAK,EAAE;;oBAAC,gBAA2C;yBAA3C,UAA2C,EAA3C,qBAA2C,EAA3C,IAA2C;wBAA3C,2BAA2C;;oBACjD,OAAA,CAAA,KAAA,KAAI,CAAC,YAAY,EAAC,IAAI,qBAAC,KAAI,EAAE,MAAM,GAAK,MAAM;iBAAC;gBACjD,MAAM,EAAE;;oBAAC,gBAA2C;yBAA3C,UAA2C,EAA3C,qBAA2C,EAA3C,IAA2C;wBAA3C,2BAA2C;;oBAClD,OAAA,CAAA,KAAA,KAAI,CAAC,aAAa,EAAC,IAAI,qBAAC,KAAI,EAAE,MAAM,GAAK,MAAM;iBAAC;gBAClD,OAAO,EAAE,UAAC,QAAgB,IAAK,OAAA,KAAI,CAAC,cAAc,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAA;gBACpE,IAAI,EAAE,cAAM,OAAA,KAAI,CAAC,WAAW,CAAC,MAAM,CAAC,GAAA;gBACpC,IAAI,EAAE,cAAM,OAAA,KAAI,CAAC,WAAW,CAAC,MAAM,CAAC,GAAA;aACrC,CAAC;SACH;QAID,6BAAQ,GAAR,UAAS,KAAQ,EAAE,aAAkB,EAAE,SAAwC;YAAxC,0BAAA,EAAA,mBAAwC;YAC7E,IAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAA,IAAI,IAAI,OAAA,SAAS,CAAC,IAAI,CAAC,KAAK,EAAE,aAAa,CAAC,GAAA,CAAC,CAAC;YAEzE,OAAO,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;SACrF;QAID,8BAAS,GAAT,UAAU,KAAQ,EAAE,SAAc,EAAE,SAAwC;YAAxC,0BAAA,EAAA,mBAAwC;YAC1E,IAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,UAAA,IAAI,IAAI,OAAA,SAAS,CAAC,IAAI,CAAC,KAAK,EAAE,SAAS,CAAC,GAAA,CAAC,CAAC;YAEjE,OAAO,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;SAC7E;QAED,+BAAU,GAAV,UAAW,KAAQ,EAAE,QAAgB;YACnC,IAAI,QAAQ,GAAG,CAAC;gBAAE,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC;iBACnC,IAAI,QAAQ,IAAI,IAAI,CAAC,IAAI;gBAAE,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YAE3D,IAAI,QAAQ,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YAE9C,IAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAE,CAAC;YAEjC,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;SAChD;QAED,4BAAO,GAAP,UAAQ,KAAQ;YACd,IAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC;YAEjC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC;YAEvB,IAAI,IAAI,CAAC,KAAK;gBAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC;;gBACtC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;YAEtB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;YAClB,IAAI,CAAC,IAAI,EAAE,CAAC;YAEZ,OAAO,IAAI,CAAC;SACb;QAED,4BAAO,GAAP,UAAQ,KAAQ;YACd,IAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC;YAEjC,IAAI,IAAI,CAAC,KAAK,EAAE;gBACd,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC;gBAC1B,IAAI,CAAC,IAAK,CAAC,IAAI,GAAG,IAAI,CAAC;gBACvB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;aAClB;iBAAM;gBACL,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;gBAClB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;aAClB;YAED,IAAI,CAAC,IAAI,EAAE,CAAC;YAEZ,OAAO,IAAI,CAAC;SACb;QAID,iCAAY,GAAZ,UACE,MAAW,EACX,aAAkB,EAClB,SAAwC;YAAxC,0BAAA,EAAA,mBAAwC;YAExC,IAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAA,IAAI,IAAI,OAAA,SAAS,CAAC,IAAI,CAAC,KAAK,EAAE,aAAa,CAAC,GAAA,CAAC,CAAC;YAEzE,OAAO,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;SAC/F;QAID,kCAAa,GAAb,UACE,MAAW,EACX,SAAc,EACd,SAAwC;YAAxC,0BAAA,EAAA,mBAAwC;YAExC,IAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,UAAA,IAAI,IAAI,OAAA,SAAS,CAAC,IAAI,CAAC,KAAK,EAAE,SAAS,CAAC,GAAA,CAAC,CAAC;YAEjE,OAAO,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;SACvF;QAED,mCAAc,GAAd,UAAe,MAAW,EAAE,QAAgB;YAC1C,IAAI,QAAQ,GAAG,CAAC;gBAAE,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC;YAExC,IAAI,QAAQ,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YAEnD,IAAI,QAAQ,IAAI,IAAI,CAAC,IAAI;gBAAE,OAAO,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YAE3D,IAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAE,CAAC;YAEjC,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;SACrD;QAED,gCAAW,GAAX,UAAY,MAAW;YAAvB,iBAKC;YAJC,OAAO,MAAM,CAAC,WAAW,CAAgB,UAAC,KAAK,EAAE,KAAK;gBACpD,KAAK,CAAC,OAAO,CAAC,KAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;gBACnC,OAAO,KAAK,CAAC;aACd,EAAE,EAAE,CAAC,CAAC;SACR;QAED,gCAAW,GAAX,UAAY,MAAW;YAAvB,iBAEC;YADC,OAAO,MAAM,CAAC,GAAG,CAAC,UAAA,KAAK,IAAI,OAAA,KAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAA,CAAC,CAAC;SACjD;QAED,yBAAI,GAAJ;YAAA,iBAUC;YATC,OAAO;gBACL,OAAO,EAAE,UAAC,QAAgB,IAAK,OAAA,KAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,GAAA;gBACzD,OAAO,EAAE;oBAAC,gBAA2C;yBAA3C,UAA2C,EAA3C,qBAA2C,EAA3C,IAA2C;wBAA3C,2BAA2C;;oBACnD,OAAA,KAAI,CAAC,WAAW,CAAC,KAAK,CAAC,KAAI,EAAE,MAAM,CAAC;iBAAA;gBACtC,UAAU,EAAE;oBAAC,gBAA2C;yBAA3C,UAA2C,EAA3C,qBAA2C,EAA3C,IAA2C;wBAA3C,2BAA2C;;oBACtD,OAAA,KAAI,CAAC,cAAc,CAAC,KAAK,CAAC,KAAI,EAAE,MAAM,CAAC;iBAAA;gBACzC,IAAI,EAAE,cAAM,OAAA,KAAI,CAAC,QAAQ,EAAE,GAAA;gBAC3B,IAAI,EAAE,cAAM,OAAA,KAAI,CAAC,QAAQ,EAAE,GAAA;aAC5B,CAAC;SACH;QAED,6BAAQ,GAAR,UAAS,KAAa;YAAtB,iBAMC;YALC,OAAO;gBACL,OAAO,EAAE,UAAC,QAAgB,IAAK,OAAA,KAAI,CAAC,eAAe,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAA;gBACpE,IAAI,EAAE,cAAM,OAAA,KAAI,CAAC,YAAY,CAAC,KAAK,CAAC,GAAA;gBACpC,IAAI,EAAE,cAAM,OAAA,KAAI,CAAC,YAAY,CAAC,KAAK,CAAC,GAAA;aACrC,CAAC;SACH;QAED,gCAAW,GAAX,UAAY,QAAgB;YAC1B,IAAI,QAAQ,GAAG,CAAC;gBAAE,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC;YAExC,IAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAEnC,OAAO,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC;SACnD;QAID,gCAAW,GAAX,UAAY,KAAU,EAAE,SAAwC;YAAxC,0BAAA,EAAA,mBAAwC;YAC9D,IAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,UAAA,IAAI,IAAI,OAAA,SAAS,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,GAAA,CAAC,CAAC;YAEtE,OAAO,QAAQ,GAAG,CAAC,GAAG,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;SAC9D;QAID,mCAAc,GAAd,UAAe,KAAU,EAAE,SAAwC;YAAxC,0BAAA,EAAA,mBAAwC;YACjE,IAAM,OAAO,GAAkB,EAAE,CAAC;YAElC,KAAK,IAAI,OAAO,GAAG,IAAI,CAAC,KAAK,EAAE,QAAQ,GAAG,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE;gBACxF,IAAI,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE;oBACnC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAE,CAAC,CAAC;iBAC5D;aACF;YAED,OAAO,OAAO,CAAC;SAChB;QAED,6BAAQ,GAAR;YACE,IAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC;YAExB,IAAI,IAAI,EAAE;gBACR,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC;gBAEvB,IAAI,IAAI,CAAC,KAAK;oBAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,SAAS,CAAC;;oBAC3C,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC;gBAE3B,IAAI,CAAC,IAAI,EAAE,CAAC;gBAEZ,OAAO,IAAI,CAAC;aACb;YAED,OAAO,SAAS,CAAC;SAClB;QAED,6BAAQ,GAAR;YACE,IAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;YAEvB,IAAI,IAAI,EAAE;gBACR,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC;gBAE1B,IAAI,IAAI,CAAC,IAAI;oBAAE,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC;;oBACrC,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;gBAE5B,IAAI,CAAC,IAAI,EAAE,CAAC;gBAEZ,OAAO,IAAI,CAAC;aACb;YAED,OAAO,SAAS,CAAC;SAClB;QAED,oCAAe,GAAf,UAAgB,KAAa,EAAE,QAAgB;YAC7C,IAAI,KAAK,IAAI,CAAC;gBAAE,OAAO,EAAE,CAAC;YAE1B,IAAI,QAAQ,GAAG,CAAC;gBAAE,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;iBAC1D,IAAI,QAAQ,IAAI,IAAI,CAAC,IAAI;gBAAE,OAAO,EAAE,CAAC;YAE1C,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,GAAG,QAAQ,CAAC,CAAC;YAE9C,IAAM,OAAO,GAAkB,EAAE,CAAC;YAElC,OAAO,KAAK,EAAE,EAAE;gBACd,IAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;gBACnC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAQ,CAAE,CAAC,CAAC;aACtC;YAED,OAAO,OAAO,CAAC;SAChB;QAED,iCAAY,GAAZ,UAAa,KAAyB;YACpC,IAAI,KAAK,IAAI,CAAC;gBAAE,OAAO,EAAE,CAAC;YAE1B,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;YAEnC,IAAM,OAAO,GAAkB,EAAE,CAAC;YAElC,OAAO,KAAK,EAAE;gBAAE,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAG,CAAC,CAAC;YAElD,OAAO,OAAO,CAAC;SAChB;QAED,iCAAY,GAAZ,UAAa,KAAyB;YACpC,IAAI,KAAK,IAAI,CAAC;gBAAE,OAAO,EAAE,CAAC;YAE1B,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;YAEnC,IAAM,OAAO,GAAkB,EAAE,CAAC;YAElC,OAAO,KAAK,EAAE;gBAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAG,CAAC,CAAC;YAE/C,OAAO,OAAO,CAAC;SAChB;QAED,yBAAI,GAAJ,UAAK,SAA4B;YAC/B,KAAK,IAAI,OAAO,GAAG,IAAI,CAAC,KAAK,EAAE,QAAQ,GAAG,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE;gBACxF,IAAI,SAAS,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC;oBAAE,OAAO,OAAO,CAAC;aACxD;YAED,OAAO,SAAS,CAAC;SAClB;QAED,8BAAS,GAAT,UAAU,SAA4B;YACpC,KAAK,IAAI,OAAO,GAAG,IAAI,CAAC,KAAK,EAAE,QAAQ,GAAG,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE;gBACxF,IAAI,SAAS,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC;oBAAE,OAAO,QAAQ,CAAC;aACzD;YAED,OAAO,CAAC,CAAC,CAAC;SACX;QAED,4BAAO,GAAP,UAAqB,UAAgC;YACnD,KAAK,IAAI,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE,QAAQ,GAAG,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE;gBAC5E,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;aAClC;SACF;QAED,wBAAG,GAAH,UAAI,QAAgB;YAClB,OAAO,IAAI,CAAC,IAAI,CAAC,UAAC,CAAC,EAAE,KAAK,IAAK,OAAA,QAAQ,KAAK,KAAK,GAAA,CAAC,CAAC;SACpD;QAID,4BAAO,GAAP,UAAQ,KAAU,EAAE,SAAwC;YAAxC,0BAAA,EAAA,mBAAwC;YAC1D,OAAO,IAAI,CAAC,SAAS,CAAC,UAAA,IAAI,IAAI,OAAA,SAAS,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,GAAA,CAAC,CAAC;SAC7D;QAED,4BAAO,GAAP;YACE,IAAM,KAAK,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAEnC,IAAI,CAAC,OAAO,CAAC,UAAC,IAAI,EAAE,KAAK,IAAK,QAAC,KAAK,CAAC,KAAM,CAAC,GAAG,IAAI,CAAC,KAAK,IAAC,CAAC,CAAC;YAE5D,OAAO,KAAK,CAAC;SACd;QAED,gCAAW,GAAX;YACE,IAAM,KAAK,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAEnC,IAAI,CAAC,OAAO,CAAC,UAAC,IAAI,EAAE,KAAK,IAAK,QAAC,KAAK,CAAC,KAAM,CAAC,GAAG,IAAI,IAAC,CAAC,CAAC;YAEtD,OAAO,KAAK,CAAC;SACd;QAED,6BAAQ,GAAR,UAAS,QAA0C;YAA1C,yBAAA,EAAA,WAA4B,IAAI,CAAC,SAAS;YACjD,OAAO,IAAI,CAAC,OAAO,EAAE;iBAClB,GAAG,CAAC,UAAA,KAAK,IAAI,OAAA,QAAQ,CAAC,KAAK,CAAC,GAAA,CAAC;iBAC7B,IAAI,CAAC,OAAO,CAAC,CAAC;SAClB;;QAGA,qBAAC,MAAM,CAAC,QAAQ,CAAC,GAAlB;;;;;wBACW,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE,QAAQ,GAAG,CAAC;;;6BAAE,IAAI;wBAC5C,qBAAM,IAAI,CAAC,KAAK,EAAA;;wBAAhB,SAAgB,CAAC;;;wBAD6B,QAAQ,EAAE,EAAE,IAAI,GAAG,IAAI,CAAC,IAAI,CAAA;;;;;SAG7E;QACH,iBAAC;IAAD,CAAC;;;;;;;;;;;;;"} \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/abp/utils/abp-utils.umd.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/abp/utils/abp-utils.umd.min.js new file mode 100644 index 0000000000..b65fa8f40f --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/abp/utils/abp-utils.umd.min.js @@ -0,0 +1,2 @@ +!function(t,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports,require("just-compare")):"function"==typeof define&&define.amd?define("@abp/utils",["exports","just-compare"],r):r(((t=t||self).abp=t.abp||{},t.abp.utils=t.abp.utils||{},t.abp.utils.common={}),t.compare)}(this,(function(t,r){"use strict";r=r&&Object.prototype.hasOwnProperty.call(r,"default")?r.default:r;function e(t,r){var e,n,i,o,a={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:u(0),throw:u(1),return:u(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function u(o){return function(u){return function(o){if(e)throw new TypeError("Generator is already executing.");for(;a;)try{if(e=1,n&&(i=2&o[0]?n.return:o[0]?n.throw||((i=n.return)&&i.call(n),0):n.next)&&!(i=i.call(n,o[1])).done)return i;switch(n=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return a.label++,{value:o[1],done:!1};case 5:a.label++,n=o[1],o=[0];continue;case 7:o=a.ops.pop(),a.trys.pop();continue;default:if(!(i=a.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){a=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]0)&&!(n=o.next()).done;)a.push(n.value)}catch(t){i={error:t}}finally{try{n&&!n.done&&(e=o.return)&&e.call(o)}finally{if(i)throw i.error}}return a}function i(){for(var t=[],r=0;r=this.size)return this.addTail(t);if(r<=0)return this.addHead(t);var e=this.get(r);return this.attach(t,e.previous,e)},t.prototype.addHead=function(t){var r=new o(t);return r.next=this.first,this.first?this.first.previous=r:this.last=r,this.first=r,this.size++,r},t.prototype.addTail=function(t){var r=new o(t);return this.first?(r.previous=this.last,this.last.next=r,this.last=r):(this.first=r,this.last=r),this.size++,r},t.prototype.addManyAfter=function(t,e,n){void 0===n&&(n=r);var i=this.find((function(t){return n(t.value,e)}));return i?this.attachMany(t,i,i.next):this.addManyTail(t)},t.prototype.addManyBefore=function(t,e,n){void 0===n&&(n=r);var i=this.find((function(t){return n(t.value,e)}));return i?this.attachMany(t,i.previous,i):this.addManyHead(t)},t.prototype.addManyByIndex=function(t,r){if(r<0&&(r+=this.size),r<=0)return this.addManyHead(t);if(r>=this.size)return this.addManyTail(t);var e=this.get(r);return this.attachMany(t,e.previous,e)},t.prototype.addManyHead=function(t){var r=this;return t.reduceRight((function(t,e){return t.unshift(r.addHead(e)),t}),[])},t.prototype.addManyTail=function(t){var r=this;return t.map((function(t){return r.addTail(t)}))},t.prototype.drop=function(){var t=this;return{byIndex:function(r){return t.dropByIndex(r)},byValue:function(){for(var r=[],e=0;e=this.size)return[];t=Math.min(t,this.size-r);for(var e=[];t--;){var n=this.get(r);e.push(this.detach(n))}return e},t.prototype.dropManyHead=function(t){if(t<=0)return[];t=Math.min(t,this.size);for(var r=[];t--;)r.unshift(this.dropHead());return r},t.prototype.dropManyTail=function(t){if(t<=0)return[];t=Math.min(t,this.size);for(var r=[];t--;)r.push(this.dropTail());return r},t.prototype.find=function(t){for(var r=this.first,e=0;r;e++,r=r.next)if(t(r,e,this))return r},t.prototype.findIndex=function(t){for(var r=this.first,e=0;r;e++,r=r.next)if(t(r,e,this))return e;return-1},t.prototype.forEach=function(t){for(var r=this.first,e=0;r;e++,r=r.next)t(r,e,this)},t.prototype.get=function(t){return this.find((function(r,e){return t===e}))},t.prototype.indexOf=function(t,e){return void 0===e&&(e=r),this.findIndex((function(r){return e(r.value,t)}))},t.prototype.toArray=function(){var t=new Array(this.size);return this.forEach((function(r,e){return t[e]=r.value})),t},t.prototype.toNodeArray=function(){var t=new Array(this.size);return this.forEach((function(r,e){return t[e]=r})),t},t.prototype.toString=function(t){return void 0===t&&(t=JSON.stringify),this.toArray().map((function(r){return t(r)})).join(" <-> ")},t.prototype[Symbol.iterator]=function(){var t;return e(this,(function(r){switch(r.label){case 0:t=this.first,0,r.label=1;case 1:return t?[4,t.value]:[3,4];case 2:r.sent(),r.label=3;case 3:return t=t.next,[3,1];case 4:return[2]}}))},t}();t.LinkedList=a,t.ListNode=o,Object.defineProperty(t,"__esModule",{value:!0})})); +//# sourceMappingURL=abp-utils.umd.min.js.map \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/abp/utils/abp-utils.umd.min.js.map b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/abp/utils/abp-utils.umd.min.js.map new file mode 100644 index 0000000000..155d2d7973 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/abp/utils/abp-utils.umd.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../node_modules/tslib/tslib.es6.js","ng://@abp/utils/lib/linked-list.ts"],"names":["__generator","thisArg","body","f","y","t","g","_","label","sent","trys","ops","next","verb","throw","return","Symbol","iterator","this","n","v","op","TypeError","call","done","value","pop","length","push","e","step","__read","o","m","r","i","ar","error","__spread","arguments","concat","LinkedList","size","Object","defineProperty","prototype","first","last","attach","previousNode","nextNode","addHead","addTail","node","ListNode","previous","attachMany","values","addManyHead","addManyTail","list","toNodeArray","detach","dropTail","dropHead","add","_this","after","params","_i","_a","addAfter","apply","before","addBefore","byIndex","position","addByIndex","head","tail","addMany","addManyAfter","addManyBefore","addManyByIndex","previousValue","compareFn","compare","find","nextValue","get","reduceRight","nodes","unshift","map","drop","dropByIndex","byValue","dropByValue","byValueAll","dropByValueAll","dropMany","count","dropManyByIndex","dropManyHead","dropManyTail","current","undefined","findIndex","dropped","Math","max","min","predicate","forEach","iteratorFn","index","indexOf","toArray","array","Array","toString","mapperFn","JSON","stringify","join"],"mappings":"+XA6EO,SAASA,EAAYC,EAASC,GACjC,IAAsGC,EAAGC,EAAGC,EAAGC,EAA3GC,EAAI,CAAEC,MAAO,EAAGC,KAAM,WAAa,GAAW,EAAPJ,EAAE,GAAQ,MAAMA,EAAE,GAAI,OAAOA,EAAE,IAAOK,KAAM,GAAIC,IAAK,IAChG,OAAOL,EAAI,CAAEM,KAAMC,EAAK,GAAIC,MAASD,EAAK,GAAIE,OAAUF,EAAK,IAAwB,mBAAXG,SAA0BV,EAAEU,OAAOC,UAAY,WAAa,OAAOC,OAAUZ,EACvJ,SAASO,EAAKM,GAAK,OAAO,SAAUC,GAAK,OACzC,SAAcC,GACV,GAAIlB,EAAG,MAAM,IAAImB,UAAU,mCAC3B,KAAOf,GAAG,IACN,GAAIJ,EAAI,EAAGC,IAAMC,EAAY,EAARgB,EAAG,GAASjB,EAAU,OAAIiB,EAAG,GAAKjB,EAAS,SAAOC,EAAID,EAAU,SAAMC,EAAEkB,KAAKnB,GAAI,GAAKA,EAAEQ,SAAWP,EAAIA,EAAEkB,KAAKnB,EAAGiB,EAAG,KAAKG,KAAM,OAAOnB,EAE3J,OADID,EAAI,EAAGC,IAAGgB,EAAK,CAAS,EAARA,EAAG,GAAQhB,EAAEoB,QACzBJ,EAAG,IACP,KAAK,EAAG,KAAK,EAAGhB,EAAIgB,EAAI,MACxB,KAAK,EAAc,OAAXd,EAAEC,QAAgB,CAAEiB,MAAOJ,EAAG,GAAIG,MAAM,GAChD,KAAK,EAAGjB,EAAEC,QAASJ,EAAIiB,EAAG,GAAIA,EAAK,CAAC,GAAI,SACxC,KAAK,EAAGA,EAAKd,EAAEI,IAAIe,MAAOnB,EAAEG,KAAKgB,MAAO,SACxC,QACI,KAAMrB,EAAIE,EAAEG,MAAML,EAAIA,EAAEsB,OAAS,GAAKtB,EAAEA,EAAEsB,OAAS,KAAkB,IAAVN,EAAG,IAAsB,IAAVA,EAAG,IAAW,CAAEd,EAAI,EAAG,SACjG,GAAc,IAAVc,EAAG,MAAchB,GAAMgB,EAAG,GAAKhB,EAAE,IAAMgB,EAAG,GAAKhB,EAAE,IAAM,CAAEE,EAAEC,MAAQa,EAAG,GAAI,MAC9E,GAAc,IAAVA,EAAG,IAAYd,EAAEC,MAAQH,EAAE,GAAI,CAAEE,EAAEC,MAAQH,EAAE,GAAIA,EAAIgB,EAAI,MAC7D,GAAIhB,GAAKE,EAAEC,MAAQH,EAAE,GAAI,CAAEE,EAAEC,MAAQH,EAAE,GAAIE,EAAEI,IAAIiB,KAAKP,GAAK,MACvDhB,EAAE,IAAIE,EAAEI,IAAIe,MAChBnB,EAAEG,KAAKgB,MAAO,SAEtBL,EAAKnB,EAAKqB,KAAKtB,EAASM,GAC1B,MAAOsB,GAAKR,EAAK,CAAC,EAAGQ,GAAIzB,EAAI,EAAI,QAAWD,EAAIE,EAAI,EACtD,GAAY,EAARgB,EAAG,GAAQ,MAAMA,EAAG,GAAI,MAAO,CAAEI,MAAOJ,EAAG,GAAKA,EAAG,QAAK,EAAQG,MAAM,GArB9BM,CAAK,CAACX,EAAGC,MAyCtD,SAASW,EAAOC,EAAGb,GACtB,IAAIc,EAAsB,mBAAXjB,QAAyBgB,EAAEhB,OAAOC,UACjD,IAAKgB,EAAG,OAAOD,EACf,IAAmBE,EAAYL,EAA3BM,EAAIF,EAAEV,KAAKS,GAAOI,EAAK,GAC3B,IACI,WAAc,IAANjB,GAAgBA,KAAM,MAAQe,EAAIC,EAAEvB,QAAQY,MAAMY,EAAGR,KAAKM,EAAET,OAExE,MAAOY,GAASR,EAAI,CAAEQ,MAAOA,GACjC,QACQ,IACQH,IAAMA,EAAEV,OAASS,EAAIE,EAAU,SAAIF,EAAEV,KAAKY,GAE1D,QAAkB,GAAIN,EAAG,MAAMA,EAAEQ,OAE7B,OAAOD,EAGJ,SAASE,IACZ,IAAK,IAAIF,EAAK,GAAID,EAAI,EAAGA,EAAII,UAAUZ,OAAQQ,IAC3CC,EAAKA,EAAGI,OAAOT,EAAOQ,UAAUJ,KACpC,OAAOC,QCtIT,SAA4BX,GAAAP,KAAAO,MAAAA,gBAG9B,SAAAgB,IAGUvB,KAAAwB,KAAO,EA+XjB,OA7XEC,OAAAC,eAAIH,EAAAI,UAAA,OAAI,KAAR,WACE,OAAO3B,KAAK4B,uCAEdH,OAAAC,eAAIH,EAAAI,UAAA,OAAI,KAAR,WACE,OAAO3B,KAAK6B,sCAEdJ,OAAAC,eAAIH,EAAAI,UAAA,SAAM,KAAV,WACE,OAAO3B,KAAKwB,sCAGND,EAAAI,UAAAG,OAAR,SACEvB,EACAwB,EACAC,GAEA,IAAKD,EAAc,OAAO/B,KAAKiC,QAAQ1B,GAEvC,IAAKyB,EAAU,OAAOhC,KAAKkC,QAAQ3B,GAEnC,IAAM4B,EAAO,IAAIC,EAAS7B,GAQ1B,OAPA4B,EAAKE,SAAWN,EAChBA,EAAarC,KAAOyC,EACpBA,EAAKzC,KAAOsC,EACZA,EAASK,SAAWF,EAEpBnC,KAAKwB,OAEEW,GAGDZ,EAAAI,UAAAW,WAAR,SACEC,EACAR,EACAC,GAEA,IAAKO,EAAO9B,OAAQ,MAAO,GAE3B,IAAKsB,EAAc,OAAO/B,KAAKwC,YAAYD,GAE3C,IAAKP,EAAU,OAAOhC,KAAKyC,YAAYF,GAEvC,IAAMG,EAAO,IAAInB,EASjB,OARAmB,EAAKD,YAAYF,GACjBG,EAAKd,MAAOS,SAAWN,EACvBA,EAAarC,KAAOgD,EAAKd,MACzBc,EAAKb,KAAMnC,KAAOsC,EAClBA,EAASK,SAAWK,EAAKb,KAEzB7B,KAAKwB,MAAQe,EAAO9B,OAEbiC,EAAKC,eAGNpB,EAAAI,UAAAiB,OAAR,SAAeT,GACb,OAAKA,EAAKE,SAELF,EAAKzC,MAEVyC,EAAKE,SAAS3C,KAAOyC,EAAKzC,KAC1ByC,EAAKzC,KAAK2C,SAAWF,EAAKE,SAE1BrC,KAAKwB,OAEEW,GAPgBnC,KAAK6C,WAFD7C,KAAK8C,YAYlCvB,EAAAI,UAAAoB,IAAA,SAAIxC,GAAJ,IAAAyC,EAAAhD,KACE,MAAO,CACLiD,MAAO,qBAACC,EAAA,GAAAC,EAAA,EAAAA,EAAA9B,UAAAZ,OAAA0C,IAAAD,EAAAC,GAAA9B,UAAA8B,GACN,OAAAC,EAAAJ,EAAKK,UAAShD,KAAIiD,MAAAF,EAAAhC,EAAA,CAAC4B,EAAMzC,GAAU2C,KACrCK,OAAQ,qBAACL,EAAA,GAAAC,EAAA,EAAAA,EAAA9B,UAAAZ,OAAA0C,IAAAD,EAAAC,GAAA9B,UAAA8B,GACP,OAAAC,EAAAJ,EAAKQ,WAAUnD,KAAIiD,MAAAF,EAAAhC,EAAA,CAAC4B,EAAMzC,GAAU2C,KACtCO,QAAS,SAACC,GAAqB,OAAAV,EAAKW,WAAWpD,EAAOmD,IACtDE,KAAM,WAAM,OAAAZ,EAAKf,QAAQ1B,IACzBsD,KAAM,WAAM,OAAAb,EAAKd,QAAQ3B,MAI7BgB,EAAAI,UAAAmC,QAAA,SAAQvB,GAAR,IAAAS,EAAAhD,KACE,MAAO,CACLiD,MAAO,qBAACC,EAAA,GAAAC,EAAA,EAAAA,EAAA9B,UAAAZ,OAAA0C,IAAAD,EAAAC,GAAA9B,UAAA8B,GACN,OAAAC,EAAAJ,EAAKe,cAAa1D,KAAIiD,MAAAF,EAAAhC,EAAA,CAAC4B,EAAMT,GAAWW,KAC1CK,OAAQ,qBAACL,EAAA,GAAAC,EAAA,EAAAA,EAAA9B,UAAAZ,OAAA0C,IAAAD,EAAAC,GAAA9B,UAAA8B,GACP,OAAAC,EAAAJ,EAAKgB,eAAc3D,KAAIiD,MAAAF,EAAAhC,EAAA,CAAC4B,EAAMT,GAAWW,KAC3CO,QAAS,SAACC,GAAqB,OAAAV,EAAKiB,eAAe1B,EAAQmB,IAC3DE,KAAM,WAAM,OAAAZ,EAAKR,YAAYD,IAC7BsB,KAAM,WAAM,OAAAb,EAAKP,YAAYF,MAMjChB,EAAAI,UAAA0B,SAAA,SAAS9C,EAAU2D,EAAoBC,QAAA,IAAAA,IAAAA,EAAAC,GACrC,IAAM/B,EAAWrC,KAAKqE,MAAK,SAAAlC,GAAQ,OAAAgC,EAAUhC,EAAK5B,MAAO2D,MAEzD,OAAO7B,EAAWrC,KAAK8B,OAAOvB,EAAO8B,EAAUA,EAAS3C,MAAQM,KAAKkC,QAAQ3B,IAK/EgB,EAAAI,UAAA6B,UAAA,SAAUjD,EAAU+D,EAAgBH,QAAA,IAAAA,IAAAA,EAAAC,GAClC,IAAM1E,EAAOM,KAAKqE,MAAK,SAAAlC,GAAQ,OAAAgC,EAAUhC,EAAK5B,MAAO+D,MAErD,OAAO5E,EAAOM,KAAK8B,OAAOvB,EAAOb,EAAK2C,SAAU3C,GAAQM,KAAKiC,QAAQ1B,IAGvEgB,EAAAI,UAAAgC,WAAA,SAAWpD,EAAUmD,GACnB,GAAIA,EAAW,EAAGA,GAAY1D,KAAKwB,UAC9B,GAAIkC,GAAY1D,KAAKwB,KAAM,OAAOxB,KAAKkC,QAAQ3B,GAEpD,GAAImD,GAAY,EAAG,OAAO1D,KAAKiC,QAAQ1B,GAEvC,IAAMb,EAAOM,KAAKuE,IAAIb,GAEtB,OAAO1D,KAAK8B,OAAOvB,EAAOb,EAAK2C,SAAU3C,IAG3C6B,EAAAI,UAAAM,QAAA,SAAQ1B,GACN,IAAM4B,EAAO,IAAIC,EAAS7B,GAU1B,OARA4B,EAAKzC,KAAOM,KAAK4B,MAEb5B,KAAK4B,MAAO5B,KAAK4B,MAAMS,SAAWF,EACjCnC,KAAK6B,KAAOM,EAEjBnC,KAAK4B,MAAQO,EACbnC,KAAKwB,OAEEW,GAGTZ,EAAAI,UAAAO,QAAA,SAAQ3B,GACN,IAAM4B,EAAO,IAAIC,EAAS7B,GAa1B,OAXIP,KAAK4B,OACPO,EAAKE,SAAWrC,KAAK6B,KACrB7B,KAAK6B,KAAMnC,KAAOyC,EAClBnC,KAAK6B,KAAOM,IAEZnC,KAAK4B,MAAQO,EACbnC,KAAK6B,KAAOM,GAGdnC,KAAKwB,OAEEW,GAKTZ,EAAAI,UAAAoC,aAAA,SACExB,EACA2B,EACAC,QAAA,IAAAA,IAAAA,EAAAC,GAEA,IAAM/B,EAAWrC,KAAKqE,MAAK,SAAAlC,GAAQ,OAAAgC,EAAUhC,EAAK5B,MAAO2D,MAEzD,OAAO7B,EAAWrC,KAAKsC,WAAWC,EAAQF,EAAUA,EAAS3C,MAAQM,KAAKyC,YAAYF,IAKxFhB,EAAAI,UAAAqC,cAAA,SACEzB,EACA+B,EACAH,QAAA,IAAAA,IAAAA,EAAAC,GAEA,IAAM1E,EAAOM,KAAKqE,MAAK,SAAAlC,GAAQ,OAAAgC,EAAUhC,EAAK5B,MAAO+D,MAErD,OAAO5E,EAAOM,KAAKsC,WAAWC,EAAQ7C,EAAK2C,SAAU3C,GAAQM,KAAKwC,YAAYD,IAGhFhB,EAAAI,UAAAsC,eAAA,SAAe1B,EAAamB,GAG1B,GAFIA,EAAW,IAAGA,GAAY1D,KAAKwB,MAE/BkC,GAAY,EAAG,OAAO1D,KAAKwC,YAAYD,GAE3C,GAAImB,GAAY1D,KAAKwB,KAAM,OAAOxB,KAAKyC,YAAYF,GAEnD,IAAM7C,EAAOM,KAAKuE,IAAIb,GAEtB,OAAO1D,KAAKsC,WAAWC,EAAQ7C,EAAK2C,SAAU3C,IAGhD6B,EAAAI,UAAAa,YAAA,SAAYD,GAAZ,IAAAS,EAAAhD,KACE,OAAOuC,EAAOiC,aAA2B,SAACC,EAAOlE,GAE/C,OADAkE,EAAMC,QAAQ1B,EAAKf,QAAQ1B,IACpBkE,IACN,KAGLlD,EAAAI,UAAAc,YAAA,SAAYF,GAAZ,IAAAS,EAAAhD,KACE,OAAOuC,EAAOoC,KAAI,SAAApE,GAAS,OAAAyC,EAAKd,QAAQ3B,OAG1CgB,EAAAI,UAAAiD,KAAA,WAAA,IAAA5B,EAAAhD,KACE,MAAO,CACLyD,QAAS,SAACC,GAAqB,OAAAV,EAAK6B,YAAYnB,IAChDoB,QAAS,eAAC,IAAA5B,EAAA,GAAAC,EAAA,EAAAA,EAAA9B,UAAAZ,OAAA0C,IAAAD,EAAAC,GAAA9B,UAAA8B,GACR,OAAAH,EAAK+B,YAAYzB,MAAMN,EAAME,IAC/B8B,WAAY,eAAC,IAAA9B,EAAA,GAAAC,EAAA,EAAAA,EAAA9B,UAAAZ,OAAA0C,IAAAD,EAAAC,GAAA9B,UAAA8B,GACX,OAAAH,EAAKiC,eAAe3B,MAAMN,EAAME,IAClCU,KAAM,WAAM,OAAAZ,EAAKF,YACjBe,KAAM,WAAM,OAAAb,EAAKH,cAIrBtB,EAAAI,UAAAuD,SAAA,SAASC,GAAT,IAAAnC,EAAAhD,KACE,MAAO,CACLyD,QAAS,SAACC,GAAqB,OAAAV,EAAKoC,gBAAgBD,EAAOzB,IAC3DE,KAAM,WAAM,OAAAZ,EAAKqC,aAAaF,IAC9BtB,KAAM,WAAM,OAAAb,EAAKsC,aAAaH,MAIlC5D,EAAAI,UAAAkD,YAAA,SAAYnB,GACNA,EAAW,IAAGA,GAAY1D,KAAKwB,MAEnC,IAAM+D,EAAUvF,KAAKuE,IAAIb,GAEzB,OAAO6B,EAAUvF,KAAK4C,OAAO2C,QAAWC,GAK1CjE,EAAAI,UAAAoD,YAAA,SAAYxE,EAAY4D,QAAA,IAAAA,IAAAA,EAAAC,GACtB,IAAMV,EAAW1D,KAAKyF,WAAU,SAAAtD,GAAQ,OAAAgC,EAAUhC,EAAK5B,MAAOA,MAE9D,OAAOmD,EAAW,OAAI8B,EAAYxF,KAAK6E,YAAYnB,IAKrDnC,EAAAI,UAAAsD,eAAA,SAAe1E,EAAY4D,QAAA,IAAAA,IAAAA,EAAAC,GAGzB,IAFA,IAAMsB,EAAyB,GAEtBH,EAAUvF,KAAK4B,MAAO8B,EAAW,EAAG6B,EAAS7B,IAAY6B,EAAUA,EAAQ7F,KAC9EyE,EAAUoB,EAAQhF,MAAOA,IAC3BmF,EAAQhF,KAAKV,KAAK6E,YAAYnB,EAAWgC,EAAQjF,SAIrD,OAAOiF,GAGTnE,EAAAI,UAAAmB,SAAA,WACE,IAAMc,EAAO5D,KAAK4B,MAElB,GAAIgC,EAQF,OAPA5D,KAAK4B,MAAQgC,EAAKlE,KAEdM,KAAK4B,MAAO5B,KAAK4B,MAAMS,cAAWmD,EACjCxF,KAAK6B,UAAO2D,EAEjBxF,KAAKwB,OAEEoC,GAMXrC,EAAAI,UAAAkB,SAAA,WACE,IAAMgB,EAAO7D,KAAK6B,KAElB,GAAIgC,EAQF,OAPA7D,KAAK6B,KAAOgC,EAAKxB,SAEbrC,KAAK6B,KAAM7B,KAAK6B,KAAKnC,UAAO8F,EAC3BxF,KAAK4B,WAAQ4D,EAElBxF,KAAKwB,OAEEqC,GAMXtC,EAAAI,UAAAyD,gBAAA,SAAgBD,EAAezB,GAC7B,GAAIyB,GAAS,EAAG,MAAO,GAEvB,GAAIzB,EAAW,EAAGA,EAAWiC,KAAKC,IAAIlC,EAAW1D,KAAKwB,KAAM,QACvD,GAAIkC,GAAY1D,KAAKwB,KAAM,MAAO,GAEvC2D,EAAQQ,KAAKE,IAAIV,EAAOnF,KAAKwB,KAAOkC,GAIpC,IAFA,IAAMgC,EAAyB,GAExBP,KAAS,CACd,IAAMI,EAAUvF,KAAKuE,IAAIb,GACzBgC,EAAQhF,KAAKV,KAAK4C,OAAO2C,IAG3B,OAAOG,GAGTnE,EAAAI,UAAA0D,aAAA,SAAaF,GACX,GAAIA,GAAS,EAAG,MAAO,GAEvBA,EAAQQ,KAAKE,IAAIV,EAAOnF,KAAKwB,MAI7B,IAFA,IAAMkE,EAAyB,GAExBP,KAASO,EAAQhB,QAAQ1E,KAAK8C,YAErC,OAAO4C,GAGTnE,EAAAI,UAAA2D,aAAA,SAAaH,GACX,GAAIA,GAAS,EAAG,MAAO,GAEvBA,EAAQQ,KAAKE,IAAIV,EAAOnF,KAAKwB,MAI7B,IAFA,IAAMkE,EAAyB,GAExBP,KAASO,EAAQhF,KAAKV,KAAK6C,YAElC,OAAO6C,GAGTnE,EAAAI,UAAA0C,KAAA,SAAKyB,GACH,IAAK,IAAIP,EAAUvF,KAAK4B,MAAO8B,EAAW,EAAG6B,EAAS7B,IAAY6B,EAAUA,EAAQ7F,KAClF,GAAIoG,EAAUP,EAAS7B,EAAU1D,MAAO,OAAOuF,GAMnDhE,EAAAI,UAAA8D,UAAA,SAAUK,GACR,IAAK,IAAIP,EAAUvF,KAAK4B,MAAO8B,EAAW,EAAG6B,EAAS7B,IAAY6B,EAAUA,EAAQ7F,KAClF,GAAIoG,EAAUP,EAAS7B,EAAU1D,MAAO,OAAO0D,EAGjD,OAAQ,GAGVnC,EAAAI,UAAAoE,QAAA,SAAqBC,GACnB,IAAK,IAAI7D,EAAOnC,KAAK4B,MAAO8B,EAAW,EAAGvB,EAAMuB,IAAYvB,EAAOA,EAAKzC,KACtEsG,EAAW7D,EAAMuB,EAAU1D,OAI/BuB,EAAAI,UAAA4C,IAAA,SAAIb,GACF,OAAO1D,KAAKqE,MAAK,SAAChF,EAAG4G,GAAU,OAAAvC,IAAauC,MAK9C1E,EAAAI,UAAAuE,QAAA,SAAQ3F,EAAY4D,GAClB,YADkB,IAAAA,IAAAA,EAAAC,GACXpE,KAAKyF,WAAU,SAAAtD,GAAQ,OAAAgC,EAAUhC,EAAK5B,MAAOA,OAGtDgB,EAAAI,UAAAwE,QAAA,WACE,IAAMC,EAAQ,IAAIC,MAAMrG,KAAKwB,MAI7B,OAFAxB,KAAK+F,SAAQ,SAAC5D,EAAM8D,GAAU,OAACG,EAAMH,GAAU9D,EAAK5B,SAE7C6F,GAGT7E,EAAAI,UAAAgB,YAAA,WACE,IAAMyD,EAAQ,IAAIC,MAAMrG,KAAKwB,MAI7B,OAFAxB,KAAK+F,SAAQ,SAAC5D,EAAM8D,GAAU,OAACG,EAAMH,GAAU9D,KAExCiE,GAGT7E,EAAAI,UAAA2E,SAAA,SAASC,GACP,YADO,IAAAA,IAAAA,EAA4BC,KAAKC,WACjCzG,KAAKmG,UACTxB,KAAI,SAAApE,GAAS,OAAAgG,EAAShG,MACtBmG,KAAK,UAITnF,EAAAI,UAAC7B,OAAOC,UAAT,mEACWoC,EAAOnC,KAAK4B,MAAkB,0BAAGO,EACxC,CAAA,EAAMA,EAAK5B,OADiC,CAAA,EAAA,UAC5C6C,EAAA7D,+BAD0D4C,EAAOA,EAAKzC,iCAI5E6B","sourcesContent":["/*! *****************************************************************************\r\nCopyright (c) Microsoft Corporation. All rights reserved.\r\nLicensed under the Apache License, Version 2.0 (the \"License\"); you may not use\r\nthis file except in compliance with the License. You may obtain a copy of the\r\nLicense at http://www.apache.org/licenses/LICENSE-2.0\r\n\r\nTHIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\r\nKIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED\r\nWARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,\r\nMERCHANTABLITY OR NON-INFRINGEMENT.\r\n\r\nSee the Apache Version 2.0 License for specific language governing permissions\r\nand limitations under the License.\r\n***************************************************************************** */\r\n/* global Reflect, Promise */\r\n\r\nvar extendStatics = function(d, b) {\r\n extendStatics = Object.setPrototypeOf ||\r\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\r\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\r\n return extendStatics(d, b);\r\n};\r\n\r\nexport function __extends(d, b) {\r\n extendStatics(d, b);\r\n function __() { this.constructor = d; }\r\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\r\n}\r\n\r\nexport var __assign = function() {\r\n __assign = Object.assign || function __assign(t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\r\n }\r\n return t;\r\n }\r\n return __assign.apply(this, arguments);\r\n}\r\n\r\nexport function __rest(s, e) {\r\n var t = {};\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\r\n t[p] = s[p];\r\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\r\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\r\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\r\n t[p[i]] = s[p[i]];\r\n }\r\n return t;\r\n}\r\n\r\nexport function __decorate(decorators, target, key, desc) {\r\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\r\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\r\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\r\n return c > 3 && r && Object.defineProperty(target, key, r), r;\r\n}\r\n\r\nexport function __param(paramIndex, decorator) {\r\n return function (target, key) { decorator(target, key, paramIndex); }\r\n}\r\n\r\nexport function __metadata(metadataKey, metadataValue) {\r\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\r\n}\r\n\r\nexport function __awaiter(thisArg, _arguments, P, generator) {\r\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\r\n return new (P || (P = Promise))(function (resolve, reject) {\r\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\r\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\r\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\r\n step((generator = generator.apply(thisArg, _arguments || [])).next());\r\n });\r\n}\r\n\r\nexport function __generator(thisArg, body) {\r\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;\r\n return g = { next: verb(0), \"throw\": verb(1), \"return\": verb(2) }, typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\r\n function verb(n) { return function (v) { return step([n, v]); }; }\r\n function step(op) {\r\n if (f) throw new TypeError(\"Generator is already executing.\");\r\n while (_) try {\r\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\r\n if (y = 0, t) op = [op[0] & 2, t.value];\r\n switch (op[0]) {\r\n case 0: case 1: t = op; break;\r\n case 4: _.label++; return { value: op[1], done: false };\r\n case 5: _.label++; y = op[1]; op = [0]; continue;\r\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\r\n default:\r\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\r\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\r\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\r\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\r\n if (t[2]) _.ops.pop();\r\n _.trys.pop(); continue;\r\n }\r\n op = body.call(thisArg, _);\r\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\r\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\r\n }\r\n}\r\n\r\nexport function __exportStar(m, exports) {\r\n for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];\r\n}\r\n\r\nexport function __values(o) {\r\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\r\n if (m) return m.call(o);\r\n if (o && typeof o.length === \"number\") return {\r\n next: function () {\r\n if (o && i >= o.length) o = void 0;\r\n return { value: o && o[i++], done: !o };\r\n }\r\n };\r\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\r\n}\r\n\r\nexport function __read(o, n) {\r\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\r\n if (!m) return o;\r\n var i = m.call(o), r, ar = [], e;\r\n try {\r\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\r\n }\r\n catch (error) { e = { error: error }; }\r\n finally {\r\n try {\r\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\r\n }\r\n finally { if (e) throw e.error; }\r\n }\r\n return ar;\r\n}\r\n\r\nexport function __spread() {\r\n for (var ar = [], i = 0; i < arguments.length; i++)\r\n ar = ar.concat(__read(arguments[i]));\r\n return ar;\r\n}\r\n\r\nexport function __spreadArrays() {\r\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\r\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\r\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\r\n r[k] = a[j];\r\n return r;\r\n};\r\n\r\nexport function __await(v) {\r\n return this instanceof __await ? (this.v = v, this) : new __await(v);\r\n}\r\n\r\nexport function __asyncGenerator(thisArg, _arguments, generator) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\r\n return i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i;\r\n function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }\r\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\r\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\r\n function fulfill(value) { resume(\"next\", value); }\r\n function reject(value) { resume(\"throw\", value); }\r\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\r\n}\r\n\r\nexport function __asyncDelegator(o) {\r\n var i, p;\r\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\r\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === \"return\" } : f ? f(v) : v; } : f; }\r\n}\r\n\r\nexport function __asyncValues(o) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var m = o[Symbol.asyncIterator], i;\r\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\r\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\r\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\r\n}\r\n\r\nexport function __makeTemplateObject(cooked, raw) {\r\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\r\n return cooked;\r\n};\r\n\r\nexport function __importStar(mod) {\r\n if (mod && mod.__esModule) return mod;\r\n var result = {};\r\n if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];\r\n result.default = mod;\r\n return result;\r\n}\r\n\r\nexport function __importDefault(mod) {\r\n return (mod && mod.__esModule) ? mod : { default: mod };\r\n}\r\n\r\nexport function __classPrivateFieldGet(receiver, privateMap) {\r\n if (!privateMap.has(receiver)) {\r\n throw new TypeError(\"attempted to get private field on non-instance\");\r\n }\r\n return privateMap.get(receiver);\r\n}\r\n\r\nexport function __classPrivateFieldSet(receiver, privateMap, value) {\r\n if (!privateMap.has(receiver)) {\r\n throw new TypeError(\"attempted to set private field on non-instance\");\r\n }\r\n privateMap.set(receiver, value);\r\n return value;\r\n}\r\n","/* tslint:disable:no-non-null-assertion */\r\n\r\nimport compare from 'just-compare';\r\n\r\nexport class ListNode {\r\n next: ListNode | undefined;\r\n previous: ListNode | undefined;\r\n constructor(public readonly value: T) {}\r\n}\r\n\r\nexport class LinkedList {\r\n private first: ListNode | undefined;\r\n private last: ListNode | undefined;\r\n private size = 0;\r\n\r\n get head(): ListNode | undefined {\r\n return this.first;\r\n }\r\n get tail(): ListNode | undefined {\r\n return this.last;\r\n }\r\n get length(): number {\r\n return this.size;\r\n }\r\n\r\n private attach(\r\n value: T,\r\n previousNode: ListNode | undefined,\r\n nextNode: ListNode | undefined,\r\n ): ListNode {\r\n if (!previousNode) return this.addHead(value);\r\n\r\n if (!nextNode) return this.addTail(value);\r\n\r\n const node = new ListNode(value);\r\n node.previous = previousNode;\r\n previousNode.next = node;\r\n node.next = nextNode;\r\n nextNode.previous = node;\r\n\r\n this.size++;\r\n\r\n return node;\r\n }\r\n\r\n private attachMany(\r\n values: T[],\r\n previousNode: ListNode | undefined,\r\n nextNode: ListNode | undefined,\r\n ): ListNode[] {\r\n if (!values.length) return [];\r\n\r\n if (!previousNode) return this.addManyHead(values);\r\n\r\n if (!nextNode) return this.addManyTail(values);\r\n\r\n const list = new LinkedList();\r\n list.addManyTail(values);\r\n list.first!.previous = previousNode;\r\n previousNode.next = list.first;\r\n list.last!.next = nextNode;\r\n nextNode.previous = list.last;\r\n\r\n this.size += values.length;\r\n\r\n return list.toNodeArray();\r\n }\r\n\r\n private detach(node: ListNode) {\r\n if (!node.previous) return this.dropHead();\r\n\r\n if (!node.next) return this.dropTail();\r\n\r\n node.previous.next = node.next;\r\n node.next.previous = node.previous;\r\n\r\n this.size--;\r\n\r\n return node;\r\n }\r\n\r\n add(value: T) {\r\n return {\r\n after: (...params: [T] | [any, ListComparisonFn]) =>\r\n this.addAfter.call(this, value, ...params),\r\n before: (...params: [T] | [any, ListComparisonFn]) =>\r\n this.addBefore.call(this, value, ...params),\r\n byIndex: (position: number) => this.addByIndex(value, position),\r\n head: () => this.addHead(value),\r\n tail: () => this.addTail(value),\r\n };\r\n }\r\n\r\n addMany(values: T[]) {\r\n return {\r\n after: (...params: [T] | [any, ListComparisonFn]) =>\r\n this.addManyAfter.call(this, values, ...params),\r\n before: (...params: [T] | [any, ListComparisonFn]) =>\r\n this.addManyBefore.call(this, values, ...params),\r\n byIndex: (position: number) => this.addManyByIndex(values, position),\r\n head: () => this.addManyHead(values),\r\n tail: () => this.addManyTail(values),\r\n };\r\n }\r\n\r\n addAfter(value: T, previousValue: T): ListNode;\r\n addAfter(value: T, previousValue: any, compareFn: ListComparisonFn): ListNode;\r\n addAfter(value: T, previousValue: any, compareFn: ListComparisonFn = compare): ListNode {\r\n const previous = this.find(node => compareFn(node.value, previousValue));\r\n\r\n return previous ? this.attach(value, previous, previous.next) : this.addTail(value);\r\n }\r\n\r\n addBefore(value: T, nextValue: T): ListNode;\r\n addBefore(value: T, nextValue: any, compareFn: ListComparisonFn): ListNode;\r\n addBefore(value: T, nextValue: any, compareFn: ListComparisonFn = compare): ListNode {\r\n const next = this.find(node => compareFn(node.value, nextValue));\r\n\r\n return next ? this.attach(value, next.previous, next) : this.addHead(value);\r\n }\r\n\r\n addByIndex(value: T, position: number): ListNode {\r\n if (position < 0) position += this.size;\r\n else if (position >= this.size) return this.addTail(value);\r\n\r\n if (position <= 0) return this.addHead(value);\r\n\r\n const next = this.get(position)!;\r\n\r\n return this.attach(value, next.previous, next);\r\n }\r\n\r\n addHead(value: T): ListNode {\r\n const node = new ListNode(value);\r\n\r\n node.next = this.first;\r\n\r\n if (this.first) this.first.previous = node;\r\n else this.last = node;\r\n\r\n this.first = node;\r\n this.size++;\r\n\r\n return node;\r\n }\r\n\r\n addTail(value: T): ListNode {\r\n const node = new ListNode(value);\r\n\r\n if (this.first) {\r\n node.previous = this.last;\r\n this.last!.next = node;\r\n this.last = node;\r\n } else {\r\n this.first = node;\r\n this.last = node;\r\n }\r\n\r\n this.size++;\r\n\r\n return node;\r\n }\r\n\r\n addManyAfter(values: T[], previousValue: T): ListNode[];\r\n addManyAfter(values: T[], previousValue: any, compareFn: ListComparisonFn): ListNode[];\r\n addManyAfter(\r\n values: T[],\r\n previousValue: any,\r\n compareFn: ListComparisonFn = compare,\r\n ): ListNode[] {\r\n const previous = this.find(node => compareFn(node.value, previousValue));\r\n\r\n return previous ? this.attachMany(values, previous, previous.next) : this.addManyTail(values);\r\n }\r\n\r\n addManyBefore(values: T[], nextValue: T): ListNode[];\r\n addManyBefore(values: T[], nextValue: any, compareFn: ListComparisonFn): ListNode[];\r\n addManyBefore(\r\n values: T[],\r\n nextValue: any,\r\n compareFn: ListComparisonFn = compare,\r\n ): ListNode[] {\r\n const next = this.find(node => compareFn(node.value, nextValue));\r\n\r\n return next ? this.attachMany(values, next.previous, next) : this.addManyHead(values);\r\n }\r\n\r\n addManyByIndex(values: T[], position: number): ListNode[] {\r\n if (position < 0) position += this.size;\r\n\r\n if (position <= 0) return this.addManyHead(values);\r\n\r\n if (position >= this.size) return this.addManyTail(values);\r\n\r\n const next = this.get(position)!;\r\n\r\n return this.attachMany(values, next.previous, next);\r\n }\r\n\r\n addManyHead(values: T[]): ListNode[] {\r\n return values.reduceRight[]>((nodes, value) => {\r\n nodes.unshift(this.addHead(value));\r\n return nodes;\r\n }, []);\r\n }\r\n\r\n addManyTail(values: T[]): ListNode[] {\r\n return values.map(value => this.addTail(value));\r\n }\r\n\r\n drop() {\r\n return {\r\n byIndex: (position: number) => this.dropByIndex(position),\r\n byValue: (...params: [T] | [any, ListComparisonFn]) =>\r\n this.dropByValue.apply(this, params),\r\n byValueAll: (...params: [T] | [any, ListComparisonFn]) =>\r\n this.dropByValueAll.apply(this, params),\r\n head: () => this.dropHead(),\r\n tail: () => this.dropTail(),\r\n };\r\n }\r\n\r\n dropMany(count: number) {\r\n return {\r\n byIndex: (position: number) => this.dropManyByIndex(count, position),\r\n head: () => this.dropManyHead(count),\r\n tail: () => this.dropManyTail(count),\r\n };\r\n }\r\n\r\n dropByIndex(position: number): ListNode | undefined {\r\n if (position < 0) position += this.size;\r\n\r\n const current = this.get(position);\r\n\r\n return current ? this.detach(current) : undefined;\r\n }\r\n\r\n dropByValue(value: T): ListNode | undefined;\r\n dropByValue(value: any, compareFn: ListComparisonFn): ListNode | undefined;\r\n dropByValue(value: any, compareFn: ListComparisonFn = compare): ListNode | undefined {\r\n const position = this.findIndex(node => compareFn(node.value, value));\r\n\r\n return position < 0 ? undefined : this.dropByIndex(position);\r\n }\r\n\r\n dropByValueAll(value: T): ListNode[];\r\n dropByValueAll(value: any, compareFn: ListComparisonFn): ListNode[];\r\n dropByValueAll(value: any, compareFn: ListComparisonFn = compare): ListNode[] {\r\n const dropped: ListNode[] = [];\r\n\r\n for (let current = this.first, position = 0; current; position++, current = current.next) {\r\n if (compareFn(current.value, value)) {\r\n dropped.push(this.dropByIndex(position - dropped.length)!);\r\n }\r\n }\r\n\r\n return dropped;\r\n }\r\n\r\n dropHead(): ListNode | undefined {\r\n const head = this.first;\r\n\r\n if (head) {\r\n this.first = head.next;\r\n\r\n if (this.first) this.first.previous = undefined;\r\n else this.last = undefined;\r\n\r\n this.size--;\r\n\r\n return head;\r\n }\r\n\r\n return undefined;\r\n }\r\n\r\n dropTail(): ListNode | undefined {\r\n const tail = this.last;\r\n\r\n if (tail) {\r\n this.last = tail.previous;\r\n\r\n if (this.last) this.last.next = undefined;\r\n else this.first = undefined;\r\n\r\n this.size--;\r\n\r\n return tail;\r\n }\r\n\r\n return undefined;\r\n }\r\n\r\n dropManyByIndex(count: number, position: number): ListNode[] {\r\n if (count <= 0) return [];\r\n\r\n if (position < 0) position = Math.max(position + this.size, 0);\r\n else if (position >= this.size) return [];\r\n\r\n count = Math.min(count, this.size - position);\r\n\r\n const dropped: ListNode[] = [];\r\n\r\n while (count--) {\r\n const current = this.get(position);\r\n dropped.push(this.detach(current!)!);\r\n }\r\n\r\n return dropped;\r\n }\r\n\r\n dropManyHead(count: Exclude): ListNode[] {\r\n if (count <= 0) return [];\r\n\r\n count = Math.min(count, this.size);\r\n\r\n const dropped: ListNode[] = [];\r\n\r\n while (count--) dropped.unshift(this.dropHead()!);\r\n\r\n return dropped;\r\n }\r\n\r\n dropManyTail(count: Exclude): ListNode[] {\r\n if (count <= 0) return [];\r\n\r\n count = Math.min(count, this.size);\r\n\r\n const dropped: ListNode[] = [];\r\n\r\n while (count--) dropped.push(this.dropTail()!);\r\n\r\n return dropped;\r\n }\r\n\r\n find(predicate: ListIteratorFn): ListNode | undefined {\r\n for (let current = this.first, position = 0; current; position++, current = current.next) {\r\n if (predicate(current, position, this)) return current;\r\n }\r\n\r\n return undefined;\r\n }\r\n\r\n findIndex(predicate: ListIteratorFn): number {\r\n for (let current = this.first, position = 0; current; position++, current = current.next) {\r\n if (predicate(current, position, this)) return position;\r\n }\r\n\r\n return -1;\r\n }\r\n\r\n forEach(iteratorFn: ListIteratorFn) {\r\n for (let node = this.first, position = 0; node; position++, node = node.next) {\r\n iteratorFn(node, position, this);\r\n }\r\n }\r\n\r\n get(position: number): ListNode | undefined {\r\n return this.find((_, index) => position === index);\r\n }\r\n\r\n indexOf(value: T): number;\r\n indexOf(value: any, compareFn: ListComparisonFn): number;\r\n indexOf(value: any, compareFn: ListComparisonFn = compare): number {\r\n return this.findIndex(node => compareFn(node.value, value));\r\n }\r\n\r\n toArray(): T[] {\r\n const array = new Array(this.size);\r\n\r\n this.forEach((node, index) => (array[index!] = node.value));\r\n\r\n return array;\r\n }\r\n\r\n toNodeArray(): ListNode[] {\r\n const array = new Array(this.size);\r\n\r\n this.forEach((node, index) => (array[index!] = node));\r\n\r\n return array;\r\n }\r\n\r\n toString(mapperFn: ListMapperFn = JSON.stringify): string {\r\n return this.toArray()\r\n .map(value => mapperFn(value))\r\n .join(' <-> ');\r\n }\r\n\r\n // Cannot use Generator type because of ng-packagr\r\n *[Symbol.iterator](): any {\r\n for (let node = this.first, position = 0; node; position++, node = node.next) {\r\n yield node.value;\r\n }\r\n }\r\n}\r\n\r\nexport type ListMapperFn = (value: T) => any;\r\n\r\nexport type ListComparisonFn = (value1: T, value2: any) => boolean;\r\n\r\nexport type ListIteratorFn = (\r\n node: ListNode,\r\n index?: number,\r\n list?: LinkedList,\r\n) => R;\r\n"]} \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/bootstrap-datepicker.css.map b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/bootstrap-datepicker.css.map new file mode 100644 index 0000000000..7e08a21044 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/bootstrap-datepicker.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["less/datepicker.less","build/build.less"],"names":[],"mappings":"AAAA;EACC,YAAA;ECsBC,0BAAA;EACG,uBAAA;EACK,kBAAA;EDnBT,cAAA;;AAHA,WAAC;EACA,YAAA;;AAGD,WAAC;EACA,cAAA;;AACA,WAFA,IAEC;EAAiB,UAAA;;AAFnB,WAAC,IAGA,MAAM,GAAG,GAAG;EACX,YAAA;;AAGF,WAAC;EACA,MAAA;EACA,OAAA;;AACA,WAHA,SAGC;EACA,SAAS,EAAT;EACA,qBAAA;EACA,kCAAA;EACA,mCAAA;EACA,6BAAA;EACA,aAAA;EACA,uCAAA;EACA,kBAAA;;AAED,WAbA,SAaC;EACA,SAAS,EAAT;EACA,qBAAA;EACA,kCAAA;EACA,mCAAA;EACA,6BAAA;EACA,aAAA;EACA,kBAAA;;AAED,WAtBA,SAsBC,uBAAuB;EAAY,SAAA;;AACpC,WAvBA,SAuBC,uBAAuB;EAAY,SAAA;;AACpC,WAxBA,SAwBC,wBAAwB;EAAW,UAAA;;AACpC,WAzBA,SAyBC,wBAAwB;EAAW,UAAA;;AACpC,WA1BA,SA0BC,yBAAyB;EAAU,SAAA;;AACpC,WA3BA,SA2BC,yBAAyB;EAAU,SAAA;;AACpC,WA5BA,SA4BC,sBAAsB;EACtB,YAAA;EACA,gBAAA;EACA,0BAAA;;AAED,WAjCA,SAiCC,sBAAsB;EACtB,YAAA;EACA,gBAAA;EACA,0BAAA;;AAlDH,WAqDC;EACC,SAAA;EACA,2BAAA;EACA,yBAAA;EACA,wBAAA;EACA,sBAAA;EACA,qBAAA;EACA,iBAAA;;AA5DF,WA8DC;AA9DD,WA8DK;EACH,kBAAA;EACA,WAAA;EACA,YAAA;EC1CA,0BAAA;EACG,uBAAA;EACK,kBAAA;ED2CR,YAAA;;AAID,cAAe,YAAE,MAAM,GACtB;AADD,cAAe,YAAE,MAAM,GAClB;EACH,6BAAA;;AAID,WADD,MAAM,GAAG,GACP,IAAI;AACL,WAFD,MAAM,GAAG,GAEP,IAAI;EACJ,gBAAA;EACA,eAAA;;AAED,WAND,MAAM,GAAG,GAMP;AACD,WAPD,MAAM,GAAG,GAOP;EACA,WAAA;;AAED,WAVD,MAAM,GAAG,GAUP;AACD,WAXD,MAAM,GAAG,GAWP,SAAS;EACT,gBAAA;EACA,WAAA;EACA,eAAA;;AAED,WAhBD,MAAM,GAAG,GAgBP;EACA,mBAAA;EACA,gBAAA;;AAED,WApBD,MAAM,GAAG,GAoBP;AACD,WArBD,MAAM,GAAG,GAqBP,MAAM;AACP,WAtBD,MAAM,GAAG,GAsBP,MAAM;AACP,WAvBD,MAAM,GAAG,GAuBP,MAAM,SAAS;EC5Cd,yBAAA;EACA,kBAAkB,iDAAlB;EACA,kBAAkB,gDAAlB;EACA,kBAAkB,sCAAsC,eAAmB,YAA3E;EACA,kBAAkB,oDAAlB;EACA,kBAAkB,+CAAlB;EACA,kBAAkB,4CAAlB;EACA,2BAAA;EACA,QAAQ,0GAAR;EAfF,qCAAA;EACA,uEAAA;EAPA,QAAQ,yDAAR;ED4DC,WAAA;;ACvED,WD6CD,MAAM,GAAG,GAoBP,MCjEA;AAAD,WD6CD,MAAM,GAAG,GAqBP,MAAM,MClEN;AAAD,WD6CD,MAAM,GAAG,GAsBP,MAAM,SCnEN;AAAD,WD6CD,MAAM,GAAG,GAuBP,MAAM,SAAS,MCpEf;AAAQ,WD6CV,MAAM,GAAG,GAoBP,MCjES;AAAD,WD6CV,MAAM,GAAG,GAqBP,MAAM,MClEG;AAAD,WD6CV,MAAM,GAAG,GAsBP,MAAM,SCnEG;AAAD,WD6CV,MAAM,GAAG,GAuBP,MAAM,SAAS,MCpEN;AAAS,WD6CpB,MAAM,GAAG,GAoBP,MCjEmB;AAAD,WD6CpB,MAAM,GAAG,GAqBP,MAAM,MClEa;AAAD,WD6CpB,MAAM,GAAG,GAsBP,MAAM,SCnEa;AAAD,WD6CpB,MAAM,GAAG,GAuBP,MAAM,SAAS,MCpEI;AAAS,WD6C9B,MAAM,GAAG,GAoBP,MCjE6B;AAAD,WD6C9B,MAAM,GAAG,GAqBP,MAAM,MClEuB;AAAD,WD6C9B,MAAM,GAAG,GAsBP,MAAM,SCnEuB;AAAD,WD6C9B,MAAM,GAAG,GAuBP,MAAM,SAAS,MCpEc;AAAW,WD6C1C,MAAM,GAAG,GAoBP,MCjEyC;AAAD,WD6C1C,MAAM,GAAG,GAqBP,MAAM,MClEmC;AAAD,WD6C1C,MAAM,GAAG,GAsBP,MAAM,SCnEmC;AAAD,WD6C1C,MAAM,GAAG,GAuBP,MAAM,SAAS,MCpE0B;EACxC,yBAAA;;AAEF,WD0CD,MAAM,GAAG,GAoBP,MC9DA;AAAD,WD0CD,MAAM,GAAG,GAqBP,MAAM,MC/DN;AAAD,WD0CD,MAAM,GAAG,GAsBP,MAAM,SChEN;AAAD,WD0CD,MAAM,GAAG,GAuBP,MAAM,SAAS,MCjEf;AACD,WDyCD,MAAM,GAAG,GAoBP,MC7DA;AAAD,WDyCD,MAAM,GAAG,GAqBP,MAAM,MC9DN;AAAD,WDyCD,MAAM,GAAG,GAsBP,MAAM,SC/DN;AAAD,WDyCD,MAAM,GAAG,GAuBP,MAAM,SAAS,MChEf;EACC,0BAAyC,EAAzC;;ADoEF,WA5BD,MAAM,GAAG,GA4BP,MAAM,MAAM;EAEZ,WAAA;;AAED,WAhCD,MAAM,GAAG,GAgCP,MAAM,OAAO;EACb,WAAA;;AAED,WAnCD,MAAM,GAAG,GAmCP;AACD,WApCD,MAAM,GAAG,GAoCP,MAAM;AACP,WArCD,MAAM,GAAG,GAqCP,MAAM;AACP,WAtCD,MAAM,GAAG,GAsCP,MAAM,SAAS;EACf,gBAAA;EC7FD,wBAAA;EACG,qBAAA;EACK,gBAAA;;AD8FR,WA1CD,MAAM,GAAG,GA0CP,MAAM;AACP,WA3CD,MAAM,GAAG,GA2CP,MAAM,MAAM;AACb,WA5CD,MAAM,GAAG,GA4CP,MAAM,MAAM;AACb,WA7CD,MAAM,GAAG,GA6CP,MAAM,MAAM,SAAS;EClEpB,yBAAA;EACA,kBAAkB,iDAAlB;EACA,kBAAkB,gDAAlB;EACA,kBAAkB,sCAAsC,eAAmB,YAA3E;EACA,kBAAkB,oDAAlB;EACA,kBAAkB,+CAAlB;EACA,kBAAkB,4CAAlB;EACA,2BAAA;EACA,QAAQ,0GAAR;EAfF,qCAAA;EACA,uEAAA;EAPA,QAAQ,yDAAR;EApBA,wBAAA;EACG,qBAAA;EACK,gBAAA;;AAOR,WD6CD,MAAM,GAAG,GA0CP,MAAM,MCvFN;AAAD,WD6CD,MAAM,GAAG,GA2CP,MAAM,MAAM,MCxFZ;AAAD,WD6CD,MAAM,GAAG,GA4CP,MAAM,MAAM,SCzFZ;AAAD,WD6CD,MAAM,GAAG,GA6CP,MAAM,MAAM,SAAS,MC1FrB;AAAQ,WD6CV,MAAM,GAAG,GA0CP,MAAM,MCvFG;AAAD,WD6CV,MAAM,GAAG,GA2CP,MAAM,MAAM,MCxFH;AAAD,WD6CV,MAAM,GAAG,GA4CP,MAAM,MAAM,SCzFH;AAAD,WD6CV,MAAM,GAAG,GA6CP,MAAM,MAAM,SAAS,MC1FZ;AAAS,WD6CpB,MAAM,GAAG,GA0CP,MAAM,MCvFa;AAAD,WD6CpB,MAAM,GAAG,GA2CP,MAAM,MAAM,MCxFO;AAAD,WD6CpB,MAAM,GAAG,GA4CP,MAAM,MAAM,SCzFO;AAAD,WD6CpB,MAAM,GAAG,GA6CP,MAAM,MAAM,SAAS,MC1FF;AAAS,WD6C9B,MAAM,GAAG,GA0CP,MAAM,MCvFuB;AAAD,WD6C9B,MAAM,GAAG,GA2CP,MAAM,MAAM,MCxFiB;AAAD,WD6C9B,MAAM,GAAG,GA4CP,MAAM,MAAM,SCzFiB;AAAD,WD6C9B,MAAM,GAAG,GA6CP,MAAM,MAAM,SAAS,MC1FQ;AAAW,WD6C1C,MAAM,GAAG,GA0CP,MAAM,MCvFmC;AAAD,WD6C1C,MAAM,GAAG,GA2CP,MAAM,MAAM,MCxF6B;AAAD,WD6C1C,MAAM,GAAG,GA4CP,MAAM,MAAM,SCzF6B;AAAD,WD6C1C,MAAM,GAAG,GA6CP,MAAM,MAAM,SAAS,MC1FoB;EACxC,yBAAA;;AAEF,WD0CD,MAAM,GAAG,GA0CP,MAAM,MCpFN;AAAD,WD0CD,MAAM,GAAG,GA2CP,MAAM,MAAM,MCrFZ;AAAD,WD0CD,MAAM,GAAG,GA4CP,MAAM,MAAM,SCtFZ;AAAD,WD0CD,MAAM,GAAG,GA6CP,MAAM,MAAM,SAAS,MCvFrB;AACD,WDyCD,MAAM,GAAG,GA0CP,MAAM,MCnFN;AAAD,WDyCD,MAAM,GAAG,GA2CP,MAAM,MAAM,MCpFZ;AAAD,WDyCD,MAAM,GAAG,GA4CP,MAAM,MAAM,SCrFZ;AAAD,WDyCD,MAAM,GAAG,GA6CP,MAAM,MAAM,SAAS,MCtFrB;EACC,0BAAyC,EAAzC;;AD0FF,WAlDD,MAAM,GAAG,GAkDP;AACD,WAnDD,MAAM,GAAG,GAmDP,SAAS;AACV,WApDD,MAAM,GAAG,GAoDP,SAAS;AACV,WArDD,MAAM,GAAG,GAqDP,SAAS,SAAS;EC1EjB,yBAAA;EACA,kBAAkB,iDAAlB;EACA,kBAAkB,gDAAlB;EACA,kBAAkB,sCAAsC,eAAmB,YAA3E;EACA,kBAAkB,oDAAlB;EACA,kBAAkB,+CAAlB;EACA,kBAAkB,4CAAlB;EACA,2BAAA;EACA,QAAQ,0GAAR;EAfF,qCAAA;EACA,uEAAA;EAPA,QAAQ,yDAAR;EDyFC,WAAA;EACA,yCAAA;;ACrGD,WD6CD,MAAM,GAAG,GAkDP,SC/FA;AAAD,WD6CD,MAAM,GAAG,GAmDP,SAAS,MChGT;AAAD,WD6CD,MAAM,GAAG,GAoDP,SAAS,SCjGT;AAAD,WD6CD,MAAM,GAAG,GAqDP,SAAS,SAAS,MClGlB;AAAQ,WD6CV,MAAM,GAAG,GAkDP,SC/FS;AAAD,WD6CV,MAAM,GAAG,GAmDP,SAAS,MChGA;AAAD,WD6CV,MAAM,GAAG,GAoDP,SAAS,SCjGA;AAAD,WD6CV,MAAM,GAAG,GAqDP,SAAS,SAAS,MClGT;AAAS,WD6CpB,MAAM,GAAG,GAkDP,SC/FmB;AAAD,WD6CpB,MAAM,GAAG,GAmDP,SAAS,MChGU;AAAD,WD6CpB,MAAM,GAAG,GAoDP,SAAS,SCjGU;AAAD,WD6CpB,MAAM,GAAG,GAqDP,SAAS,SAAS,MClGC;AAAS,WD6C9B,MAAM,GAAG,GAkDP,SC/F6B;AAAD,WD6C9B,MAAM,GAAG,GAmDP,SAAS,MChGoB;AAAD,WD6C9B,MAAM,GAAG,GAoDP,SAAS,SCjGoB;AAAD,WD6C9B,MAAM,GAAG,GAqDP,SAAS,SAAS,MClGW;AAAW,WD6C1C,MAAM,GAAG,GAkDP,SC/FyC;AAAD,WD6C1C,MAAM,GAAG,GAmDP,SAAS,MChGgC;AAAD,WD6C1C,MAAM,GAAG,GAoDP,SAAS,SCjGgC;AAAD,WD6C1C,MAAM,GAAG,GAqDP,SAAS,SAAS,MClGuB;EACxC,yBAAA;;AAEF,WD0CD,MAAM,GAAG,GAkDP,SC5FA;AAAD,WD0CD,MAAM,GAAG,GAmDP,SAAS,MC7FT;AAAD,WD0CD,MAAM,GAAG,GAoDP,SAAS,SC9FT;AAAD,WD0CD,MAAM,GAAG,GAqDP,SAAS,SAAS,MC/FlB;AACD,WDyCD,MAAM,GAAG,GAkDP,SC3FA;AAAD,WDyCD,MAAM,GAAG,GAmDP,SAAS,MC5FT;AAAD,WDyCD,MAAM,GAAG,GAoDP,SAAS,SC7FT;AAAD,WDyCD,MAAM,GAAG,GAqDP,SAAS,SAAS,MC9FlB;EACC,0BAAyC,EAAzC;;ADkGF,WA1DD,MAAM,GAAG,GA0DP;AACD,WA3DD,MAAM,GAAG,GA2DP,OAAO;AACR,WA5DD,MAAM,GAAG,GA4DP,OAAO;AACR,WA7DD,MAAM,GAAG,GA6DP,OAAO,SAAS;EClFf,yBAAA;EACA,kBAAkB,8CAAlB;EACA,kBAAkB,6CAAlB;EACA,kBAAkB,sCAAsC,YAAmB,YAA3E;EACA,kBAAkB,iDAAlB;EACA,kBAAkB,4CAAlB;EACA,kBAAkB,yCAAlB;EACA,2BAAA;EACA,QAAQ,uGAAR;EAfF,qCAAA;EACA,uEAAA;EAPA,QAAQ,yDAAR;EDiGC,WAAA;EACA,yCAAA;;AC7GD,WD6CD,MAAM,GAAG,GA0DP,OCvGA;AAAD,WD6CD,MAAM,GAAG,GA2DP,OAAO,MCxGP;AAAD,WD6CD,MAAM,GAAG,GA4DP,OAAO,SCzGP;AAAD,WD6CD,MAAM,GAAG,GA6DP,OAAO,SAAS,MC1GhB;AAAQ,WD6CV,MAAM,GAAG,GA0DP,OCvGS;AAAD,WD6CV,MAAM,GAAG,GA2DP,OAAO,MCxGE;AAAD,WD6CV,MAAM,GAAG,GA4DP,OAAO,SCzGE;AAAD,WD6CV,MAAM,GAAG,GA6DP,OAAO,SAAS,MC1GP;AAAS,WD6CpB,MAAM,GAAG,GA0DP,OCvGmB;AAAD,WD6CpB,MAAM,GAAG,GA2DP,OAAO,MCxGY;AAAD,WD6CpB,MAAM,GAAG,GA4DP,OAAO,SCzGY;AAAD,WD6CpB,MAAM,GAAG,GA6DP,OAAO,SAAS,MC1GG;AAAS,WD6C9B,MAAM,GAAG,GA0DP,OCvG6B;AAAD,WD6C9B,MAAM,GAAG,GA2DP,OAAO,MCxGsB;AAAD,WD6C9B,MAAM,GAAG,GA4DP,OAAO,SCzGsB;AAAD,WD6C9B,MAAM,GAAG,GA6DP,OAAO,SAAS,MC1Ga;AAAW,WD6C1C,MAAM,GAAG,GA0DP,OCvGyC;AAAD,WD6C1C,MAAM,GAAG,GA2DP,OAAO,MCxGkC;AAAD,WD6C1C,MAAM,GAAG,GA4DP,OAAO,SCzGkC;AAAD,WD6C1C,MAAM,GAAG,GA6DP,OAAO,SAAS,MC1GyB;EACxC,yBAAA;;AAEF,WD0CD,MAAM,GAAG,GA0DP,OCpGA;AAAD,WD0CD,MAAM,GAAG,GA2DP,OAAO,MCrGP;AAAD,WD0CD,MAAM,GAAG,GA4DP,OAAO,SCtGP;AAAD,WD0CD,MAAM,GAAG,GA6DP,OAAO,SAAS,MCvGhB;AACD,WDyCD,MAAM,GAAG,GA0DP,OCnGA;AAAD,WDyCD,MAAM,GAAG,GA2DP,OAAO,MCpGP;AAAD,WDyCD,MAAM,GAAG,GA4DP,OAAO,SCrGP;AAAD,WDyCD,MAAM,GAAG,GA6DP,OAAO,SAAS,MCtGhB;EACC,0BAAyC,EAAzC;;ADrCJ,WA6EC,MAAM,GAAG,GAkER;EACC,cAAA;EACA,UAAA;EACA,YAAA;EACA,iBAAA;EACA,WAAA;EACA,UAAA;EACA,eAAA;EC/HD,0BAAA;EACG,uBAAA;EACK,kBAAA;;AD+HP,WA3EF,MAAM,GAAG,GAkER,KASE;AACD,WA5EF,MAAM,GAAG,GAkER,KAUE;EACA,gBAAA;;AAED,WA/EF,MAAM,GAAG,GAkER,KAaE;AACD,WAhFF,MAAM,GAAG,GAkER,KAcE,SAAS;EACT,gBAAA;EACA,WAAA;EACA,eAAA;;AAED,WArFF,MAAM,GAAG,GAkER,KAmBE;AACD,WAtFF,MAAM,GAAG,GAkER,KAoBE,OAAO;AACR,WAvFF,MAAM,GAAG,GAkER,KAqBE,OAAO;AACR,WAxFF,MAAM,GAAG,GAkER,KAsBE,OAAO,SAAS;EC7GhB,yBAAA;EACA,kBAAkB,8CAAlB;EACA,kBAAkB,6CAAlB;EACA,kBAAkB,sCAAsC,YAAmB,YAA3E;EACA,kBAAkB,iDAAlB;EACA,kBAAkB,4CAAlB;EACA,kBAAkB,yCAAlB;EACA,2BAAA;EACA,QAAQ,uGAAR;EAfF,qCAAA;EACA,uEAAA;EAPA,QAAQ,yDAAR;ED4HE,WAAA;EACA,yCAAA;;ACxIF,WD6CD,MAAM,GAAG,GAkER,KAmBE,OClID;AAAD,WD6CD,MAAM,GAAG,GAkER,KAoBE,OAAO,MCnIR;AAAD,WD6CD,MAAM,GAAG,GAkER,KAqBE,OAAO,SCpIR;AAAD,WD6CD,MAAM,GAAG,GAkER,KAsBE,OAAO,SAAS,MCrIjB;AAAQ,WD6CV,MAAM,GAAG,GAkER,KAmBE,OClIQ;AAAD,WD6CV,MAAM,GAAG,GAkER,KAoBE,OAAO,MCnIC;AAAD,WD6CV,MAAM,GAAG,GAkER,KAqBE,OAAO,SCpIC;AAAD,WD6CV,MAAM,GAAG,GAkER,KAsBE,OAAO,SAAS,MCrIR;AAAS,WD6CpB,MAAM,GAAG,GAkER,KAmBE,OClIkB;AAAD,WD6CpB,MAAM,GAAG,GAkER,KAoBE,OAAO,MCnIW;AAAD,WD6CpB,MAAM,GAAG,GAkER,KAqBE,OAAO,SCpIW;AAAD,WD6CpB,MAAM,GAAG,GAkER,KAsBE,OAAO,SAAS,MCrIE;AAAS,WD6C9B,MAAM,GAAG,GAkER,KAmBE,OClI4B;AAAD,WD6C9B,MAAM,GAAG,GAkER,KAoBE,OAAO,MCnIqB;AAAD,WD6C9B,MAAM,GAAG,GAkER,KAqBE,OAAO,SCpIqB;AAAD,WD6C9B,MAAM,GAAG,GAkER,KAsBE,OAAO,SAAS,MCrIY;AAAW,WD6C1C,MAAM,GAAG,GAkER,KAmBE,OClIwC;AAAD,WD6C1C,MAAM,GAAG,GAkER,KAoBE,OAAO,MCnIiC;AAAD,WD6C1C,MAAM,GAAG,GAkER,KAqBE,OAAO,SCpIiC;AAAD,WD6C1C,MAAM,GAAG,GAkER,KAsBE,OAAO,SAAS,MCrIwB;EACxC,yBAAA;;AAEF,WD0CD,MAAM,GAAG,GAkER,KAmBE,OC/HD;AAAD,WD0CD,MAAM,GAAG,GAkER,KAoBE,OAAO,MChIR;AAAD,WD0CD,MAAM,GAAG,GAkER,KAqBE,OAAO,SCjIR;AAAD,WD0CD,MAAM,GAAG,GAkER,KAsBE,OAAO,SAAS,MClIjB;AACD,WDyCD,MAAM,GAAG,GAkER,KAmBE,OC9HD;AAAD,WDyCD,MAAM,GAAG,GAkER,KAoBE,OAAO,MC/HR;AAAD,WDyCD,MAAM,GAAG,GAkER,KAqBE,OAAO,SChIR;AAAD,WDyCD,MAAM,GAAG,GAkER,KAsBE,OAAO,SAAS,MCjIjB;EACC,0BAAyC,EAAzC;;ADqID,WA7FF,MAAM,GAAG,GAkER,KA2BE;AACD,WA9FF,MAAM,GAAG,GAkER,KA4BE;EACA,WAAA;;AA5KJ,WAiLC;EACC,YAAA;;AAlLF,WAqLC;AArLD,WAsLC;AAtLD,WAuLC;AAvLD,WAwLC,MAAM,GAAG;EACR,eAAA;;AACA,WALD,mBAKE;AAAD,WAJD,MAIE;AAAD,WAHD,MAGE;AAAD,WAFD,MAAM,GAAG,GAEP;EACA,gBAAA;;AAKD,WADD,MACE;AAAD,WADM,MACL;EACA,kBAAA;;AAjMH,WAsMC;EACC,eAAA;EACA,WAAA;EACA,oBAAA;EACA,sBAAA;;AAKD,aAAC,KAAM;AAAP,cAAC,KAAM;EACN,eAAA;;AADD,aAAC,KAAM,QAGN;AAHD,cAAC,KAAM,QAGN;EACC,eAAA;;AAIH,gBACC;EACC,kBAAA;;AAFF,gBAIC,MAAK;ECpMJ,kCAAA;EACG,+BAAA;EACK,0BAAA;;AD8LV,gBAOC,MAAK;ECvMJ,kCAAA;EACG,+BAAA;EACK,0BAAA;;AD8LV,gBAUC;EACC,qBAAA;EACA,WAAA;EACA,eAAA;EACA,YAAA;EACA,gBAAA;EACA,mBAAA;EACA,iBAAA;EACA,kBAAA;EACA,yBAAA;EACA,sBAAA;EACA,sBAAA;EACA,sBAAA;EACA,iBAAA;EACA,kBAAA","sourcesContent":[".datepicker {\n\tpadding: 4px;\n\t.border-radius(@baseBorderRadius);\n\t&-inline {\n\t\twidth: 220px;\n\t}\n\tdirection: ltr;\n\t&-rtl {\n\t\tdirection: rtl;\n\t\t&.dropdown-menu { left: auto; }\n\t\ttable tr td span {\n\t\t\tfloat: right;\n\t\t}\n\t}\n\t&-dropdown {\n\t\ttop: 0;\n\t\tleft: 0;\n\t\t&:before {\n\t\t\tcontent: '';\n\t\t\tdisplay: inline-block;\n\t\t\tborder-left: 7px solid transparent;\n\t\t\tborder-right: 7px solid transparent;\n\t\t\tborder-bottom: 7px solid @grayLight;\n\t\t\tborder-top: 0;\n\t\t\tborder-bottom-color: rgba(0,0,0,.2);\n\t\t\tposition: absolute;\n\t\t}\n\t\t&:after {\n\t\t\tcontent: '';\n\t\t\tdisplay: inline-block;\n\t\t\tborder-left: 6px solid transparent;\n\t\t\tborder-right: 6px solid transparent;\n\t\t\tborder-bottom: 6px solid @white;\n\t\t\tborder-top: 0;\n\t\t\tposition: absolute;\n\t\t}\n\t\t&.datepicker-orient-left:before { left: 6px; }\n\t\t&.datepicker-orient-left:after { left: 7px; }\n\t\t&.datepicker-orient-right:before { right: 6px; }\n\t\t&.datepicker-orient-right:after { right: 7px; }\n\t\t&.datepicker-orient-bottom:before { top: -7px; }\n\t\t&.datepicker-orient-bottom:after { top: -6px; }\n\t\t&.datepicker-orient-top:before {\n\t\t\tbottom: -7px;\n\t\t\tborder-bottom: 0;\n\t\t\tborder-top: 7px solid @grayLight;\n\t\t}\n\t\t&.datepicker-orient-top:after {\n\t\t\tbottom: -6px;\n\t\t\tborder-bottom: 0;\n\t\t\tborder-top: 6px solid @white;\n\t\t}\n\t}\n\ttable {\n\t\tmargin: 0;\n\t\t-webkit-touch-callout: none;\n\t\t-webkit-user-select: none;\n\t\t-khtml-user-select: none;\n\t\t-moz-user-select: none;\n\t\t-ms-user-select: none;\n\t\tuser-select: none;\n\t}\n\ttd, th {\n\t\ttext-align: center;\n\t\twidth: 20px;\n\t\theight: 20px;\n\t\t.border-radius(4px);\n\n\t\tborder: none;\n\t}\n\t// Inline display inside a table presents some problems with\n\t// border and background colors.\n\t.table-striped & table tr {\n\t\ttd, th {\n\t\t\tbackground-color: transparent;\n\t\t}\n\t}\n\ttable tr td {\n\t\t&.day:hover,\n\t\t&.day.focused {\n\t\t\tbackground: @grayLighter;\n\t\t\tcursor: pointer;\n\t\t}\n\t\t&.old,\n\t\t&.new {\n\t\t\tcolor: @grayLight;\n\t\t}\n\t\t&.disabled,\n\t\t&.disabled:hover {\n\t\t\tbackground: none;\n\t\t\tcolor: @grayLight;\n\t\t\tcursor: default;\n\t\t}\n\t\t&.highlighted {\n\t\t\tbackground: @infoBackground;\n\t\t\tborder-radius: 0;\n\t\t}\n\t\t&.today,\n\t\t&.today:hover,\n\t\t&.today.disabled,\n\t\t&.today.disabled:hover {\n\t\t\t@todayBackground: lighten(@orange, 30%);\n\t\t\t.buttonBackground(@todayBackground, spin(@todayBackground, 20));\n\t\t\tcolor: #000;\n\t\t}\n\t\t&.today:hover:hover { // Thank bootstrap 2.0 for this selector...\n\t\t\t// TODO: Bump min BS to 2.1, use @textColor in buttonBackground above\n\t\t\tcolor: #000;\n\t\t}\n\t\t&.today.active:hover {\n\t\t\tcolor: #fff;\n\t\t}\n\t\t&.range,\n\t\t&.range:hover,\n\t\t&.range.disabled,\n\t\t&.range.disabled:hover {\n\t\t\tbackground: @grayLighter;\n\t\t\t.border-radius(0);\n\t\t}\n\t\t&.range.today,\n\t\t&.range.today:hover,\n\t\t&.range.today.disabled,\n\t\t&.range.today.disabled:hover {\n\t\t\t@todayBackground: mix(@orange, @grayLighter, 50%);\n\t\t\t.buttonBackground(@todayBackground, spin(@todayBackground, 20));\n\t\t\t.border-radius(0);\n\t\t}\n\t\t&.selected,\n\t\t&.selected:hover,\n\t\t&.selected.disabled,\n\t\t&.selected.disabled:hover {\n\t\t\t.buttonBackground(lighten(@grayLight, 10), darken(@grayLight, 10));\n\t\t\tcolor: #fff;\n\t\t\ttext-shadow: 0 -1px 0 rgba(0,0,0,.25);\n\t\t}\n\t\t&.active,\n\t\t&.active:hover,\n\t\t&.active.disabled,\n\t\t&.active.disabled:hover {\n\t\t\t.buttonBackground(@btnPrimaryBackground, spin(@btnPrimaryBackground, 20));\n\t\t\tcolor: #fff;\n\t\t\ttext-shadow: 0 -1px 0 rgba(0,0,0,.25);\n\t\t}\n\t\tspan {\n\t\t\tdisplay: block;\n\t\t\twidth: 23%;\n\t\t\theight: 54px;\n\t\t\tline-height: 54px;\n\t\t\tfloat: left;\n\t\t\tmargin: 1%;\n\t\t\tcursor: pointer;\n\t\t\t.border-radius(4px);\n\t\t\t&:hover,\n\t\t\t&.focused {\n\t\t\t\tbackground: @grayLighter;\n\t\t\t}\n\t\t\t&.disabled,\n\t\t\t&.disabled:hover {\n\t\t\t\tbackground: none;\n\t\t\t\tcolor: @grayLight;\n\t\t\t\tcursor: default;\n\t\t\t}\n\t\t\t&.active,\n\t\t\t&.active:hover,\n\t\t\t&.active.disabled,\n\t\t\t&.active.disabled:hover {\n\t\t\t\t.buttonBackground(@btnPrimaryBackground, spin(@btnPrimaryBackground, 20));\n\t\t\t\tcolor: #fff;\n\t\t\t\ttext-shadow: 0 -1px 0 rgba(0,0,0,.25);\n\t\t\t}\n\t\t\t&.old,\n\t\t\t&.new {\n\t\t\t\tcolor: @grayLight;\n\t\t\t}\n\t\t}\n\t}\n\n\t.datepicker-switch {\n\t\twidth: 145px;\n\t}\n\n\t.datepicker-switch,\n\t.prev,\n\t.next,\n\ttfoot tr th {\n\t\tcursor: pointer;\n\t\t&:hover {\n\t\t\tbackground: @grayLighter;\n\t\t}\n\t}\n\n\t.prev, .next {\n\t\t&.disabled {\n\t\t\tvisibility: hidden;\n\t\t}\n\t}\n\n\t// Basic styling for calendar-week cells\n\t.cw {\n\t\tfont-size: 10px;\n\t\twidth: 12px;\n\t\tpadding: 0 2px 0 5px;\n\t\tvertical-align: middle;\n\t}\n}\n.input-append,\n.input-prepend {\n\t&.date .add-on {\n\t\tcursor: pointer;\n\n\t\ti {\n\t\t\tmargin-top: 3px;\n\t\t}\n\t}\n}\n.input-daterange {\n\tinput {\n\t\ttext-align:center;\n\t}\n\tinput:first-child {\n\t\t.border-radius(3px 0 0 3px);\n\t}\n\tinput:last-child {\n\t\t.border-radius(0 3px 3px 0);\n\t}\n\t.add-on {\n\t\tdisplay: inline-block;\n\t\twidth: auto;\n\t\tmin-width: 16px;\n\t\theight: @baseLineHeight;\n\t\tpadding: 4px 5px;\n\t\tfont-weight: normal;\n\t\tline-height: @baseLineHeight;\n\t\ttext-align: center;\n\t\ttext-shadow: 0 1px 0 @white;\n\t\tvertical-align: middle;\n\t\tbackground-color: @grayLighter;\n\t\tborder: 1px solid #ccc;\n\t\tmargin-left: -5px;\n\t\tmargin-right: -5px;\n\t}\n}\n","// Datepicker .less buildfile. Includes select mixins/variables from bootstrap\n// and imports the included datepicker.less to output a minimal datepicker.css\n//\n// Usage:\n// lessc build.less datepicker.css\n//\n// Variables and mixins copied from bootstrap 2.0.2\n\n// Variables\n@grayLight: #999;\n@grayLighter: #eee;\n@white: #fff;\n@linkColor: #08c;\n@btnPrimaryBackground: @linkColor;\n@orange: #f89406;\n@infoBackground: #d9edf7;\n@baseLineHeight: 18px;\n@baseBorderRadius: 4px;\n\n// Mixins\n\n// Border Radius\n.border-radius(@radius: 5px) {\n -webkit-border-radius: @radius;\n -moz-border-radius: @radius;\n border-radius: @radius;\n}\n\n// Button backgrounds\n.buttonBackground(@startColor, @endColor) {\n .gradientBar(@startColor, @endColor);\n .reset-filter();\n &:hover, &:active, &.active, &.disabled, &[disabled] {\n background-color: @endColor;\n }\n &:active,\n &.active {\n background-color: darken(@endColor, 10%) e(\"\\9\");\n }\n}\n\n// Reset filters for IE\n.reset-filter() {\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n}\n\n// Gradient Bar Colors for buttons and alerts\n.gradientBar(@primaryColor, @secondaryColor) {\n #gradient > .vertical(@primaryColor, @secondaryColor);\n border-color: @secondaryColor @secondaryColor darken(@secondaryColor, 15%);\n border-color: rgba(0,0,0,.1) rgba(0,0,0,.1) fadein(rgba(0,0,0,.1), 15%);\n}\n\n// Gradients\n#gradient {\n .vertical(@startColor: #555, @endColor: #333) {\n background-color: mix(@startColor, @endColor, 60%);\n background-image: -moz-linear-gradient(to bottom, @startColor, @endColor); // FF 3.6+\n background-image: -ms-linear-gradient(to bottom, @startColor, @endColor); // IE10\n background-image: -webkit-gradient(linear, 0 0, 0 100%, from(@startColor), to(@endColor)); // Safari 4+, Chrome 2+\n background-image: -webkit-linear-gradient(to bottom, @startColor, @endColor); // Safari 5.1+, Chrome 10+\n background-image: -o-linear-gradient(to bottom, @startColor, @endColor); // Opera 11.10\n background-image: linear-gradient(to bottom, @startColor, @endColor); // The standard\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",@startColor,@endColor)); // IE9 and down\n }\n}\n\n@import \"../less/datepicker.less\";\n"]} \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/bootstrap-datepicker.min.css b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/bootstrap-datepicker.min.css new file mode 100644 index 0000000000..eb681513fb --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/bootstrap-datepicker.min.css @@ -0,0 +1,7 @@ +/*! + * Datepicker for Bootstrap v1.9.0 (https://github.com/uxsolutions/bootstrap-datepicker) + * + * Licensed under the Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0) + */ + +.datepicker{padding:4px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;direction:ltr}.datepicker-inline{width:220px}.datepicker-rtl{direction:rtl}.datepicker-rtl.dropdown-menu{left:auto}.datepicker-rtl table tr td span{float:right}.datepicker-dropdown{top:0;left:0}.datepicker-dropdown:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #999;border-top:0;border-bottom-color:rgba(0,0,0,.2);position:absolute}.datepicker-dropdown:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;border-top:0;position:absolute}.datepicker-dropdown.datepicker-orient-left:before{left:6px}.datepicker-dropdown.datepicker-orient-left:after{left:7px}.datepicker-dropdown.datepicker-orient-right:before{right:6px}.datepicker-dropdown.datepicker-orient-right:after{right:7px}.datepicker-dropdown.datepicker-orient-bottom:before{top:-7px}.datepicker-dropdown.datepicker-orient-bottom:after{top:-6px}.datepicker-dropdown.datepicker-orient-top:before{bottom:-7px;border-bottom:0;border-top:7px solid #999}.datepicker-dropdown.datepicker-orient-top:after{bottom:-6px;border-bottom:0;border-top:6px solid #fff}.datepicker table{margin:0;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.datepicker td,.datepicker th{text-align:center;width:20px;height:20px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;border:none}.table-striped .datepicker table tr td,.table-striped .datepicker table tr th{background-color:transparent}.datepicker table tr td.day.focused,.datepicker table tr td.day:hover{background:#eee;cursor:pointer}.datepicker table tr td.new,.datepicker table tr td.old{color:#999}.datepicker table tr td.disabled,.datepicker table tr td.disabled:hover{background:0 0;color:#999;cursor:default}.datepicker table tr td.highlighted{background:#d9edf7;border-radius:0}.datepicker table tr td.today,.datepicker table tr td.today.disabled,.datepicker table tr td.today.disabled:hover,.datepicker table tr td.today:hover{background-color:#fde19a;background-image:-moz-linear-gradient(to bottom,#fdd49a,#fdf59a);background-image:-ms-linear-gradient(to bottom,#fdd49a,#fdf59a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fdd49a),to(#fdf59a));background-image:-webkit-linear-gradient(to bottom,#fdd49a,#fdf59a);background-image:-o-linear-gradient(to bottom,#fdd49a,#fdf59a);background-image:linear-gradient(to bottom,#fdd49a,#fdf59a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdd49a', endColorstr='#fdf59a', GradientType=0);border-color:#fdf59a #fdf59a #fbed50;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);color:#000}.datepicker table tr td.today.active,.datepicker table tr td.today.disabled,.datepicker table tr td.today.disabled.active,.datepicker table tr td.today.disabled.disabled,.datepicker table tr td.today.disabled:active,.datepicker table tr td.today.disabled:hover,.datepicker table tr td.today.disabled:hover.active,.datepicker table tr td.today.disabled:hover.disabled,.datepicker table tr td.today.disabled:hover:active,.datepicker table tr td.today.disabled:hover:hover,.datepicker table tr td.today.disabled:hover[disabled],.datepicker table tr td.today.disabled[disabled],.datepicker table tr td.today:active,.datepicker table tr td.today:hover,.datepicker table tr td.today:hover.active,.datepicker table tr td.today:hover.disabled,.datepicker table tr td.today:hover:active,.datepicker table tr td.today:hover:hover,.datepicker table tr td.today:hover[disabled],.datepicker table tr td.today[disabled]{background-color:#fdf59a}.datepicker table tr td.today.active,.datepicker table tr td.today.disabled.active,.datepicker table tr td.today.disabled:active,.datepicker table tr td.today.disabled:hover.active,.datepicker table tr td.today.disabled:hover:active,.datepicker table tr td.today:active,.datepicker table tr td.today:hover.active,.datepicker table tr td.today:hover:active{background-color:#fbf069\9}.datepicker table tr td.today:hover:hover{color:#000}.datepicker table tr td.today.active:hover{color:#fff}.datepicker table tr td.range,.datepicker table tr td.range.disabled,.datepicker table tr td.range.disabled:hover,.datepicker table tr td.range:hover{background:#eee;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.datepicker table tr td.range.today,.datepicker table tr td.range.today.disabled,.datepicker table tr td.range.today.disabled:hover,.datepicker table tr td.range.today:hover{background-color:#f3d17a;background-image:-moz-linear-gradient(to bottom,#f3c17a,#f3e97a);background-image:-ms-linear-gradient(to bottom,#f3c17a,#f3e97a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f3c17a),to(#f3e97a));background-image:-webkit-linear-gradient(to bottom,#f3c17a,#f3e97a);background-image:-o-linear-gradient(to bottom,#f3c17a,#f3e97a);background-image:linear-gradient(to bottom,#f3c17a,#f3e97a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f3c17a', endColorstr='#f3e97a', GradientType=0);border-color:#f3e97a #f3e97a #edde34;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.datepicker table tr td.range.today.active,.datepicker table tr td.range.today.disabled,.datepicker table tr td.range.today.disabled.active,.datepicker table tr td.range.today.disabled.disabled,.datepicker table tr td.range.today.disabled:active,.datepicker table tr td.range.today.disabled:hover,.datepicker table tr td.range.today.disabled:hover.active,.datepicker table tr td.range.today.disabled:hover.disabled,.datepicker table tr td.range.today.disabled:hover:active,.datepicker table tr td.range.today.disabled:hover:hover,.datepicker table tr td.range.today.disabled:hover[disabled],.datepicker table tr td.range.today.disabled[disabled],.datepicker table tr td.range.today:active,.datepicker table tr td.range.today:hover,.datepicker table tr td.range.today:hover.active,.datepicker table tr td.range.today:hover.disabled,.datepicker table tr td.range.today:hover:active,.datepicker table tr td.range.today:hover:hover,.datepicker table tr td.range.today:hover[disabled],.datepicker table tr td.range.today[disabled]{background-color:#f3e97a}.datepicker table tr td.range.today.active,.datepicker table tr td.range.today.disabled.active,.datepicker table tr td.range.today.disabled:active,.datepicker table tr td.range.today.disabled:hover.active,.datepicker table tr td.range.today.disabled:hover:active,.datepicker table tr td.range.today:active,.datepicker table tr td.range.today:hover.active,.datepicker table tr td.range.today:hover:active{background-color:#efe24b\9}.datepicker table tr td.selected,.datepicker table tr td.selected.disabled,.datepicker table tr td.selected.disabled:hover,.datepicker table tr td.selected:hover{background-color:#9e9e9e;background-image:-moz-linear-gradient(to bottom,#b3b3b3,grey);background-image:-ms-linear-gradient(to bottom,#b3b3b3,grey);background-image:-webkit-gradient(linear,0 0,0 100%,from(#b3b3b3),to(grey));background-image:-webkit-linear-gradient(to bottom,#b3b3b3,grey);background-image:-o-linear-gradient(to bottom,#b3b3b3,grey);background-image:linear-gradient(to bottom,#b3b3b3,grey);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#b3b3b3', endColorstr='#808080', GradientType=0);border-color:grey grey #595959;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25)}.datepicker table tr td.selected.active,.datepicker table tr td.selected.disabled,.datepicker table tr td.selected.disabled.active,.datepicker table tr td.selected.disabled.disabled,.datepicker table tr td.selected.disabled:active,.datepicker table tr td.selected.disabled:hover,.datepicker table tr td.selected.disabled:hover.active,.datepicker table tr td.selected.disabled:hover.disabled,.datepicker table tr td.selected.disabled:hover:active,.datepicker table tr td.selected.disabled:hover:hover,.datepicker table tr td.selected.disabled:hover[disabled],.datepicker table tr td.selected.disabled[disabled],.datepicker table tr td.selected:active,.datepicker table tr td.selected:hover,.datepicker table tr td.selected:hover.active,.datepicker table tr td.selected:hover.disabled,.datepicker table tr td.selected:hover:active,.datepicker table tr td.selected:hover:hover,.datepicker table tr td.selected:hover[disabled],.datepicker table tr td.selected[disabled]{background-color:grey}.datepicker table tr td.selected.active,.datepicker table tr td.selected.disabled.active,.datepicker table tr td.selected.disabled:active,.datepicker table tr td.selected.disabled:hover.active,.datepicker table tr td.selected.disabled:hover:active,.datepicker table tr td.selected:active,.datepicker table tr td.selected:hover.active,.datepicker table tr td.selected:hover:active{background-color:#666\9}.datepicker table tr td.active,.datepicker table tr td.active.disabled,.datepicker table tr td.active.disabled:hover,.datepicker table tr td.active:hover{background-color:#006dcc;background-image:-moz-linear-gradient(to bottom,#08c,#04c);background-image:-ms-linear-gradient(to bottom,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(to bottom,#08c,#04c);background-image:-o-linear-gradient(to bottom,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#08c', endColorstr='#0044cc', GradientType=0);border-color:#04c #04c #002a80;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25)}.datepicker table tr td.active.active,.datepicker table tr td.active.disabled,.datepicker table tr td.active.disabled.active,.datepicker table tr td.active.disabled.disabled,.datepicker table tr td.active.disabled:active,.datepicker table tr td.active.disabled:hover,.datepicker table tr td.active.disabled:hover.active,.datepicker table tr td.active.disabled:hover.disabled,.datepicker table tr td.active.disabled:hover:active,.datepicker table tr td.active.disabled:hover:hover,.datepicker table tr td.active.disabled:hover[disabled],.datepicker table tr td.active.disabled[disabled],.datepicker table tr td.active:active,.datepicker table tr td.active:hover,.datepicker table tr td.active:hover.active,.datepicker table tr td.active:hover.disabled,.datepicker table tr td.active:hover:active,.datepicker table tr td.active:hover:hover,.datepicker table tr td.active:hover[disabled],.datepicker table tr td.active[disabled]{background-color:#04c}.datepicker table tr td.active.active,.datepicker table tr td.active.disabled.active,.datepicker table tr td.active.disabled:active,.datepicker table tr td.active.disabled:hover.active,.datepicker table tr td.active.disabled:hover:active,.datepicker table tr td.active:active,.datepicker table tr td.active:hover.active,.datepicker table tr td.active:hover:active{background-color:#039\9}.datepicker table tr td span{display:block;width:23%;height:54px;line-height:54px;float:left;margin:1%;cursor:pointer;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.datepicker table tr td span.focused,.datepicker table tr td span:hover{background:#eee}.datepicker table tr td span.disabled,.datepicker table tr td span.disabled:hover{background:0 0;color:#999;cursor:default}.datepicker table tr td span.active,.datepicker table tr td span.active.disabled,.datepicker table tr td span.active.disabled:hover,.datepicker table tr td span.active:hover{background-color:#006dcc;background-image:-moz-linear-gradient(to bottom,#08c,#04c);background-image:-ms-linear-gradient(to bottom,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(to bottom,#08c,#04c);background-image:-o-linear-gradient(to bottom,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#08c', endColorstr='#0044cc', GradientType=0);border-color:#04c #04c #002a80;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25)}.datepicker table tr td span.active.active,.datepicker table tr td span.active.disabled,.datepicker table tr td span.active.disabled.active,.datepicker table tr td span.active.disabled.disabled,.datepicker table tr td span.active.disabled:active,.datepicker table tr td span.active.disabled:hover,.datepicker table tr td span.active.disabled:hover.active,.datepicker table tr td span.active.disabled:hover.disabled,.datepicker table tr td span.active.disabled:hover:active,.datepicker table tr td span.active.disabled:hover:hover,.datepicker table tr td span.active.disabled:hover[disabled],.datepicker table tr td span.active.disabled[disabled],.datepicker table tr td span.active:active,.datepicker table tr td span.active:hover,.datepicker table tr td span.active:hover.active,.datepicker table tr td span.active:hover.disabled,.datepicker table tr td span.active:hover:active,.datepicker table tr td span.active:hover:hover,.datepicker table tr td span.active:hover[disabled],.datepicker table tr td span.active[disabled]{background-color:#04c}.datepicker table tr td span.active.active,.datepicker table tr td span.active.disabled.active,.datepicker table tr td span.active.disabled:active,.datepicker table tr td span.active.disabled:hover.active,.datepicker table tr td span.active.disabled:hover:active,.datepicker table tr td span.active:active,.datepicker table tr td span.active:hover.active,.datepicker table tr td span.active:hover:active{background-color:#039\9}.datepicker table tr td span.new,.datepicker table tr td span.old{color:#999}.datepicker .datepicker-switch{width:145px}.datepicker .datepicker-switch,.datepicker .next,.datepicker .prev,.datepicker tfoot tr th{cursor:pointer}.datepicker .datepicker-switch:hover,.datepicker .next:hover,.datepicker .prev:hover,.datepicker tfoot tr th:hover{background:#eee}.datepicker .next.disabled,.datepicker .prev.disabled{visibility:hidden}.datepicker .cw{font-size:10px;width:12px;padding:0 2px 0 5px;vertical-align:middle}.input-append.date .add-on,.input-prepend.date .add-on{cursor:pointer}.input-append.date .add-on i,.input-prepend.date .add-on i{margin-top:3px}.input-daterange input{text-align:center}.input-daterange input:first-child{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.input-daterange input:last-child{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.input-daterange .add-on{display:inline-block;width:auto;min-width:16px;height:18px;padding:4px 5px;font-weight:400;line-height:18px;text-align:center;text-shadow:0 1px 0 #fff;vertical-align:middle;background-color:#eee;border:1px solid #ccc;margin-left:-5px;margin-right:-5px} \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/bootstrap-datepicker.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/bootstrap-datepicker.min.js new file mode 100644 index 0000000000..8800106e8f --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/bootstrap-datepicker.min.js @@ -0,0 +1,8 @@ +/*! + * Datepicker for Bootstrap v1.9.0 (https://github.com/uxsolutions/bootstrap-datepicker) + * + * Licensed under the Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0) + */ + +!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a("object"==typeof exports?require("jquery"):jQuery)}(function(a,b){function c(){return new Date(Date.UTC.apply(Date,arguments))}function d(){var a=new Date;return c(a.getFullYear(),a.getMonth(),a.getDate())}function e(a,b){return a.getUTCFullYear()===b.getUTCFullYear()&&a.getUTCMonth()===b.getUTCMonth()&&a.getUTCDate()===b.getUTCDate()}function f(c,d){return function(){return d!==b&&a.fn.datepicker.deprecated(d),this[c].apply(this,arguments)}}function g(a){return a&&!isNaN(a.getTime())}function h(b,c){function d(a,b){return b.toLowerCase()}var e,f=a(b).data(),g={},h=new RegExp("^"+c.toLowerCase()+"([A-Z])");c=new RegExp("^"+c.toLowerCase());for(var i in f)c.test(i)&&(e=i.replace(h,d),g[e]=f[i]);return g}function i(b){var c={};if(q[b]||(b=b.split("-")[0],q[b])){var d=q[b];return a.each(p,function(a,b){b in d&&(c[b]=d[b])}),c}}var j=function(){var b={get:function(a){return this.slice(a)[0]},contains:function(a){for(var b=a&&a.valueOf(),c=0,d=this.length;c]/g)||[]).length<=0)return!0;return a(c).length>0}catch(a){return!1}},_process_options:function(b){this._o=a.extend({},this._o,b);var e=this.o=a.extend({},this._o),f=e.language;q[f]||(f=f.split("-")[0],q[f]||(f=o.language)),e.language=f,e.startView=this._resolveViewName(e.startView),e.minViewMode=this._resolveViewName(e.minViewMode),e.maxViewMode=this._resolveViewName(e.maxViewMode),e.startView=Math.max(this.o.minViewMode,Math.min(this.o.maxViewMode,e.startView)),!0!==e.multidate&&(e.multidate=Number(e.multidate)||!1,!1!==e.multidate&&(e.multidate=Math.max(0,e.multidate))),e.multidateSeparator=String(e.multidateSeparator),e.weekStart%=7,e.weekEnd=(e.weekStart+6)%7;var g=r.parseFormat(e.format);e.startDate!==-1/0&&(e.startDate?e.startDate instanceof Date?e.startDate=this._local_to_utc(this._zero_time(e.startDate)):e.startDate=r.parseDate(e.startDate,g,e.language,e.assumeNearbyYear):e.startDate=-1/0),e.endDate!==1/0&&(e.endDate?e.endDate instanceof Date?e.endDate=this._local_to_utc(this._zero_time(e.endDate)):e.endDate=r.parseDate(e.endDate,g,e.language,e.assumeNearbyYear):e.endDate=1/0),e.daysOfWeekDisabled=this._resolveDaysOfWeek(e.daysOfWeekDisabled||[]),e.daysOfWeekHighlighted=this._resolveDaysOfWeek(e.daysOfWeekHighlighted||[]),e.datesDisabled=e.datesDisabled||[],a.isArray(e.datesDisabled)||(e.datesDisabled=e.datesDisabled.split(",")),e.datesDisabled=a.map(e.datesDisabled,function(a){return r.parseDate(a,g,e.language,e.assumeNearbyYear)});var h=String(e.orientation).toLowerCase().split(/\s+/g),i=e.orientation.toLowerCase();if(h=a.grep(h,function(a){return/^auto|left|right|top|bottom$/.test(a)}),e.orientation={x:"auto",y:"auto"},i&&"auto"!==i)if(1===h.length)switch(h[0]){case"top":case"bottom":e.orientation.y=h[0];break;case"left":case"right":e.orientation.x=h[0]}else i=a.grep(h,function(a){return/^left|right$/.test(a)}),e.orientation.x=i[0]||"auto",i=a.grep(h,function(a){return/^top|bottom$/.test(a)}),e.orientation.y=i[0]||"auto";else;if(e.defaultViewDate instanceof Date||"string"==typeof e.defaultViewDate)e.defaultViewDate=r.parseDate(e.defaultViewDate,g,e.language,e.assumeNearbyYear);else if(e.defaultViewDate){var j=e.defaultViewDate.year||(new Date).getFullYear(),k=e.defaultViewDate.month||0,l=e.defaultViewDate.day||1;e.defaultViewDate=c(j,k,l)}else e.defaultViewDate=d()},_applyEvents:function(a){for(var c,d,e,f=0;fe?(this.picker.addClass("datepicker-orient-right"),m+=l-b):this.o.rtl?this.picker.addClass("datepicker-orient-right"):this.picker.addClass("datepicker-orient-left");var o,p=this.o.orientation.y;if("auto"===p&&(o=-f+n-c,p=o<0?"bottom":"top"),this.picker.addClass("datepicker-orient-"+p),"top"===p?n-=c+parseInt(this.picker.css("padding-top")):n+=k,this.o.rtl){var q=e-(m+l);this.picker.css({top:n,right:q,zIndex:i})}else this.picker.css({top:n,left:m,zIndex:i});return this},_allow_update:!0,update:function(){if(!this._allow_update)return this;var b=this.dates.copy(),c=[],d=!1;return arguments.length?(a.each(arguments,a.proxy(function(a,b){b instanceof Date&&(b=this._local_to_utc(b)),c.push(b)},this)),d=!0):(c=this.isInput?this.element.val():this.element.data("date")||this.inputField.val(),c=c&&this.o.multidate?c.split(this.o.multidateSeparator):[c],delete this.element.data().date),c=a.map(c,a.proxy(function(a){return r.parseDate(a,this.o.format,this.o.language,this.o.assumeNearbyYear)},this)),c=a.grep(c,a.proxy(function(a){return!this.dateWithinRange(a)||!a},this),!0),this.dates.replace(c),this.o.updateViewDate&&(this.dates.length?this.viewDate=new Date(this.dates.get(-1)):this.viewDatethis.o.endDate?this.viewDate=new Date(this.o.endDate):this.viewDate=this.o.defaultViewDate),d?(this.setValue(),this.element.change()):this.dates.length&&String(b)!==String(this.dates)&&d&&(this._trigger("changeDate"),this.element.change()),!this.dates.length&&b.length&&(this._trigger("clearDate"),this.element.change()),this.fill(),this},fillDow:function(){if(this.o.showWeekDays){var b=this.o.weekStart,c="";for(this.o.calendarWeeks&&(c+=' ');b";c+="",this.picker.find(".datepicker-days thead").append(c)}},fillMonths:function(){for(var a,b=this._utc_to_local(this.viewDate),c="",d=0;d<12;d++)a=b&&b.getMonth()===d?" focused":"",c+=''+q[this.o.language].monthsShort[d]+"";this.picker.find(".datepicker-months td").html(c)},setRange:function(b){b&&b.length?this.range=a.map(b,function(a){return a.valueOf()}):delete this.range,this.fill()},getClassNames:function(b){var c=[],f=this.viewDate.getUTCFullYear(),g=this.viewDate.getUTCMonth(),h=d();return b.getUTCFullYear()f||b.getUTCFullYear()===f&&b.getUTCMonth()>g)&&c.push("new"),this.focusDate&&b.valueOf()===this.focusDate.valueOf()&&c.push("focused"),this.o.todayHighlight&&e(b,h)&&c.push("today"),-1!==this.dates.contains(b)&&c.push("active"),this.dateWithinRange(b)||c.push("disabled"),this.dateIsDisabled(b)&&c.push("disabled","disabled-date"),-1!==a.inArray(b.getUTCDay(),this.o.daysOfWeekHighlighted)&&c.push("highlighted"),this.range&&(b>this.range[0]&&bh)&&j.push("disabled"),t===r&&j.push("focused"),i!==a.noop&&(l=i(new Date(t,0,1)),l===b?l={}:"boolean"==typeof l?l={enabled:l}:"string"==typeof l&&(l={classes:l}),!1===l.enabled&&j.push("disabled"),l.classes&&(j=j.concat(l.classes.split(/\s+/))),l.tooltip&&(k=l.tooltip)),m+='"+t+"";o.find(".datepicker-switch").text(p+"-"+q),o.find("td").html(m)},fill:function(){var e,f,g=new Date(this.viewDate),h=g.getUTCFullYear(),i=g.getUTCMonth(),j=this.o.startDate!==-1/0?this.o.startDate.getUTCFullYear():-1/0,k=this.o.startDate!==-1/0?this.o.startDate.getUTCMonth():-1/0,l=this.o.endDate!==1/0?this.o.endDate.getUTCFullYear():1/0,m=this.o.endDate!==1/0?this.o.endDate.getUTCMonth():1/0,n=q[this.o.language].today||q.en.today||"",o=q[this.o.language].clear||q.en.clear||"",p=q[this.o.language].titleFormat||q.en.titleFormat,s=d(),t=(!0===this.o.todayBtn||"linked"===this.o.todayBtn)&&s>=this.o.startDate&&s<=this.o.endDate&&!this.weekOfDateIsDisabled(s);if(!isNaN(h)&&!isNaN(i)){this.picker.find(".datepicker-days .datepicker-switch").text(r.formatDate(g,p,this.o.language)),this.picker.find("tfoot .today").text(n).css("display",t?"table-cell":"none"),this.picker.find("tfoot .clear").text(o).css("display",!0===this.o.clearBtn?"table-cell":"none"),this.picker.find("thead .datepicker-title").text(this.o.title).css("display","string"==typeof this.o.title&&""!==this.o.title?"table-cell":"none"),this.updateNavArrows(),this.fillMonths();var u=c(h,i,0),v=u.getUTCDate();u.setUTCDate(v-(u.getUTCDay()-this.o.weekStart+7)%7);var w=new Date(u);u.getUTCFullYear()<100&&w.setUTCFullYear(u.getUTCFullYear()),w.setUTCDate(w.getUTCDate()+42),w=w.valueOf();for(var x,y,z=[];u.valueOf()"),this.o.calendarWeeks)){var A=new Date(+u+(this.o.weekStart-x-7)%7*864e5),B=new Date(Number(A)+(11-A.getUTCDay())%7*864e5),C=new Date(Number(C=c(B.getUTCFullYear(),0,1))+(11-C.getUTCDay())%7*864e5),D=(B-C)/864e5/7+1;z.push(''+D+"")}y=this.getClassNames(u),y.push("day");var E=u.getUTCDate();this.o.beforeShowDay!==a.noop&&(f=this.o.beforeShowDay(this._utc_to_local(u)),f===b?f={}:"boolean"==typeof f?f={enabled:f}:"string"==typeof f&&(f={classes:f}),!1===f.enabled&&y.push("disabled"),f.classes&&(y=y.concat(f.classes.split(/\s+/))),f.tooltip&&(e=f.tooltip),f.content&&(E=f.content)),y=a.isFunction(a.uniqueSort)?a.uniqueSort(y):a.unique(y),z.push(''+E+""),e=null,x===this.o.weekEnd&&z.push(""),u.setUTCDate(u.getUTCDate()+1)}this.picker.find(".datepicker-days tbody").html(z.join(""));var F=q[this.o.language].monthsTitle||q.en.monthsTitle||"Months",G=this.picker.find(".datepicker-months").find(".datepicker-switch").text(this.o.maxViewMode<2?F:h).end().find("tbody span").removeClass("active");if(a.each(this.dates,function(a,b){b.getUTCFullYear()===h&&G.eq(b.getUTCMonth()).addClass("active")}),(hl)&&G.addClass("disabled"),h===j&&G.slice(0,k).addClass("disabled"),h===l&&G.slice(m+1).addClass("disabled"),this.o.beforeShowMonth!==a.noop){var H=this;a.each(G,function(c,d){var e=new Date(h,c,1),f=H.o.beforeShowMonth(e);f===b?f={}:"boolean"==typeof f?f={enabled:f}:"string"==typeof f&&(f={classes:f}),!1!==f.enabled||a(d).hasClass("disabled")||a(d).addClass("disabled"),f.classes&&a(d).addClass(f.classes),f.tooltip&&a(d).prop("title",f.tooltip)})}this._fill_yearsView(".datepicker-years","year",10,h,j,l,this.o.beforeShowYear),this._fill_yearsView(".datepicker-decades","decade",100,h,j,l,this.o.beforeShowDecade),this._fill_yearsView(".datepicker-centuries","century",1e3,h,j,l,this.o.beforeShowCentury)}},updateNavArrows:function(){if(this._allow_update){var a,b,c=new Date(this.viewDate),d=c.getUTCFullYear(),e=c.getUTCMonth(),f=this.o.startDate!==-1/0?this.o.startDate.getUTCFullYear():-1/0,g=this.o.startDate!==-1/0?this.o.startDate.getUTCMonth():-1/0,h=this.o.endDate!==1/0?this.o.endDate.getUTCFullYear():1/0,i=this.o.endDate!==1/0?this.o.endDate.getUTCMonth():1/0,j=1;switch(this.viewMode){case 4:j*=10;case 3:j*=10;case 2:j*=10;case 1:a=Math.floor(d/j)*j<=f,b=Math.floor(d/j)*j+j>h;break;case 0:a=d<=f&&e<=g,b=d>=h&&e>=i}this.picker.find(".prev").toggleClass("disabled",a),this.picker.find(".next").toggleClass("disabled",b)}},click:function(b){b.preventDefault(),b.stopPropagation();var e,f,g,h;e=a(b.target),e.hasClass("datepicker-switch")&&this.viewMode!==this.o.maxViewMode&&this.setViewMode(this.viewMode+1),e.hasClass("today")&&!e.hasClass("day")&&(this.setViewMode(0),this._setDate(d(),"linked"===this.o.todayBtn?null:"view")),e.hasClass("clear")&&this.clearDates(),e.hasClass("disabled")||(e.hasClass("month")||e.hasClass("year")||e.hasClass("decade")||e.hasClass("century"))&&(this.viewDate.setUTCDate(1),f=1,1===this.viewMode?(h=e.parent().find("span").index(e),g=this.viewDate.getUTCFullYear(),this.viewDate.setUTCMonth(h)):(h=0,g=Number(e.text()),this.viewDate.setUTCFullYear(g)),this._trigger(r.viewModes[this.viewMode-1].e,this.viewDate),this.viewMode===this.o.minViewMode?this._setDate(c(g,h,f)):(this.setViewMode(this.viewMode-1),this.fill())),this.picker.is(":visible")&&this._focused_from&&this._focused_from.focus(),delete this._focused_from},dayCellClick:function(b){var c=a(b.currentTarget),d=c.data("date"),e=new Date(d);this.o.updateViewDate&&(e.getUTCFullYear()!==this.viewDate.getUTCFullYear()&&this._trigger("changeYear",this.viewDate),e.getUTCMonth()!==this.viewDate.getUTCMonth()&&this._trigger("changeMonth",this.viewDate)),this._setDate(e)},navArrowsClick:function(b){var c=a(b.currentTarget),d=c.hasClass("prev")?-1:1;0!==this.viewMode&&(d*=12*r.viewModes[this.viewMode].navStep),this.viewDate=this.moveMonth(this.viewDate,d),this._trigger(r.viewModes[this.viewMode].e,this.viewDate),this.fill()},_toggle_multidate:function(a){var b=this.dates.contains(a);if(a||this.dates.clear(),-1!==b?(!0===this.o.multidate||this.o.multidate>1||this.o.toggleActive)&&this.dates.remove(b):!1===this.o.multidate?(this.dates.clear(),this.dates.push(a)):this.dates.push(a),"number"==typeof this.o.multidate)for(;this.dates.length>this.o.multidate;)this.dates.remove(0)},_setDate:function(a,b){b&&"date"!==b||this._toggle_multidate(a&&new Date(a)),(!b&&this.o.updateViewDate||"view"===b)&&(this.viewDate=a&&new Date(a)),this.fill(),this.setValue(),b&&"view"===b||this._trigger("changeDate"),this.inputField.trigger("change"),!this.o.autoclose||b&&"date"!==b||this.hide()},moveDay:function(a,b){var c=new Date(a);return c.setUTCDate(a.getUTCDate()+b),c},moveWeek:function(a,b){return this.moveDay(a,7*b)},moveMonth:function(a,b){if(!g(a))return this.o.defaultViewDate;if(!b)return a;var c,d,e=new Date(a.valueOf()),f=e.getUTCDate(),h=e.getUTCMonth(),i=Math.abs(b);if(b=b>0?1:-1,1===i)d=-1===b?function(){return e.getUTCMonth()===h}:function(){return e.getUTCMonth()!==c},c=h+b,e.setUTCMonth(c),c=(c+12)%12;else{for(var j=0;j0},dateWithinRange:function(a){return a>=this.o.startDate&&a<=this.o.endDate},keydown:function(a){if(!this.picker.is(":visible"))return void(40!==a.keyCode&&27!==a.keyCode||(this.show(),a.stopPropagation()));var b,c,d=!1,e=this.focusDate||this.viewDate;switch(a.keyCode){case 27:this.focusDate?(this.focusDate=null,this.viewDate=this.dates.get(-1)||this.viewDate,this.fill()):this.hide(),a.preventDefault(),a.stopPropagation();break;case 37:case 38:case 39:case 40:if(!this.o.keyboardNavigation||7===this.o.daysOfWeekDisabled.length)break;b=37===a.keyCode||38===a.keyCode?-1:1,0===this.viewMode?a.ctrlKey?(c=this.moveAvailableDate(e,b,"moveYear"))&&this._trigger("changeYear",this.viewDate):a.shiftKey?(c=this.moveAvailableDate(e,b,"moveMonth"))&&this._trigger("changeMonth",this.viewDate):37===a.keyCode||39===a.keyCode?c=this.moveAvailableDate(e,b,"moveDay"):this.weekOfDateIsDisabled(e)||(c=this.moveAvailableDate(e,b,"moveWeek")):1===this.viewMode?(38!==a.keyCode&&40!==a.keyCode||(b*=4),c=this.moveAvailableDate(e,b,"moveMonth")):2===this.viewMode&&(38!==a.keyCode&&40!==a.keyCode||(b*=4),c=this.moveAvailableDate(e,b,"moveYear")),c&&(this.focusDate=this.viewDate=c,this.setValue(),this.fill(),a.preventDefault());break;case 13:if(!this.o.forceParse)break;e=this.focusDate||this.dates.get(-1)||this.viewDate,this.o.keyboardNavigation&&(this._toggle_multidate(e),d=!0),this.focusDate=null,this.viewDate=this.dates.get(-1)||this.viewDate,this.setValue(),this.fill(),this.picker.is(":visible")&&(a.preventDefault(),a.stopPropagation(),this.o.autoclose&&this.hide());break;case 9:this.focusDate=null,this.viewDate=this.dates.get(-1)||this.viewDate,this.fill(),this.hide()}d&&(this.dates.length?this._trigger("changeDate"):this._trigger("clearDate"),this.inputField.trigger("change"))},setViewMode:function(a){this.viewMode=a,this.picker.children("div").hide().filter(".datepicker-"+r.viewModes[this.viewMode].clsName).show(),this.updateNavArrows(),this._trigger("changeViewMode",new Date(this.viewDate))}};var l=function(b,c){a.data(b,"datepicker",this),this.element=a(b),this.inputs=a.map(c.inputs,function(a){return a.jquery?a[0]:a}),delete c.inputs,this.keepEmptyValues=c.keepEmptyValues,delete c.keepEmptyValues,n.call(a(this.inputs),c).on("changeDate",a.proxy(this.dateUpdated,this)),this.pickers=a.map(this.inputs,function(b){return a.data(b,"datepicker")}),this.updateDates()};l.prototype={updateDates:function(){this.dates=a.map(this.pickers,function(a){return a.getUTCDate()}),this.updateRanges()},updateRanges:function(){var b=a.map(this.dates,function(a){return a.valueOf()});a.each(this.pickers,function(a,c){c.setRange(b)})},clearDates:function(){a.each(this.pickers,function(a,b){b.clearDates()})},dateUpdated:function(c){if(!this.updating){this.updating=!0;var d=a.data(c.target,"datepicker");if(d!==b){var e=d.getUTCDate(),f=this.keepEmptyValues,g=a.inArray(c.target,this.inputs),h=g-1,i=g+1,j=this.inputs.length;if(-1!==g){if(a.each(this.pickers,function(a,b){b.getUTCDate()||b!==d&&f||b.setUTCDate(e)}),e=0&&ethis.dates[i])for(;ithis.dates[i];)this.pickers[i++].setUTCDate(e);this.updateDates(),delete this.updating}}}},destroy:function(){a.map(this.pickers,function(a){a.destroy()}),a(this.inputs).off("changeDate",this.dateUpdated),delete this.element.data().datepicker},remove:f("destroy","Method `remove` is deprecated and will be removed in version 2.0. Use `destroy` instead")};var m=a.fn.datepicker,n=function(c){var d=Array.apply(null,arguments);d.shift();var e;if(this.each(function(){var b=a(this),f=b.data("datepicker"),g="object"==typeof c&&c;if(!f){var j=h(this,"date"),m=a.extend({},o,j,g),n=i(m.language),p=a.extend({},o,n,j,g);b.hasClass("input-daterange")||p.inputs?(a.extend(p,{inputs:p.inputs||b.find("input").toArray()}),f=new l(this,p)):f=new k(this,p),b.data("datepicker",f)}"string"==typeof c&&"function"==typeof f[c]&&(e=f[c].apply(f,d))}),e===b||e instanceof k||e instanceof l)return this;if(this.length>1)throw new Error("Using only allowed for the collection of a single element ("+c+" function)");return e};a.fn.datepicker=n;var o=a.fn.datepicker.defaults={assumeNearbyYear:!1,autoclose:!1,beforeShowDay:a.noop,beforeShowMonth:a.noop,beforeShowYear:a.noop,beforeShowDecade:a.noop,beforeShowCentury:a.noop,calendarWeeks:!1,clearBtn:!1,toggleActive:!1,daysOfWeekDisabled:[],daysOfWeekHighlighted:[],datesDisabled:[],endDate:1/0,forceParse:!0,format:"mm/dd/yyyy",keepEmptyValues:!1,keyboardNavigation:!0,language:"en",minViewMode:0,maxViewMode:4,multidate:!1,multidateSeparator:",",orientation:"auto",rtl:!1,startDate:-1/0,startView:0,todayBtn:!1,todayHighlight:!1,updateViewDate:!0,weekStart:0,disableTouchKeyboard:!1,enableOnReadonly:!0,showOnFocus:!0,zIndexOffset:10,container:"body",immediateUpdates:!1,title:"",templates:{leftArrow:"«",rightArrow:"»"},showWeekDays:!0},p=a.fn.datepicker.locale_opts=["format","rtl","weekStart"];a.fn.datepicker.Constructor=k;var q=a.fn.datepicker.dates={en:{days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],daysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],daysMin:["Su","Mo","Tu","We","Th","Fr","Sa"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],monthsShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],today:"Today",clear:"Clear",titleFormat:"MM yyyy"}},r={viewModes:[{names:["days","month"],clsName:"days",e:"changeMonth"},{names:["months","year"],clsName:"months",e:"changeYear",navStep:1},{names:["years","decade"],clsName:"years",e:"changeDecade",navStep:10},{names:["decades","century"],clsName:"decades",e:"changeCentury",navStep:100},{names:["centuries","millennium"],clsName:"centuries",e:"changeMillennium",navStep:1e3}],validParts:/dd?|DD?|mm?|MM?|yy(?:yy)?/g,nonpunctuation:/[^ -\/:-@\u5e74\u6708\u65e5\[-`{-~\t\n\r]+/g,parseFormat:function(a){if("function"==typeof a.toValue&&"function"==typeof a.toDisplay)return a;var b=a.replace(this.validParts,"\0").split("\0"),c=a.match(this.validParts);if(!b||!b.length||!c||0===c.length)throw new Error("Invalid date format.");return{separators:b,parts:c}},parseDate:function(c,e,f,g){function h(a,b){return!0===b&&(b=10),a<100&&(a+=2e3)>(new Date).getFullYear()+b&&(a-=100),a}function i(){var a=this.slice(0,j[n].length),b=j[n].slice(0,a.length);return a.toLowerCase()===b.toLowerCase()}if(!c)return b;if(c instanceof Date)return c;if("string"==typeof e&&(e=r.parseFormat(e)),e.toValue)return e.toValue(c,e,f);var j,l,m,n,o,p={d:"moveDay",m:"moveMonth",w:"moveWeek",y:"moveYear"},s={yesterday:"-1d",today:"+0d",tomorrow:"+1d"};if(c in s&&(c=s[c]),/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/i.test(c)){for(j=c.match(/([\-+]\d+)([dmwy])/gi),c=new Date,n=0;n'+o.templates.leftArrow+''+o.templates.rightArrow+"",contTemplate:'',footTemplate:''};r.template='
'+r.headTemplate+""+r.footTemplate+'
'+r.headTemplate+r.contTemplate+r.footTemplate+'
'+r.headTemplate+r.contTemplate+r.footTemplate+'
'+r.headTemplate+r.contTemplate+r.footTemplate+'
'+r.headTemplate+r.contTemplate+r.footTemplate+"
",a.fn.datepicker.DPGlobal=r,a.fn.datepicker.noConflict=function(){return a.fn.datepicker=m,this},a.fn.datepicker.version="1.9.0",a.fn.datepicker.deprecated=function(a){var b=window.console;b&&b.warn&&b.warn("DEPRECATED: "+a)},a(document).on("focus.datepicker.data-api click.datepicker.data-api",'[data-provide="datepicker"]',function(b){var c=a(this);c.data("datepicker")||(b.preventDefault(),n.call(c,"show"))}),a(function(){n.call(a('[data-provide="datepicker-inline"]'))})}); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker-en-CA.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker-en-CA.min.js new file mode 100644 index 0000000000..0aab38f3a4 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker-en-CA.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates["en-CA"]={days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],daysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],daysMin:["Su","Mo","Tu","We","Th","Fr","Sa"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],monthsShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],today:"Today",monthsTitle:"Months",clear:"Clear",weekStart:0,format:"yyyy-mm-dd"},a.fn.datepicker.deprecated("This filename doesn't follow the convention, use bootstrap-datepicker.en-CA.js instead.")}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.ar-tn.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.ar-tn.min.js new file mode 100644 index 0000000000..9d70dc2fa3 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.ar-tn.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates["ar-tn"]={days:["الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت","الأحد"],daysShort:["أحد","اثنين","ثلاثاء","أربعاء","خميس","جمعة","سبت","أحد"],daysMin:["ح","ن","ث","ع","خ","ج","س","ح"],months:["جانفي","فيفري","مارس","أفريل","ماي","جوان","جويليه","أوت","سبتمبر","أكتوبر","نوفمبر","ديسمبر"],monthsShort:["جانفي","فيفري","مارس","أفريل","ماي","جوان","جويليه","أوت","سبتمبر","أكتوبر","نوفمبر","ديسمبر"],today:"هذا اليوم",rtl:!0}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.ar.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.ar.min.js new file mode 100644 index 0000000000..ece41af725 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.ar.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.ar={days:["الأحد","الاثنين","الثلاثاء","الأربعاء","الخميس","الجمعة","السبت","الأحد"],daysShort:["أحد","اثنين","ثلاثاء","أربعاء","خميس","جمعة","سبت","أحد"],daysMin:["ح","ن","ث","ع","خ","ج","س","ح"],months:["يناير","فبراير","مارس","أبريل","مايو","يونيو","يوليو","أغسطس","سبتمبر","أكتوبر","نوفمبر","ديسمبر"],monthsShort:["يناير","فبراير","مارس","أبريل","مايو","يونيو","يوليو","أغسطس","سبتمبر","أكتوبر","نوفمبر","ديسمبر"],today:"هذا اليوم",rtl:!0}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.az.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.az.min.js new file mode 100644 index 0000000000..aa1edbf4f8 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.az.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.az={days:["Bazar","Bazar ertəsi","Çərşənbə axşamı","Çərşənbə","Cümə axşamı","Cümə","Şənbə"],daysShort:["B.","B.e","Ç.a","Ç.","C.a","C.","Ş."],daysMin:["B.","B.e","Ç.a","Ç.","C.a","C.","Ş."],months:["Yanvar","Fevral","Mart","Aprel","May","İyun","İyul","Avqust","Sentyabr","Oktyabr","Noyabr","Dekabr"],monthsShort:["Yan","Fev","Mar","Apr","May","İyun","İyul","Avq","Sen","Okt","Noy","Dek"],today:"Bu gün",weekStart:1,clear:"Təmizlə",monthsTitle:"Aylar"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.bg.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.bg.min.js new file mode 100644 index 0000000000..28e8b22dcf --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.bg.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.bg={days:["Неделя","Понеделник","Вторник","Сряда","Четвъртък","Петък","Събота"],daysShort:["Нед","Пон","Вто","Сря","Чет","Пет","Съб"],daysMin:["Н","П","В","С","Ч","П","С"],months:["Януари","Февруари","Март","Април","Май","Юни","Юли","Август","Септември","Октомври","Ноември","Декември"],monthsShort:["Ян","Фев","Мар","Апр","Май","Юни","Юли","Авг","Сеп","Окт","Ное","Дек"],today:"днес"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.bm.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.bm.min.js new file mode 100644 index 0000000000..e0796a3ba7 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.bm.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.bm={days:["Kari","Ntɛnɛn","Tarata","Araba","Alamisa","Juma","Sibiri"],daysShort:["Kar","Ntɛ","Tar","Ara","Ala","Jum","Sib"],daysMin:["Ka","Nt","Ta","Ar","Al","Ju","Si"],months:["Zanwuyekalo","Fewuruyekalo","Marisikalo","Awirilikalo","Mɛkalo","Zuwɛnkalo","Zuluyekalo","Utikalo","Sɛtanburukalo","ɔkutɔburukalo","Nowanburukalo","Desanburukalo"],monthsShort:["Zan","Few","Mar","Awi","Mɛ","Zuw","Zul","Uti","Sɛt","ɔku","Now","Des"],today:"Bi",monthsTitle:"Kalo",clear:"Ka jɔsi",weekStart:1,format:"dd/mm/yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.bn.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.bn.min.js new file mode 100644 index 0000000000..f67b5e26e7 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.bn.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.bn={days:["রবিবার","সোমবার","মঙ্গলবার","বুধবার","বৃহস্পতিবার","শুক্রবার","শনিবার"],daysShort:["রবিবার","সোমবার","মঙ্গলবার","বুধবার","বৃহস্পতিবার","শুক্রবার","শনিবার"],daysMin:["রবি","সোম","মঙ্গল","বুধ","বৃহস্পতি","শুক্র","শনি"],months:["জানুয়ারী","ফেব্রুয়ারি","মার্চ","এপ্রিল","মে","জুন","জুলাই","অগাস্ট","সেপ্টেম্বর","অক্টোবর","নভেম্বর","ডিসেম্বর"],monthsShort:["জানুয়ারী","ফেব্রুয়ারি","মার্চ","এপ্রিল","মে","জুন","জুলাই","অগাস্ট","সেপ্টেম্বর","অক্টোবর","নভেম্বর","ডিসেম্বর"],today:"আজ",monthsTitle:"মাস",clear:"পরিষ্কার",weekStart:0,format:"mm/dd/yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.br.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.br.min.js new file mode 100644 index 0000000000..af3e3bd0af --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.br.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.br={days:["Sul","Lun","Meurzh","Merc'her","Yaou","Gwener","Sadorn"],daysShort:["Sul","Lun","Meu.","Mer.","Yao.","Gwe.","Sad."],daysMin:["Su","L","Meu","Mer","Y","G","Sa"],months:["Genver","C'hwevrer","Meurzh","Ebrel","Mae","Mezheven","Gouere","Eost","Gwengolo","Here","Du","Kerzu"],monthsShort:["Genv.","C'hw.","Meur.","Ebre.","Mae","Mezh.","Goue.","Eost","Gwen.","Here","Du","Kerz."],today:"Hiziv",monthsTitle:"Miz",clear:"Dilemel",weekStart:1,format:"dd/mm/yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.bs.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.bs.min.js new file mode 100644 index 0000000000..cfb06fde79 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.bs.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.bs={days:["Nedjelja","Ponedjeljak","Utorak","Srijeda","Četvrtak","Petak","Subota"],daysShort:["Ned","Pon","Uto","Sri","Čet","Pet","Sub"],daysMin:["N","Po","U","Sr","Č","Pe","Su"],months:["Januar","Februar","Mart","April","Maj","Juni","Juli","August","Septembar","Oktobar","Novembar","Decembar"],monthsShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Aug","Sep","Okt","Nov","Dec"],today:"Danas",weekStart:1,format:"dd.mm.yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.ca.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.ca.min.js new file mode 100644 index 0000000000..ac107894c4 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.ca.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.ca={days:["Diumenge","Dilluns","Dimarts","Dimecres","Dijous","Divendres","Dissabte"],daysShort:["Diu","Dil","Dmt","Dmc","Dij","Div","Dis"],daysMin:["dg","dl","dt","dc","dj","dv","ds"],months:["Gener","Febrer","Març","Abril","Maig","Juny","Juliol","Agost","Setembre","Octubre","Novembre","Desembre"],monthsShort:["Gen","Feb","Mar","Abr","Mai","Jun","Jul","Ago","Set","Oct","Nov","Des"],today:"Avui",monthsTitle:"Mesos",clear:"Esborrar",weekStart:1,format:"dd/mm/yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.cs.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.cs.min.js new file mode 100644 index 0000000000..42dfd1a29d --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.cs.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.cs={days:["Neděle","Pondělí","Úterý","Středa","Čtvrtek","Pátek","Sobota"],daysShort:["Ned","Pon","Úte","Stř","Čtv","Pát","Sob"],daysMin:["Ne","Po","Út","St","Čt","Pá","So"],months:["Leden","Únor","Březen","Duben","Květen","Červen","Červenec","Srpen","Září","Říjen","Listopad","Prosinec"],monthsShort:["Led","Úno","Bře","Dub","Kvě","Čer","Čnc","Srp","Zář","Říj","Lis","Pro"],today:"Dnes",clear:"Vymazat",monthsTitle:"Měsíc",weekStart:1,format:"dd.mm.yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.cy.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.cy.min.js new file mode 100644 index 0000000000..f85ea031dd --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.cy.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.cy={days:["Sul","Llun","Mawrth","Mercher","Iau","Gwener","Sadwrn"],daysShort:["Sul","Llu","Maw","Mer","Iau","Gwe","Sad"],daysMin:["Su","Ll","Ma","Me","Ia","Gwe","Sa"],months:["Ionawr","Chewfror","Mawrth","Ebrill","Mai","Mehefin","Gorfennaf","Awst","Medi","Hydref","Tachwedd","Rhagfyr"],monthsShort:["Ion","Chw","Maw","Ebr","Mai","Meh","Gor","Aws","Med","Hyd","Tach","Rha"],today:"Heddiw"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.da.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.da.min.js new file mode 100644 index 0000000000..53c8180528 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.da.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.da={days:["Søndag","Mandag","Tirsdag","Onsdag","Torsdag","Fredag","Lørdag"],daysShort:["Søn","Man","Tir","Ons","Tor","Fre","Lør"],daysMin:["Sø","Ma","Ti","On","To","Fr","Lø"],months:["Januar","Februar","Marts","April","Maj","Juni","Juli","August","September","Oktober","November","December"],monthsShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Aug","Sep","Okt","Nov","Dec"],today:"I Dag",weekStart:1,clear:"Nulstil",format:"dd/mm/yyyy",monthsTitle:"Måneder"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.de.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.de.min.js new file mode 100644 index 0000000000..1b5d6a2474 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.de.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.de={days:["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"],daysShort:["Son","Mon","Die","Mit","Don","Fre","Sam"],daysMin:["So","Mo","Di","Mi","Do","Fr","Sa"],months:["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],monthsShort:["Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],today:"Heute",monthsTitle:"Monate",clear:"Löschen",weekStart:1,format:"dd.mm.yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.el.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.el.min.js new file mode 100644 index 0000000000..046e9eb5ef --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.el.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.el={days:["Κυριακή","Δευτέρα","Τρίτη","Τετάρτη","Πέμπτη","Παρασκευή","Σάββατο"],daysShort:["Κυρ","Δευ","Τρι","Τετ","Πεμ","Παρ","Σαβ"],daysMin:["Κυ","Δε","Τρ","Τε","Πε","Πα","Σα"],months:["Ιανουάριος","Φεβρουάριος","Μάρτιος","Απρίλιος","Μάιος","Ιούνιος","Ιούλιος","Αύγουστος","Σεπτέμβριος","Οκτώβριος","Νοέμβριος","Δεκέμβριος"],monthsShort:["Ιαν","Φεβ","Μαρ","Απρ","Μάι","Ιουν","Ιουλ","Αυγ","Σεπ","Οκτ","Νοε","Δεκ"],today:"Σήμερα",clear:"Καθαρισμός",weekStart:1,format:"d/m/yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.en-AU.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.en-AU.min.js new file mode 100644 index 0000000000..b8d5f41cfa --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.en-AU.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates["en-AU"]={days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],daysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],daysMin:["Su","Mo","Tu","We","Th","Fr","Sa"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],monthsShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],today:"Today",monthsTitle:"Months",clear:"Clear",weekStart:1,format:"d/mm/yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.en-CA.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.en-CA.min.js new file mode 100644 index 0000000000..7b1070f74d --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.en-CA.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates["en-CA"]={days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],daysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],daysMin:["Su","Mo","Tu","We","Th","Fr","Sa"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],monthsShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],today:"Today",monthsTitle:"Months",clear:"Clear",weekStart:0,format:"yyyy-mm-dd"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.en-GB.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.en-GB.min.js new file mode 100644 index 0000000000..2966f5414e --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.en-GB.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates["en-GB"]={days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],daysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],daysMin:["Su","Mo","Tu","We","Th","Fr","Sa"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],monthsShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],today:"Today",monthsTitle:"Months",clear:"Clear",weekStart:1,format:"dd/mm/yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.en-IE.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.en-IE.min.js new file mode 100644 index 0000000000..dc8f71c026 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.en-IE.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates["en-IE"]={days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],daysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],daysMin:["Su","Mo","Tu","We","Th","Fr","Sa"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],monthsShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],today:"Today",monthsTitle:"Months",clear:"Clear",weekStart:1,format:"dd/mm/yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.en-NZ.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.en-NZ.min.js new file mode 100644 index 0000000000..c374a8d40c --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.en-NZ.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates["en-NZ"]={days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],daysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],daysMin:["Su","Mo","Tu","We","Th","Fr","Sa"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],monthsShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],today:"Today",monthsTitle:"Months",clear:"Clear",weekStart:1,format:"d/mm/yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.en-ZA.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.en-ZA.min.js new file mode 100644 index 0000000000..885a928cba --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.en-ZA.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates["en-ZA"]={days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],daysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],daysMin:["Su","Mo","Tu","We","Th","Fr","Sa"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],monthsShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],today:"Today",monthsTitle:"Months",clear:"Clear",weekStart:1,format:"yyyy/mm/d"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.eo.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.eo.min.js new file mode 100644 index 0000000000..736db021ae --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.eo.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.eo={days:["dimanĉo","lundo","mardo","merkredo","ĵaŭdo","vendredo","sabato"],daysShort:["dim.","lun.","mar.","mer.","ĵaŭ.","ven.","sam."],daysMin:["d","l","ma","me","ĵ","v","s"],months:["januaro","februaro","marto","aprilo","majo","junio","julio","aŭgusto","septembro","oktobro","novembro","decembro"],monthsShort:["jan.","feb.","mar.","apr.","majo","jun.","jul.","aŭg.","sep.","okt.","nov.","dec."],today:"Hodiaŭ",clear:"Nuligi",weekStart:1,format:"yyyy-mm-dd"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.es.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.es.min.js new file mode 100644 index 0000000000..f3cef5d2b9 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.es.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.es={days:["Domingo","Lunes","Martes","Miércoles","Jueves","Viernes","Sábado"],daysShort:["Dom","Lun","Mar","Mié","Jue","Vie","Sáb"],daysMin:["Do","Lu","Ma","Mi","Ju","Vi","Sa"],months:["Enero","Febrero","Marzo","Abril","Mayo","Junio","Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre"],monthsShort:["Ene","Feb","Mar","Abr","May","Jun","Jul","Ago","Sep","Oct","Nov","Dic"],today:"Hoy",monthsTitle:"Meses",clear:"Borrar",weekStart:1,format:"dd/mm/yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.et.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.et.min.js new file mode 100644 index 0000000000..34cd9c60e9 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.et.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.et={days:["Pühapäev","Esmaspäev","Teisipäev","Kolmapäev","Neljapäev","Reede","Laupäev"],daysShort:["Pühap","Esmasp","Teisip","Kolmap","Neljap","Reede","Laup"],daysMin:["P","E","T","K","N","R","L"],months:["Jaanuar","Veebruar","Märts","Aprill","Mai","Juuni","Juuli","August","September","Oktoober","November","Detsember"],monthsShort:["Jaan","Veebr","Märts","Apr","Mai","Juuni","Juuli","Aug","Sept","Okt","Nov","Dets"],today:"Täna",clear:"Tühjenda",weekStart:1,format:"dd.mm.yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.eu.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.eu.min.js new file mode 100644 index 0000000000..c5aa359e48 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.eu.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.eu={days:["Igandea","Astelehena","Asteartea","Asteazkena","Osteguna","Ostirala","Larunbata"],daysShort:["Ig","Al","Ar","Az","Og","Ol","Lr"],daysMin:["Ig","Al","Ar","Az","Og","Ol","Lr"],months:["Urtarrila","Otsaila","Martxoa","Apirila","Maiatza","Ekaina","Uztaila","Abuztua","Iraila","Urria","Azaroa","Abendua"],monthsShort:["Urt","Ots","Mar","Api","Mai","Eka","Uzt","Abu","Ira","Urr","Aza","Abe"],today:"Gaur",monthsTitle:"Hilabeteak",clear:"Ezabatu",weekStart:1,format:"yyyy/mm/dd"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.fa.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.fa.min.js new file mode 100644 index 0000000000..8575237a07 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.fa.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.fa={days:["یک‌شنبه","دوشنبه","سه‌شنبه","چهارشنبه","پنج‌شنبه","جمعه","شنبه","یک‌شنبه"],daysShort:["یک","دو","سه","چهار","پنج","جمعه","شنبه","یک"],daysMin:["ی","د","س","چ","پ","ج","ش","ی"],months:["ژانویه","فوریه","مارس","آوریل","مه","ژوئن","ژوئیه","اوت","سپتامبر","اکتبر","نوامبر","دسامبر"],monthsShort:["ژان","فور","مار","آور","مه","ژون","ژوی","اوت","سپت","اکت","نوا","دسا"],today:"امروز",clear:"پاک کن",weekStart:1,format:"yyyy/mm/dd"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.fi.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.fi.min.js new file mode 100644 index 0000000000..239dfb796c --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.fi.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.fi={days:["sunnuntai","maanantai","tiistai","keskiviikko","torstai","perjantai","lauantai"],daysShort:["sun","maa","tii","kes","tor","per","lau"],daysMin:["su","ma","ti","ke","to","pe","la"],months:["tammikuu","helmikuu","maaliskuu","huhtikuu","toukokuu","kesäkuu","heinäkuu","elokuu","syyskuu","lokakuu","marraskuu","joulukuu"],monthsShort:["tam","hel","maa","huh","tou","kes","hei","elo","syy","lok","mar","jou"],today:"tänään",clear:"Tyhjennä",weekStart:1,format:"d.m.yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.fo.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.fo.min.js new file mode 100644 index 0000000000..fa24e3a127 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.fo.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.fo={days:["Sunnudagur","Mánadagur","Týsdagur","Mikudagur","Hósdagur","Fríggjadagur","Leygardagur"],daysShort:["Sun","Mán","Týs","Mik","Hós","Frí","Ley"],daysMin:["Su","Má","Tý","Mi","Hó","Fr","Le"],months:["Januar","Februar","Marts","Apríl","Mei","Juni","Juli","August","Septembur","Oktobur","Novembur","Desembur"],monthsShort:["Jan","Feb","Mar","Apr","Mei","Jun","Jul","Aug","Sep","Okt","Nov","Des"],today:"Í Dag",clear:"Reinsa"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.fr-CH.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.fr-CH.min.js new file mode 100644 index 0000000000..1c6bcdcbfc --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.fr-CH.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.fr={days:["Dimanche","Lundi","Mardi","Mercredi","Jeudi","Vendredi","Samedi"],daysShort:["Dim","Lun","Mar","Mer","Jeu","Ven","Sam"],daysMin:["D","L","Ma","Me","J","V","S"],months:["Janvier","Février","Mars","Avril","Mai","Juin","Juillet","Août","Septembre","Octobre","Novembre","Décembre"],monthsShort:["Jan","Fév","Mar","Avr","Mai","Jui","Jul","Aou","Sep","Oct","Nov","Déc"],today:"Aujourd'hui",monthsTitle:"Mois",clear:"Effacer",weekStart:1,format:"dd.mm.yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.fr.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.fr.min.js new file mode 100644 index 0000000000..244cfba800 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.fr.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.fr={days:["dimanche","lundi","mardi","mercredi","jeudi","vendredi","samedi"],daysShort:["dim.","lun.","mar.","mer.","jeu.","ven.","sam."],daysMin:["d","l","ma","me","j","v","s"],months:["janvier","février","mars","avril","mai","juin","juillet","août","septembre","octobre","novembre","décembre"],monthsShort:["janv.","févr.","mars","avril","mai","juin","juil.","août","sept.","oct.","nov.","déc."],today:"Aujourd'hui",monthsTitle:"Mois",clear:"Effacer",weekStart:1,format:"dd/mm/yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.gl.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.gl.min.js new file mode 100644 index 0000000000..3d92606b3b --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.gl.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.gl={days:["Domingo","Luns","Martes","Mércores","Xoves","Venres","Sábado"],daysShort:["Dom","Lun","Mar","Mér","Xov","Ven","Sáb"],daysMin:["Do","Lu","Ma","Me","Xo","Ve","Sa"],months:["Xaneiro","Febreiro","Marzo","Abril","Maio","Xuño","Xullo","Agosto","Setembro","Outubro","Novembro","Decembro"],monthsShort:["Xan","Feb","Mar","Abr","Mai","Xun","Xul","Ago","Sep","Out","Nov","Dec"],today:"Hoxe",clear:"Limpar",weekStart:1,format:"dd/mm/yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.he.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.he.min.js new file mode 100644 index 0000000000..191cb453a0 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.he.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.he={days:["ראשון","שני","שלישי","רביעי","חמישי","שישי","שבת","ראשון"],daysShort:["א","ב","ג","ד","ה","ו","ש","א"],daysMin:["א","ב","ג","ד","ה","ו","ש","א"],months:["ינואר","פברואר","מרץ","אפריל","מאי","יוני","יולי","אוגוסט","ספטמבר","אוקטובר","נובמבר","דצמבר"],monthsShort:["ינו","פבר","מרץ","אפר","מאי","יונ","יול","אוג","ספט","אוק","נוב","דצמ"],today:"היום",rtl:!0}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.hi.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.hi.min.js new file mode 100644 index 0000000000..635baffa8b --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.hi.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.hi={days:["रविवार","सोमवार","मंगलवार","बुधवार","गुरुवार","शुक्रवार","शनिवार"],daysShort:["सूर्य","सोम","मंगल","बुध","गुरु","शुक्र","शनि"],daysMin:["र","सो","मं","बु","गु","शु","श"],months:["जनवरी","फ़रवरी","मार्च","अप्रैल","मई","जून","जुलाई","अगस्त","सितम्बर","अक्टूबर","नवंबर","दिसम्बर"],monthsShort:["जन","फ़रवरी","मार्च","अप्रैल","मई","जून","जुलाई","अगस्त","सितं","अक्टूबर","नवं","दिसम्बर"],today:"आज",monthsTitle:"महीने",clear:"साफ",weekStart:1,format:"dd / mm / yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.hr.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.hr.min.js new file mode 100644 index 0000000000..8b34bce0fd --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.hr.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.hr={days:["Nedjelja","Ponedjeljak","Utorak","Srijeda","Četvrtak","Petak","Subota"],daysShort:["Ned","Pon","Uto","Sri","Čet","Pet","Sub"],daysMin:["Ne","Po","Ut","Sr","Če","Pe","Su"],months:["Siječanj","Veljača","Ožujak","Travanj","Svibanj","Lipanj","Srpanj","Kolovoz","Rujan","Listopad","Studeni","Prosinac"],monthsShort:["Sij","Velj","Ožu","Tra","Svi","Lip","Srp","Kol","Ruj","Lis","Stu","Pro"],today:"Danas"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.hu.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.hu.min.js new file mode 100644 index 0000000000..f9decf9a2c --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.hu.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.hu={days:["vasárnap","hétfő","kedd","szerda","csütörtök","péntek","szombat"],daysShort:["vas","hét","ked","sze","csü","pén","szo"],daysMin:["V","H","K","Sze","Cs","P","Szo"],months:["január","február","március","április","május","június","július","augusztus","szeptember","október","november","december"],monthsShort:["jan","feb","már","ápr","máj","jún","júl","aug","sze","okt","nov","dec"],today:"ma",weekStart:1,clear:"töröl",titleFormat:"yyyy. MM",format:"yyyy.mm.dd"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.hy.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.hy.min.js new file mode 100644 index 0000000000..a1cf653d38 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.hy.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.hy={days:["Կիրակի","Երկուշաբթի","Երեքշաբթի","Չորեքշաբթի","Հինգշաբթի","Ուրբաթ","Շաբաթ"],daysShort:["Կիր","Երկ","Երե","Չոր","Հին","Ուրբ","Շաբ"],daysMin:["Կի","Եկ","Եք","Չո","Հի","Ու","Շա"],months:["Հունվար","Փետրվար","Մարտ","Ապրիլ","Մայիս","Հունիս","Հուլիս","Օգոստոս","Սեպտեմբեր","Հոկտեմբեր","Նոյեմբեր","Դեկտեմբեր"],monthsShort:["Հնվ","Փետ","Մար","Ապր","Մայ","Հուն","Հուլ","Օգս","Սեպ","Հոկ","Նոյ","Դեկ"],today:"Այսօր",clear:"Ջնջել",format:"dd.mm.yyyy",weekStart:1,monthsTitle:"Ամիսնէր"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.id.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.id.min.js new file mode 100644 index 0000000000..7c3220a643 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.id.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.id={days:["Minggu","Senin","Selasa","Rabu","Kamis","Jumat","Sabtu"],daysShort:["Mgu","Sen","Sel","Rab","Kam","Jum","Sab"],daysMin:["Mg","Sn","Sl","Ra","Ka","Ju","Sa"],months:["Januari","Februari","Maret","April","Mei","Juni","Juli","Agustus","September","Oktober","November","Desember"],monthsShort:["Jan","Feb","Mar","Apr","Mei","Jun","Jul","Ags","Sep","Okt","Nov","Des"],today:"Hari Ini",clear:"Kosongkan"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.is.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.is.min.js new file mode 100644 index 0000000000..f49bd18cc2 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.is.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.is={days:["Sunnudagur","Mánudagur","Þriðjudagur","Miðvikudagur","Fimmtudagur","Föstudagur","Laugardagur"],daysShort:["Sun","Mán","Þri","Mið","Fim","Fös","Lau"],daysMin:["Su","Má","Þr","Mi","Fi","Fö","La"],months:["Janúar","Febrúar","Mars","Apríl","Maí","Júní","Júlí","Ágúst","September","Október","Nóvember","Desember"],monthsShort:["Jan","Feb","Mar","Apr","Maí","Jún","Júl","Ágú","Sep","Okt","Nóv","Des"],today:"Í Dag"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.it-CH.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.it-CH.min.js new file mode 100644 index 0000000000..7e1adbb95d --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.it-CH.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.it={days:["Domenica","Lunedì","Martedì","Mercoledì","Giovedì","Venerdì","Sabato"],daysShort:["Dom","Lun","Mar","Mer","Gio","Ven","Sab"],daysMin:["Do","Lu","Ma","Me","Gi","Ve","Sa"],months:["Gennaio","Febbraio","Marzo","Aprile","Maggio","Giugno","Luglio","Agosto","Settembre","Ottobre","Novembre","Dicembre"],monthsShort:["Gen","Feb","Mar","Apr","Mag","Giu","Lug","Ago","Set","Ott","Nov","Dic"],today:"Oggi",clear:"Cancella",weekStart:1,format:"dd.mm.yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.it.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.it.min.js new file mode 100644 index 0000000000..cc30766ffa --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.it.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.it={days:["Domenica","Lunedì","Martedì","Mercoledì","Giovedì","Venerdì","Sabato"],daysShort:["Dom","Lun","Mar","Mer","Gio","Ven","Sab"],daysMin:["Do","Lu","Ma","Me","Gi","Ve","Sa"],months:["Gennaio","Febbraio","Marzo","Aprile","Maggio","Giugno","Luglio","Agosto","Settembre","Ottobre","Novembre","Dicembre"],monthsShort:["Gen","Feb","Mar","Apr","Mag","Giu","Lug","Ago","Set","Ott","Nov","Dic"],today:"Oggi",monthsTitle:"Mesi",clear:"Cancella",weekStart:1,format:"dd/mm/yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.ja.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.ja.min.js new file mode 100644 index 0000000000..e321f04ffe --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.ja.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.ja={days:["日曜","月曜","火曜","水曜","木曜","金曜","土曜"],daysShort:["日","月","火","水","木","金","土"],daysMin:["日","月","火","水","木","金","土"],months:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],monthsShort:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],today:"今日",format:"yyyy/mm/dd",titleFormat:"yyyy年mm月",clear:"クリア"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.ka.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.ka.min.js new file mode 100644 index 0000000000..84f14c0e90 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.ka.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.ka={days:["კვირა","ორშაბათი","სამშაბათი","ოთხშაბათი","ხუთშაბათი","პარასკევი","შაბათი"],daysShort:["კვი","ორშ","სამ","ოთხ","ხუთ","პარ","შაბ"],daysMin:["კვ","ორ","სა","ოთ","ხუ","პა","შა"],months:["იანვარი","თებერვალი","მარტი","აპრილი","მაისი","ივნისი","ივლისი","აგვისტო","სექტემბერი","ოქტომბერი","ნოემბერი","დეკემბერი"],monthsShort:["იან","თებ","მარ","აპრ","მაი","ივნ","ივლ","აგვ","სექ","ოქტ","ნოე","დეკ"],today:"დღეს",clear:"გასუფთავება",weekStart:1,format:"dd.mm.yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.kh.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.kh.min.js new file mode 100644 index 0000000000..bf2abc5d82 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.kh.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.kh={days:["អាទិត្យ","ចន្ទ","អង្គារ","ពុធ","ព្រហស្បតិ៍","សុក្រ","សៅរ៍"],daysShort:["អា.ទិ","ចន្ទ","អង្គារ","ពុធ","ព្រ.ហ","សុក្រ","សៅរ៍"],daysMin:["អា.ទិ","ចន្ទ","អង្គារ","ពុធ","ព្រ.ហ","សុក្រ","សៅរ៍"],months:["មករា","កុម្ភះ","មិនា","មេសា","ឧសភា","មិថុនា","កក្កដា","សីហា","កញ្ញា","តុលា","វិច្ឆិកា","ធ្នូ"],monthsShort:["មករា","កុម្ភះ","មិនា","មេសា","ឧសភា","មិថុនា","កក្កដា","សីហា","កញ្ញា","តុលា","វិច្ឆិកា","ធ្នូ"],today:"ថ្ងៃនេះ",clear:"សំអាត"},a.fn.datepicker.deprecated('The language code "kh" is deprecated and will be removed in 2.0. For Khmer support use "km" instead.')}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.kk.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.kk.min.js new file mode 100644 index 0000000000..f4e2f3f1a2 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.kk.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.kk={days:["Жексенбі","Дүйсенбі","Сейсенбі","Сәрсенбі","Бейсенбі","Жұма","Сенбі"],daysShort:["Жек","Дүй","Сей","Сәр","Бей","Жұм","Сен"],daysMin:["Жк","Дс","Сс","Ср","Бс","Жм","Сн"],months:["Қаңтар","Ақпан","Наурыз","Сәуір","Мамыр","Маусым","Шілде","Тамыз","Қыркүйек","Қазан","Қараша","Желтоқсан"],monthsShort:["Қаң","Ақп","Нау","Сәу","Мам","Мау","Шіл","Там","Қыр","Қаз","Қар","Жел"],today:"Бүгін",weekStart:1}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.km.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.km.min.js new file mode 100644 index 0000000000..648d83f84f --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.km.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.km={days:["អាទិត្យ","ចន្ទ","អង្គារ","ពុធ","ព្រហស្បតិ៍","សុក្រ","សៅរ៍"],daysShort:["អា.ទិ","ចន្ទ","អង្គារ","ពុធ","ព្រ.ហ","សុក្រ","សៅរ៍"],daysMin:["អា.ទិ","ចន្ទ","អង្គារ","ពុធ","ព្រ.ហ","សុក្រ","សៅរ៍"],months:["មករា","កុម្ភះ","មិនា","មេសា","ឧសភា","មិថុនា","កក្កដា","សីហា","កញ្ញា","តុលា","វិច្ឆិកា","ធ្នូ"],monthsShort:["មករា","កុម្ភះ","មិនា","មេសា","ឧសភា","មិថុនា","កក្កដា","សីហា","កញ្ញា","តុលា","វិច្ឆិកា","ធ្នូ"],today:"ថ្ងៃនេះ",clear:"សំអាត"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.ko.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.ko.min.js new file mode 100644 index 0000000000..9751ee5c22 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.ko.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.ko={days:["일요일","월요일","화요일","수요일","목요일","금요일","토요일"],daysShort:["일","월","화","수","목","금","토"],daysMin:["일","월","화","수","목","금","토"],months:["1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"],monthsShort:["1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"],today:"오늘",clear:"삭제",format:"yyyy-mm-dd",titleFormat:"yyyy년mm월",weekStart:0}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.kr.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.kr.min.js new file mode 100644 index 0000000000..43393409e0 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.kr.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.kr={days:["일요일","월요일","화요일","수요일","목요일","금요일","토요일"],daysShort:["일","월","화","수","목","금","토"],daysMin:["일","월","화","수","목","금","토"],months:["1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"],monthsShort:["1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"]},a.fn.datepicker.deprecated('The language code "kr" is deprecated and will be removed in 2.0. For korean support use "ko" instead.')}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.lt.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.lt.min.js new file mode 100644 index 0000000000..da78ea85f3 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.lt.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.lt={days:["Sekmadienis","Pirmadienis","Antradienis","Trečiadienis","Ketvirtadienis","Penktadienis","Šeštadienis"],daysShort:["S","Pr","A","T","K","Pn","Š"],daysMin:["Sk","Pr","An","Tr","Ke","Pn","Št"],months:["Sausis","Vasaris","Kovas","Balandis","Gegužė","Birželis","Liepa","Rugpjūtis","Rugsėjis","Spalis","Lapkritis","Gruodis"],monthsShort:["Sau","Vas","Kov","Bal","Geg","Bir","Lie","Rugp","Rugs","Spa","Lap","Gru"],today:"Šiandien",monthsTitle:"Mėnesiai",clear:"Išvalyti",weekStart:1,format:"yyyy-mm-dd"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.lv.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.lv.min.js new file mode 100644 index 0000000000..89cea00f8b --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.lv.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.lv={days:["Svētdiena","Pirmdiena","Otrdiena","Trešdiena","Ceturtdiena","Piektdiena","Sestdiena"],daysShort:["Sv","P","O","T","C","Pk","S"],daysMin:["Sv","Pr","Ot","Tr","Ce","Pk","Se"],months:["Janvāris","Februāris","Marts","Aprīlis","Maijs","Jūnijs","Jūlijs","Augusts","Septembris","Oktobris","Novembris","Decembris"],monthsShort:["Jan","Feb","Mar","Apr","Mai","Jūn","Jūl","Aug","Sep","Okt","Nov","Dec"],monthsTitle:"Mēneši",today:"Šodien",clear:"Nodzēst",weekStart:1}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.me.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.me.min.js new file mode 100644 index 0000000000..c65a891646 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.me.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.me={days:["Nedjelja","Ponedjeljak","Utorak","Srijeda","Četvrtak","Petak","Subota"],daysShort:["Ned","Pon","Uto","Sri","Čet","Pet","Sub"],daysMin:["Ne","Po","Ut","Sr","Če","Pe","Su"],months:["Januar","Februar","Mart","April","Maj","Jun","Jul","Avgust","Septembar","Oktobar","Novembar","Decembar"],monthsShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Avg","Sep","Okt","Nov","Dec"],today:"Danas",weekStart:1,clear:"Izbriši",format:"dd.mm.yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.mk.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.mk.min.js new file mode 100644 index 0000000000..46423f7581 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.mk.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.mk={days:["Недела","Понеделник","Вторник","Среда","Четврток","Петок","Сабота"],daysShort:["Нед","Пон","Вто","Сре","Чет","Пет","Саб"],daysMin:["Не","По","Вт","Ср","Че","Пе","Са"],months:["Јануари","Февруари","Март","Април","Мај","Јуни","Јули","Август","Септември","Октомври","Ноември","Декември"],monthsShort:["Јан","Фев","Мар","Апр","Мај","Јун","Јул","Авг","Сеп","Окт","Ное","Дек"],today:"Денес",format:"dd.mm.yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.mn.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.mn.min.js new file mode 100644 index 0000000000..6ebaec9d86 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.mn.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.mn={days:["Ням","Даваа","Мягмар","Лхагва","Пүрэв","Баасан","Бямба"],daysShort:["Ням","Дав","Мяг","Лха","Пүр","Баа","Бям"],daysMin:["Ня","Да","Мя","Лх","Пү","Ба","Бя"],months:["Хулгана","Үхэр","Бар","Туулай","Луу","Могой","Морь","Хонь","Бич","Тахиа","Нохой","Гахай"],monthsShort:["Хул","Үхэ","Бар","Туу","Луу","Мог","Мор","Хон","Бич","Тах","Нох","Гах"],today:"Өнөөдөр",clear:"Тодорхой",format:"yyyy.mm.dd",weekStart:1}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.ms.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.ms.min.js new file mode 100644 index 0000000000..47efafdc2f --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.ms.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.ms={days:["Ahad","Isnin","Selasa","Rabu","Khamis","Jumaat","Sabtu"],daysShort:["Aha","Isn","Sel","Rab","Kha","Jum","Sab"],daysMin:["Ah","Is","Se","Ra","Kh","Ju","Sa"],months:["Januari","Februari","Mac","April","Mei","Jun","Julai","Ogos","September","Oktober","November","Disember"],monthsShort:["Jan","Feb","Mar","Apr","Mei","Jun","Jul","Ogo","Sep","Okt","Nov","Dis"],today:"Hari Ini",clear:"Bersihkan"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.nl-BE.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.nl-BE.min.js new file mode 100644 index 0000000000..85d3146df1 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.nl-BE.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates["nl-BE"]={days:["zondag","maandag","dinsdag","woensdag","donderdag","vrijdag","zaterdag"],daysShort:["zo","ma","di","wo","do","vr","za"],daysMin:["zo","ma","di","wo","do","vr","za"],months:["januari","februari","maart","april","mei","juni","juli","augustus","september","oktober","november","december"],monthsShort:["jan","feb","mrt","apr","mei","jun","jul","aug","sep","okt","nov","dec"],today:"Vandaag",monthsTitle:"Maanden",clear:"Leegmaken",weekStart:1,format:"dd/mm/yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.nl.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.nl.min.js new file mode 100644 index 0000000000..af977b71ea --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.nl.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.nl={days:["zondag","maandag","dinsdag","woensdag","donderdag","vrijdag","zaterdag"],daysShort:["zo","ma","di","wo","do","vr","za"],daysMin:["zo","ma","di","wo","do","vr","za"],months:["januari","februari","maart","april","mei","juni","juli","augustus","september","oktober","november","december"],monthsShort:["jan","feb","mrt","apr","mei","jun","jul","aug","sep","okt","nov","dec"],today:"Vandaag",monthsTitle:"Maanden",clear:"Wissen",weekStart:1,format:"dd-mm-yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.no.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.no.min.js new file mode 100644 index 0000000000..0c5136e441 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.no.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.no={days:["søndag","mandag","tirsdag","onsdag","torsdag","fredag","lørdag"],daysShort:["søn","man","tir","ons","tor","fre","lør"],daysMin:["sø","ma","ti","on","to","fr","lø"],months:["januar","februar","mars","april","mai","juni","juli","august","september","oktober","november","desember"],monthsShort:["jan","feb","mar","apr","mai","jun","jul","aug","sep","okt","nov","des"],today:"i dag",monthsTitle:"Måneder",clear:"Nullstill",weekStart:1,format:"dd.mm.yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.oc.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.oc.min.js new file mode 100644 index 0000000000..630fa16b96 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.oc.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.oc={days:["Dimenge","Diluns","Dimars","Dimècres","Dijòus","Divendres","Dissabte"],daysShort:["Dim","Dil","Dmr","Dmc","Dij","Div","Dis"],daysMin:["dg","dl","dr","dc","dj","dv","ds"],months:["Genièr","Febrièr","Març","Abrial","Mai","Junh","Julhet","Agost","Setembre","Octobre","Novembre","Decembre"],monthsShort:["Gen","Feb","Mar","Abr","Mai","Jun","Jul","Ago","Set","Oct","Nov","Dec"],today:"Uèi",monthsTitle:"Meses",clear:"Escafar",weekStart:1,format:"dd/mm/yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.pl.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.pl.min.js new file mode 100644 index 0000000000..ffb30ec8b1 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.pl.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.pl={days:["Niedziela","Poniedziałek","Wtorek","Środa","Czwartek","Piątek","Sobota"],daysShort:["Niedz.","Pon.","Wt.","Śr.","Czw.","Piąt.","Sob."],daysMin:["Ndz.","Pn.","Wt.","Śr.","Czw.","Pt.","Sob."],months:["Styczeń","Luty","Marzec","Kwiecień","Maj","Czerwiec","Lipiec","Sierpień","Wrzesień","Październik","Listopad","Grudzień"],monthsShort:["Sty.","Lut.","Mar.","Kwi.","Maj","Cze.","Lip.","Sie.","Wrz.","Paź.","Lis.","Gru."],today:"Dzisiaj",weekStart:1,clear:"Wyczyść",format:"dd.mm.yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.pt-BR.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.pt-BR.min.js new file mode 100644 index 0000000000..2d3f8afdac --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.pt-BR.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates["pt-BR"]={days:["Domingo","Segunda","Terça","Quarta","Quinta","Sexta","Sábado"],daysShort:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],daysMin:["Do","Se","Te","Qu","Qu","Se","Sa"],months:["Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"],monthsShort:["Jan","Fev","Mar","Abr","Mai","Jun","Jul","Ago","Set","Out","Nov","Dez"],today:"Hoje",monthsTitle:"Meses",clear:"Limpar",format:"dd/mm/yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.pt.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.pt.min.js new file mode 100644 index 0000000000..e2b4e64d77 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.pt.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.pt={days:["Domingo","Segunda","Terça","Quarta","Quinta","Sexta","Sábado"],daysShort:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],daysMin:["Do","Se","Te","Qu","Qu","Se","Sa"],months:["Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"],monthsShort:["Jan","Fev","Mar","Abr","Mai","Jun","Jul","Ago","Set","Out","Nov","Dez"],today:"Hoje",monthsTitle:"Meses",clear:"Limpar",format:"dd/mm/yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.ro.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.ro.min.js new file mode 100644 index 0000000000..5fff2986df --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.ro.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.ro={days:["Duminică","Luni","Marţi","Miercuri","Joi","Vineri","Sâmbătă"],daysShort:["Dum","Lun","Mar","Mie","Joi","Vin","Sâm"],daysMin:["Du","Lu","Ma","Mi","Jo","Vi","Sâ"],months:["Ianuarie","Februarie","Martie","Aprilie","Mai","Iunie","Iulie","August","Septembrie","Octombrie","Noiembrie","Decembrie"],monthsShort:["Ian","Feb","Mar","Apr","Mai","Iun","Iul","Aug","Sep","Oct","Nov","Dec"],today:"Astăzi",clear:"Șterge",weekStart:1,format:"dd/mm/yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.rs-latin.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.rs-latin.min.js new file mode 100644 index 0000000000..e520c95733 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.rs-latin.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates["rs-latin"]={days:["Nedelja","Ponedeljak","Utorak","Sreda","Četvrtak","Petak","Subota"],daysShort:["Ned","Pon","Uto","Sre","Čet","Pet","Sub"],daysMin:["N","Po","U","Sr","Č","Pe","Su"],months:["Januar","Februar","Mart","April","Maj","Jun","Jul","Avgust","Septembar","Oktobar","Novembar","Decembar"],monthsShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Avg","Sep","Okt","Nov","Dec"],today:"Danas",weekStart:1,format:"dd.mm.yyyy"},a.fn.datepicker.deprecated('This language code "rs-latin" is deprecated (invalid serbian language code) and will be removed in 2.0. For Serbian latin support use "sr-latin" instead.')}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.rs.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.rs.min.js new file mode 100644 index 0000000000..ba95ae298f --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.rs.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.rs={days:["Недеља","Понедељак","Уторак","Среда","Четвртак","Петак","Субота"],daysShort:["Нед","Пон","Уто","Сре","Чет","Пет","Суб"],daysMin:["Н","По","У","Ср","Ч","Пе","Су"],months:["Јануар","Фебруар","Март","Април","Мај","Јун","Јул","Август","Септембар","Октобар","Новембар","Децембар"],monthsShort:["Јан","Феб","Мар","Апр","Мај","Јун","Јул","Авг","Сеп","Окт","Нов","Дец"],today:"Данас",weekStart:1,format:"dd.mm.yyyy"},a.fn.datepicker.deprecated('This language code "rs" is deprecated (invalid serbian language code) and will be removed in 2.0. For Serbian support use "sr" instead.')}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.ru.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.ru.min.js new file mode 100644 index 0000000000..52bc010b97 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.ru.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.ru={days:["Воскресенье","Понедельник","Вторник","Среда","Четверг","Пятница","Суббота"],daysShort:["Вск","Пнд","Втр","Срд","Чтв","Птн","Суб"],daysMin:["Вс","Пн","Вт","Ср","Чт","Пт","Сб"],months:["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь"],monthsShort:["Янв","Фев","Мар","Апр","Май","Июн","Июл","Авг","Сен","Окт","Ноя","Дек"],today:"Сегодня",clear:"Очистить",format:"dd.mm.yyyy",weekStart:1,monthsTitle:"Месяцы"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.si.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.si.min.js new file mode 100644 index 0000000000..b9746b8fc1 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.si.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.si={days:["ඉරිදා","සඳුදා","අඟහරුවාදා","බදාදා","බ්‍රහස්පතින්දා","සිකුරාදා","සෙනසුරාදා"],daysShort:["ඉරි","සඳු","අඟ","බදා","බ්‍රහ","සිකු","සෙන"],daysMin:["ඉ","ස","අ","බ","බ්‍ර","සි","සෙ"],months:["ජනවාරි","පෙබරවාරි","මාර්තු","අප්‍රේල්","මැයි","ජුනි","ජූලි","අගෝස්තු","සැප්තැම්බර්","ඔක්තෝබර්","නොවැම්බර්","දෙසැම්බර්"],monthsShort:["ජන","පෙබ","මාර්","අප්‍රේ","මැයි","ජුනි","ජූලි","අගෝ","සැප්","ඔක්","නොවැ","දෙසැ"],today:"අද",monthsTitle:"මාස",clear:"මකන්න",weekStart:0,format:"yyyy-mm-dd"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.sk.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.sk.min.js new file mode 100644 index 0000000000..79a9267fd5 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.sk.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.sk={days:["Nedeľa","Pondelok","Utorok","Streda","Štvrtok","Piatok","Sobota"],daysShort:["Ned","Pon","Uto","Str","Štv","Pia","Sob"],daysMin:["Ne","Po","Ut","St","Št","Pia","So"],months:["Január","Február","Marec","Apríl","Máj","Jún","Júl","August","September","Október","November","December"],monthsShort:["Jan","Feb","Mar","Apr","Máj","Jún","Júl","Aug","Sep","Okt","Nov","Dec"],today:"Dnes",clear:"Vymazať",weekStart:1,format:"d.m.yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.sl.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.sl.min.js new file mode 100644 index 0000000000..831cf73903 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.sl.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.sl={days:["Nedelja","Ponedeljek","Torek","Sreda","Četrtek","Petek","Sobota"],daysShort:["Ned","Pon","Tor","Sre","Čet","Pet","Sob"],daysMin:["Ne","Po","To","Sr","Če","Pe","So"],months:["Januar","Februar","Marec","April","Maj","Junij","Julij","Avgust","September","Oktober","November","December"],monthsShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Avg","Sep","Okt","Nov","Dec"],today:"Danes",weekStart:1}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.sq.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.sq.min.js new file mode 100644 index 0000000000..8c586055a0 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.sq.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.sq={days:["E Diel","E Hënë","E Martē","E Mërkurë","E Enjte","E Premte","E Shtunë"],daysShort:["Die","Hën","Mar","Mër","Enj","Pre","Shtu"],daysMin:["Di","Hë","Ma","Më","En","Pr","Sht"],months:["Janar","Shkurt","Mars","Prill","Maj","Qershor","Korrik","Gusht","Shtator","Tetor","Nëntor","Dhjetor"],monthsShort:["Jan","Shk","Mar","Pri","Maj","Qer","Korr","Gu","Sht","Tet","Nën","Dhjet"],monthsTitle:"Muaj",today:"Sot",weekStart:1,format:"dd/mm/yyyy",clear:"Pastro"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.sr-latin.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.sr-latin.min.js new file mode 100644 index 0000000000..c6b7001ace --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.sr-latin.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates["sr-latin"]={days:["Nedelja","Ponedeljak","Utorak","Sreda","Četvrtak","Petak","Subota"],daysShort:["Ned","Pon","Uto","Sre","Čet","Pet","Sub"],daysMin:["N","Po","U","Sr","Č","Pe","Su"],months:["Januar","Februar","Mart","April","Maj","Jun","Jul","Avgust","Septembar","Oktobar","Novembar","Decembar"],monthsShort:["Jan","Feb","Mar","Apr","Maj","Jun","Jul","Avg","Sep","Okt","Nov","Dec"],today:"Danas",weekStart:1,format:"dd.mm.yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.sr.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.sr.min.js new file mode 100644 index 0000000000..4e46dbf64b --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.sr.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.sr={days:["Недеља","Понедељак","Уторак","Среда","Четвртак","Петак","Субота"],daysShort:["Нед","Пон","Уто","Сре","Чет","Пет","Суб"],daysMin:["Н","По","У","Ср","Ч","Пе","Су"],months:["Јануар","Фебруар","Март","Април","Мај","Јун","Јул","Август","Септембар","Октобар","Новембар","Децембар"],monthsShort:["Јан","Феб","Мар","Апр","Мај","Јун","Јул","Авг","Сеп","Окт","Нов","Дец"],today:"Данас",weekStart:1,format:"dd.mm.yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.sv.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.sv.min.js new file mode 100644 index 0000000000..7ab6becb92 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.sv.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.sv={days:["söndag","måndag","tisdag","onsdag","torsdag","fredag","lördag"],daysShort:["sön","mån","tis","ons","tor","fre","lör"],daysMin:["sö","må","ti","on","to","fr","lö"],months:["januari","februari","mars","april","maj","juni","juli","augusti","september","oktober","november","december"],monthsShort:["jan","feb","mar","apr","maj","jun","jul","aug","sep","okt","nov","dec"],today:"Idag",format:"yyyy-mm-dd",weekStart:1,clear:"Rensa"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.sw.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.sw.min.js new file mode 100644 index 0000000000..454d3053cb --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.sw.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.sw={days:["Jumapili","Jumatatu","Jumanne","Jumatano","Alhamisi","Ijumaa","Jumamosi"],daysShort:["J2","J3","J4","J5","Alh","Ij","J1"],daysMin:["2","3","4","5","A","I","1"],months:["Januari","Februari","Machi","Aprili","Mei","Juni","Julai","Agosti","Septemba","Oktoba","Novemba","Desemba"],monthsShort:["Jan","Feb","Mac","Apr","Mei","Jun","Jul","Ago","Sep","Okt","Nov","Des"],today:"Leo"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.ta.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.ta.min.js new file mode 100644 index 0000000000..e7909494aa --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.ta.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.ta={days:["ஞாயிறு","திங்கள்","செவ்வாய்","புதன்","வியாழன்","வெள்ளி","சனி"],daysShort:["ஞாயி","திங்","செவ்","புத","வியா","வெள்","சனி"],daysMin:["ஞா","தி","செ","பு","வி","வெ","ச"],months:["ஜனவரி","பிப்ரவரி","மார்ச்","ஏப்ரல்","மே","ஜூன்","ஜூலை","ஆகஸ்டு","செப்டம்பர்","அக்டோபர்","நவம்பர்","டிசம்பர்"],monthsShort:["ஜன","பிப்","மார்","ஏப்","மே","ஜூன்","ஜூலை","ஆக","செப்","அக்","நவ","டிச"],today:"இன்று",monthsTitle:"மாதங்கள்",clear:"நீக்கு",weekStart:1,format:"dd/mm/yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.tg.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.tg.min.js new file mode 100644 index 0000000000..104b6dd95b --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.tg.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.tg={days:["Якшанбе","Душанбе","Сешанбе","Чоршанбе","Панҷшанбе","Ҷумъа","Шанбе"],daysShort:["Яшб","Дшб","Сшб","Чшб","Пшб","Ҷум","Шнб"],daysMin:["Яш","Дш","Сш","Чш","Пш","Ҷм","Шб"],months:["Январ","Феврал","Март","Апрел","Май","Июн","Июл","Август","Сентябр","Октябр","Ноябр","Декабр"],monthsShort:["Янв","Фев","Мар","Апр","Май","Июн","Июл","Авг","Сен","Окт","Ноя","Дек"],today:"Имрӯз",monthsTitle:"Моҳҳо",clear:"Тоза намудан",weekStart:1,format:"dd.mm.yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.th.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.th.min.js new file mode 100644 index 0000000000..1e398ba8bc --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.th.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.th={days:["อาทิตย์","จันทร์","อังคาร","พุธ","พฤหัส","ศุกร์","เสาร์","อาทิตย์"],daysShort:["อา","จ","อ","พ","พฤ","ศ","ส","อา"],daysMin:["อา","จ","อ","พ","พฤ","ศ","ส","อา"],months:["มกราคม","กุมภาพันธ์","มีนาคม","เมษายน","พฤษภาคม","มิถุนายน","กรกฎาคม","สิงหาคม","กันยายน","ตุลาคม","พฤศจิกายน","ธันวาคม"],monthsShort:["ม.ค.","ก.พ.","มี.ค.","เม.ย.","พ.ค.","มิ.ย.","ก.ค.","ส.ค.","ก.ย.","ต.ค.","พ.ย.","ธ.ค."],today:"วันนี้"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.tk.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.tk.min.js new file mode 100644 index 0000000000..716edef2ee --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.tk.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.tk={days:["Ýekşenbe","Duşenbe","Sişenbe","Çarşenbe","Penşenbe","Anna","Şenbe"],daysShort:["Ýek","Duş","Siş","Çar","Pen","Ann","Şen"],daysMin:["Ýe","Du","Si","Ça","Pe","An","Şe"],months:["Ýanwar","Fewral","Mart","Aprel","Maý","Iýun","Iýul","Awgust","Sentýabr","Oktýabr","Noýabr","Dekabr"],monthsShort:["Ýan","Few","Mar","Apr","Maý","Iýn","Iýl","Awg","Sen","Okt","Noý","Dek"],today:"Bu gün",monthsTitle:"Aýlar",clear:"Aýyr",weekStart:1,format:"dd.mm.yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.tr.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.tr.min.js new file mode 100644 index 0000000000..7889b1135d --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.tr.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.tr={days:["Pazar","Pazartesi","Salı","Çarşamba","Perşembe","Cuma","Cumartesi"],daysShort:["Pz","Pzt","Sal","Çrş","Prş","Cu","Cts"],daysMin:["Pz","Pzt","Sa","Çr","Pr","Cu","Ct"],months:["Ocak","Şubat","Mart","Nisan","Mayıs","Haziran","Temmuz","Ağustos","Eylül","Ekim","Kasım","Aralık"],monthsShort:["Oca","Şub","Mar","Nis","May","Haz","Tem","Ağu","Eyl","Eki","Kas","Ara"],today:"Bugün",clear:"Temizle",weekStart:1,format:"dd.mm.yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.uk.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.uk.min.js new file mode 100644 index 0000000000..41b02e6b28 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.uk.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.uk={days:["Неділя","Понеділок","Вівторок","Середа","Четвер","П'ятниця","Субота"],daysShort:["Нед","Пнд","Втр","Срд","Чтв","Птн","Суб"],daysMin:["Нд","Пн","Вт","Ср","Чт","Пт","Сб"],months:["Cічень","Лютий","Березень","Квітень","Травень","Червень","Липень","Серпень","Вересень","Жовтень","Листопад","Грудень"],monthsShort:["Січ","Лют","Бер","Кві","Тра","Чер","Лип","Сер","Вер","Жов","Лис","Гру"],today:"Сьогодні",clear:"Очистити",format:"dd.mm.yyyy",weekStart:1}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.uz-cyrl.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.uz-cyrl.min.js new file mode 100644 index 0000000000..a0a8f213ce --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.uz-cyrl.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates["uz-cyrl"]={days:["Якшанба","Душанба","Сешанба","Чоршанба","Пайшанба","Жума","Шанба"],daysShort:["Якш","Ду","Се","Чор","Пай","Жу","Ша"],daysMin:["Як","Ду","Се","Чо","Па","Жу","Ша"],months:["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь"],monthsShort:["Янв","Фев","Мар","Апр","Май","Июн","Июл","Авг","Сен","Окт","Ноя","Дек"],today:"Бугун",clear:"Ўчириш",format:"dd.mm.yyyy",weekStart:1,monthsTitle:"Ойлар"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.uz-latn.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.uz-latn.min.js new file mode 100644 index 0000000000..2f58e343ef --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.uz-latn.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates["uz-latn"]={days:["Yakshanba","Dushanba","Seshanba","Chorshanba","Payshanba","Juma","Shanba"],daysShort:["Yak","Du","Se","Chor","Pay","Ju","Sha"],daysMin:["Ya","Du","Se","Cho","Pa","Ju","Sha"],months:["Yanvar","Fevral","Mart","Aprel","May","Iyun","Iyul","Avgust","Sentabr","Oktabr","Noyabr","Dekabr"],monthsShort:["Yan","Fev","Mar","Apr","May","Iyn","Iyl","Avg","Sen","Okt","Noy","Dek"],today:"Bugun",clear:"O'chirish",format:"dd.mm.yyyy",weekStart:1,monthsTitle:"Oylar"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.vi.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.vi.min.js new file mode 100644 index 0000000000..3311d23f86 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.vi.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates.vi={days:["Chủ nhật","Thứ hai","Thứ ba","Thứ tư","Thứ năm","Thứ sáu","Thứ bảy"],daysShort:["CN","Thứ 2","Thứ 3","Thứ 4","Thứ 5","Thứ 6","Thứ 7"],daysMin:["CN","T2","T3","T4","T5","T6","T7"],months:["Tháng 1","Tháng 2","Tháng 3","Tháng 4","Tháng 5","Tháng 6","Tháng 7","Tháng 8","Tháng 9","Tháng 10","Tháng 11","Tháng 12"],monthsShort:["Th1","Th2","Th3","Th4","Th5","Th6","Th7","Th8","Th9","Th10","Th11","Th12"],today:"Hôm nay",clear:"Xóa",format:"dd/mm/yyyy"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.zh-CN.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.zh-CN.min.js new file mode 100644 index 0000000000..8e6920b0c8 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.zh-CN.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates["zh-CN"]={days:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],daysShort:["周日","周一","周二","周三","周四","周五","周六"],daysMin:["日","一","二","三","四","五","六"],months:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],monthsShort:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],today:"今天",monthsTitle:"选择月份",clear:"清除",format:"yyyy-mm-dd",titleFormat:"yyyy年mm月",weekStart:1}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.zh-TW.min.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.zh-TW.min.js new file mode 100644 index 0000000000..e309c1d7d6 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap-datepicker/locales/bootstrap-datepicker.zh-TW.min.js @@ -0,0 +1 @@ +!function(a){a.fn.datepicker.dates["zh-TW"]={days:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],daysShort:["週日","週一","週二","週三","週四","週五","週六"],daysMin:["日","一","二","三","四","五","六"],months:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],monthsShort:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],today:"今天",format:"yyyy年mm月dd日",weekStart:1,clear:"清除"}}(jQuery); \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap/css/bootstrap.css b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap/css/bootstrap.css new file mode 100644 index 0000000000..e461d3fb9e --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap/css/bootstrap.css @@ -0,0 +1,10278 @@ +/*! + * Bootstrap v4.5.0 (https://getbootstrap.com/) + * Copyright 2011-2020 The Bootstrap Authors + * Copyright 2011-2020 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +:root { + --blue: #007bff; + --indigo: #6610f2; + --purple: #6f42c1; + --pink: #e83e8c; + --red: #dc3545; + --orange: #fd7e14; + --yellow: #ffc107; + --green: #28a745; + --teal: #20c997; + --cyan: #17a2b8; + --white: #fff; + --gray: #6c757d; + --gray-dark: #343a40; + --primary: #007bff; + --secondary: #6c757d; + --success: #28a745; + --info: #17a2b8; + --warning: #ffc107; + --danger: #dc3545; + --light: #f8f9fa; + --dark: #343a40; + --breakpoint-xs: 0; + --breakpoint-sm: 576px; + --breakpoint-md: 768px; + --breakpoint-lg: 992px; + --breakpoint-xl: 1200px; + --font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; +} + +*, +*::before, +*::after { + box-sizing: border-box; +} + +html { + font-family: sans-serif; + line-height: 1.15; + -webkit-text-size-adjust: 100%; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} + +article, aside, figcaption, figure, footer, header, hgroup, main, nav, section { + display: block; +} + +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #212529; + text-align: left; + background-color: #fff; +} + +[tabindex="-1"]:focus:not(:focus-visible) { + outline: 0 !important; +} + +hr { + box-sizing: content-box; + height: 0; + overflow: visible; +} + +h1, h2, h3, h4, h5, h6 { + margin-top: 0; + margin-bottom: 0.5rem; +} + +p { + margin-top: 0; + margin-bottom: 1rem; +} + +abbr[title], +abbr[data-original-title] { + text-decoration: underline; + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; + cursor: help; + border-bottom: 0; + -webkit-text-decoration-skip-ink: none; + text-decoration-skip-ink: none; +} + +address { + margin-bottom: 1rem; + font-style: normal; + line-height: inherit; +} + +ol, +ul, +dl { + margin-top: 0; + margin-bottom: 1rem; +} + +ol ol, +ul ul, +ol ul, +ul ol { + margin-bottom: 0; +} + +dt { + font-weight: 700; +} + +dd { + margin-bottom: .5rem; + margin-left: 0; +} + +blockquote { + margin: 0 0 1rem; +} + +b, +strong { + font-weight: bolder; +} + +small { + font-size: 80%; +} + +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} + +sub { + bottom: -.25em; +} + +sup { + top: -.5em; +} + +a { + color: #007bff; + text-decoration: none; + background-color: transparent; +} + +a:hover { + color: #0056b3; + text-decoration: underline; +} + +a:not([href]) { + color: inherit; + text-decoration: none; +} + +a:not([href]):hover { + color: inherit; + text-decoration: none; +} + +pre, +code, +kbd, +samp { + font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + font-size: 1em; +} + +pre { + margin-top: 0; + margin-bottom: 1rem; + overflow: auto; + -ms-overflow-style: scrollbar; +} + +figure { + margin: 0 0 1rem; +} + +img { + vertical-align: middle; + border-style: none; +} + +svg { + overflow: hidden; + vertical-align: middle; +} + +table { + border-collapse: collapse; +} + +caption { + padding-top: 0.75rem; + padding-bottom: 0.75rem; + color: #6c757d; + text-align: left; + caption-side: bottom; +} + +th { + text-align: inherit; +} + +label { + display: inline-block; + margin-bottom: 0.5rem; +} + +button { + border-radius: 0; +} + +button:focus { + outline: 1px dotted; + outline: 5px auto -webkit-focus-ring-color; +} + +input, +button, +select, +optgroup, +textarea { + margin: 0; + font-family: inherit; + font-size: inherit; + line-height: inherit; +} + +button, +input { + overflow: visible; +} + +button, +select { + text-transform: none; +} + +[role="button"] { + cursor: pointer; +} + +select { + word-wrap: normal; +} + +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; +} + +button:not(:disabled), +[type="button"]:not(:disabled), +[type="reset"]:not(:disabled), +[type="submit"]:not(:disabled) { + cursor: pointer; +} + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + padding: 0; + border-style: none; +} + +input[type="radio"], +input[type="checkbox"] { + box-sizing: border-box; + padding: 0; +} + +textarea { + overflow: auto; + resize: vertical; +} + +fieldset { + min-width: 0; + padding: 0; + margin: 0; + border: 0; +} + +legend { + display: block; + width: 100%; + max-width: 100%; + padding: 0; + margin-bottom: .5rem; + font-size: 1.5rem; + line-height: inherit; + color: inherit; + white-space: normal; +} + +progress { + vertical-align: baseline; +} + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +[type="search"] { + outline-offset: -2px; + -webkit-appearance: none; +} + +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +::-webkit-file-upload-button { + font: inherit; + -webkit-appearance: button; +} + +output { + display: inline-block; +} + +summary { + display: list-item; + cursor: pointer; +} + +template { + display: none; +} + +[hidden] { + display: none !important; +} + +h1, h2, h3, h4, h5, h6, +.h1, .h2, .h3, .h4, .h5, .h6 { + margin-bottom: 0.5rem; + font-weight: 500; + line-height: 1.2; +} + +h1, .h1 { + font-size: 2.5rem; +} + +h2, .h2 { + font-size: 2rem; +} + +h3, .h3 { + font-size: 1.75rem; +} + +h4, .h4 { + font-size: 1.5rem; +} + +h5, .h5 { + font-size: 1.25rem; +} + +h6, .h6 { + font-size: 1rem; +} + +.lead { + font-size: 1.25rem; + font-weight: 300; +} + +.display-1 { + font-size: 6rem; + font-weight: 300; + line-height: 1.2; +} + +.display-2 { + font-size: 5.5rem; + font-weight: 300; + line-height: 1.2; +} + +.display-3 { + font-size: 4.5rem; + font-weight: 300; + line-height: 1.2; +} + +.display-4 { + font-size: 3.5rem; + font-weight: 300; + line-height: 1.2; +} + +hr { + margin-top: 1rem; + margin-bottom: 1rem; + border: 0; + border-top: 1px solid rgba(0, 0, 0, 0.1); +} + +small, +.small { + font-size: 80%; + font-weight: 400; +} + +mark, +.mark { + padding: 0.2em; + background-color: #fcf8e3; +} + +.list-unstyled { + padding-left: 0; + list-style: none; +} + +.list-inline { + padding-left: 0; + list-style: none; +} + +.list-inline-item { + display: inline-block; +} + +.list-inline-item:not(:last-child) { + margin-right: 0.5rem; +} + +.initialism { + font-size: 90%; + text-transform: uppercase; +} + +.blockquote { + margin-bottom: 1rem; + font-size: 1.25rem; +} + +.blockquote-footer { + display: block; + font-size: 80%; + color: #6c757d; +} + +.blockquote-footer::before { + content: "\2014\00A0"; +} + +.img-fluid { + max-width: 100%; + height: auto; +} + +.img-thumbnail { + padding: 0.25rem; + background-color: #fff; + border: 1px solid #dee2e6; + border-radius: 0.25rem; + max-width: 100%; + height: auto; +} + +.figure { + display: inline-block; +} + +.figure-img { + margin-bottom: 0.5rem; + line-height: 1; +} + +.figure-caption { + font-size: 90%; + color: #6c757d; +} + +code { + font-size: 87.5%; + color: #e83e8c; + word-wrap: break-word; +} + +a > code { + color: inherit; +} + +kbd { + padding: 0.2rem 0.4rem; + font-size: 87.5%; + color: #fff; + background-color: #212529; + border-radius: 0.2rem; +} + +kbd kbd { + padding: 0; + font-size: 100%; + font-weight: 700; +} + +pre { + display: block; + font-size: 87.5%; + color: #212529; +} + +pre code { + font-size: inherit; + color: inherit; + word-break: normal; +} + +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} + +.container { + width: 100%; + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} + +@media (min-width: 576px) { + .container { + max-width: 540px; + } +} + +@media (min-width: 768px) { + .container { + max-width: 720px; + } +} + +@media (min-width: 992px) { + .container { + max-width: 960px; + } +} + +@media (min-width: 1200px) { + .container { + max-width: 1140px; + } +} + +.container-fluid, .container-sm, .container-md, .container-lg, .container-xl { + width: 100%; + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} + +@media (min-width: 576px) { + .container, .container-sm { + max-width: 540px; + } +} + +@media (min-width: 768px) { + .container, .container-sm, .container-md { + max-width: 720px; + } +} + +@media (min-width: 992px) { + .container, .container-sm, .container-md, .container-lg { + max-width: 960px; + } +} + +@media (min-width: 1200px) { + .container, .container-sm, .container-md, .container-lg, .container-xl { + max-width: 1140px; + } +} + +.row { + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + margin-right: -15px; + margin-left: -15px; +} + +.no-gutters { + margin-right: 0; + margin-left: 0; +} + +.no-gutters > .col, +.no-gutters > [class*="col-"] { + padding-right: 0; + padding-left: 0; +} + +.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col, +.col-auto, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm, +.col-sm-auto, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md, +.col-md-auto, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg, +.col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl, +.col-xl-auto { + position: relative; + width: 100%; + padding-right: 15px; + padding-left: 15px; +} + +.col { + -ms-flex-preferred-size: 0; + flex-basis: 0; + -ms-flex-positive: 1; + flex-grow: 1; + min-width: 0; + max-width: 100%; +} + +.row-cols-1 > * { + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100%; +} + +.row-cols-2 > * { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50%; +} + +.row-cols-3 > * { + -ms-flex: 0 0 33.333333%; + flex: 0 0 33.333333%; + max-width: 33.333333%; +} + +.row-cols-4 > * { + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25%; +} + +.row-cols-5 > * { + -ms-flex: 0 0 20%; + flex: 0 0 20%; + max-width: 20%; +} + +.row-cols-6 > * { + -ms-flex: 0 0 16.666667%; + flex: 0 0 16.666667%; + max-width: 16.666667%; +} + +.col-auto { + -ms-flex: 0 0 auto; + flex: 0 0 auto; + width: auto; + max-width: 100%; +} + +.col-1 { + -ms-flex: 0 0 8.333333%; + flex: 0 0 8.333333%; + max-width: 8.333333%; +} + +.col-2 { + -ms-flex: 0 0 16.666667%; + flex: 0 0 16.666667%; + max-width: 16.666667%; +} + +.col-3 { + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25%; +} + +.col-4 { + -ms-flex: 0 0 33.333333%; + flex: 0 0 33.333333%; + max-width: 33.333333%; +} + +.col-5 { + -ms-flex: 0 0 41.666667%; + flex: 0 0 41.666667%; + max-width: 41.666667%; +} + +.col-6 { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50%; +} + +.col-7 { + -ms-flex: 0 0 58.333333%; + flex: 0 0 58.333333%; + max-width: 58.333333%; +} + +.col-8 { + -ms-flex: 0 0 66.666667%; + flex: 0 0 66.666667%; + max-width: 66.666667%; +} + +.col-9 { + -ms-flex: 0 0 75%; + flex: 0 0 75%; + max-width: 75%; +} + +.col-10 { + -ms-flex: 0 0 83.333333%; + flex: 0 0 83.333333%; + max-width: 83.333333%; +} + +.col-11 { + -ms-flex: 0 0 91.666667%; + flex: 0 0 91.666667%; + max-width: 91.666667%; +} + +.col-12 { + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100%; +} + +.order-first { + -ms-flex-order: -1; + order: -1; +} + +.order-last { + -ms-flex-order: 13; + order: 13; +} + +.order-0 { + -ms-flex-order: 0; + order: 0; +} + +.order-1 { + -ms-flex-order: 1; + order: 1; +} + +.order-2 { + -ms-flex-order: 2; + order: 2; +} + +.order-3 { + -ms-flex-order: 3; + order: 3; +} + +.order-4 { + -ms-flex-order: 4; + order: 4; +} + +.order-5 { + -ms-flex-order: 5; + order: 5; +} + +.order-6 { + -ms-flex-order: 6; + order: 6; +} + +.order-7 { + -ms-flex-order: 7; + order: 7; +} + +.order-8 { + -ms-flex-order: 8; + order: 8; +} + +.order-9 { + -ms-flex-order: 9; + order: 9; +} + +.order-10 { + -ms-flex-order: 10; + order: 10; +} + +.order-11 { + -ms-flex-order: 11; + order: 11; +} + +.order-12 { + -ms-flex-order: 12; + order: 12; +} + +.offset-1 { + margin-left: 8.333333%; +} + +.offset-2 { + margin-left: 16.666667%; +} + +.offset-3 { + margin-left: 25%; +} + +.offset-4 { + margin-left: 33.333333%; +} + +.offset-5 { + margin-left: 41.666667%; +} + +.offset-6 { + margin-left: 50%; +} + +.offset-7 { + margin-left: 58.333333%; +} + +.offset-8 { + margin-left: 66.666667%; +} + +.offset-9 { + margin-left: 75%; +} + +.offset-10 { + margin-left: 83.333333%; +} + +.offset-11 { + margin-left: 91.666667%; +} + +@media (min-width: 576px) { + .col-sm { + -ms-flex-preferred-size: 0; + flex-basis: 0; + -ms-flex-positive: 1; + flex-grow: 1; + min-width: 0; + max-width: 100%; + } + .row-cols-sm-1 > * { + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100%; + } + .row-cols-sm-2 > * { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50%; + } + .row-cols-sm-3 > * { + -ms-flex: 0 0 33.333333%; + flex: 0 0 33.333333%; + max-width: 33.333333%; + } + .row-cols-sm-4 > * { + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25%; + } + .row-cols-sm-5 > * { + -ms-flex: 0 0 20%; + flex: 0 0 20%; + max-width: 20%; + } + .row-cols-sm-6 > * { + -ms-flex: 0 0 16.666667%; + flex: 0 0 16.666667%; + max-width: 16.666667%; + } + .col-sm-auto { + -ms-flex: 0 0 auto; + flex: 0 0 auto; + width: auto; + max-width: 100%; + } + .col-sm-1 { + -ms-flex: 0 0 8.333333%; + flex: 0 0 8.333333%; + max-width: 8.333333%; + } + .col-sm-2 { + -ms-flex: 0 0 16.666667%; + flex: 0 0 16.666667%; + max-width: 16.666667%; + } + .col-sm-3 { + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25%; + } + .col-sm-4 { + -ms-flex: 0 0 33.333333%; + flex: 0 0 33.333333%; + max-width: 33.333333%; + } + .col-sm-5 { + -ms-flex: 0 0 41.666667%; + flex: 0 0 41.666667%; + max-width: 41.666667%; + } + .col-sm-6 { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50%; + } + .col-sm-7 { + -ms-flex: 0 0 58.333333%; + flex: 0 0 58.333333%; + max-width: 58.333333%; + } + .col-sm-8 { + -ms-flex: 0 0 66.666667%; + flex: 0 0 66.666667%; + max-width: 66.666667%; + } + .col-sm-9 { + -ms-flex: 0 0 75%; + flex: 0 0 75%; + max-width: 75%; + } + .col-sm-10 { + -ms-flex: 0 0 83.333333%; + flex: 0 0 83.333333%; + max-width: 83.333333%; + } + .col-sm-11 { + -ms-flex: 0 0 91.666667%; + flex: 0 0 91.666667%; + max-width: 91.666667%; + } + .col-sm-12 { + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100%; + } + .order-sm-first { + -ms-flex-order: -1; + order: -1; + } + .order-sm-last { + -ms-flex-order: 13; + order: 13; + } + .order-sm-0 { + -ms-flex-order: 0; + order: 0; + } + .order-sm-1 { + -ms-flex-order: 1; + order: 1; + } + .order-sm-2 { + -ms-flex-order: 2; + order: 2; + } + .order-sm-3 { + -ms-flex-order: 3; + order: 3; + } + .order-sm-4 { + -ms-flex-order: 4; + order: 4; + } + .order-sm-5 { + -ms-flex-order: 5; + order: 5; + } + .order-sm-6 { + -ms-flex-order: 6; + order: 6; + } + .order-sm-7 { + -ms-flex-order: 7; + order: 7; + } + .order-sm-8 { + -ms-flex-order: 8; + order: 8; + } + .order-sm-9 { + -ms-flex-order: 9; + order: 9; + } + .order-sm-10 { + -ms-flex-order: 10; + order: 10; + } + .order-sm-11 { + -ms-flex-order: 11; + order: 11; + } + .order-sm-12 { + -ms-flex-order: 12; + order: 12; + } + .offset-sm-0 { + margin-left: 0; + } + .offset-sm-1 { + margin-left: 8.333333%; + } + .offset-sm-2 { + margin-left: 16.666667%; + } + .offset-sm-3 { + margin-left: 25%; + } + .offset-sm-4 { + margin-left: 33.333333%; + } + .offset-sm-5 { + margin-left: 41.666667%; + } + .offset-sm-6 { + margin-left: 50%; + } + .offset-sm-7 { + margin-left: 58.333333%; + } + .offset-sm-8 { + margin-left: 66.666667%; + } + .offset-sm-9 { + margin-left: 75%; + } + .offset-sm-10 { + margin-left: 83.333333%; + } + .offset-sm-11 { + margin-left: 91.666667%; + } +} + +@media (min-width: 768px) { + .col-md { + -ms-flex-preferred-size: 0; + flex-basis: 0; + -ms-flex-positive: 1; + flex-grow: 1; + min-width: 0; + max-width: 100%; + } + .row-cols-md-1 > * { + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100%; + } + .row-cols-md-2 > * { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50%; + } + .row-cols-md-3 > * { + -ms-flex: 0 0 33.333333%; + flex: 0 0 33.333333%; + max-width: 33.333333%; + } + .row-cols-md-4 > * { + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25%; + } + .row-cols-md-5 > * { + -ms-flex: 0 0 20%; + flex: 0 0 20%; + max-width: 20%; + } + .row-cols-md-6 > * { + -ms-flex: 0 0 16.666667%; + flex: 0 0 16.666667%; + max-width: 16.666667%; + } + .col-md-auto { + -ms-flex: 0 0 auto; + flex: 0 0 auto; + width: auto; + max-width: 100%; + } + .col-md-1 { + -ms-flex: 0 0 8.333333%; + flex: 0 0 8.333333%; + max-width: 8.333333%; + } + .col-md-2 { + -ms-flex: 0 0 16.666667%; + flex: 0 0 16.666667%; + max-width: 16.666667%; + } + .col-md-3 { + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25%; + } + .col-md-4 { + -ms-flex: 0 0 33.333333%; + flex: 0 0 33.333333%; + max-width: 33.333333%; + } + .col-md-5 { + -ms-flex: 0 0 41.666667%; + flex: 0 0 41.666667%; + max-width: 41.666667%; + } + .col-md-6 { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50%; + } + .col-md-7 { + -ms-flex: 0 0 58.333333%; + flex: 0 0 58.333333%; + max-width: 58.333333%; + } + .col-md-8 { + -ms-flex: 0 0 66.666667%; + flex: 0 0 66.666667%; + max-width: 66.666667%; + } + .col-md-9 { + -ms-flex: 0 0 75%; + flex: 0 0 75%; + max-width: 75%; + } + .col-md-10 { + -ms-flex: 0 0 83.333333%; + flex: 0 0 83.333333%; + max-width: 83.333333%; + } + .col-md-11 { + -ms-flex: 0 0 91.666667%; + flex: 0 0 91.666667%; + max-width: 91.666667%; + } + .col-md-12 { + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100%; + } + .order-md-first { + -ms-flex-order: -1; + order: -1; + } + .order-md-last { + -ms-flex-order: 13; + order: 13; + } + .order-md-0 { + -ms-flex-order: 0; + order: 0; + } + .order-md-1 { + -ms-flex-order: 1; + order: 1; + } + .order-md-2 { + -ms-flex-order: 2; + order: 2; + } + .order-md-3 { + -ms-flex-order: 3; + order: 3; + } + .order-md-4 { + -ms-flex-order: 4; + order: 4; + } + .order-md-5 { + -ms-flex-order: 5; + order: 5; + } + .order-md-6 { + -ms-flex-order: 6; + order: 6; + } + .order-md-7 { + -ms-flex-order: 7; + order: 7; + } + .order-md-8 { + -ms-flex-order: 8; + order: 8; + } + .order-md-9 { + -ms-flex-order: 9; + order: 9; + } + .order-md-10 { + -ms-flex-order: 10; + order: 10; + } + .order-md-11 { + -ms-flex-order: 11; + order: 11; + } + .order-md-12 { + -ms-flex-order: 12; + order: 12; + } + .offset-md-0 { + margin-left: 0; + } + .offset-md-1 { + margin-left: 8.333333%; + } + .offset-md-2 { + margin-left: 16.666667%; + } + .offset-md-3 { + margin-left: 25%; + } + .offset-md-4 { + margin-left: 33.333333%; + } + .offset-md-5 { + margin-left: 41.666667%; + } + .offset-md-6 { + margin-left: 50%; + } + .offset-md-7 { + margin-left: 58.333333%; + } + .offset-md-8 { + margin-left: 66.666667%; + } + .offset-md-9 { + margin-left: 75%; + } + .offset-md-10 { + margin-left: 83.333333%; + } + .offset-md-11 { + margin-left: 91.666667%; + } +} + +@media (min-width: 992px) { + .col-lg { + -ms-flex-preferred-size: 0; + flex-basis: 0; + -ms-flex-positive: 1; + flex-grow: 1; + min-width: 0; + max-width: 100%; + } + .row-cols-lg-1 > * { + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100%; + } + .row-cols-lg-2 > * { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50%; + } + .row-cols-lg-3 > * { + -ms-flex: 0 0 33.333333%; + flex: 0 0 33.333333%; + max-width: 33.333333%; + } + .row-cols-lg-4 > * { + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25%; + } + .row-cols-lg-5 > * { + -ms-flex: 0 0 20%; + flex: 0 0 20%; + max-width: 20%; + } + .row-cols-lg-6 > * { + -ms-flex: 0 0 16.666667%; + flex: 0 0 16.666667%; + max-width: 16.666667%; + } + .col-lg-auto { + -ms-flex: 0 0 auto; + flex: 0 0 auto; + width: auto; + max-width: 100%; + } + .col-lg-1 { + -ms-flex: 0 0 8.333333%; + flex: 0 0 8.333333%; + max-width: 8.333333%; + } + .col-lg-2 { + -ms-flex: 0 0 16.666667%; + flex: 0 0 16.666667%; + max-width: 16.666667%; + } + .col-lg-3 { + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25%; + } + .col-lg-4 { + -ms-flex: 0 0 33.333333%; + flex: 0 0 33.333333%; + max-width: 33.333333%; + } + .col-lg-5 { + -ms-flex: 0 0 41.666667%; + flex: 0 0 41.666667%; + max-width: 41.666667%; + } + .col-lg-6 { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50%; + } + .col-lg-7 { + -ms-flex: 0 0 58.333333%; + flex: 0 0 58.333333%; + max-width: 58.333333%; + } + .col-lg-8 { + -ms-flex: 0 0 66.666667%; + flex: 0 0 66.666667%; + max-width: 66.666667%; + } + .col-lg-9 { + -ms-flex: 0 0 75%; + flex: 0 0 75%; + max-width: 75%; + } + .col-lg-10 { + -ms-flex: 0 0 83.333333%; + flex: 0 0 83.333333%; + max-width: 83.333333%; + } + .col-lg-11 { + -ms-flex: 0 0 91.666667%; + flex: 0 0 91.666667%; + max-width: 91.666667%; + } + .col-lg-12 { + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100%; + } + .order-lg-first { + -ms-flex-order: -1; + order: -1; + } + .order-lg-last { + -ms-flex-order: 13; + order: 13; + } + .order-lg-0 { + -ms-flex-order: 0; + order: 0; + } + .order-lg-1 { + -ms-flex-order: 1; + order: 1; + } + .order-lg-2 { + -ms-flex-order: 2; + order: 2; + } + .order-lg-3 { + -ms-flex-order: 3; + order: 3; + } + .order-lg-4 { + -ms-flex-order: 4; + order: 4; + } + .order-lg-5 { + -ms-flex-order: 5; + order: 5; + } + .order-lg-6 { + -ms-flex-order: 6; + order: 6; + } + .order-lg-7 { + -ms-flex-order: 7; + order: 7; + } + .order-lg-8 { + -ms-flex-order: 8; + order: 8; + } + .order-lg-9 { + -ms-flex-order: 9; + order: 9; + } + .order-lg-10 { + -ms-flex-order: 10; + order: 10; + } + .order-lg-11 { + -ms-flex-order: 11; + order: 11; + } + .order-lg-12 { + -ms-flex-order: 12; + order: 12; + } + .offset-lg-0 { + margin-left: 0; + } + .offset-lg-1 { + margin-left: 8.333333%; + } + .offset-lg-2 { + margin-left: 16.666667%; + } + .offset-lg-3 { + margin-left: 25%; + } + .offset-lg-4 { + margin-left: 33.333333%; + } + .offset-lg-5 { + margin-left: 41.666667%; + } + .offset-lg-6 { + margin-left: 50%; + } + .offset-lg-7 { + margin-left: 58.333333%; + } + .offset-lg-8 { + margin-left: 66.666667%; + } + .offset-lg-9 { + margin-left: 75%; + } + .offset-lg-10 { + margin-left: 83.333333%; + } + .offset-lg-11 { + margin-left: 91.666667%; + } +} + +@media (min-width: 1200px) { + .col-xl { + -ms-flex-preferred-size: 0; + flex-basis: 0; + -ms-flex-positive: 1; + flex-grow: 1; + min-width: 0; + max-width: 100%; + } + .row-cols-xl-1 > * { + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100%; + } + .row-cols-xl-2 > * { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50%; + } + .row-cols-xl-3 > * { + -ms-flex: 0 0 33.333333%; + flex: 0 0 33.333333%; + max-width: 33.333333%; + } + .row-cols-xl-4 > * { + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25%; + } + .row-cols-xl-5 > * { + -ms-flex: 0 0 20%; + flex: 0 0 20%; + max-width: 20%; + } + .row-cols-xl-6 > * { + -ms-flex: 0 0 16.666667%; + flex: 0 0 16.666667%; + max-width: 16.666667%; + } + .col-xl-auto { + -ms-flex: 0 0 auto; + flex: 0 0 auto; + width: auto; + max-width: 100%; + } + .col-xl-1 { + -ms-flex: 0 0 8.333333%; + flex: 0 0 8.333333%; + max-width: 8.333333%; + } + .col-xl-2 { + -ms-flex: 0 0 16.666667%; + flex: 0 0 16.666667%; + max-width: 16.666667%; + } + .col-xl-3 { + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25%; + } + .col-xl-4 { + -ms-flex: 0 0 33.333333%; + flex: 0 0 33.333333%; + max-width: 33.333333%; + } + .col-xl-5 { + -ms-flex: 0 0 41.666667%; + flex: 0 0 41.666667%; + max-width: 41.666667%; + } + .col-xl-6 { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50%; + } + .col-xl-7 { + -ms-flex: 0 0 58.333333%; + flex: 0 0 58.333333%; + max-width: 58.333333%; + } + .col-xl-8 { + -ms-flex: 0 0 66.666667%; + flex: 0 0 66.666667%; + max-width: 66.666667%; + } + .col-xl-9 { + -ms-flex: 0 0 75%; + flex: 0 0 75%; + max-width: 75%; + } + .col-xl-10 { + -ms-flex: 0 0 83.333333%; + flex: 0 0 83.333333%; + max-width: 83.333333%; + } + .col-xl-11 { + -ms-flex: 0 0 91.666667%; + flex: 0 0 91.666667%; + max-width: 91.666667%; + } + .col-xl-12 { + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100%; + } + .order-xl-first { + -ms-flex-order: -1; + order: -1; + } + .order-xl-last { + -ms-flex-order: 13; + order: 13; + } + .order-xl-0 { + -ms-flex-order: 0; + order: 0; + } + .order-xl-1 { + -ms-flex-order: 1; + order: 1; + } + .order-xl-2 { + -ms-flex-order: 2; + order: 2; + } + .order-xl-3 { + -ms-flex-order: 3; + order: 3; + } + .order-xl-4 { + -ms-flex-order: 4; + order: 4; + } + .order-xl-5 { + -ms-flex-order: 5; + order: 5; + } + .order-xl-6 { + -ms-flex-order: 6; + order: 6; + } + .order-xl-7 { + -ms-flex-order: 7; + order: 7; + } + .order-xl-8 { + -ms-flex-order: 8; + order: 8; + } + .order-xl-9 { + -ms-flex-order: 9; + order: 9; + } + .order-xl-10 { + -ms-flex-order: 10; + order: 10; + } + .order-xl-11 { + -ms-flex-order: 11; + order: 11; + } + .order-xl-12 { + -ms-flex-order: 12; + order: 12; + } + .offset-xl-0 { + margin-left: 0; + } + .offset-xl-1 { + margin-left: 8.333333%; + } + .offset-xl-2 { + margin-left: 16.666667%; + } + .offset-xl-3 { + margin-left: 25%; + } + .offset-xl-4 { + margin-left: 33.333333%; + } + .offset-xl-5 { + margin-left: 41.666667%; + } + .offset-xl-6 { + margin-left: 50%; + } + .offset-xl-7 { + margin-left: 58.333333%; + } + .offset-xl-8 { + margin-left: 66.666667%; + } + .offset-xl-9 { + margin-left: 75%; + } + .offset-xl-10 { + margin-left: 83.333333%; + } + .offset-xl-11 { + margin-left: 91.666667%; + } +} + +.table { + width: 100%; + margin-bottom: 1rem; + color: #212529; +} + +.table th, +.table td { + padding: 0.75rem; + vertical-align: top; + border-top: 1px solid #dee2e6; +} + +.table thead th { + vertical-align: bottom; + border-bottom: 2px solid #dee2e6; +} + +.table tbody + tbody { + border-top: 2px solid #dee2e6; +} + +.table-sm th, +.table-sm td { + padding: 0.3rem; +} + +.table-bordered { + border: 1px solid #dee2e6; +} + +.table-bordered th, +.table-bordered td { + border: 1px solid #dee2e6; +} + +.table-bordered thead th, +.table-bordered thead td { + border-bottom-width: 2px; +} + +.table-borderless th, +.table-borderless td, +.table-borderless thead th, +.table-borderless tbody + tbody { + border: 0; +} + +.table-striped tbody tr:nth-of-type(odd) { + background-color: rgba(0, 0, 0, 0.05); +} + +.table-hover tbody tr:hover { + color: #212529; + background-color: rgba(0, 0, 0, 0.075); +} + +.table-primary, +.table-primary > th, +.table-primary > td { + background-color: #b8daff; +} + +.table-primary th, +.table-primary td, +.table-primary thead th, +.table-primary tbody + tbody { + border-color: #7abaff; +} + +.table-hover .table-primary:hover { + background-color: #9fcdff; +} + +.table-hover .table-primary:hover > td, +.table-hover .table-primary:hover > th { + background-color: #9fcdff; +} + +.table-secondary, +.table-secondary > th, +.table-secondary > td { + background-color: #d6d8db; +} + +.table-secondary th, +.table-secondary td, +.table-secondary thead th, +.table-secondary tbody + tbody { + border-color: #b3b7bb; +} + +.table-hover .table-secondary:hover { + background-color: #c8cbcf; +} + +.table-hover .table-secondary:hover > td, +.table-hover .table-secondary:hover > th { + background-color: #c8cbcf; +} + +.table-success, +.table-success > th, +.table-success > td { + background-color: #c3e6cb; +} + +.table-success th, +.table-success td, +.table-success thead th, +.table-success tbody + tbody { + border-color: #8fd19e; +} + +.table-hover .table-success:hover { + background-color: #b1dfbb; +} + +.table-hover .table-success:hover > td, +.table-hover .table-success:hover > th { + background-color: #b1dfbb; +} + +.table-info, +.table-info > th, +.table-info > td { + background-color: #bee5eb; +} + +.table-info th, +.table-info td, +.table-info thead th, +.table-info tbody + tbody { + border-color: #86cfda; +} + +.table-hover .table-info:hover { + background-color: #abdde5; +} + +.table-hover .table-info:hover > td, +.table-hover .table-info:hover > th { + background-color: #abdde5; +} + +.table-warning, +.table-warning > th, +.table-warning > td { + background-color: #ffeeba; +} + +.table-warning th, +.table-warning td, +.table-warning thead th, +.table-warning tbody + tbody { + border-color: #ffdf7e; +} + +.table-hover .table-warning:hover { + background-color: #ffe8a1; +} + +.table-hover .table-warning:hover > td, +.table-hover .table-warning:hover > th { + background-color: #ffe8a1; +} + +.table-danger, +.table-danger > th, +.table-danger > td { + background-color: #f5c6cb; +} + +.table-danger th, +.table-danger td, +.table-danger thead th, +.table-danger tbody + tbody { + border-color: #ed969e; +} + +.table-hover .table-danger:hover { + background-color: #f1b0b7; +} + +.table-hover .table-danger:hover > td, +.table-hover .table-danger:hover > th { + background-color: #f1b0b7; +} + +.table-light, +.table-light > th, +.table-light > td { + background-color: #fdfdfe; +} + +.table-light th, +.table-light td, +.table-light thead th, +.table-light tbody + tbody { + border-color: #fbfcfc; +} + +.table-hover .table-light:hover { + background-color: #ececf6; +} + +.table-hover .table-light:hover > td, +.table-hover .table-light:hover > th { + background-color: #ececf6; +} + +.table-dark, +.table-dark > th, +.table-dark > td { + background-color: #c6c8ca; +} + +.table-dark th, +.table-dark td, +.table-dark thead th, +.table-dark tbody + tbody { + border-color: #95999c; +} + +.table-hover .table-dark:hover { + background-color: #b9bbbe; +} + +.table-hover .table-dark:hover > td, +.table-hover .table-dark:hover > th { + background-color: #b9bbbe; +} + +.table-active, +.table-active > th, +.table-active > td { + background-color: rgba(0, 0, 0, 0.075); +} + +.table-hover .table-active:hover { + background-color: rgba(0, 0, 0, 0.075); +} + +.table-hover .table-active:hover > td, +.table-hover .table-active:hover > th { + background-color: rgba(0, 0, 0, 0.075); +} + +.table .thead-dark th { + color: #fff; + background-color: #343a40; + border-color: #454d55; +} + +.table .thead-light th { + color: #495057; + background-color: #e9ecef; + border-color: #dee2e6; +} + +.table-dark { + color: #fff; + background-color: #343a40; +} + +.table-dark th, +.table-dark td, +.table-dark thead th { + border-color: #454d55; +} + +.table-dark.table-bordered { + border: 0; +} + +.table-dark.table-striped tbody tr:nth-of-type(odd) { + background-color: rgba(255, 255, 255, 0.05); +} + +.table-dark.table-hover tbody tr:hover { + color: #fff; + background-color: rgba(255, 255, 255, 0.075); +} + +@media (max-width: 575.98px) { + .table-responsive-sm { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + } + .table-responsive-sm > .table-bordered { + border: 0; + } +} + +@media (max-width: 767.98px) { + .table-responsive-md { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + } + .table-responsive-md > .table-bordered { + border: 0; + } +} + +@media (max-width: 991.98px) { + .table-responsive-lg { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + } + .table-responsive-lg > .table-bordered { + border: 0; + } +} + +@media (max-width: 1199.98px) { + .table-responsive-xl { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + } + .table-responsive-xl > .table-bordered { + border: 0; + } +} + +.table-responsive { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; +} + +.table-responsive > .table-bordered { + border: 0; +} + +.form-control { + display: block; + width: 100%; + height: calc(1.5em + 0.75rem + 2px); + padding: 0.375rem 0.75rem; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #495057; + background-color: #fff; + background-clip: padding-box; + border: 1px solid #ced4da; + border-radius: 0.25rem; + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +} + +@media (prefers-reduced-motion: reduce) { + .form-control { + transition: none; + } +} + +.form-control::-ms-expand { + background-color: transparent; + border: 0; +} + +.form-control:-moz-focusring { + color: transparent; + text-shadow: 0 0 0 #495057; +} + +.form-control:focus { + color: #495057; + background-color: #fff; + border-color: #80bdff; + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); +} + +.form-control::-webkit-input-placeholder { + color: #6c757d; + opacity: 1; +} + +.form-control::-moz-placeholder { + color: #6c757d; + opacity: 1; +} + +.form-control:-ms-input-placeholder { + color: #6c757d; + opacity: 1; +} + +.form-control::-ms-input-placeholder { + color: #6c757d; + opacity: 1; +} + +.form-control::placeholder { + color: #6c757d; + opacity: 1; +} + +.form-control:disabled, .form-control[readonly] { + background-color: #e9ecef; + opacity: 1; +} + +input[type="date"].form-control, +input[type="time"].form-control, +input[type="datetime-local"].form-control, +input[type="month"].form-control { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +select.form-control:focus::-ms-value { + color: #495057; + background-color: #fff; +} + +.form-control-file, +.form-control-range { + display: block; + width: 100%; +} + +.col-form-label { + padding-top: calc(0.375rem + 1px); + padding-bottom: calc(0.375rem + 1px); + margin-bottom: 0; + font-size: inherit; + line-height: 1.5; +} + +.col-form-label-lg { + padding-top: calc(0.5rem + 1px); + padding-bottom: calc(0.5rem + 1px); + font-size: 1.25rem; + line-height: 1.5; +} + +.col-form-label-sm { + padding-top: calc(0.25rem + 1px); + padding-bottom: calc(0.25rem + 1px); + font-size: 0.875rem; + line-height: 1.5; +} + +.form-control-plaintext { + display: block; + width: 100%; + padding: 0.375rem 0; + margin-bottom: 0; + font-size: 1rem; + line-height: 1.5; + color: #212529; + background-color: transparent; + border: solid transparent; + border-width: 1px 0; +} + +.form-control-plaintext.form-control-sm, .form-control-plaintext.form-control-lg { + padding-right: 0; + padding-left: 0; +} + +.form-control-sm { + height: calc(1.5em + 0.5rem + 2px); + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + line-height: 1.5; + border-radius: 0.2rem; +} + +.form-control-lg { + height: calc(1.5em + 1rem + 2px); + padding: 0.5rem 1rem; + font-size: 1.25rem; + line-height: 1.5; + border-radius: 0.3rem; +} + +select.form-control[size], select.form-control[multiple] { + height: auto; +} + +textarea.form-control { + height: auto; +} + +.form-group { + margin-bottom: 1rem; +} + +.form-text { + display: block; + margin-top: 0.25rem; +} + +.form-row { + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + margin-right: -5px; + margin-left: -5px; +} + +.form-row > .col, +.form-row > [class*="col-"] { + padding-right: 5px; + padding-left: 5px; +} + +.form-check { + position: relative; + display: block; + padding-left: 1.25rem; +} + +.form-check-input { + position: absolute; + margin-top: 0.3rem; + margin-left: -1.25rem; +} + +.form-check-input[disabled] ~ .form-check-label, +.form-check-input:disabled ~ .form-check-label { + color: #6c757d; +} + +.form-check-label { + margin-bottom: 0; +} + +.form-check-inline { + display: -ms-inline-flexbox; + display: inline-flex; + -ms-flex-align: center; + align-items: center; + padding-left: 0; + margin-right: 0.75rem; +} + +.form-check-inline .form-check-input { + position: static; + margin-top: 0; + margin-right: 0.3125rem; + margin-left: 0; +} + +.valid-feedback { + display: none; + width: 100%; + margin-top: 0.25rem; + font-size: 80%; + color: #28a745; +} + +.valid-tooltip { + position: absolute; + top: 100%; + z-index: 5; + display: none; + max-width: 100%; + padding: 0.25rem 0.5rem; + margin-top: .1rem; + font-size: 0.875rem; + line-height: 1.5; + color: #fff; + background-color: rgba(40, 167, 69, 0.9); + border-radius: 0.25rem; +} + +.was-validated :valid ~ .valid-feedback, +.was-validated :valid ~ .valid-tooltip, +.is-valid ~ .valid-feedback, +.is-valid ~ .valid-tooltip { + display: block; +} + +.was-validated .form-control:valid, .form-control.is-valid { + border-color: #28a745; + padding-right: calc(1.5em + 0.75rem); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); + background-repeat: no-repeat; + background-position: right calc(0.375em + 0.1875rem) center; + background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); +} + +.was-validated .form-control:valid:focus, .form-control.is-valid:focus { + border-color: #28a745; + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); +} + +.was-validated textarea.form-control:valid, textarea.form-control.is-valid { + padding-right: calc(1.5em + 0.75rem); + background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem); +} + +.was-validated .custom-select:valid, .custom-select.is-valid { + border-color: #28a745; + padding-right: calc(0.75em + 2.3125rem); + background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); +} + +.was-validated .custom-select:valid:focus, .custom-select.is-valid:focus { + border-color: #28a745; + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); +} + +.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label { + color: #28a745; +} + +.was-validated .form-check-input:valid ~ .valid-feedback, +.was-validated .form-check-input:valid ~ .valid-tooltip, .form-check-input.is-valid ~ .valid-feedback, +.form-check-input.is-valid ~ .valid-tooltip { + display: block; +} + +.was-validated .custom-control-input:valid ~ .custom-control-label, .custom-control-input.is-valid ~ .custom-control-label { + color: #28a745; +} + +.was-validated .custom-control-input:valid ~ .custom-control-label::before, .custom-control-input.is-valid ~ .custom-control-label::before { + border-color: #28a745; +} + +.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before, .custom-control-input.is-valid:checked ~ .custom-control-label::before { + border-color: #34ce57; + background-color: #34ce57; +} + +.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before, .custom-control-input.is-valid:focus ~ .custom-control-label::before { + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); +} + +.was-validated .custom-control-input:valid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-valid:focus:not(:checked) ~ .custom-control-label::before { + border-color: #28a745; +} + +.was-validated .custom-file-input:valid ~ .custom-file-label, .custom-file-input.is-valid ~ .custom-file-label { + border-color: #28a745; +} + +.was-validated .custom-file-input:valid:focus ~ .custom-file-label, .custom-file-input.is-valid:focus ~ .custom-file-label { + border-color: #28a745; + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); +} + +.invalid-feedback { + display: none; + width: 100%; + margin-top: 0.25rem; + font-size: 80%; + color: #dc3545; +} + +.invalid-tooltip { + position: absolute; + top: 100%; + z-index: 5; + display: none; + max-width: 100%; + padding: 0.25rem 0.5rem; + margin-top: .1rem; + font-size: 0.875rem; + line-height: 1.5; + color: #fff; + background-color: rgba(220, 53, 69, 0.9); + border-radius: 0.25rem; +} + +.was-validated :invalid ~ .invalid-feedback, +.was-validated :invalid ~ .invalid-tooltip, +.is-invalid ~ .invalid-feedback, +.is-invalid ~ .invalid-tooltip { + display: block; +} + +.was-validated .form-control:invalid, .form-control.is-invalid { + border-color: #dc3545; + padding-right: calc(1.5em + 0.75rem); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); + background-repeat: no-repeat; + background-position: right calc(0.375em + 0.1875rem) center; + background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); +} + +.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus { + border-color: #dc3545; + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); +} + +.was-validated textarea.form-control:invalid, textarea.form-control.is-invalid { + padding-right: calc(1.5em + 0.75rem); + background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem); +} + +.was-validated .custom-select:invalid, .custom-select.is-invalid { + border-color: #dc3545; + padding-right: calc(0.75em + 2.3125rem); + background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); +} + +.was-validated .custom-select:invalid:focus, .custom-select.is-invalid:focus { + border-color: #dc3545; + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); +} + +.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label { + color: #dc3545; +} + +.was-validated .form-check-input:invalid ~ .invalid-feedback, +.was-validated .form-check-input:invalid ~ .invalid-tooltip, .form-check-input.is-invalid ~ .invalid-feedback, +.form-check-input.is-invalid ~ .invalid-tooltip { + display: block; +} + +.was-validated .custom-control-input:invalid ~ .custom-control-label, .custom-control-input.is-invalid ~ .custom-control-label { + color: #dc3545; +} + +.was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before { + border-color: #dc3545; +} + +.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before { + border-color: #e4606d; + background-color: #e4606d; +} + +.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before, .custom-control-input.is-invalid:focus ~ .custom-control-label::before { + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); +} + +.was-validated .custom-control-input:invalid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-invalid:focus:not(:checked) ~ .custom-control-label::before { + border-color: #dc3545; +} + +.was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label { + border-color: #dc3545; +} + +.was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label { + border-color: #dc3545; + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); +} + +.form-inline { + display: -ms-flexbox; + display: flex; + -ms-flex-flow: row wrap; + flex-flow: row wrap; + -ms-flex-align: center; + align-items: center; +} + +.form-inline .form-check { + width: 100%; +} + +@media (min-width: 576px) { + .form-inline label { + display: -ms-flexbox; + display: flex; + -ms-flex-align: center; + align-items: center; + -ms-flex-pack: center; + justify-content: center; + margin-bottom: 0; + } + .form-inline .form-group { + display: -ms-flexbox; + display: flex; + -ms-flex: 0 0 auto; + flex: 0 0 auto; + -ms-flex-flow: row wrap; + flex-flow: row wrap; + -ms-flex-align: center; + align-items: center; + margin-bottom: 0; + } + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .form-inline .form-control-plaintext { + display: inline-block; + } + .form-inline .input-group, + .form-inline .custom-select { + width: auto; + } + .form-inline .form-check { + display: -ms-flexbox; + display: flex; + -ms-flex-align: center; + align-items: center; + -ms-flex-pack: center; + justify-content: center; + width: auto; + padding-left: 0; + } + .form-inline .form-check-input { + position: relative; + -ms-flex-negative: 0; + flex-shrink: 0; + margin-top: 0; + margin-right: 0.25rem; + margin-left: 0; + } + .form-inline .custom-control { + -ms-flex-align: center; + align-items: center; + -ms-flex-pack: center; + justify-content: center; + } + .form-inline .custom-control-label { + margin-bottom: 0; + } +} + +.btn { + display: inline-block; + font-weight: 400; + color: #212529; + text-align: center; + vertical-align: middle; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-color: transparent; + border: 1px solid transparent; + padding: 0.375rem 0.75rem; + font-size: 1rem; + line-height: 1.5; + border-radius: 0.25rem; + transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +} + +@media (prefers-reduced-motion: reduce) { + .btn { + transition: none; + } +} + +.btn:hover { + color: #212529; + text-decoration: none; +} + +.btn:focus, .btn.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); +} + +.btn.disabled, .btn:disabled { + opacity: 0.65; +} + +.btn:not(:disabled):not(.disabled) { + cursor: pointer; +} + +a.btn.disabled, +fieldset:disabled a.btn { + pointer-events: none; +} + +.btn-primary { + color: #fff; + background-color: #007bff; + border-color: #007bff; +} + +.btn-primary:hover { + color: #fff; + background-color: #0069d9; + border-color: #0062cc; +} + +.btn-primary:focus, .btn-primary.focus { + color: #fff; + background-color: #0069d9; + border-color: #0062cc; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} + +.btn-primary.disabled, .btn-primary:disabled { + color: #fff; + background-color: #007bff; + border-color: #007bff; +} + +.btn-primary:not(:disabled):not(.disabled):active, .btn-primary:not(:disabled):not(.disabled).active, +.show > .btn-primary.dropdown-toggle { + color: #fff; + background-color: #0062cc; + border-color: #005cbf; +} + +.btn-primary:not(:disabled):not(.disabled):active:focus, .btn-primary:not(:disabled):not(.disabled).active:focus, +.show > .btn-primary.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); +} + +.btn-secondary { + color: #fff; + background-color: #6c757d; + border-color: #6c757d; +} + +.btn-secondary:hover { + color: #fff; + background-color: #5a6268; + border-color: #545b62; +} + +.btn-secondary:focus, .btn-secondary.focus { + color: #fff; + background-color: #5a6268; + border-color: #545b62; + box-shadow: 0 0 0 0.2rem rgba(130, 138, 145, 0.5); +} + +.btn-secondary.disabled, .btn-secondary:disabled { + color: #fff; + background-color: #6c757d; + border-color: #6c757d; +} + +.btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active, +.show > .btn-secondary.dropdown-toggle { + color: #fff; + background-color: #545b62; + border-color: #4e555b; +} + +.btn-secondary:not(:disabled):not(.disabled):active:focus, .btn-secondary:not(:disabled):not(.disabled).active:focus, +.show > .btn-secondary.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(130, 138, 145, 0.5); +} + +.btn-success { + color: #fff; + background-color: #28a745; + border-color: #28a745; +} + +.btn-success:hover { + color: #fff; + background-color: #218838; + border-color: #1e7e34; +} + +.btn-success:focus, .btn-success.focus { + color: #fff; + background-color: #218838; + border-color: #1e7e34; + box-shadow: 0 0 0 0.2rem rgba(72, 180, 97, 0.5); +} + +.btn-success.disabled, .btn-success:disabled { + color: #fff; + background-color: #28a745; + border-color: #28a745; +} + +.btn-success:not(:disabled):not(.disabled):active, .btn-success:not(:disabled):not(.disabled).active, +.show > .btn-success.dropdown-toggle { + color: #fff; + background-color: #1e7e34; + border-color: #1c7430; +} + +.btn-success:not(:disabled):not(.disabled):active:focus, .btn-success:not(:disabled):not(.disabled).active:focus, +.show > .btn-success.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(72, 180, 97, 0.5); +} + +.btn-info { + color: #fff; + background-color: #17a2b8; + border-color: #17a2b8; +} + +.btn-info:hover { + color: #fff; + background-color: #138496; + border-color: #117a8b; +} + +.btn-info:focus, .btn-info.focus { + color: #fff; + background-color: #138496; + border-color: #117a8b; + box-shadow: 0 0 0 0.2rem rgba(58, 176, 195, 0.5); +} + +.btn-info.disabled, .btn-info:disabled { + color: #fff; + background-color: #17a2b8; + border-color: #17a2b8; +} + +.btn-info:not(:disabled):not(.disabled):active, .btn-info:not(:disabled):not(.disabled).active, +.show > .btn-info.dropdown-toggle { + color: #fff; + background-color: #117a8b; + border-color: #10707f; +} + +.btn-info:not(:disabled):not(.disabled):active:focus, .btn-info:not(:disabled):not(.disabled).active:focus, +.show > .btn-info.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(58, 176, 195, 0.5); +} + +.btn-warning { + color: #212529; + background-color: #ffc107; + border-color: #ffc107; +} + +.btn-warning:hover { + color: #212529; + background-color: #e0a800; + border-color: #d39e00; +} + +.btn-warning:focus, .btn-warning.focus { + color: #212529; + background-color: #e0a800; + border-color: #d39e00; + box-shadow: 0 0 0 0.2rem rgba(222, 170, 12, 0.5); +} + +.btn-warning.disabled, .btn-warning:disabled { + color: #212529; + background-color: #ffc107; + border-color: #ffc107; +} + +.btn-warning:not(:disabled):not(.disabled):active, .btn-warning:not(:disabled):not(.disabled).active, +.show > .btn-warning.dropdown-toggle { + color: #212529; + background-color: #d39e00; + border-color: #c69500; +} + +.btn-warning:not(:disabled):not(.disabled):active:focus, .btn-warning:not(:disabled):not(.disabled).active:focus, +.show > .btn-warning.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(222, 170, 12, 0.5); +} + +.btn-danger { + color: #fff; + background-color: #dc3545; + border-color: #dc3545; +} + +.btn-danger:hover { + color: #fff; + background-color: #c82333; + border-color: #bd2130; +} + +.btn-danger:focus, .btn-danger.focus { + color: #fff; + background-color: #c82333; + border-color: #bd2130; + box-shadow: 0 0 0 0.2rem rgba(225, 83, 97, 0.5); +} + +.btn-danger.disabled, .btn-danger:disabled { + color: #fff; + background-color: #dc3545; + border-color: #dc3545; +} + +.btn-danger:not(:disabled):not(.disabled):active, .btn-danger:not(:disabled):not(.disabled).active, +.show > .btn-danger.dropdown-toggle { + color: #fff; + background-color: #bd2130; + border-color: #b21f2d; +} + +.btn-danger:not(:disabled):not(.disabled):active:focus, .btn-danger:not(:disabled):not(.disabled).active:focus, +.show > .btn-danger.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(225, 83, 97, 0.5); +} + +.btn-light { + color: #212529; + background-color: #f8f9fa; + border-color: #f8f9fa; +} + +.btn-light:hover { + color: #212529; + background-color: #e2e6ea; + border-color: #dae0e5; +} + +.btn-light:focus, .btn-light.focus { + color: #212529; + background-color: #e2e6ea; + border-color: #dae0e5; + box-shadow: 0 0 0 0.2rem rgba(216, 217, 219, 0.5); +} + +.btn-light.disabled, .btn-light:disabled { + color: #212529; + background-color: #f8f9fa; + border-color: #f8f9fa; +} + +.btn-light:not(:disabled):not(.disabled):active, .btn-light:not(:disabled):not(.disabled).active, +.show > .btn-light.dropdown-toggle { + color: #212529; + background-color: #dae0e5; + border-color: #d3d9df; +} + +.btn-light:not(:disabled):not(.disabled):active:focus, .btn-light:not(:disabled):not(.disabled).active:focus, +.show > .btn-light.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(216, 217, 219, 0.5); +} + +.btn-dark { + color: #fff; + background-color: #343a40; + border-color: #343a40; +} + +.btn-dark:hover { + color: #fff; + background-color: #23272b; + border-color: #1d2124; +} + +.btn-dark:focus, .btn-dark.focus { + color: #fff; + background-color: #23272b; + border-color: #1d2124; + box-shadow: 0 0 0 0.2rem rgba(82, 88, 93, 0.5); +} + +.btn-dark.disabled, .btn-dark:disabled { + color: #fff; + background-color: #343a40; + border-color: #343a40; +} + +.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active, +.show > .btn-dark.dropdown-toggle { + color: #fff; + background-color: #1d2124; + border-color: #171a1d; +} + +.btn-dark:not(:disabled):not(.disabled):active:focus, .btn-dark:not(:disabled):not(.disabled).active:focus, +.show > .btn-dark.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(82, 88, 93, 0.5); +} + +.btn-outline-primary { + color: #007bff; + border-color: #007bff; +} + +.btn-outline-primary:hover { + color: #fff; + background-color: #007bff; + border-color: #007bff; +} + +.btn-outline-primary:focus, .btn-outline-primary.focus { + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); +} + +.btn-outline-primary.disabled, .btn-outline-primary:disabled { + color: #007bff; + background-color: transparent; +} + +.btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active, +.show > .btn-outline-primary.dropdown-toggle { + color: #fff; + background-color: #007bff; + border-color: #007bff; +} + +.btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-primary.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); +} + +.btn-outline-secondary { + color: #6c757d; + border-color: #6c757d; +} + +.btn-outline-secondary:hover { + color: #fff; + background-color: #6c757d; + border-color: #6c757d; +} + +.btn-outline-secondary:focus, .btn-outline-secondary.focus { + box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5); +} + +.btn-outline-secondary.disabled, .btn-outline-secondary:disabled { + color: #6c757d; + background-color: transparent; +} + +.btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active, +.show > .btn-outline-secondary.dropdown-toggle { + color: #fff; + background-color: #6c757d; + border-color: #6c757d; +} + +.btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-secondary.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5); +} + +.btn-outline-success { + color: #28a745; + border-color: #28a745; +} + +.btn-outline-success:hover { + color: #fff; + background-color: #28a745; + border-color: #28a745; +} + +.btn-outline-success:focus, .btn-outline-success.focus { + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); +} + +.btn-outline-success.disabled, .btn-outline-success:disabled { + color: #28a745; + background-color: transparent; +} + +.btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active, +.show > .btn-outline-success.dropdown-toggle { + color: #fff; + background-color: #28a745; + border-color: #28a745; +} + +.btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-success.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); +} + +.btn-outline-info { + color: #17a2b8; + border-color: #17a2b8; +} + +.btn-outline-info:hover { + color: #fff; + background-color: #17a2b8; + border-color: #17a2b8; +} + +.btn-outline-info:focus, .btn-outline-info.focus { + box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); +} + +.btn-outline-info.disabled, .btn-outline-info:disabled { + color: #17a2b8; + background-color: transparent; +} + +.btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active, +.show > .btn-outline-info.dropdown-toggle { + color: #fff; + background-color: #17a2b8; + border-color: #17a2b8; +} + +.btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-info.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); +} + +.btn-outline-warning { + color: #ffc107; + border-color: #ffc107; +} + +.btn-outline-warning:hover { + color: #212529; + background-color: #ffc107; + border-color: #ffc107; +} + +.btn-outline-warning:focus, .btn-outline-warning.focus { + box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5); +} + +.btn-outline-warning.disabled, .btn-outline-warning:disabled { + color: #ffc107; + background-color: transparent; +} + +.btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active, +.show > .btn-outline-warning.dropdown-toggle { + color: #212529; + background-color: #ffc107; + border-color: #ffc107; +} + +.btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-warning.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5); +} + +.btn-outline-danger { + color: #dc3545; + border-color: #dc3545; +} + +.btn-outline-danger:hover { + color: #fff; + background-color: #dc3545; + border-color: #dc3545; +} + +.btn-outline-danger:focus, .btn-outline-danger.focus { + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); +} + +.btn-outline-danger.disabled, .btn-outline-danger:disabled { + color: #dc3545; + background-color: transparent; +} + +.btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active, +.show > .btn-outline-danger.dropdown-toggle { + color: #fff; + background-color: #dc3545; + border-color: #dc3545; +} + +.btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-danger.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); +} + +.btn-outline-light { + color: #f8f9fa; + border-color: #f8f9fa; +} + +.btn-outline-light:hover { + color: #212529; + background-color: #f8f9fa; + border-color: #f8f9fa; +} + +.btn-outline-light:focus, .btn-outline-light.focus { + box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); +} + +.btn-outline-light.disabled, .btn-outline-light:disabled { + color: #f8f9fa; + background-color: transparent; +} + +.btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active, +.show > .btn-outline-light.dropdown-toggle { + color: #212529; + background-color: #f8f9fa; + border-color: #f8f9fa; +} + +.btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-light.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); +} + +.btn-outline-dark { + color: #343a40; + border-color: #343a40; +} + +.btn-outline-dark:hover { + color: #fff; + background-color: #343a40; + border-color: #343a40; +} + +.btn-outline-dark:focus, .btn-outline-dark.focus { + box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); +} + +.btn-outline-dark.disabled, .btn-outline-dark:disabled { + color: #343a40; + background-color: transparent; +} + +.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active, +.show > .btn-outline-dark.dropdown-toggle { + color: #fff; + background-color: #343a40; + border-color: #343a40; +} + +.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-dark.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); +} + +.btn-link { + font-weight: 400; + color: #007bff; + text-decoration: none; +} + +.btn-link:hover { + color: #0056b3; + text-decoration: underline; +} + +.btn-link:focus, .btn-link.focus { + text-decoration: underline; +} + +.btn-link:disabled, .btn-link.disabled { + color: #6c757d; + pointer-events: none; +} + +.btn-lg, .btn-group-lg > .btn { + padding: 0.5rem 1rem; + font-size: 1.25rem; + line-height: 1.5; + border-radius: 0.3rem; +} + +.btn-sm, .btn-group-sm > .btn { + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + line-height: 1.5; + border-radius: 0.2rem; +} + +.btn-block { + display: block; + width: 100%; +} + +.btn-block + .btn-block { + margin-top: 0.5rem; +} + +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} + +.fade { + transition: opacity 0.15s linear; +} + +@media (prefers-reduced-motion: reduce) { + .fade { + transition: none; + } +} + +.fade:not(.show) { + opacity: 0; +} + +.collapse:not(.show) { + display: none; +} + +.collapsing { + position: relative; + height: 0; + overflow: hidden; + transition: height 0.35s ease; +} + +@media (prefers-reduced-motion: reduce) { + .collapsing { + transition: none; + } +} + +.dropup, +.dropright, +.dropdown, +.dropleft { + position: relative; +} + +.dropdown-toggle { + white-space: nowrap; +} + +.dropdown-toggle::after { + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid; + border-right: 0.3em solid transparent; + border-bottom: 0; + border-left: 0.3em solid transparent; +} + +.dropdown-toggle:empty::after { + margin-left: 0; +} + +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 10rem; + padding: 0.5rem 0; + margin: 0.125rem 0 0; + font-size: 1rem; + color: #212529; + text-align: left; + list-style: none; + background-color: #fff; + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 0.25rem; +} + +.dropdown-menu-left { + right: auto; + left: 0; +} + +.dropdown-menu-right { + right: 0; + left: auto; +} + +@media (min-width: 576px) { + .dropdown-menu-sm-left { + right: auto; + left: 0; + } + .dropdown-menu-sm-right { + right: 0; + left: auto; + } +} + +@media (min-width: 768px) { + .dropdown-menu-md-left { + right: auto; + left: 0; + } + .dropdown-menu-md-right { + right: 0; + left: auto; + } +} + +@media (min-width: 992px) { + .dropdown-menu-lg-left { + right: auto; + left: 0; + } + .dropdown-menu-lg-right { + right: 0; + left: auto; + } +} + +@media (min-width: 1200px) { + .dropdown-menu-xl-left { + right: auto; + left: 0; + } + .dropdown-menu-xl-right { + right: 0; + left: auto; + } +} + +.dropup .dropdown-menu { + top: auto; + bottom: 100%; + margin-top: 0; + margin-bottom: 0.125rem; +} + +.dropup .dropdown-toggle::after { + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0; + border-right: 0.3em solid transparent; + border-bottom: 0.3em solid; + border-left: 0.3em solid transparent; +} + +.dropup .dropdown-toggle:empty::after { + margin-left: 0; +} + +.dropright .dropdown-menu { + top: 0; + right: auto; + left: 100%; + margin-top: 0; + margin-left: 0.125rem; +} + +.dropright .dropdown-toggle::after { + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid transparent; + border-right: 0; + border-bottom: 0.3em solid transparent; + border-left: 0.3em solid; +} + +.dropright .dropdown-toggle:empty::after { + margin-left: 0; +} + +.dropright .dropdown-toggle::after { + vertical-align: 0; +} + +.dropleft .dropdown-menu { + top: 0; + right: 100%; + left: auto; + margin-top: 0; + margin-right: 0.125rem; +} + +.dropleft .dropdown-toggle::after { + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; +} + +.dropleft .dropdown-toggle::after { + display: none; +} + +.dropleft .dropdown-toggle::before { + display: inline-block; + margin-right: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid transparent; + border-right: 0.3em solid; + border-bottom: 0.3em solid transparent; +} + +.dropleft .dropdown-toggle:empty::after { + margin-left: 0; +} + +.dropleft .dropdown-toggle::before { + vertical-align: 0; +} + +.dropdown-menu[x-placement^="top"], .dropdown-menu[x-placement^="right"], .dropdown-menu[x-placement^="bottom"], .dropdown-menu[x-placement^="left"] { + right: auto; + bottom: auto; +} + +.dropdown-divider { + height: 0; + margin: 0.5rem 0; + overflow: hidden; + border-top: 1px solid #e9ecef; +} + +.dropdown-item { + display: block; + width: 100%; + padding: 0.25rem 1.5rem; + clear: both; + font-weight: 400; + color: #212529; + text-align: inherit; + white-space: nowrap; + background-color: transparent; + border: 0; +} + +.dropdown-item:hover, .dropdown-item:focus { + color: #16181b; + text-decoration: none; + background-color: #f8f9fa; +} + +.dropdown-item.active, .dropdown-item:active { + color: #fff; + text-decoration: none; + background-color: #007bff; +} + +.dropdown-item.disabled, .dropdown-item:disabled { + color: #6c757d; + pointer-events: none; + background-color: transparent; +} + +.dropdown-menu.show { + display: block; +} + +.dropdown-header { + display: block; + padding: 0.5rem 1.5rem; + margin-bottom: 0; + font-size: 0.875rem; + color: #6c757d; + white-space: nowrap; +} + +.dropdown-item-text { + display: block; + padding: 0.25rem 1.5rem; + color: #212529; +} + +.btn-group, +.btn-group-vertical { + position: relative; + display: -ms-inline-flexbox; + display: inline-flex; + vertical-align: middle; +} + +.btn-group > .btn, +.btn-group-vertical > .btn { + position: relative; + -ms-flex: 1 1 auto; + flex: 1 1 auto; +} + +.btn-group > .btn:hover, +.btn-group-vertical > .btn:hover { + z-index: 1; +} + +.btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active, +.btn-group-vertical > .btn:focus, +.btn-group-vertical > .btn:active, +.btn-group-vertical > .btn.active { + z-index: 1; +} + +.btn-toolbar { + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -ms-flex-pack: start; + justify-content: flex-start; +} + +.btn-toolbar .input-group { + width: auto; +} + +.btn-group > .btn:not(:first-child), +.btn-group > .btn-group:not(:first-child) { + margin-left: -1px; +} + +.btn-group > .btn:not(:last-child):not(.dropdown-toggle), +.btn-group > .btn-group:not(:last-child) > .btn { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.btn-group > .btn:not(:first-child), +.btn-group > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.dropdown-toggle-split { + padding-right: 0.5625rem; + padding-left: 0.5625rem; +} + +.dropdown-toggle-split::after, +.dropup .dropdown-toggle-split::after, +.dropright .dropdown-toggle-split::after { + margin-left: 0; +} + +.dropleft .dropdown-toggle-split::before { + margin-right: 0; +} + +.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split { + padding-right: 0.375rem; + padding-left: 0.375rem; +} + +.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split { + padding-right: 0.75rem; + padding-left: 0.75rem; +} + +.btn-group-vertical { + -ms-flex-direction: column; + flex-direction: column; + -ms-flex-align: start; + align-items: flex-start; + -ms-flex-pack: center; + justify-content: center; +} + +.btn-group-vertical > .btn, +.btn-group-vertical > .btn-group { + width: 100%; +} + +.btn-group-vertical > .btn:not(:first-child), +.btn-group-vertical > .btn-group:not(:first-child) { + margin-top: -1px; +} + +.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle), +.btn-group-vertical > .btn-group:not(:last-child) > .btn { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} + +.btn-group-vertical > .btn:not(:first-child), +.btn-group-vertical > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.btn-group-toggle > .btn, +.btn-group-toggle > .btn-group > .btn { + margin-bottom: 0; +} + +.btn-group-toggle > .btn input[type="radio"], +.btn-group-toggle > .btn input[type="checkbox"], +.btn-group-toggle > .btn-group > .btn input[type="radio"], +.btn-group-toggle > .btn-group > .btn input[type="checkbox"] { + position: absolute; + clip: rect(0, 0, 0, 0); + pointer-events: none; +} + +.input-group { + position: relative; + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -ms-flex-align: stretch; + align-items: stretch; + width: 100%; +} + +.input-group > .form-control, +.input-group > .form-control-plaintext, +.input-group > .custom-select, +.input-group > .custom-file { + position: relative; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + width: 1%; + min-width: 0; + margin-bottom: 0; +} + +.input-group > .form-control + .form-control, +.input-group > .form-control + .custom-select, +.input-group > .form-control + .custom-file, +.input-group > .form-control-plaintext + .form-control, +.input-group > .form-control-plaintext + .custom-select, +.input-group > .form-control-plaintext + .custom-file, +.input-group > .custom-select + .form-control, +.input-group > .custom-select + .custom-select, +.input-group > .custom-select + .custom-file, +.input-group > .custom-file + .form-control, +.input-group > .custom-file + .custom-select, +.input-group > .custom-file + .custom-file { + margin-left: -1px; +} + +.input-group > .form-control:focus, +.input-group > .custom-select:focus, +.input-group > .custom-file .custom-file-input:focus ~ .custom-file-label { + z-index: 3; +} + +.input-group > .custom-file .custom-file-input:focus { + z-index: 4; +} + +.input-group > .form-control:not(:last-child), +.input-group > .custom-select:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.input-group > .form-control:not(:first-child), +.input-group > .custom-select:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.input-group > .custom-file { + display: -ms-flexbox; + display: flex; + -ms-flex-align: center; + align-items: center; +} + +.input-group > .custom-file:not(:last-child) .custom-file-label, +.input-group > .custom-file:not(:last-child) .custom-file-label::after { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.input-group > .custom-file:not(:first-child) .custom-file-label { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.input-group-prepend, +.input-group-append { + display: -ms-flexbox; + display: flex; +} + +.input-group-prepend .btn, +.input-group-append .btn { + position: relative; + z-index: 2; +} + +.input-group-prepend .btn:focus, +.input-group-append .btn:focus { + z-index: 3; +} + +.input-group-prepend .btn + .btn, +.input-group-prepend .btn + .input-group-text, +.input-group-prepend .input-group-text + .input-group-text, +.input-group-prepend .input-group-text + .btn, +.input-group-append .btn + .btn, +.input-group-append .btn + .input-group-text, +.input-group-append .input-group-text + .input-group-text, +.input-group-append .input-group-text + .btn { + margin-left: -1px; +} + +.input-group-prepend { + margin-right: -1px; +} + +.input-group-append { + margin-left: -1px; +} + +.input-group-text { + display: -ms-flexbox; + display: flex; + -ms-flex-align: center; + align-items: center; + padding: 0.375rem 0.75rem; + margin-bottom: 0; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #495057; + text-align: center; + white-space: nowrap; + background-color: #e9ecef; + border: 1px solid #ced4da; + border-radius: 0.25rem; +} + +.input-group-text input[type="radio"], +.input-group-text input[type="checkbox"] { + margin-top: 0; +} + +.input-group-lg > .form-control:not(textarea), +.input-group-lg > .custom-select { + height: calc(1.5em + 1rem + 2px); +} + +.input-group-lg > .form-control, +.input-group-lg > .custom-select, +.input-group-lg > .input-group-prepend > .input-group-text, +.input-group-lg > .input-group-append > .input-group-text, +.input-group-lg > .input-group-prepend > .btn, +.input-group-lg > .input-group-append > .btn { + padding: 0.5rem 1rem; + font-size: 1.25rem; + line-height: 1.5; + border-radius: 0.3rem; +} + +.input-group-sm > .form-control:not(textarea), +.input-group-sm > .custom-select { + height: calc(1.5em + 0.5rem + 2px); +} + +.input-group-sm > .form-control, +.input-group-sm > .custom-select, +.input-group-sm > .input-group-prepend > .input-group-text, +.input-group-sm > .input-group-append > .input-group-text, +.input-group-sm > .input-group-prepend > .btn, +.input-group-sm > .input-group-append > .btn { + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + line-height: 1.5; + border-radius: 0.2rem; +} + +.input-group-lg > .custom-select, +.input-group-sm > .custom-select { + padding-right: 1.75rem; +} + +.input-group > .input-group-prepend > .btn, +.input-group > .input-group-prepend > .input-group-text, +.input-group > .input-group-append:not(:last-child) > .btn, +.input-group > .input-group-append:not(:last-child) > .input-group-text, +.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.input-group > .input-group-append > .btn, +.input-group > .input-group-append > .input-group-text, +.input-group > .input-group-prepend:not(:first-child) > .btn, +.input-group > .input-group-prepend:not(:first-child) > .input-group-text, +.input-group > .input-group-prepend:first-child > .btn:not(:first-child), +.input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.custom-control { + position: relative; + display: block; + min-height: 1.5rem; + padding-left: 1.5rem; +} + +.custom-control-inline { + display: -ms-inline-flexbox; + display: inline-flex; + margin-right: 1rem; +} + +.custom-control-input { + position: absolute; + left: 0; + z-index: -1; + width: 1rem; + height: 1.25rem; + opacity: 0; +} + +.custom-control-input:checked ~ .custom-control-label::before { + color: #fff; + border-color: #007bff; + background-color: #007bff; +} + +.custom-control-input:focus ~ .custom-control-label::before { + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); +} + +.custom-control-input:focus:not(:checked) ~ .custom-control-label::before { + border-color: #80bdff; +} + +.custom-control-input:not(:disabled):active ~ .custom-control-label::before { + color: #fff; + background-color: #b3d7ff; + border-color: #b3d7ff; +} + +.custom-control-input[disabled] ~ .custom-control-label, .custom-control-input:disabled ~ .custom-control-label { + color: #6c757d; +} + +.custom-control-input[disabled] ~ .custom-control-label::before, .custom-control-input:disabled ~ .custom-control-label::before { + background-color: #e9ecef; +} + +.custom-control-label { + position: relative; + margin-bottom: 0; + vertical-align: top; +} + +.custom-control-label::before { + position: absolute; + top: 0.25rem; + left: -1.5rem; + display: block; + width: 1rem; + height: 1rem; + pointer-events: none; + content: ""; + background-color: #fff; + border: #adb5bd solid 1px; +} + +.custom-control-label::after { + position: absolute; + top: 0.25rem; + left: -1.5rem; + display: block; + width: 1rem; + height: 1rem; + content: ""; + background: no-repeat 50% / 50% 50%; +} + +.custom-checkbox .custom-control-label::before { + border-radius: 0.25rem; +} + +.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e"); +} + +.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before { + border-color: #007bff; + background-color: #007bff; +} + +.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e"); +} + +.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before { + background-color: rgba(0, 123, 255, 0.5); +} + +.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before { + background-color: rgba(0, 123, 255, 0.5); +} + +.custom-radio .custom-control-label::before { + border-radius: 50%; +} + +.custom-radio .custom-control-input:checked ~ .custom-control-label::after { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e"); +} + +.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before { + background-color: rgba(0, 123, 255, 0.5); +} + +.custom-switch { + padding-left: 2.25rem; +} + +.custom-switch .custom-control-label::before { + left: -2.25rem; + width: 1.75rem; + pointer-events: all; + border-radius: 0.5rem; +} + +.custom-switch .custom-control-label::after { + top: calc(0.25rem + 2px); + left: calc(-2.25rem + 2px); + width: calc(1rem - 4px); + height: calc(1rem - 4px); + background-color: #adb5bd; + border-radius: 0.5rem; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out; + transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out; +} + +@media (prefers-reduced-motion: reduce) { + .custom-switch .custom-control-label::after { + transition: none; + } +} + +.custom-switch .custom-control-input:checked ~ .custom-control-label::after { + background-color: #fff; + -webkit-transform: translateX(0.75rem); + transform: translateX(0.75rem); +} + +.custom-switch .custom-control-input:disabled:checked ~ .custom-control-label::before { + background-color: rgba(0, 123, 255, 0.5); +} + +.custom-select { + display: inline-block; + width: 100%; + height: calc(1.5em + 0.75rem + 2px); + padding: 0.375rem 1.75rem 0.375rem 0.75rem; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #495057; + vertical-align: middle; + background: #fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px; + border: 1px solid #ced4da; + border-radius: 0.25rem; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +.custom-select:focus { + border-color: #80bdff; + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); +} + +.custom-select:focus::-ms-value { + color: #495057; + background-color: #fff; +} + +.custom-select[multiple], .custom-select[size]:not([size="1"]) { + height: auto; + padding-right: 0.75rem; + background-image: none; +} + +.custom-select:disabled { + color: #6c757d; + background-color: #e9ecef; +} + +.custom-select::-ms-expand { + display: none; +} + +.custom-select:-moz-focusring { + color: transparent; + text-shadow: 0 0 0 #495057; +} + +.custom-select-sm { + height: calc(1.5em + 0.5rem + 2px); + padding-top: 0.25rem; + padding-bottom: 0.25rem; + padding-left: 0.5rem; + font-size: 0.875rem; +} + +.custom-select-lg { + height: calc(1.5em + 1rem + 2px); + padding-top: 0.5rem; + padding-bottom: 0.5rem; + padding-left: 1rem; + font-size: 1.25rem; +} + +.custom-file { + position: relative; + display: inline-block; + width: 100%; + height: calc(1.5em + 0.75rem + 2px); + margin-bottom: 0; +} + +.custom-file-input { + position: relative; + z-index: 2; + width: 100%; + height: calc(1.5em + 0.75rem + 2px); + margin: 0; + opacity: 0; +} + +.custom-file-input:focus ~ .custom-file-label { + border-color: #80bdff; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); +} + +.custom-file-input[disabled] ~ .custom-file-label, +.custom-file-input:disabled ~ .custom-file-label { + background-color: #e9ecef; +} + +.custom-file-input:lang(en) ~ .custom-file-label::after { + content: "Browse"; +} + +.custom-file-input ~ .custom-file-label[data-browse]::after { + content: attr(data-browse); +} + +.custom-file-label { + position: absolute; + top: 0; + right: 0; + left: 0; + z-index: 1; + height: calc(1.5em + 0.75rem + 2px); + padding: 0.375rem 0.75rem; + font-weight: 400; + line-height: 1.5; + color: #495057; + background-color: #fff; + border: 1px solid #ced4da; + border-radius: 0.25rem; +} + +.custom-file-label::after { + position: absolute; + top: 0; + right: 0; + bottom: 0; + z-index: 3; + display: block; + height: calc(1.5em + 0.75rem); + padding: 0.375rem 0.75rem; + line-height: 1.5; + color: #495057; + content: "Browse"; + background-color: #e9ecef; + border-left: inherit; + border-radius: 0 0.25rem 0.25rem 0; +} + +.custom-range { + width: 100%; + height: 1.4rem; + padding: 0; + background-color: transparent; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +.custom-range:focus { + outline: none; +} + +.custom-range:focus::-webkit-slider-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25); +} + +.custom-range:focus::-moz-range-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25); +} + +.custom-range:focus::-ms-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25); +} + +.custom-range::-moz-focus-outer { + border: 0; +} + +.custom-range::-webkit-slider-thumb { + width: 1rem; + height: 1rem; + margin-top: -0.25rem; + background-color: #007bff; + border: 0; + border-radius: 1rem; + -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + -webkit-appearance: none; + appearance: none; +} + +@media (prefers-reduced-motion: reduce) { + .custom-range::-webkit-slider-thumb { + -webkit-transition: none; + transition: none; + } +} + +.custom-range::-webkit-slider-thumb:active { + background-color: #b3d7ff; +} + +.custom-range::-webkit-slider-runnable-track { + width: 100%; + height: 0.5rem; + color: transparent; + cursor: pointer; + background-color: #dee2e6; + border-color: transparent; + border-radius: 1rem; +} + +.custom-range::-moz-range-thumb { + width: 1rem; + height: 1rem; + background-color: #007bff; + border: 0; + border-radius: 1rem; + -moz-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + -moz-appearance: none; + appearance: none; +} + +@media (prefers-reduced-motion: reduce) { + .custom-range::-moz-range-thumb { + -moz-transition: none; + transition: none; + } +} + +.custom-range::-moz-range-thumb:active { + background-color: #b3d7ff; +} + +.custom-range::-moz-range-track { + width: 100%; + height: 0.5rem; + color: transparent; + cursor: pointer; + background-color: #dee2e6; + border-color: transparent; + border-radius: 1rem; +} + +.custom-range::-ms-thumb { + width: 1rem; + height: 1rem; + margin-top: 0; + margin-right: 0.2rem; + margin-left: 0.2rem; + background-color: #007bff; + border: 0; + border-radius: 1rem; + -ms-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + appearance: none; +} + +@media (prefers-reduced-motion: reduce) { + .custom-range::-ms-thumb { + -ms-transition: none; + transition: none; + } +} + +.custom-range::-ms-thumb:active { + background-color: #b3d7ff; +} + +.custom-range::-ms-track { + width: 100%; + height: 0.5rem; + color: transparent; + cursor: pointer; + background-color: transparent; + border-color: transparent; + border-width: 0.5rem; +} + +.custom-range::-ms-fill-lower { + background-color: #dee2e6; + border-radius: 1rem; +} + +.custom-range::-ms-fill-upper { + margin-right: 15px; + background-color: #dee2e6; + border-radius: 1rem; +} + +.custom-range:disabled::-webkit-slider-thumb { + background-color: #adb5bd; +} + +.custom-range:disabled::-webkit-slider-runnable-track { + cursor: default; +} + +.custom-range:disabled::-moz-range-thumb { + background-color: #adb5bd; +} + +.custom-range:disabled::-moz-range-track { + cursor: default; +} + +.custom-range:disabled::-ms-thumb { + background-color: #adb5bd; +} + +.custom-control-label::before, +.custom-file-label, +.custom-select { + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +} + +@media (prefers-reduced-motion: reduce) { + .custom-control-label::before, + .custom-file-label, + .custom-select { + transition: none; + } +} + +.nav { + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + padding-left: 0; + margin-bottom: 0; + list-style: none; +} + +.nav-link { + display: block; + padding: 0.5rem 1rem; +} + +.nav-link:hover, .nav-link:focus { + text-decoration: none; +} + +.nav-link.disabled { + color: #6c757d; + pointer-events: none; + cursor: default; +} + +.nav-tabs { + border-bottom: 1px solid #dee2e6; +} + +.nav-tabs .nav-item { + margin-bottom: -1px; +} + +.nav-tabs .nav-link { + border: 1px solid transparent; + border-top-left-radius: 0.25rem; + border-top-right-radius: 0.25rem; +} + +.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus { + border-color: #e9ecef #e9ecef #dee2e6; +} + +.nav-tabs .nav-link.disabled { + color: #6c757d; + background-color: transparent; + border-color: transparent; +} + +.nav-tabs .nav-link.active, +.nav-tabs .nav-item.show .nav-link { + color: #495057; + background-color: #fff; + border-color: #dee2e6 #dee2e6 #fff; +} + +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.nav-pills .nav-link { + border-radius: 0.25rem; +} + +.nav-pills .nav-link.active, +.nav-pills .show > .nav-link { + color: #fff; + background-color: #007bff; +} + +.nav-fill .nav-item { + -ms-flex: 1 1 auto; + flex: 1 1 auto; + text-align: center; +} + +.nav-justified .nav-item { + -ms-flex-preferred-size: 0; + flex-basis: 0; + -ms-flex-positive: 1; + flex-grow: 1; + text-align: center; +} + +.tab-content > .tab-pane { + display: none; +} + +.tab-content > .active { + display: block; +} + +.navbar { + position: relative; + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -ms-flex-align: center; + align-items: center; + -ms-flex-pack: justify; + justify-content: space-between; + padding: 0.5rem 1rem; +} + +.navbar .container, +.navbar .container-fluid, .navbar .container-sm, .navbar .container-md, .navbar .container-lg, .navbar .container-xl { + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -ms-flex-align: center; + align-items: center; + -ms-flex-pack: justify; + justify-content: space-between; +} + +.navbar-brand { + display: inline-block; + padding-top: 0.3125rem; + padding-bottom: 0.3125rem; + margin-right: 1rem; + font-size: 1.25rem; + line-height: inherit; + white-space: nowrap; +} + +.navbar-brand:hover, .navbar-brand:focus { + text-decoration: none; +} + +.navbar-nav { + display: -ms-flexbox; + display: flex; + -ms-flex-direction: column; + flex-direction: column; + padding-left: 0; + margin-bottom: 0; + list-style: none; +} + +.navbar-nav .nav-link { + padding-right: 0; + padding-left: 0; +} + +.navbar-nav .dropdown-menu { + position: static; + float: none; +} + +.navbar-text { + display: inline-block; + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +.navbar-collapse { + -ms-flex-preferred-size: 100%; + flex-basis: 100%; + -ms-flex-positive: 1; + flex-grow: 1; + -ms-flex-align: center; + align-items: center; +} + +.navbar-toggler { + padding: 0.25rem 0.75rem; + font-size: 1.25rem; + line-height: 1; + background-color: transparent; + border: 1px solid transparent; + border-radius: 0.25rem; +} + +.navbar-toggler:hover, .navbar-toggler:focus { + text-decoration: none; +} + +.navbar-toggler-icon { + display: inline-block; + width: 1.5em; + height: 1.5em; + vertical-align: middle; + content: ""; + background: no-repeat center center; + background-size: 100% 100%; +} + +@media (max-width: 575.98px) { + .navbar-expand-sm > .container, + .navbar-expand-sm > .container-fluid, .navbar-expand-sm > .container-sm, .navbar-expand-sm > .container-md, .navbar-expand-sm > .container-lg, .navbar-expand-sm > .container-xl { + padding-right: 0; + padding-left: 0; + } +} + +@media (min-width: 576px) { + .navbar-expand-sm { + -ms-flex-flow: row nowrap; + flex-flow: row nowrap; + -ms-flex-pack: start; + justify-content: flex-start; + } + .navbar-expand-sm .navbar-nav { + -ms-flex-direction: row; + flex-direction: row; + } + .navbar-expand-sm .navbar-nav .dropdown-menu { + position: absolute; + } + .navbar-expand-sm .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; + } + .navbar-expand-sm > .container, + .navbar-expand-sm > .container-fluid, .navbar-expand-sm > .container-sm, .navbar-expand-sm > .container-md, .navbar-expand-sm > .container-lg, .navbar-expand-sm > .container-xl { + -ms-flex-wrap: nowrap; + flex-wrap: nowrap; + } + .navbar-expand-sm .navbar-collapse { + display: -ms-flexbox !important; + display: flex !important; + -ms-flex-preferred-size: auto; + flex-basis: auto; + } + .navbar-expand-sm .navbar-toggler { + display: none; + } +} + +@media (max-width: 767.98px) { + .navbar-expand-md > .container, + .navbar-expand-md > .container-fluid, .navbar-expand-md > .container-sm, .navbar-expand-md > .container-md, .navbar-expand-md > .container-lg, .navbar-expand-md > .container-xl { + padding-right: 0; + padding-left: 0; + } +} + +@media (min-width: 768px) { + .navbar-expand-md { + -ms-flex-flow: row nowrap; + flex-flow: row nowrap; + -ms-flex-pack: start; + justify-content: flex-start; + } + .navbar-expand-md .navbar-nav { + -ms-flex-direction: row; + flex-direction: row; + } + .navbar-expand-md .navbar-nav .dropdown-menu { + position: absolute; + } + .navbar-expand-md .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; + } + .navbar-expand-md > .container, + .navbar-expand-md > .container-fluid, .navbar-expand-md > .container-sm, .navbar-expand-md > .container-md, .navbar-expand-md > .container-lg, .navbar-expand-md > .container-xl { + -ms-flex-wrap: nowrap; + flex-wrap: nowrap; + } + .navbar-expand-md .navbar-collapse { + display: -ms-flexbox !important; + display: flex !important; + -ms-flex-preferred-size: auto; + flex-basis: auto; + } + .navbar-expand-md .navbar-toggler { + display: none; + } +} + +@media (max-width: 991.98px) { + .navbar-expand-lg > .container, + .navbar-expand-lg > .container-fluid, .navbar-expand-lg > .container-sm, .navbar-expand-lg > .container-md, .navbar-expand-lg > .container-lg, .navbar-expand-lg > .container-xl { + padding-right: 0; + padding-left: 0; + } +} + +@media (min-width: 992px) { + .navbar-expand-lg { + -ms-flex-flow: row nowrap; + flex-flow: row nowrap; + -ms-flex-pack: start; + justify-content: flex-start; + } + .navbar-expand-lg .navbar-nav { + -ms-flex-direction: row; + flex-direction: row; + } + .navbar-expand-lg .navbar-nav .dropdown-menu { + position: absolute; + } + .navbar-expand-lg .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; + } + .navbar-expand-lg > .container, + .navbar-expand-lg > .container-fluid, .navbar-expand-lg > .container-sm, .navbar-expand-lg > .container-md, .navbar-expand-lg > .container-lg, .navbar-expand-lg > .container-xl { + -ms-flex-wrap: nowrap; + flex-wrap: nowrap; + } + .navbar-expand-lg .navbar-collapse { + display: -ms-flexbox !important; + display: flex !important; + -ms-flex-preferred-size: auto; + flex-basis: auto; + } + .navbar-expand-lg .navbar-toggler { + display: none; + } +} + +@media (max-width: 1199.98px) { + .navbar-expand-xl > .container, + .navbar-expand-xl > .container-fluid, .navbar-expand-xl > .container-sm, .navbar-expand-xl > .container-md, .navbar-expand-xl > .container-lg, .navbar-expand-xl > .container-xl { + padding-right: 0; + padding-left: 0; + } +} + +@media (min-width: 1200px) { + .navbar-expand-xl { + -ms-flex-flow: row nowrap; + flex-flow: row nowrap; + -ms-flex-pack: start; + justify-content: flex-start; + } + .navbar-expand-xl .navbar-nav { + -ms-flex-direction: row; + flex-direction: row; + } + .navbar-expand-xl .navbar-nav .dropdown-menu { + position: absolute; + } + .navbar-expand-xl .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; + } + .navbar-expand-xl > .container, + .navbar-expand-xl > .container-fluid, .navbar-expand-xl > .container-sm, .navbar-expand-xl > .container-md, .navbar-expand-xl > .container-lg, .navbar-expand-xl > .container-xl { + -ms-flex-wrap: nowrap; + flex-wrap: nowrap; + } + .navbar-expand-xl .navbar-collapse { + display: -ms-flexbox !important; + display: flex !important; + -ms-flex-preferred-size: auto; + flex-basis: auto; + } + .navbar-expand-xl .navbar-toggler { + display: none; + } +} + +.navbar-expand { + -ms-flex-flow: row nowrap; + flex-flow: row nowrap; + -ms-flex-pack: start; + justify-content: flex-start; +} + +.navbar-expand > .container, +.navbar-expand > .container-fluid, .navbar-expand > .container-sm, .navbar-expand > .container-md, .navbar-expand > .container-lg, .navbar-expand > .container-xl { + padding-right: 0; + padding-left: 0; +} + +.navbar-expand .navbar-nav { + -ms-flex-direction: row; + flex-direction: row; +} + +.navbar-expand .navbar-nav .dropdown-menu { + position: absolute; +} + +.navbar-expand .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; +} + +.navbar-expand > .container, +.navbar-expand > .container-fluid, .navbar-expand > .container-sm, .navbar-expand > .container-md, .navbar-expand > .container-lg, .navbar-expand > .container-xl { + -ms-flex-wrap: nowrap; + flex-wrap: nowrap; +} + +.navbar-expand .navbar-collapse { + display: -ms-flexbox !important; + display: flex !important; + -ms-flex-preferred-size: auto; + flex-basis: auto; +} + +.navbar-expand .navbar-toggler { + display: none; +} + +.navbar-light .navbar-brand { + color: rgba(0, 0, 0, 0.9); +} + +.navbar-light .navbar-brand:hover, .navbar-light .navbar-brand:focus { + color: rgba(0, 0, 0, 0.9); +} + +.navbar-light .navbar-nav .nav-link { + color: rgba(0, 0, 0, 0.5); +} + +.navbar-light .navbar-nav .nav-link:hover, .navbar-light .navbar-nav .nav-link:focus { + color: rgba(0, 0, 0, 0.7); +} + +.navbar-light .navbar-nav .nav-link.disabled { + color: rgba(0, 0, 0, 0.3); +} + +.navbar-light .navbar-nav .show > .nav-link, +.navbar-light .navbar-nav .active > .nav-link, +.navbar-light .navbar-nav .nav-link.show, +.navbar-light .navbar-nav .nav-link.active { + color: rgba(0, 0, 0, 0.9); +} + +.navbar-light .navbar-toggler { + color: rgba(0, 0, 0, 0.5); + border-color: rgba(0, 0, 0, 0.1); +} + +.navbar-light .navbar-toggler-icon { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); +} + +.navbar-light .navbar-text { + color: rgba(0, 0, 0, 0.5); +} + +.navbar-light .navbar-text a { + color: rgba(0, 0, 0, 0.9); +} + +.navbar-light .navbar-text a:hover, .navbar-light .navbar-text a:focus { + color: rgba(0, 0, 0, 0.9); +} + +.navbar-dark .navbar-brand { + color: #fff; +} + +.navbar-dark .navbar-brand:hover, .navbar-dark .navbar-brand:focus { + color: #fff; +} + +.navbar-dark .navbar-nav .nav-link { + color: rgba(255, 255, 255, 0.5); +} + +.navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus { + color: rgba(255, 255, 255, 0.75); +} + +.navbar-dark .navbar-nav .nav-link.disabled { + color: rgba(255, 255, 255, 0.25); +} + +.navbar-dark .navbar-nav .show > .nav-link, +.navbar-dark .navbar-nav .active > .nav-link, +.navbar-dark .navbar-nav .nav-link.show, +.navbar-dark .navbar-nav .nav-link.active { + color: #fff; +} + +.navbar-dark .navbar-toggler { + color: rgba(255, 255, 255, 0.5); + border-color: rgba(255, 255, 255, 0.1); +} + +.navbar-dark .navbar-toggler-icon { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); +} + +.navbar-dark .navbar-text { + color: rgba(255, 255, 255, 0.5); +} + +.navbar-dark .navbar-text a { + color: #fff; +} + +.navbar-dark .navbar-text a:hover, .navbar-dark .navbar-text a:focus { + color: #fff; +} + +.card { + position: relative; + display: -ms-flexbox; + display: flex; + -ms-flex-direction: column; + flex-direction: column; + min-width: 0; + word-wrap: break-word; + background-color: #fff; + background-clip: border-box; + border: 1px solid rgba(0, 0, 0, 0.125); + border-radius: 0.25rem; +} + +.card > hr { + margin-right: 0; + margin-left: 0; +} + +.card > .list-group { + border-top: inherit; + border-bottom: inherit; +} + +.card > .list-group:first-child { + border-top-width: 0; + border-top-left-radius: calc(0.25rem - 1px); + border-top-right-radius: calc(0.25rem - 1px); +} + +.card > .list-group:last-child { + border-bottom-width: 0; + border-bottom-right-radius: calc(0.25rem - 1px); + border-bottom-left-radius: calc(0.25rem - 1px); +} + +.card-body { + -ms-flex: 1 1 auto; + flex: 1 1 auto; + min-height: 1px; + padding: 1.25rem; +} + +.card-title { + margin-bottom: 0.75rem; +} + +.card-subtitle { + margin-top: -0.375rem; + margin-bottom: 0; +} + +.card-text:last-child { + margin-bottom: 0; +} + +.card-link:hover { + text-decoration: none; +} + +.card-link + .card-link { + margin-left: 1.25rem; +} + +.card-header { + padding: 0.75rem 1.25rem; + margin-bottom: 0; + background-color: rgba(0, 0, 0, 0.03); + border-bottom: 1px solid rgba(0, 0, 0, 0.125); +} + +.card-header:first-child { + border-radius: calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0; +} + +.card-header + .list-group .list-group-item:first-child { + border-top: 0; +} + +.card-footer { + padding: 0.75rem 1.25rem; + background-color: rgba(0, 0, 0, 0.03); + border-top: 1px solid rgba(0, 0, 0, 0.125); +} + +.card-footer:last-child { + border-radius: 0 0 calc(0.25rem - 1px) calc(0.25rem - 1px); +} + +.card-header-tabs { + margin-right: -0.625rem; + margin-bottom: -0.75rem; + margin-left: -0.625rem; + border-bottom: 0; +} + +.card-header-pills { + margin-right: -0.625rem; + margin-left: -0.625rem; +} + +.card-img-overlay { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + padding: 1.25rem; +} + +.card-img, +.card-img-top, +.card-img-bottom { + -ms-flex-negative: 0; + flex-shrink: 0; + width: 100%; +} + +.card-img, +.card-img-top { + border-top-left-radius: calc(0.25rem - 1px); + border-top-right-radius: calc(0.25rem - 1px); +} + +.card-img, +.card-img-bottom { + border-bottom-right-radius: calc(0.25rem - 1px); + border-bottom-left-radius: calc(0.25rem - 1px); +} + +.card-deck .card { + margin-bottom: 15px; +} + +@media (min-width: 576px) { + .card-deck { + display: -ms-flexbox; + display: flex; + -ms-flex-flow: row wrap; + flex-flow: row wrap; + margin-right: -15px; + margin-left: -15px; + } + .card-deck .card { + -ms-flex: 1 0 0%; + flex: 1 0 0%; + margin-right: 15px; + margin-bottom: 0; + margin-left: 15px; + } +} + +.card-group > .card { + margin-bottom: 15px; +} + +@media (min-width: 576px) { + .card-group { + display: -ms-flexbox; + display: flex; + -ms-flex-flow: row wrap; + flex-flow: row wrap; + } + .card-group > .card { + -ms-flex: 1 0 0%; + flex: 1 0 0%; + margin-bottom: 0; + } + .card-group > .card + .card { + margin-left: 0; + border-left: 0; + } + .card-group > .card:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + .card-group > .card:not(:last-child) .card-img-top, + .card-group > .card:not(:last-child) .card-header { + border-top-right-radius: 0; + } + .card-group > .card:not(:last-child) .card-img-bottom, + .card-group > .card:not(:last-child) .card-footer { + border-bottom-right-radius: 0; + } + .card-group > .card:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + .card-group > .card:not(:first-child) .card-img-top, + .card-group > .card:not(:first-child) .card-header { + border-top-left-radius: 0; + } + .card-group > .card:not(:first-child) .card-img-bottom, + .card-group > .card:not(:first-child) .card-footer { + border-bottom-left-radius: 0; + } +} + +.card-columns .card { + margin-bottom: 0.75rem; +} + +@media (min-width: 576px) { + .card-columns { + -webkit-column-count: 3; + -moz-column-count: 3; + column-count: 3; + -webkit-column-gap: 1.25rem; + -moz-column-gap: 1.25rem; + column-gap: 1.25rem; + orphans: 1; + widows: 1; + } + .card-columns .card { + display: inline-block; + width: 100%; + } +} + +.accordion > .card { + overflow: hidden; +} + +.accordion > .card:not(:last-of-type) { + border-bottom: 0; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} + +.accordion > .card:not(:first-of-type) { + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.accordion > .card > .card-header { + border-radius: 0; + margin-bottom: -1px; +} + +.breadcrumb { + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + padding: 0.75rem 1rem; + margin-bottom: 1rem; + list-style: none; + background-color: #e9ecef; + border-radius: 0.25rem; +} + +.breadcrumb-item { + display: -ms-flexbox; + display: flex; +} + +.breadcrumb-item + .breadcrumb-item { + padding-left: 0.5rem; +} + +.breadcrumb-item + .breadcrumb-item::before { + display: inline-block; + padding-right: 0.5rem; + color: #6c757d; + content: "/"; +} + +.breadcrumb-item + .breadcrumb-item:hover::before { + text-decoration: underline; +} + +.breadcrumb-item + .breadcrumb-item:hover::before { + text-decoration: none; +} + +.breadcrumb-item.active { + color: #6c757d; +} + +.pagination { + display: -ms-flexbox; + display: flex; + padding-left: 0; + list-style: none; + border-radius: 0.25rem; +} + +.page-link { + position: relative; + display: block; + padding: 0.5rem 0.75rem; + margin-left: -1px; + line-height: 1.25; + color: #007bff; + background-color: #fff; + border: 1px solid #dee2e6; +} + +.page-link:hover { + z-index: 2; + color: #0056b3; + text-decoration: none; + background-color: #e9ecef; + border-color: #dee2e6; +} + +.page-link:focus { + z-index: 3; + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); +} + +.page-item:first-child .page-link { + margin-left: 0; + border-top-left-radius: 0.25rem; + border-bottom-left-radius: 0.25rem; +} + +.page-item:last-child .page-link { + border-top-right-radius: 0.25rem; + border-bottom-right-radius: 0.25rem; +} + +.page-item.active .page-link { + z-index: 3; + color: #fff; + background-color: #007bff; + border-color: #007bff; +} + +.page-item.disabled .page-link { + color: #6c757d; + pointer-events: none; + cursor: auto; + background-color: #fff; + border-color: #dee2e6; +} + +.pagination-lg .page-link { + padding: 0.75rem 1.5rem; + font-size: 1.25rem; + line-height: 1.5; +} + +.pagination-lg .page-item:first-child .page-link { + border-top-left-radius: 0.3rem; + border-bottom-left-radius: 0.3rem; +} + +.pagination-lg .page-item:last-child .page-link { + border-top-right-radius: 0.3rem; + border-bottom-right-radius: 0.3rem; +} + +.pagination-sm .page-link { + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + line-height: 1.5; +} + +.pagination-sm .page-item:first-child .page-link { + border-top-left-radius: 0.2rem; + border-bottom-left-radius: 0.2rem; +} + +.pagination-sm .page-item:last-child .page-link { + border-top-right-radius: 0.2rem; + border-bottom-right-radius: 0.2rem; +} + +.badge { + display: inline-block; + padding: 0.25em 0.4em; + font-size: 75%; + font-weight: 700; + line-height: 1; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: 0.25rem; + transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +} + +@media (prefers-reduced-motion: reduce) { + .badge { + transition: none; + } +} + +a.badge:hover, a.badge:focus { + text-decoration: none; +} + +.badge:empty { + display: none; +} + +.btn .badge { + position: relative; + top: -1px; +} + +.badge-pill { + padding-right: 0.6em; + padding-left: 0.6em; + border-radius: 10rem; +} + +.badge-primary { + color: #fff; + background-color: #007bff; +} + +a.badge-primary:hover, a.badge-primary:focus { + color: #fff; + background-color: #0062cc; +} + +a.badge-primary:focus, a.badge-primary.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); +} + +.badge-secondary { + color: #fff; + background-color: #6c757d; +} + +a.badge-secondary:hover, a.badge-secondary:focus { + color: #fff; + background-color: #545b62; +} + +a.badge-secondary:focus, a.badge-secondary.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5); +} + +.badge-success { + color: #fff; + background-color: #28a745; +} + +a.badge-success:hover, a.badge-success:focus { + color: #fff; + background-color: #1e7e34; +} + +a.badge-success:focus, a.badge-success.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); +} + +.badge-info { + color: #fff; + background-color: #17a2b8; +} + +a.badge-info:hover, a.badge-info:focus { + color: #fff; + background-color: #117a8b; +} + +a.badge-info:focus, a.badge-info.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); +} + +.badge-warning { + color: #212529; + background-color: #ffc107; +} + +a.badge-warning:hover, a.badge-warning:focus { + color: #212529; + background-color: #d39e00; +} + +a.badge-warning:focus, a.badge-warning.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5); +} + +.badge-danger { + color: #fff; + background-color: #dc3545; +} + +a.badge-danger:hover, a.badge-danger:focus { + color: #fff; + background-color: #bd2130; +} + +a.badge-danger:focus, a.badge-danger.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); +} + +.badge-light { + color: #212529; + background-color: #f8f9fa; +} + +a.badge-light:hover, a.badge-light:focus { + color: #212529; + background-color: #dae0e5; +} + +a.badge-light:focus, a.badge-light.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); +} + +.badge-dark { + color: #fff; + background-color: #343a40; +} + +a.badge-dark:hover, a.badge-dark:focus { + color: #fff; + background-color: #1d2124; +} + +a.badge-dark:focus, a.badge-dark.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); +} + +.jumbotron { + padding: 2rem 1rem; + margin-bottom: 2rem; + background-color: #e9ecef; + border-radius: 0.3rem; +} + +@media (min-width: 576px) { + .jumbotron { + padding: 4rem 2rem; + } +} + +.jumbotron-fluid { + padding-right: 0; + padding-left: 0; + border-radius: 0; +} + +.alert { + position: relative; + padding: 0.75rem 1.25rem; + margin-bottom: 1rem; + border: 1px solid transparent; + border-radius: 0.25rem; +} + +.alert-heading { + color: inherit; +} + +.alert-link { + font-weight: 700; +} + +.alert-dismissible { + padding-right: 4rem; +} + +.alert-dismissible .close { + position: absolute; + top: 0; + right: 0; + padding: 0.75rem 1.25rem; + color: inherit; +} + +.alert-primary { + color: #004085; + background-color: #cce5ff; + border-color: #b8daff; +} + +.alert-primary hr { + border-top-color: #9fcdff; +} + +.alert-primary .alert-link { + color: #002752; +} + +.alert-secondary { + color: #383d41; + background-color: #e2e3e5; + border-color: #d6d8db; +} + +.alert-secondary hr { + border-top-color: #c8cbcf; +} + +.alert-secondary .alert-link { + color: #202326; +} + +.alert-success { + color: #155724; + background-color: #d4edda; + border-color: #c3e6cb; +} + +.alert-success hr { + border-top-color: #b1dfbb; +} + +.alert-success .alert-link { + color: #0b2e13; +} + +.alert-info { + color: #0c5460; + background-color: #d1ecf1; + border-color: #bee5eb; +} + +.alert-info hr { + border-top-color: #abdde5; +} + +.alert-info .alert-link { + color: #062c33; +} + +.alert-warning { + color: #856404; + background-color: #fff3cd; + border-color: #ffeeba; +} + +.alert-warning hr { + border-top-color: #ffe8a1; +} + +.alert-warning .alert-link { + color: #533f03; +} + +.alert-danger { + color: #721c24; + background-color: #f8d7da; + border-color: #f5c6cb; +} + +.alert-danger hr { + border-top-color: #f1b0b7; +} + +.alert-danger .alert-link { + color: #491217; +} + +.alert-light { + color: #818182; + background-color: #fefefe; + border-color: #fdfdfe; +} + +.alert-light hr { + border-top-color: #ececf6; +} + +.alert-light .alert-link { + color: #686868; +} + +.alert-dark { + color: #1b1e21; + background-color: #d6d8d9; + border-color: #c6c8ca; +} + +.alert-dark hr { + border-top-color: #b9bbbe; +} + +.alert-dark .alert-link { + color: #040505; +} + +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 1rem 0; + } + to { + background-position: 0 0; + } +} + +@keyframes progress-bar-stripes { + from { + background-position: 1rem 0; + } + to { + background-position: 0 0; + } +} + +.progress { + display: -ms-flexbox; + display: flex; + height: 1rem; + overflow: hidden; + line-height: 0; + font-size: 0.75rem; + background-color: #e9ecef; + border-radius: 0.25rem; +} + +.progress-bar { + display: -ms-flexbox; + display: flex; + -ms-flex-direction: column; + flex-direction: column; + -ms-flex-pack: center; + justify-content: center; + overflow: hidden; + color: #fff; + text-align: center; + white-space: nowrap; + background-color: #007bff; + transition: width 0.6s ease; +} + +@media (prefers-reduced-motion: reduce) { + .progress-bar { + transition: none; + } +} + +.progress-bar-striped { + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-size: 1rem 1rem; +} + +.progress-bar-animated { + -webkit-animation: progress-bar-stripes 1s linear infinite; + animation: progress-bar-stripes 1s linear infinite; +} + +@media (prefers-reduced-motion: reduce) { + .progress-bar-animated { + -webkit-animation: none; + animation: none; + } +} + +.media { + display: -ms-flexbox; + display: flex; + -ms-flex-align: start; + align-items: flex-start; +} + +.media-body { + -ms-flex: 1; + flex: 1; +} + +.list-group { + display: -ms-flexbox; + display: flex; + -ms-flex-direction: column; + flex-direction: column; + padding-left: 0; + margin-bottom: 0; + border-radius: 0.25rem; +} + +.list-group-item-action { + width: 100%; + color: #495057; + text-align: inherit; +} + +.list-group-item-action:hover, .list-group-item-action:focus { + z-index: 1; + color: #495057; + text-decoration: none; + background-color: #f8f9fa; +} + +.list-group-item-action:active { + color: #212529; + background-color: #e9ecef; +} + +.list-group-item { + position: relative; + display: block; + padding: 0.75rem 1.25rem; + background-color: #fff; + border: 1px solid rgba(0, 0, 0, 0.125); +} + +.list-group-item:first-child { + border-top-left-radius: inherit; + border-top-right-radius: inherit; +} + +.list-group-item:last-child { + border-bottom-right-radius: inherit; + border-bottom-left-radius: inherit; +} + +.list-group-item.disabled, .list-group-item:disabled { + color: #6c757d; + pointer-events: none; + background-color: #fff; +} + +.list-group-item.active { + z-index: 2; + color: #fff; + background-color: #007bff; + border-color: #007bff; +} + +.list-group-item + .list-group-item { + border-top-width: 0; +} + +.list-group-item + .list-group-item.active { + margin-top: -1px; + border-top-width: 1px; +} + +.list-group-horizontal { + -ms-flex-direction: row; + flex-direction: row; +} + +.list-group-horizontal > .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; +} + +.list-group-horizontal > .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; +} + +.list-group-horizontal > .list-group-item.active { + margin-top: 0; +} + +.list-group-horizontal > .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; +} + +.list-group-horizontal > .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; +} + +@media (min-width: 576px) { + .list-group-horizontal-sm { + -ms-flex-direction: row; + flex-direction: row; + } + .list-group-horizontal-sm > .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; + } + .list-group-horizontal-sm > .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; + } + .list-group-horizontal-sm > .list-group-item.active { + margin-top: 0; + } + .list-group-horizontal-sm > .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; + } + .list-group-horizontal-sm > .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; + } +} + +@media (min-width: 768px) { + .list-group-horizontal-md { + -ms-flex-direction: row; + flex-direction: row; + } + .list-group-horizontal-md > .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; + } + .list-group-horizontal-md > .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; + } + .list-group-horizontal-md > .list-group-item.active { + margin-top: 0; + } + .list-group-horizontal-md > .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; + } + .list-group-horizontal-md > .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; + } +} + +@media (min-width: 992px) { + .list-group-horizontal-lg { + -ms-flex-direction: row; + flex-direction: row; + } + .list-group-horizontal-lg > .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; + } + .list-group-horizontal-lg > .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; + } + .list-group-horizontal-lg > .list-group-item.active { + margin-top: 0; + } + .list-group-horizontal-lg > .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; + } + .list-group-horizontal-lg > .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; + } +} + +@media (min-width: 1200px) { + .list-group-horizontal-xl { + -ms-flex-direction: row; + flex-direction: row; + } + .list-group-horizontal-xl > .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; + } + .list-group-horizontal-xl > .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; + } + .list-group-horizontal-xl > .list-group-item.active { + margin-top: 0; + } + .list-group-horizontal-xl > .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; + } + .list-group-horizontal-xl > .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; + } +} + +.list-group-flush { + border-radius: 0; +} + +.list-group-flush > .list-group-item { + border-width: 0 0 1px; +} + +.list-group-flush > .list-group-item:last-child { + border-bottom-width: 0; +} + +.list-group-item-primary { + color: #004085; + background-color: #b8daff; +} + +.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus { + color: #004085; + background-color: #9fcdff; +} + +.list-group-item-primary.list-group-item-action.active { + color: #fff; + background-color: #004085; + border-color: #004085; +} + +.list-group-item-secondary { + color: #383d41; + background-color: #d6d8db; +} + +.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus { + color: #383d41; + background-color: #c8cbcf; +} + +.list-group-item-secondary.list-group-item-action.active { + color: #fff; + background-color: #383d41; + border-color: #383d41; +} + +.list-group-item-success { + color: #155724; + background-color: #c3e6cb; +} + +.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus { + color: #155724; + background-color: #b1dfbb; +} + +.list-group-item-success.list-group-item-action.active { + color: #fff; + background-color: #155724; + border-color: #155724; +} + +.list-group-item-info { + color: #0c5460; + background-color: #bee5eb; +} + +.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus { + color: #0c5460; + background-color: #abdde5; +} + +.list-group-item-info.list-group-item-action.active { + color: #fff; + background-color: #0c5460; + border-color: #0c5460; +} + +.list-group-item-warning { + color: #856404; + background-color: #ffeeba; +} + +.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus { + color: #856404; + background-color: #ffe8a1; +} + +.list-group-item-warning.list-group-item-action.active { + color: #fff; + background-color: #856404; + border-color: #856404; +} + +.list-group-item-danger { + color: #721c24; + background-color: #f5c6cb; +} + +.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus { + color: #721c24; + background-color: #f1b0b7; +} + +.list-group-item-danger.list-group-item-action.active { + color: #fff; + background-color: #721c24; + border-color: #721c24; +} + +.list-group-item-light { + color: #818182; + background-color: #fdfdfe; +} + +.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus { + color: #818182; + background-color: #ececf6; +} + +.list-group-item-light.list-group-item-action.active { + color: #fff; + background-color: #818182; + border-color: #818182; +} + +.list-group-item-dark { + color: #1b1e21; + background-color: #c6c8ca; +} + +.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus { + color: #1b1e21; + background-color: #b9bbbe; +} + +.list-group-item-dark.list-group-item-action.active { + color: #fff; + background-color: #1b1e21; + border-color: #1b1e21; +} + +.close { + float: right; + font-size: 1.5rem; + font-weight: 700; + line-height: 1; + color: #000; + text-shadow: 0 1px 0 #fff; + opacity: .5; +} + +.close:hover { + color: #000; + text-decoration: none; +} + +.close:not(:disabled):not(.disabled):hover, .close:not(:disabled):not(.disabled):focus { + opacity: .75; +} + +button.close { + padding: 0; + background-color: transparent; + border: 0; +} + +a.close.disabled { + pointer-events: none; +} + +.toast { + max-width: 350px; + overflow: hidden; + font-size: 0.875rem; + background-color: rgba(255, 255, 255, 0.85); + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, 0.1); + box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.1); + -webkit-backdrop-filter: blur(10px); + backdrop-filter: blur(10px); + opacity: 0; + border-radius: 0.25rem; +} + +.toast:not(:last-child) { + margin-bottom: 0.75rem; +} + +.toast.showing { + opacity: 1; +} + +.toast.show { + display: block; + opacity: 1; +} + +.toast.hide { + display: none; +} + +.toast-header { + display: -ms-flexbox; + display: flex; + -ms-flex-align: center; + align-items: center; + padding: 0.25rem 0.75rem; + color: #6c757d; + background-color: rgba(255, 255, 255, 0.85); + background-clip: padding-box; + border-bottom: 1px solid rgba(0, 0, 0, 0.05); +} + +.toast-body { + padding: 0.75rem; +} + +.modal-open { + overflow: hidden; +} + +.modal-open .modal { + overflow-x: hidden; + overflow-y: auto; +} + +.modal { + position: fixed; + top: 0; + left: 0; + z-index: 1050; + display: none; + width: 100%; + height: 100%; + overflow: hidden; + outline: 0; +} + +.modal-dialog { + position: relative; + width: auto; + margin: 0.5rem; + pointer-events: none; +} + +.modal.fade .modal-dialog { + transition: -webkit-transform 0.3s ease-out; + transition: transform 0.3s ease-out; + transition: transform 0.3s ease-out, -webkit-transform 0.3s ease-out; + -webkit-transform: translate(0, -50px); + transform: translate(0, -50px); +} + +@media (prefers-reduced-motion: reduce) { + .modal.fade .modal-dialog { + transition: none; + } +} + +.modal.show .modal-dialog { + -webkit-transform: none; + transform: none; +} + +.modal.modal-static .modal-dialog { + -webkit-transform: scale(1.02); + transform: scale(1.02); +} + +.modal-dialog-scrollable { + display: -ms-flexbox; + display: flex; + max-height: calc(100% - 1rem); +} + +.modal-dialog-scrollable .modal-content { + max-height: calc(100vh - 1rem); + overflow: hidden; +} + +.modal-dialog-scrollable .modal-header, +.modal-dialog-scrollable .modal-footer { + -ms-flex-negative: 0; + flex-shrink: 0; +} + +.modal-dialog-scrollable .modal-body { + overflow-y: auto; +} + +.modal-dialog-centered { + display: -ms-flexbox; + display: flex; + -ms-flex-align: center; + align-items: center; + min-height: calc(100% - 1rem); +} + +.modal-dialog-centered::before { + display: block; + height: calc(100vh - 1rem); + height: -webkit-min-content; + height: -moz-min-content; + height: min-content; + content: ""; +} + +.modal-dialog-centered.modal-dialog-scrollable { + -ms-flex-direction: column; + flex-direction: column; + -ms-flex-pack: center; + justify-content: center; + height: 100%; +} + +.modal-dialog-centered.modal-dialog-scrollable .modal-content { + max-height: none; +} + +.modal-dialog-centered.modal-dialog-scrollable::before { + content: none; +} + +.modal-content { + position: relative; + display: -ms-flexbox; + display: flex; + -ms-flex-direction: column; + flex-direction: column; + width: 100%; + pointer-events: auto; + background-color: #fff; + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 0.3rem; + outline: 0; +} + +.modal-backdrop { + position: fixed; + top: 0; + left: 0; + z-index: 1040; + width: 100vw; + height: 100vh; + background-color: #000; +} + +.modal-backdrop.fade { + opacity: 0; +} + +.modal-backdrop.show { + opacity: 0.5; +} + +.modal-header { + display: -ms-flexbox; + display: flex; + -ms-flex-align: start; + align-items: flex-start; + -ms-flex-pack: justify; + justify-content: space-between; + padding: 1rem 1rem; + border-bottom: 1px solid #dee2e6; + border-top-left-radius: calc(0.3rem - 1px); + border-top-right-radius: calc(0.3rem - 1px); +} + +.modal-header .close { + padding: 1rem 1rem; + margin: -1rem -1rem -1rem auto; +} + +.modal-title { + margin-bottom: 0; + line-height: 1.5; +} + +.modal-body { + position: relative; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + padding: 1rem; +} + +.modal-footer { + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -ms-flex-align: center; + align-items: center; + -ms-flex-pack: end; + justify-content: flex-end; + padding: 0.75rem; + border-top: 1px solid #dee2e6; + border-bottom-right-radius: calc(0.3rem - 1px); + border-bottom-left-radius: calc(0.3rem - 1px); +} + +.modal-footer > * { + margin: 0.25rem; +} + +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll; +} + +@media (min-width: 576px) { + .modal-dialog { + max-width: 500px; + margin: 1.75rem auto; + } + .modal-dialog-scrollable { + max-height: calc(100% - 3.5rem); + } + .modal-dialog-scrollable .modal-content { + max-height: calc(100vh - 3.5rem); + } + .modal-dialog-centered { + min-height: calc(100% - 3.5rem); + } + .modal-dialog-centered::before { + height: calc(100vh - 3.5rem); + height: -webkit-min-content; + height: -moz-min-content; + height: min-content; + } + .modal-sm { + max-width: 300px; + } +} + +@media (min-width: 992px) { + .modal-lg, + .modal-xl { + max-width: 800px; + } +} + +@media (min-width: 1200px) { + .modal-xl { + max-width: 1140px; + } +} + +.tooltip { + position: absolute; + z-index: 1070; + display: block; + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + font-style: normal; + font-weight: 400; + line-height: 1.5; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + white-space: normal; + line-break: auto; + font-size: 0.875rem; + word-wrap: break-word; + opacity: 0; +} + +.tooltip.show { + opacity: 0.9; +} + +.tooltip .arrow { + position: absolute; + display: block; + width: 0.8rem; + height: 0.4rem; +} + +.tooltip .arrow::before { + position: absolute; + content: ""; + border-color: transparent; + border-style: solid; +} + +.bs-tooltip-top, .bs-tooltip-auto[x-placement^="top"] { + padding: 0.4rem 0; +} + +.bs-tooltip-top .arrow, .bs-tooltip-auto[x-placement^="top"] .arrow { + bottom: 0; +} + +.bs-tooltip-top .arrow::before, .bs-tooltip-auto[x-placement^="top"] .arrow::before { + top: 0; + border-width: 0.4rem 0.4rem 0; + border-top-color: #000; +} + +.bs-tooltip-right, .bs-tooltip-auto[x-placement^="right"] { + padding: 0 0.4rem; +} + +.bs-tooltip-right .arrow, .bs-tooltip-auto[x-placement^="right"] .arrow { + left: 0; + width: 0.4rem; + height: 0.8rem; +} + +.bs-tooltip-right .arrow::before, .bs-tooltip-auto[x-placement^="right"] .arrow::before { + right: 0; + border-width: 0.4rem 0.4rem 0.4rem 0; + border-right-color: #000; +} + +.bs-tooltip-bottom, .bs-tooltip-auto[x-placement^="bottom"] { + padding: 0.4rem 0; +} + +.bs-tooltip-bottom .arrow, .bs-tooltip-auto[x-placement^="bottom"] .arrow { + top: 0; +} + +.bs-tooltip-bottom .arrow::before, .bs-tooltip-auto[x-placement^="bottom"] .arrow::before { + bottom: 0; + border-width: 0 0.4rem 0.4rem; + border-bottom-color: #000; +} + +.bs-tooltip-left, .bs-tooltip-auto[x-placement^="left"] { + padding: 0 0.4rem; +} + +.bs-tooltip-left .arrow, .bs-tooltip-auto[x-placement^="left"] .arrow { + right: 0; + width: 0.4rem; + height: 0.8rem; +} + +.bs-tooltip-left .arrow::before, .bs-tooltip-auto[x-placement^="left"] .arrow::before { + left: 0; + border-width: 0.4rem 0 0.4rem 0.4rem; + border-left-color: #000; +} + +.tooltip-inner { + max-width: 200px; + padding: 0.25rem 0.5rem; + color: #fff; + text-align: center; + background-color: #000; + border-radius: 0.25rem; +} + +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1060; + display: block; + max-width: 276px; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + font-style: normal; + font-weight: 400; + line-height: 1.5; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + white-space: normal; + line-break: auto; + font-size: 0.875rem; + word-wrap: break-word; + background-color: #fff; + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 0.3rem; +} + +.popover .arrow { + position: absolute; + display: block; + width: 1rem; + height: 0.5rem; + margin: 0 0.3rem; +} + +.popover .arrow::before, .popover .arrow::after { + position: absolute; + display: block; + content: ""; + border-color: transparent; + border-style: solid; +} + +.bs-popover-top, .bs-popover-auto[x-placement^="top"] { + margin-bottom: 0.5rem; +} + +.bs-popover-top > .arrow, .bs-popover-auto[x-placement^="top"] > .arrow { + bottom: calc(-0.5rem - 1px); +} + +.bs-popover-top > .arrow::before, .bs-popover-auto[x-placement^="top"] > .arrow::before { + bottom: 0; + border-width: 0.5rem 0.5rem 0; + border-top-color: rgba(0, 0, 0, 0.25); +} + +.bs-popover-top > .arrow::after, .bs-popover-auto[x-placement^="top"] > .arrow::after { + bottom: 1px; + border-width: 0.5rem 0.5rem 0; + border-top-color: #fff; +} + +.bs-popover-right, .bs-popover-auto[x-placement^="right"] { + margin-left: 0.5rem; +} + +.bs-popover-right > .arrow, .bs-popover-auto[x-placement^="right"] > .arrow { + left: calc(-0.5rem - 1px); + width: 0.5rem; + height: 1rem; + margin: 0.3rem 0; +} + +.bs-popover-right > .arrow::before, .bs-popover-auto[x-placement^="right"] > .arrow::before { + left: 0; + border-width: 0.5rem 0.5rem 0.5rem 0; + border-right-color: rgba(0, 0, 0, 0.25); +} + +.bs-popover-right > .arrow::after, .bs-popover-auto[x-placement^="right"] > .arrow::after { + left: 1px; + border-width: 0.5rem 0.5rem 0.5rem 0; + border-right-color: #fff; +} + +.bs-popover-bottom, .bs-popover-auto[x-placement^="bottom"] { + margin-top: 0.5rem; +} + +.bs-popover-bottom > .arrow, .bs-popover-auto[x-placement^="bottom"] > .arrow { + top: calc(-0.5rem - 1px); +} + +.bs-popover-bottom > .arrow::before, .bs-popover-auto[x-placement^="bottom"] > .arrow::before { + top: 0; + border-width: 0 0.5rem 0.5rem 0.5rem; + border-bottom-color: rgba(0, 0, 0, 0.25); +} + +.bs-popover-bottom > .arrow::after, .bs-popover-auto[x-placement^="bottom"] > .arrow::after { + top: 1px; + border-width: 0 0.5rem 0.5rem 0.5rem; + border-bottom-color: #fff; +} + +.bs-popover-bottom .popover-header::before, .bs-popover-auto[x-placement^="bottom"] .popover-header::before { + position: absolute; + top: 0; + left: 50%; + display: block; + width: 1rem; + margin-left: -0.5rem; + content: ""; + border-bottom: 1px solid #f7f7f7; +} + +.bs-popover-left, .bs-popover-auto[x-placement^="left"] { + margin-right: 0.5rem; +} + +.bs-popover-left > .arrow, .bs-popover-auto[x-placement^="left"] > .arrow { + right: calc(-0.5rem - 1px); + width: 0.5rem; + height: 1rem; + margin: 0.3rem 0; +} + +.bs-popover-left > .arrow::before, .bs-popover-auto[x-placement^="left"] > .arrow::before { + right: 0; + border-width: 0.5rem 0 0.5rem 0.5rem; + border-left-color: rgba(0, 0, 0, 0.25); +} + +.bs-popover-left > .arrow::after, .bs-popover-auto[x-placement^="left"] > .arrow::after { + right: 1px; + border-width: 0.5rem 0 0.5rem 0.5rem; + border-left-color: #fff; +} + +.popover-header { + padding: 0.5rem 0.75rem; + margin-bottom: 0; + font-size: 1rem; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-top-left-radius: calc(0.3rem - 1px); + border-top-right-radius: calc(0.3rem - 1px); +} + +.popover-header:empty { + display: none; +} + +.popover-body { + padding: 0.5rem 0.75rem; + color: #212529; +} + +.carousel { + position: relative; +} + +.carousel.pointer-event { + -ms-touch-action: pan-y; + touch-action: pan-y; +} + +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; +} + +.carousel-inner::after { + display: block; + clear: both; + content: ""; +} + +.carousel-item { + position: relative; + display: none; + float: left; + width: 100%; + margin-right: -100%; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + transition: -webkit-transform 0.6s ease-in-out; + transition: transform 0.6s ease-in-out; + transition: transform 0.6s ease-in-out, -webkit-transform 0.6s ease-in-out; +} + +@media (prefers-reduced-motion: reduce) { + .carousel-item { + transition: none; + } +} + +.carousel-item.active, +.carousel-item-next, +.carousel-item-prev { + display: block; +} + +.carousel-item-next:not(.carousel-item-left), +.active.carousel-item-right { + -webkit-transform: translateX(100%); + transform: translateX(100%); +} + +.carousel-item-prev:not(.carousel-item-right), +.active.carousel-item-left { + -webkit-transform: translateX(-100%); + transform: translateX(-100%); +} + +.carousel-fade .carousel-item { + opacity: 0; + transition-property: opacity; + -webkit-transform: none; + transform: none; +} + +.carousel-fade .carousel-item.active, +.carousel-fade .carousel-item-next.carousel-item-left, +.carousel-fade .carousel-item-prev.carousel-item-right { + z-index: 1; + opacity: 1; +} + +.carousel-fade .active.carousel-item-left, +.carousel-fade .active.carousel-item-right { + z-index: 0; + opacity: 0; + transition: opacity 0s 0.6s; +} + +@media (prefers-reduced-motion: reduce) { + .carousel-fade .active.carousel-item-left, + .carousel-fade .active.carousel-item-right { + transition: none; + } +} + +.carousel-control-prev, +.carousel-control-next { + position: absolute; + top: 0; + bottom: 0; + z-index: 1; + display: -ms-flexbox; + display: flex; + -ms-flex-align: center; + align-items: center; + -ms-flex-pack: center; + justify-content: center; + width: 15%; + color: #fff; + text-align: center; + opacity: 0.5; + transition: opacity 0.15s ease; +} + +@media (prefers-reduced-motion: reduce) { + .carousel-control-prev, + .carousel-control-next { + transition: none; + } +} + +.carousel-control-prev:hover, .carousel-control-prev:focus, +.carousel-control-next:hover, +.carousel-control-next:focus { + color: #fff; + text-decoration: none; + outline: 0; + opacity: 0.9; +} + +.carousel-control-prev { + left: 0; +} + +.carousel-control-next { + right: 0; +} + +.carousel-control-prev-icon, +.carousel-control-next-icon { + display: inline-block; + width: 20px; + height: 20px; + background: no-repeat 50% / 100% 100%; +} + +.carousel-control-prev-icon { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e"); +} + +.carousel-control-next-icon { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e"); +} + +.carousel-indicators { + position: absolute; + right: 0; + bottom: 0; + left: 0; + z-index: 15; + display: -ms-flexbox; + display: flex; + -ms-flex-pack: center; + justify-content: center; + padding-left: 0; + margin-right: 15%; + margin-left: 15%; + list-style: none; +} + +.carousel-indicators li { + box-sizing: content-box; + -ms-flex: 0 1 auto; + flex: 0 1 auto; + width: 30px; + height: 3px; + margin-right: 3px; + margin-left: 3px; + text-indent: -999px; + cursor: pointer; + background-color: #fff; + background-clip: padding-box; + border-top: 10px solid transparent; + border-bottom: 10px solid transparent; + opacity: .5; + transition: opacity 0.6s ease; +} + +@media (prefers-reduced-motion: reduce) { + .carousel-indicators li { + transition: none; + } +} + +.carousel-indicators .active { + opacity: 1; +} + +.carousel-caption { + position: absolute; + right: 15%; + bottom: 20px; + left: 15%; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #fff; + text-align: center; +} + +@-webkit-keyframes spinner-border { + to { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} + +@keyframes spinner-border { + to { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} + +.spinner-border { + display: inline-block; + width: 2rem; + height: 2rem; + vertical-align: text-bottom; + border: 0.25em solid currentColor; + border-right-color: transparent; + border-radius: 50%; + -webkit-animation: spinner-border .75s linear infinite; + animation: spinner-border .75s linear infinite; +} + +.spinner-border-sm { + width: 1rem; + height: 1rem; + border-width: 0.2em; +} + +@-webkit-keyframes spinner-grow { + 0% { + -webkit-transform: scale(0); + transform: scale(0); + } + 50% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@keyframes spinner-grow { + 0% { + -webkit-transform: scale(0); + transform: scale(0); + } + 50% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +.spinner-grow { + display: inline-block; + width: 2rem; + height: 2rem; + vertical-align: text-bottom; + background-color: currentColor; + border-radius: 50%; + opacity: 0; + -webkit-animation: spinner-grow .75s linear infinite; + animation: spinner-grow .75s linear infinite; +} + +.spinner-grow-sm { + width: 1rem; + height: 1rem; +} + +.align-baseline { + vertical-align: baseline !important; +} + +.align-top { + vertical-align: top !important; +} + +.align-middle { + vertical-align: middle !important; +} + +.align-bottom { + vertical-align: bottom !important; +} + +.align-text-bottom { + vertical-align: text-bottom !important; +} + +.align-text-top { + vertical-align: text-top !important; +} + +.bg-primary { + background-color: #007bff !important; +} + +a.bg-primary:hover, a.bg-primary:focus, +button.bg-primary:hover, +button.bg-primary:focus { + background-color: #0062cc !important; +} + +.bg-secondary { + background-color: #6c757d !important; +} + +a.bg-secondary:hover, a.bg-secondary:focus, +button.bg-secondary:hover, +button.bg-secondary:focus { + background-color: #545b62 !important; +} + +.bg-success { + background-color: #28a745 !important; +} + +a.bg-success:hover, a.bg-success:focus, +button.bg-success:hover, +button.bg-success:focus { + background-color: #1e7e34 !important; +} + +.bg-info { + background-color: #17a2b8 !important; +} + +a.bg-info:hover, a.bg-info:focus, +button.bg-info:hover, +button.bg-info:focus { + background-color: #117a8b !important; +} + +.bg-warning { + background-color: #ffc107 !important; +} + +a.bg-warning:hover, a.bg-warning:focus, +button.bg-warning:hover, +button.bg-warning:focus { + background-color: #d39e00 !important; +} + +.bg-danger { + background-color: #dc3545 !important; +} + +a.bg-danger:hover, a.bg-danger:focus, +button.bg-danger:hover, +button.bg-danger:focus { + background-color: #bd2130 !important; +} + +.bg-light { + background-color: #f8f9fa !important; +} + +a.bg-light:hover, a.bg-light:focus, +button.bg-light:hover, +button.bg-light:focus { + background-color: #dae0e5 !important; +} + +.bg-dark { + background-color: #343a40 !important; +} + +a.bg-dark:hover, a.bg-dark:focus, +button.bg-dark:hover, +button.bg-dark:focus { + background-color: #1d2124 !important; +} + +.bg-white { + background-color: #fff !important; +} + +.bg-transparent { + background-color: transparent !important; +} + +.border { + border: 1px solid #dee2e6 !important; +} + +.border-top { + border-top: 1px solid #dee2e6 !important; +} + +.border-right { + border-right: 1px solid #dee2e6 !important; +} + +.border-bottom { + border-bottom: 1px solid #dee2e6 !important; +} + +.border-left { + border-left: 1px solid #dee2e6 !important; +} + +.border-0 { + border: 0 !important; +} + +.border-top-0 { + border-top: 0 !important; +} + +.border-right-0 { + border-right: 0 !important; +} + +.border-bottom-0 { + border-bottom: 0 !important; +} + +.border-left-0 { + border-left: 0 !important; +} + +.border-primary { + border-color: #007bff !important; +} + +.border-secondary { + border-color: #6c757d !important; +} + +.border-success { + border-color: #28a745 !important; +} + +.border-info { + border-color: #17a2b8 !important; +} + +.border-warning { + border-color: #ffc107 !important; +} + +.border-danger { + border-color: #dc3545 !important; +} + +.border-light { + border-color: #f8f9fa !important; +} + +.border-dark { + border-color: #343a40 !important; +} + +.border-white { + border-color: #fff !important; +} + +.rounded-sm { + border-radius: 0.2rem !important; +} + +.rounded { + border-radius: 0.25rem !important; +} + +.rounded-top { + border-top-left-radius: 0.25rem !important; + border-top-right-radius: 0.25rem !important; +} + +.rounded-right { + border-top-right-radius: 0.25rem !important; + border-bottom-right-radius: 0.25rem !important; +} + +.rounded-bottom { + border-bottom-right-radius: 0.25rem !important; + border-bottom-left-radius: 0.25rem !important; +} + +.rounded-left { + border-top-left-radius: 0.25rem !important; + border-bottom-left-radius: 0.25rem !important; +} + +.rounded-lg { + border-radius: 0.3rem !important; +} + +.rounded-circle { + border-radius: 50% !important; +} + +.rounded-pill { + border-radius: 50rem !important; +} + +.rounded-0 { + border-radius: 0 !important; +} + +.clearfix::after { + display: block; + clear: both; + content: ""; +} + +.d-none { + display: none !important; +} + +.d-inline { + display: inline !important; +} + +.d-inline-block { + display: inline-block !important; +} + +.d-block { + display: block !important; +} + +.d-table { + display: table !important; +} + +.d-table-row { + display: table-row !important; +} + +.d-table-cell { + display: table-cell !important; +} + +.d-flex { + display: -ms-flexbox !important; + display: flex !important; +} + +.d-inline-flex { + display: -ms-inline-flexbox !important; + display: inline-flex !important; +} + +@media (min-width: 576px) { + .d-sm-none { + display: none !important; + } + .d-sm-inline { + display: inline !important; + } + .d-sm-inline-block { + display: inline-block !important; + } + .d-sm-block { + display: block !important; + } + .d-sm-table { + display: table !important; + } + .d-sm-table-row { + display: table-row !important; + } + .d-sm-table-cell { + display: table-cell !important; + } + .d-sm-flex { + display: -ms-flexbox !important; + display: flex !important; + } + .d-sm-inline-flex { + display: -ms-inline-flexbox !important; + display: inline-flex !important; + } +} + +@media (min-width: 768px) { + .d-md-none { + display: none !important; + } + .d-md-inline { + display: inline !important; + } + .d-md-inline-block { + display: inline-block !important; + } + .d-md-block { + display: block !important; + } + .d-md-table { + display: table !important; + } + .d-md-table-row { + display: table-row !important; + } + .d-md-table-cell { + display: table-cell !important; + } + .d-md-flex { + display: -ms-flexbox !important; + display: flex !important; + } + .d-md-inline-flex { + display: -ms-inline-flexbox !important; + display: inline-flex !important; + } +} + +@media (min-width: 992px) { + .d-lg-none { + display: none !important; + } + .d-lg-inline { + display: inline !important; + } + .d-lg-inline-block { + display: inline-block !important; + } + .d-lg-block { + display: block !important; + } + .d-lg-table { + display: table !important; + } + .d-lg-table-row { + display: table-row !important; + } + .d-lg-table-cell { + display: table-cell !important; + } + .d-lg-flex { + display: -ms-flexbox !important; + display: flex !important; + } + .d-lg-inline-flex { + display: -ms-inline-flexbox !important; + display: inline-flex !important; + } +} + +@media (min-width: 1200px) { + .d-xl-none { + display: none !important; + } + .d-xl-inline { + display: inline !important; + } + .d-xl-inline-block { + display: inline-block !important; + } + .d-xl-block { + display: block !important; + } + .d-xl-table { + display: table !important; + } + .d-xl-table-row { + display: table-row !important; + } + .d-xl-table-cell { + display: table-cell !important; + } + .d-xl-flex { + display: -ms-flexbox !important; + display: flex !important; + } + .d-xl-inline-flex { + display: -ms-inline-flexbox !important; + display: inline-flex !important; + } +} + +@media print { + .d-print-none { + display: none !important; + } + .d-print-inline { + display: inline !important; + } + .d-print-inline-block { + display: inline-block !important; + } + .d-print-block { + display: block !important; + } + .d-print-table { + display: table !important; + } + .d-print-table-row { + display: table-row !important; + } + .d-print-table-cell { + display: table-cell !important; + } + .d-print-flex { + display: -ms-flexbox !important; + display: flex !important; + } + .d-print-inline-flex { + display: -ms-inline-flexbox !important; + display: inline-flex !important; + } +} + +.embed-responsive { + position: relative; + display: block; + width: 100%; + padding: 0; + overflow: hidden; +} + +.embed-responsive::before { + display: block; + content: ""; +} + +.embed-responsive .embed-responsive-item, +.embed-responsive iframe, +.embed-responsive embed, +.embed-responsive object, +.embed-responsive video { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + border: 0; +} + +.embed-responsive-21by9::before { + padding-top: 42.857143%; +} + +.embed-responsive-16by9::before { + padding-top: 56.25%; +} + +.embed-responsive-4by3::before { + padding-top: 75%; +} + +.embed-responsive-1by1::before { + padding-top: 100%; +} + +.flex-row { + -ms-flex-direction: row !important; + flex-direction: row !important; +} + +.flex-column { + -ms-flex-direction: column !important; + flex-direction: column !important; +} + +.flex-row-reverse { + -ms-flex-direction: row-reverse !important; + flex-direction: row-reverse !important; +} + +.flex-column-reverse { + -ms-flex-direction: column-reverse !important; + flex-direction: column-reverse !important; +} + +.flex-wrap { + -ms-flex-wrap: wrap !important; + flex-wrap: wrap !important; +} + +.flex-nowrap { + -ms-flex-wrap: nowrap !important; + flex-wrap: nowrap !important; +} + +.flex-wrap-reverse { + -ms-flex-wrap: wrap-reverse !important; + flex-wrap: wrap-reverse !important; +} + +.flex-fill { + -ms-flex: 1 1 auto !important; + flex: 1 1 auto !important; +} + +.flex-grow-0 { + -ms-flex-positive: 0 !important; + flex-grow: 0 !important; +} + +.flex-grow-1 { + -ms-flex-positive: 1 !important; + flex-grow: 1 !important; +} + +.flex-shrink-0 { + -ms-flex-negative: 0 !important; + flex-shrink: 0 !important; +} + +.flex-shrink-1 { + -ms-flex-negative: 1 !important; + flex-shrink: 1 !important; +} + +.justify-content-start { + -ms-flex-pack: start !important; + justify-content: flex-start !important; +} + +.justify-content-end { + -ms-flex-pack: end !important; + justify-content: flex-end !important; +} + +.justify-content-center { + -ms-flex-pack: center !important; + justify-content: center !important; +} + +.justify-content-between { + -ms-flex-pack: justify !important; + justify-content: space-between !important; +} + +.justify-content-around { + -ms-flex-pack: distribute !important; + justify-content: space-around !important; +} + +.align-items-start { + -ms-flex-align: start !important; + align-items: flex-start !important; +} + +.align-items-end { + -ms-flex-align: end !important; + align-items: flex-end !important; +} + +.align-items-center { + -ms-flex-align: center !important; + align-items: center !important; +} + +.align-items-baseline { + -ms-flex-align: baseline !important; + align-items: baseline !important; +} + +.align-items-stretch { + -ms-flex-align: stretch !important; + align-items: stretch !important; +} + +.align-content-start { + -ms-flex-line-pack: start !important; + align-content: flex-start !important; +} + +.align-content-end { + -ms-flex-line-pack: end !important; + align-content: flex-end !important; +} + +.align-content-center { + -ms-flex-line-pack: center !important; + align-content: center !important; +} + +.align-content-between { + -ms-flex-line-pack: justify !important; + align-content: space-between !important; +} + +.align-content-around { + -ms-flex-line-pack: distribute !important; + align-content: space-around !important; +} + +.align-content-stretch { + -ms-flex-line-pack: stretch !important; + align-content: stretch !important; +} + +.align-self-auto { + -ms-flex-item-align: auto !important; + align-self: auto !important; +} + +.align-self-start { + -ms-flex-item-align: start !important; + align-self: flex-start !important; +} + +.align-self-end { + -ms-flex-item-align: end !important; + align-self: flex-end !important; +} + +.align-self-center { + -ms-flex-item-align: center !important; + align-self: center !important; +} + +.align-self-baseline { + -ms-flex-item-align: baseline !important; + align-self: baseline !important; +} + +.align-self-stretch { + -ms-flex-item-align: stretch !important; + align-self: stretch !important; +} + +@media (min-width: 576px) { + .flex-sm-row { + -ms-flex-direction: row !important; + flex-direction: row !important; + } + .flex-sm-column { + -ms-flex-direction: column !important; + flex-direction: column !important; + } + .flex-sm-row-reverse { + -ms-flex-direction: row-reverse !important; + flex-direction: row-reverse !important; + } + .flex-sm-column-reverse { + -ms-flex-direction: column-reverse !important; + flex-direction: column-reverse !important; + } + .flex-sm-wrap { + -ms-flex-wrap: wrap !important; + flex-wrap: wrap !important; + } + .flex-sm-nowrap { + -ms-flex-wrap: nowrap !important; + flex-wrap: nowrap !important; + } + .flex-sm-wrap-reverse { + -ms-flex-wrap: wrap-reverse !important; + flex-wrap: wrap-reverse !important; + } + .flex-sm-fill { + -ms-flex: 1 1 auto !important; + flex: 1 1 auto !important; + } + .flex-sm-grow-0 { + -ms-flex-positive: 0 !important; + flex-grow: 0 !important; + } + .flex-sm-grow-1 { + -ms-flex-positive: 1 !important; + flex-grow: 1 !important; + } + .flex-sm-shrink-0 { + -ms-flex-negative: 0 !important; + flex-shrink: 0 !important; + } + .flex-sm-shrink-1 { + -ms-flex-negative: 1 !important; + flex-shrink: 1 !important; + } + .justify-content-sm-start { + -ms-flex-pack: start !important; + justify-content: flex-start !important; + } + .justify-content-sm-end { + -ms-flex-pack: end !important; + justify-content: flex-end !important; + } + .justify-content-sm-center { + -ms-flex-pack: center !important; + justify-content: center !important; + } + .justify-content-sm-between { + -ms-flex-pack: justify !important; + justify-content: space-between !important; + } + .justify-content-sm-around { + -ms-flex-pack: distribute !important; + justify-content: space-around !important; + } + .align-items-sm-start { + -ms-flex-align: start !important; + align-items: flex-start !important; + } + .align-items-sm-end { + -ms-flex-align: end !important; + align-items: flex-end !important; + } + .align-items-sm-center { + -ms-flex-align: center !important; + align-items: center !important; + } + .align-items-sm-baseline { + -ms-flex-align: baseline !important; + align-items: baseline !important; + } + .align-items-sm-stretch { + -ms-flex-align: stretch !important; + align-items: stretch !important; + } + .align-content-sm-start { + -ms-flex-line-pack: start !important; + align-content: flex-start !important; + } + .align-content-sm-end { + -ms-flex-line-pack: end !important; + align-content: flex-end !important; + } + .align-content-sm-center { + -ms-flex-line-pack: center !important; + align-content: center !important; + } + .align-content-sm-between { + -ms-flex-line-pack: justify !important; + align-content: space-between !important; + } + .align-content-sm-around { + -ms-flex-line-pack: distribute !important; + align-content: space-around !important; + } + .align-content-sm-stretch { + -ms-flex-line-pack: stretch !important; + align-content: stretch !important; + } + .align-self-sm-auto { + -ms-flex-item-align: auto !important; + align-self: auto !important; + } + .align-self-sm-start { + -ms-flex-item-align: start !important; + align-self: flex-start !important; + } + .align-self-sm-end { + -ms-flex-item-align: end !important; + align-self: flex-end !important; + } + .align-self-sm-center { + -ms-flex-item-align: center !important; + align-self: center !important; + } + .align-self-sm-baseline { + -ms-flex-item-align: baseline !important; + align-self: baseline !important; + } + .align-self-sm-stretch { + -ms-flex-item-align: stretch !important; + align-self: stretch !important; + } +} + +@media (min-width: 768px) { + .flex-md-row { + -ms-flex-direction: row !important; + flex-direction: row !important; + } + .flex-md-column { + -ms-flex-direction: column !important; + flex-direction: column !important; + } + .flex-md-row-reverse { + -ms-flex-direction: row-reverse !important; + flex-direction: row-reverse !important; + } + .flex-md-column-reverse { + -ms-flex-direction: column-reverse !important; + flex-direction: column-reverse !important; + } + .flex-md-wrap { + -ms-flex-wrap: wrap !important; + flex-wrap: wrap !important; + } + .flex-md-nowrap { + -ms-flex-wrap: nowrap !important; + flex-wrap: nowrap !important; + } + .flex-md-wrap-reverse { + -ms-flex-wrap: wrap-reverse !important; + flex-wrap: wrap-reverse !important; + } + .flex-md-fill { + -ms-flex: 1 1 auto !important; + flex: 1 1 auto !important; + } + .flex-md-grow-0 { + -ms-flex-positive: 0 !important; + flex-grow: 0 !important; + } + .flex-md-grow-1 { + -ms-flex-positive: 1 !important; + flex-grow: 1 !important; + } + .flex-md-shrink-0 { + -ms-flex-negative: 0 !important; + flex-shrink: 0 !important; + } + .flex-md-shrink-1 { + -ms-flex-negative: 1 !important; + flex-shrink: 1 !important; + } + .justify-content-md-start { + -ms-flex-pack: start !important; + justify-content: flex-start !important; + } + .justify-content-md-end { + -ms-flex-pack: end !important; + justify-content: flex-end !important; + } + .justify-content-md-center { + -ms-flex-pack: center !important; + justify-content: center !important; + } + .justify-content-md-between { + -ms-flex-pack: justify !important; + justify-content: space-between !important; + } + .justify-content-md-around { + -ms-flex-pack: distribute !important; + justify-content: space-around !important; + } + .align-items-md-start { + -ms-flex-align: start !important; + align-items: flex-start !important; + } + .align-items-md-end { + -ms-flex-align: end !important; + align-items: flex-end !important; + } + .align-items-md-center { + -ms-flex-align: center !important; + align-items: center !important; + } + .align-items-md-baseline { + -ms-flex-align: baseline !important; + align-items: baseline !important; + } + .align-items-md-stretch { + -ms-flex-align: stretch !important; + align-items: stretch !important; + } + .align-content-md-start { + -ms-flex-line-pack: start !important; + align-content: flex-start !important; + } + .align-content-md-end { + -ms-flex-line-pack: end !important; + align-content: flex-end !important; + } + .align-content-md-center { + -ms-flex-line-pack: center !important; + align-content: center !important; + } + .align-content-md-between { + -ms-flex-line-pack: justify !important; + align-content: space-between !important; + } + .align-content-md-around { + -ms-flex-line-pack: distribute !important; + align-content: space-around !important; + } + .align-content-md-stretch { + -ms-flex-line-pack: stretch !important; + align-content: stretch !important; + } + .align-self-md-auto { + -ms-flex-item-align: auto !important; + align-self: auto !important; + } + .align-self-md-start { + -ms-flex-item-align: start !important; + align-self: flex-start !important; + } + .align-self-md-end { + -ms-flex-item-align: end !important; + align-self: flex-end !important; + } + .align-self-md-center { + -ms-flex-item-align: center !important; + align-self: center !important; + } + .align-self-md-baseline { + -ms-flex-item-align: baseline !important; + align-self: baseline !important; + } + .align-self-md-stretch { + -ms-flex-item-align: stretch !important; + align-self: stretch !important; + } +} + +@media (min-width: 992px) { + .flex-lg-row { + -ms-flex-direction: row !important; + flex-direction: row !important; + } + .flex-lg-column { + -ms-flex-direction: column !important; + flex-direction: column !important; + } + .flex-lg-row-reverse { + -ms-flex-direction: row-reverse !important; + flex-direction: row-reverse !important; + } + .flex-lg-column-reverse { + -ms-flex-direction: column-reverse !important; + flex-direction: column-reverse !important; + } + .flex-lg-wrap { + -ms-flex-wrap: wrap !important; + flex-wrap: wrap !important; + } + .flex-lg-nowrap { + -ms-flex-wrap: nowrap !important; + flex-wrap: nowrap !important; + } + .flex-lg-wrap-reverse { + -ms-flex-wrap: wrap-reverse !important; + flex-wrap: wrap-reverse !important; + } + .flex-lg-fill { + -ms-flex: 1 1 auto !important; + flex: 1 1 auto !important; + } + .flex-lg-grow-0 { + -ms-flex-positive: 0 !important; + flex-grow: 0 !important; + } + .flex-lg-grow-1 { + -ms-flex-positive: 1 !important; + flex-grow: 1 !important; + } + .flex-lg-shrink-0 { + -ms-flex-negative: 0 !important; + flex-shrink: 0 !important; + } + .flex-lg-shrink-1 { + -ms-flex-negative: 1 !important; + flex-shrink: 1 !important; + } + .justify-content-lg-start { + -ms-flex-pack: start !important; + justify-content: flex-start !important; + } + .justify-content-lg-end { + -ms-flex-pack: end !important; + justify-content: flex-end !important; + } + .justify-content-lg-center { + -ms-flex-pack: center !important; + justify-content: center !important; + } + .justify-content-lg-between { + -ms-flex-pack: justify !important; + justify-content: space-between !important; + } + .justify-content-lg-around { + -ms-flex-pack: distribute !important; + justify-content: space-around !important; + } + .align-items-lg-start { + -ms-flex-align: start !important; + align-items: flex-start !important; + } + .align-items-lg-end { + -ms-flex-align: end !important; + align-items: flex-end !important; + } + .align-items-lg-center { + -ms-flex-align: center !important; + align-items: center !important; + } + .align-items-lg-baseline { + -ms-flex-align: baseline !important; + align-items: baseline !important; + } + .align-items-lg-stretch { + -ms-flex-align: stretch !important; + align-items: stretch !important; + } + .align-content-lg-start { + -ms-flex-line-pack: start !important; + align-content: flex-start !important; + } + .align-content-lg-end { + -ms-flex-line-pack: end !important; + align-content: flex-end !important; + } + .align-content-lg-center { + -ms-flex-line-pack: center !important; + align-content: center !important; + } + .align-content-lg-between { + -ms-flex-line-pack: justify !important; + align-content: space-between !important; + } + .align-content-lg-around { + -ms-flex-line-pack: distribute !important; + align-content: space-around !important; + } + .align-content-lg-stretch { + -ms-flex-line-pack: stretch !important; + align-content: stretch !important; + } + .align-self-lg-auto { + -ms-flex-item-align: auto !important; + align-self: auto !important; + } + .align-self-lg-start { + -ms-flex-item-align: start !important; + align-self: flex-start !important; + } + .align-self-lg-end { + -ms-flex-item-align: end !important; + align-self: flex-end !important; + } + .align-self-lg-center { + -ms-flex-item-align: center !important; + align-self: center !important; + } + .align-self-lg-baseline { + -ms-flex-item-align: baseline !important; + align-self: baseline !important; + } + .align-self-lg-stretch { + -ms-flex-item-align: stretch !important; + align-self: stretch !important; + } +} + +@media (min-width: 1200px) { + .flex-xl-row { + -ms-flex-direction: row !important; + flex-direction: row !important; + } + .flex-xl-column { + -ms-flex-direction: column !important; + flex-direction: column !important; + } + .flex-xl-row-reverse { + -ms-flex-direction: row-reverse !important; + flex-direction: row-reverse !important; + } + .flex-xl-column-reverse { + -ms-flex-direction: column-reverse !important; + flex-direction: column-reverse !important; + } + .flex-xl-wrap { + -ms-flex-wrap: wrap !important; + flex-wrap: wrap !important; + } + .flex-xl-nowrap { + -ms-flex-wrap: nowrap !important; + flex-wrap: nowrap !important; + } + .flex-xl-wrap-reverse { + -ms-flex-wrap: wrap-reverse !important; + flex-wrap: wrap-reverse !important; + } + .flex-xl-fill { + -ms-flex: 1 1 auto !important; + flex: 1 1 auto !important; + } + .flex-xl-grow-0 { + -ms-flex-positive: 0 !important; + flex-grow: 0 !important; + } + .flex-xl-grow-1 { + -ms-flex-positive: 1 !important; + flex-grow: 1 !important; + } + .flex-xl-shrink-0 { + -ms-flex-negative: 0 !important; + flex-shrink: 0 !important; + } + .flex-xl-shrink-1 { + -ms-flex-negative: 1 !important; + flex-shrink: 1 !important; + } + .justify-content-xl-start { + -ms-flex-pack: start !important; + justify-content: flex-start !important; + } + .justify-content-xl-end { + -ms-flex-pack: end !important; + justify-content: flex-end !important; + } + .justify-content-xl-center { + -ms-flex-pack: center !important; + justify-content: center !important; + } + .justify-content-xl-between { + -ms-flex-pack: justify !important; + justify-content: space-between !important; + } + .justify-content-xl-around { + -ms-flex-pack: distribute !important; + justify-content: space-around !important; + } + .align-items-xl-start { + -ms-flex-align: start !important; + align-items: flex-start !important; + } + .align-items-xl-end { + -ms-flex-align: end !important; + align-items: flex-end !important; + } + .align-items-xl-center { + -ms-flex-align: center !important; + align-items: center !important; + } + .align-items-xl-baseline { + -ms-flex-align: baseline !important; + align-items: baseline !important; + } + .align-items-xl-stretch { + -ms-flex-align: stretch !important; + align-items: stretch !important; + } + .align-content-xl-start { + -ms-flex-line-pack: start !important; + align-content: flex-start !important; + } + .align-content-xl-end { + -ms-flex-line-pack: end !important; + align-content: flex-end !important; + } + .align-content-xl-center { + -ms-flex-line-pack: center !important; + align-content: center !important; + } + .align-content-xl-between { + -ms-flex-line-pack: justify !important; + align-content: space-between !important; + } + .align-content-xl-around { + -ms-flex-line-pack: distribute !important; + align-content: space-around !important; + } + .align-content-xl-stretch { + -ms-flex-line-pack: stretch !important; + align-content: stretch !important; + } + .align-self-xl-auto { + -ms-flex-item-align: auto !important; + align-self: auto !important; + } + .align-self-xl-start { + -ms-flex-item-align: start !important; + align-self: flex-start !important; + } + .align-self-xl-end { + -ms-flex-item-align: end !important; + align-self: flex-end !important; + } + .align-self-xl-center { + -ms-flex-item-align: center !important; + align-self: center !important; + } + .align-self-xl-baseline { + -ms-flex-item-align: baseline !important; + align-self: baseline !important; + } + .align-self-xl-stretch { + -ms-flex-item-align: stretch !important; + align-self: stretch !important; + } +} + +.float-left { + float: left !important; +} + +.float-right { + float: right !important; +} + +.float-none { + float: none !important; +} + +@media (min-width: 576px) { + .float-sm-left { + float: left !important; + } + .float-sm-right { + float: right !important; + } + .float-sm-none { + float: none !important; + } +} + +@media (min-width: 768px) { + .float-md-left { + float: left !important; + } + .float-md-right { + float: right !important; + } + .float-md-none { + float: none !important; + } +} + +@media (min-width: 992px) { + .float-lg-left { + float: left !important; + } + .float-lg-right { + float: right !important; + } + .float-lg-none { + float: none !important; + } +} + +@media (min-width: 1200px) { + .float-xl-left { + float: left !important; + } + .float-xl-right { + float: right !important; + } + .float-xl-none { + float: none !important; + } +} + +.user-select-all { + -webkit-user-select: all !important; + -moz-user-select: all !important; + -ms-user-select: all !important; + user-select: all !important; +} + +.user-select-auto { + -webkit-user-select: auto !important; + -moz-user-select: auto !important; + -ms-user-select: auto !important; + user-select: auto !important; +} + +.user-select-none { + -webkit-user-select: none !important; + -moz-user-select: none !important; + -ms-user-select: none !important; + user-select: none !important; +} + +.overflow-auto { + overflow: auto !important; +} + +.overflow-hidden { + overflow: hidden !important; +} + +.position-static { + position: static !important; +} + +.position-relative { + position: relative !important; +} + +.position-absolute { + position: absolute !important; +} + +.position-fixed { + position: fixed !important; +} + +.position-sticky { + position: -webkit-sticky !important; + position: sticky !important; +} + +.fixed-top { + position: fixed; + top: 0; + right: 0; + left: 0; + z-index: 1030; +} + +.fixed-bottom { + position: fixed; + right: 0; + bottom: 0; + left: 0; + z-index: 1030; +} + +@supports ((position: -webkit-sticky) or (position: sticky)) { + .sticky-top { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1020; + } +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +.sr-only-focusable:active, .sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + overflow: visible; + clip: auto; + white-space: normal; +} + +.shadow-sm { + box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important; +} + +.shadow { + box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; +} + +.shadow-lg { + box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important; +} + +.shadow-none { + box-shadow: none !important; +} + +.w-25 { + width: 25% !important; +} + +.w-50 { + width: 50% !important; +} + +.w-75 { + width: 75% !important; +} + +.w-100 { + width: 100% !important; +} + +.w-auto { + width: auto !important; +} + +.h-25 { + height: 25% !important; +} + +.h-50 { + height: 50% !important; +} + +.h-75 { + height: 75% !important; +} + +.h-100 { + height: 100% !important; +} + +.h-auto { + height: auto !important; +} + +.mw-100 { + max-width: 100% !important; +} + +.mh-100 { + max-height: 100% !important; +} + +.min-vw-100 { + min-width: 100vw !important; +} + +.min-vh-100 { + min-height: 100vh !important; +} + +.vw-100 { + width: 100vw !important; +} + +.vh-100 { + height: 100vh !important; +} + +.m-0 { + margin: 0 !important; +} + +.mt-0, +.my-0 { + margin-top: 0 !important; +} + +.mr-0, +.mx-0 { + margin-right: 0 !important; +} + +.mb-0, +.my-0 { + margin-bottom: 0 !important; +} + +.ml-0, +.mx-0 { + margin-left: 0 !important; +} + +.m-1 { + margin: 0.25rem !important; +} + +.mt-1, +.my-1 { + margin-top: 0.25rem !important; +} + +.mr-1, +.mx-1 { + margin-right: 0.25rem !important; +} + +.mb-1, +.my-1 { + margin-bottom: 0.25rem !important; +} + +.ml-1, +.mx-1 { + margin-left: 0.25rem !important; +} + +.m-2 { + margin: 0.5rem !important; +} + +.mt-2, +.my-2 { + margin-top: 0.5rem !important; +} + +.mr-2, +.mx-2 { + margin-right: 0.5rem !important; +} + +.mb-2, +.my-2 { + margin-bottom: 0.5rem !important; +} + +.ml-2, +.mx-2 { + margin-left: 0.5rem !important; +} + +.m-3 { + margin: 1rem !important; +} + +.mt-3, +.my-3 { + margin-top: 1rem !important; +} + +.mr-3, +.mx-3 { + margin-right: 1rem !important; +} + +.mb-3, +.my-3 { + margin-bottom: 1rem !important; +} + +.ml-3, +.mx-3 { + margin-left: 1rem !important; +} + +.m-4 { + margin: 1.5rem !important; +} + +.mt-4, +.my-4 { + margin-top: 1.5rem !important; +} + +.mr-4, +.mx-4 { + margin-right: 1.5rem !important; +} + +.mb-4, +.my-4 { + margin-bottom: 1.5rem !important; +} + +.ml-4, +.mx-4 { + margin-left: 1.5rem !important; +} + +.m-5 { + margin: 3rem !important; +} + +.mt-5, +.my-5 { + margin-top: 3rem !important; +} + +.mr-5, +.mx-5 { + margin-right: 3rem !important; +} + +.mb-5, +.my-5 { + margin-bottom: 3rem !important; +} + +.ml-5, +.mx-5 { + margin-left: 3rem !important; +} + +.p-0 { + padding: 0 !important; +} + +.pt-0, +.py-0 { + padding-top: 0 !important; +} + +.pr-0, +.px-0 { + padding-right: 0 !important; +} + +.pb-0, +.py-0 { + padding-bottom: 0 !important; +} + +.pl-0, +.px-0 { + padding-left: 0 !important; +} + +.p-1 { + padding: 0.25rem !important; +} + +.pt-1, +.py-1 { + padding-top: 0.25rem !important; +} + +.pr-1, +.px-1 { + padding-right: 0.25rem !important; +} + +.pb-1, +.py-1 { + padding-bottom: 0.25rem !important; +} + +.pl-1, +.px-1 { + padding-left: 0.25rem !important; +} + +.p-2 { + padding: 0.5rem !important; +} + +.pt-2, +.py-2 { + padding-top: 0.5rem !important; +} + +.pr-2, +.px-2 { + padding-right: 0.5rem !important; +} + +.pb-2, +.py-2 { + padding-bottom: 0.5rem !important; +} + +.pl-2, +.px-2 { + padding-left: 0.5rem !important; +} + +.p-3 { + padding: 1rem !important; +} + +.pt-3, +.py-3 { + padding-top: 1rem !important; +} + +.pr-3, +.px-3 { + padding-right: 1rem !important; +} + +.pb-3, +.py-3 { + padding-bottom: 1rem !important; +} + +.pl-3, +.px-3 { + padding-left: 1rem !important; +} + +.p-4 { + padding: 1.5rem !important; +} + +.pt-4, +.py-4 { + padding-top: 1.5rem !important; +} + +.pr-4, +.px-4 { + padding-right: 1.5rem !important; +} + +.pb-4, +.py-4 { + padding-bottom: 1.5rem !important; +} + +.pl-4, +.px-4 { + padding-left: 1.5rem !important; +} + +.p-5 { + padding: 3rem !important; +} + +.pt-5, +.py-5 { + padding-top: 3rem !important; +} + +.pr-5, +.px-5 { + padding-right: 3rem !important; +} + +.pb-5, +.py-5 { + padding-bottom: 3rem !important; +} + +.pl-5, +.px-5 { + padding-left: 3rem !important; +} + +.m-n1 { + margin: -0.25rem !important; +} + +.mt-n1, +.my-n1 { + margin-top: -0.25rem !important; +} + +.mr-n1, +.mx-n1 { + margin-right: -0.25rem !important; +} + +.mb-n1, +.my-n1 { + margin-bottom: -0.25rem !important; +} + +.ml-n1, +.mx-n1 { + margin-left: -0.25rem !important; +} + +.m-n2 { + margin: -0.5rem !important; +} + +.mt-n2, +.my-n2 { + margin-top: -0.5rem !important; +} + +.mr-n2, +.mx-n2 { + margin-right: -0.5rem !important; +} + +.mb-n2, +.my-n2 { + margin-bottom: -0.5rem !important; +} + +.ml-n2, +.mx-n2 { + margin-left: -0.5rem !important; +} + +.m-n3 { + margin: -1rem !important; +} + +.mt-n3, +.my-n3 { + margin-top: -1rem !important; +} + +.mr-n3, +.mx-n3 { + margin-right: -1rem !important; +} + +.mb-n3, +.my-n3 { + margin-bottom: -1rem !important; +} + +.ml-n3, +.mx-n3 { + margin-left: -1rem !important; +} + +.m-n4 { + margin: -1.5rem !important; +} + +.mt-n4, +.my-n4 { + margin-top: -1.5rem !important; +} + +.mr-n4, +.mx-n4 { + margin-right: -1.5rem !important; +} + +.mb-n4, +.my-n4 { + margin-bottom: -1.5rem !important; +} + +.ml-n4, +.mx-n4 { + margin-left: -1.5rem !important; +} + +.m-n5 { + margin: -3rem !important; +} + +.mt-n5, +.my-n5 { + margin-top: -3rem !important; +} + +.mr-n5, +.mx-n5 { + margin-right: -3rem !important; +} + +.mb-n5, +.my-n5 { + margin-bottom: -3rem !important; +} + +.ml-n5, +.mx-n5 { + margin-left: -3rem !important; +} + +.m-auto { + margin: auto !important; +} + +.mt-auto, +.my-auto { + margin-top: auto !important; +} + +.mr-auto, +.mx-auto { + margin-right: auto !important; +} + +.mb-auto, +.my-auto { + margin-bottom: auto !important; +} + +.ml-auto, +.mx-auto { + margin-left: auto !important; +} + +@media (min-width: 576px) { + .m-sm-0 { + margin: 0 !important; + } + .mt-sm-0, + .my-sm-0 { + margin-top: 0 !important; + } + .mr-sm-0, + .mx-sm-0 { + margin-right: 0 !important; + } + .mb-sm-0, + .my-sm-0 { + margin-bottom: 0 !important; + } + .ml-sm-0, + .mx-sm-0 { + margin-left: 0 !important; + } + .m-sm-1 { + margin: 0.25rem !important; + } + .mt-sm-1, + .my-sm-1 { + margin-top: 0.25rem !important; + } + .mr-sm-1, + .mx-sm-1 { + margin-right: 0.25rem !important; + } + .mb-sm-1, + .my-sm-1 { + margin-bottom: 0.25rem !important; + } + .ml-sm-1, + .mx-sm-1 { + margin-left: 0.25rem !important; + } + .m-sm-2 { + margin: 0.5rem !important; + } + .mt-sm-2, + .my-sm-2 { + margin-top: 0.5rem !important; + } + .mr-sm-2, + .mx-sm-2 { + margin-right: 0.5rem !important; + } + .mb-sm-2, + .my-sm-2 { + margin-bottom: 0.5rem !important; + } + .ml-sm-2, + .mx-sm-2 { + margin-left: 0.5rem !important; + } + .m-sm-3 { + margin: 1rem !important; + } + .mt-sm-3, + .my-sm-3 { + margin-top: 1rem !important; + } + .mr-sm-3, + .mx-sm-3 { + margin-right: 1rem !important; + } + .mb-sm-3, + .my-sm-3 { + margin-bottom: 1rem !important; + } + .ml-sm-3, + .mx-sm-3 { + margin-left: 1rem !important; + } + .m-sm-4 { + margin: 1.5rem !important; + } + .mt-sm-4, + .my-sm-4 { + margin-top: 1.5rem !important; + } + .mr-sm-4, + .mx-sm-4 { + margin-right: 1.5rem !important; + } + .mb-sm-4, + .my-sm-4 { + margin-bottom: 1.5rem !important; + } + .ml-sm-4, + .mx-sm-4 { + margin-left: 1.5rem !important; + } + .m-sm-5 { + margin: 3rem !important; + } + .mt-sm-5, + .my-sm-5 { + margin-top: 3rem !important; + } + .mr-sm-5, + .mx-sm-5 { + margin-right: 3rem !important; + } + .mb-sm-5, + .my-sm-5 { + margin-bottom: 3rem !important; + } + .ml-sm-5, + .mx-sm-5 { + margin-left: 3rem !important; + } + .p-sm-0 { + padding: 0 !important; + } + .pt-sm-0, + .py-sm-0 { + padding-top: 0 !important; + } + .pr-sm-0, + .px-sm-0 { + padding-right: 0 !important; + } + .pb-sm-0, + .py-sm-0 { + padding-bottom: 0 !important; + } + .pl-sm-0, + .px-sm-0 { + padding-left: 0 !important; + } + .p-sm-1 { + padding: 0.25rem !important; + } + .pt-sm-1, + .py-sm-1 { + padding-top: 0.25rem !important; + } + .pr-sm-1, + .px-sm-1 { + padding-right: 0.25rem !important; + } + .pb-sm-1, + .py-sm-1 { + padding-bottom: 0.25rem !important; + } + .pl-sm-1, + .px-sm-1 { + padding-left: 0.25rem !important; + } + .p-sm-2 { + padding: 0.5rem !important; + } + .pt-sm-2, + .py-sm-2 { + padding-top: 0.5rem !important; + } + .pr-sm-2, + .px-sm-2 { + padding-right: 0.5rem !important; + } + .pb-sm-2, + .py-sm-2 { + padding-bottom: 0.5rem !important; + } + .pl-sm-2, + .px-sm-2 { + padding-left: 0.5rem !important; + } + .p-sm-3 { + padding: 1rem !important; + } + .pt-sm-3, + .py-sm-3 { + padding-top: 1rem !important; + } + .pr-sm-3, + .px-sm-3 { + padding-right: 1rem !important; + } + .pb-sm-3, + .py-sm-3 { + padding-bottom: 1rem !important; + } + .pl-sm-3, + .px-sm-3 { + padding-left: 1rem !important; + } + .p-sm-4 { + padding: 1.5rem !important; + } + .pt-sm-4, + .py-sm-4 { + padding-top: 1.5rem !important; + } + .pr-sm-4, + .px-sm-4 { + padding-right: 1.5rem !important; + } + .pb-sm-4, + .py-sm-4 { + padding-bottom: 1.5rem !important; + } + .pl-sm-4, + .px-sm-4 { + padding-left: 1.5rem !important; + } + .p-sm-5 { + padding: 3rem !important; + } + .pt-sm-5, + .py-sm-5 { + padding-top: 3rem !important; + } + .pr-sm-5, + .px-sm-5 { + padding-right: 3rem !important; + } + .pb-sm-5, + .py-sm-5 { + padding-bottom: 3rem !important; + } + .pl-sm-5, + .px-sm-5 { + padding-left: 3rem !important; + } + .m-sm-n1 { + margin: -0.25rem !important; + } + .mt-sm-n1, + .my-sm-n1 { + margin-top: -0.25rem !important; + } + .mr-sm-n1, + .mx-sm-n1 { + margin-right: -0.25rem !important; + } + .mb-sm-n1, + .my-sm-n1 { + margin-bottom: -0.25rem !important; + } + .ml-sm-n1, + .mx-sm-n1 { + margin-left: -0.25rem !important; + } + .m-sm-n2 { + margin: -0.5rem !important; + } + .mt-sm-n2, + .my-sm-n2 { + margin-top: -0.5rem !important; + } + .mr-sm-n2, + .mx-sm-n2 { + margin-right: -0.5rem !important; + } + .mb-sm-n2, + .my-sm-n2 { + margin-bottom: -0.5rem !important; + } + .ml-sm-n2, + .mx-sm-n2 { + margin-left: -0.5rem !important; + } + .m-sm-n3 { + margin: -1rem !important; + } + .mt-sm-n3, + .my-sm-n3 { + margin-top: -1rem !important; + } + .mr-sm-n3, + .mx-sm-n3 { + margin-right: -1rem !important; + } + .mb-sm-n3, + .my-sm-n3 { + margin-bottom: -1rem !important; + } + .ml-sm-n3, + .mx-sm-n3 { + margin-left: -1rem !important; + } + .m-sm-n4 { + margin: -1.5rem !important; + } + .mt-sm-n4, + .my-sm-n4 { + margin-top: -1.5rem !important; + } + .mr-sm-n4, + .mx-sm-n4 { + margin-right: -1.5rem !important; + } + .mb-sm-n4, + .my-sm-n4 { + margin-bottom: -1.5rem !important; + } + .ml-sm-n4, + .mx-sm-n4 { + margin-left: -1.5rem !important; + } + .m-sm-n5 { + margin: -3rem !important; + } + .mt-sm-n5, + .my-sm-n5 { + margin-top: -3rem !important; + } + .mr-sm-n5, + .mx-sm-n5 { + margin-right: -3rem !important; + } + .mb-sm-n5, + .my-sm-n5 { + margin-bottom: -3rem !important; + } + .ml-sm-n5, + .mx-sm-n5 { + margin-left: -3rem !important; + } + .m-sm-auto { + margin: auto !important; + } + .mt-sm-auto, + .my-sm-auto { + margin-top: auto !important; + } + .mr-sm-auto, + .mx-sm-auto { + margin-right: auto !important; + } + .mb-sm-auto, + .my-sm-auto { + margin-bottom: auto !important; + } + .ml-sm-auto, + .mx-sm-auto { + margin-left: auto !important; + } +} + +@media (min-width: 768px) { + .m-md-0 { + margin: 0 !important; + } + .mt-md-0, + .my-md-0 { + margin-top: 0 !important; + } + .mr-md-0, + .mx-md-0 { + margin-right: 0 !important; + } + .mb-md-0, + .my-md-0 { + margin-bottom: 0 !important; + } + .ml-md-0, + .mx-md-0 { + margin-left: 0 !important; + } + .m-md-1 { + margin: 0.25rem !important; + } + .mt-md-1, + .my-md-1 { + margin-top: 0.25rem !important; + } + .mr-md-1, + .mx-md-1 { + margin-right: 0.25rem !important; + } + .mb-md-1, + .my-md-1 { + margin-bottom: 0.25rem !important; + } + .ml-md-1, + .mx-md-1 { + margin-left: 0.25rem !important; + } + .m-md-2 { + margin: 0.5rem !important; + } + .mt-md-2, + .my-md-2 { + margin-top: 0.5rem !important; + } + .mr-md-2, + .mx-md-2 { + margin-right: 0.5rem !important; + } + .mb-md-2, + .my-md-2 { + margin-bottom: 0.5rem !important; + } + .ml-md-2, + .mx-md-2 { + margin-left: 0.5rem !important; + } + .m-md-3 { + margin: 1rem !important; + } + .mt-md-3, + .my-md-3 { + margin-top: 1rem !important; + } + .mr-md-3, + .mx-md-3 { + margin-right: 1rem !important; + } + .mb-md-3, + .my-md-3 { + margin-bottom: 1rem !important; + } + .ml-md-3, + .mx-md-3 { + margin-left: 1rem !important; + } + .m-md-4 { + margin: 1.5rem !important; + } + .mt-md-4, + .my-md-4 { + margin-top: 1.5rem !important; + } + .mr-md-4, + .mx-md-4 { + margin-right: 1.5rem !important; + } + .mb-md-4, + .my-md-4 { + margin-bottom: 1.5rem !important; + } + .ml-md-4, + .mx-md-4 { + margin-left: 1.5rem !important; + } + .m-md-5 { + margin: 3rem !important; + } + .mt-md-5, + .my-md-5 { + margin-top: 3rem !important; + } + .mr-md-5, + .mx-md-5 { + margin-right: 3rem !important; + } + .mb-md-5, + .my-md-5 { + margin-bottom: 3rem !important; + } + .ml-md-5, + .mx-md-5 { + margin-left: 3rem !important; + } + .p-md-0 { + padding: 0 !important; + } + .pt-md-0, + .py-md-0 { + padding-top: 0 !important; + } + .pr-md-0, + .px-md-0 { + padding-right: 0 !important; + } + .pb-md-0, + .py-md-0 { + padding-bottom: 0 !important; + } + .pl-md-0, + .px-md-0 { + padding-left: 0 !important; + } + .p-md-1 { + padding: 0.25rem !important; + } + .pt-md-1, + .py-md-1 { + padding-top: 0.25rem !important; + } + .pr-md-1, + .px-md-1 { + padding-right: 0.25rem !important; + } + .pb-md-1, + .py-md-1 { + padding-bottom: 0.25rem !important; + } + .pl-md-1, + .px-md-1 { + padding-left: 0.25rem !important; + } + .p-md-2 { + padding: 0.5rem !important; + } + .pt-md-2, + .py-md-2 { + padding-top: 0.5rem !important; + } + .pr-md-2, + .px-md-2 { + padding-right: 0.5rem !important; + } + .pb-md-2, + .py-md-2 { + padding-bottom: 0.5rem !important; + } + .pl-md-2, + .px-md-2 { + padding-left: 0.5rem !important; + } + .p-md-3 { + padding: 1rem !important; + } + .pt-md-3, + .py-md-3 { + padding-top: 1rem !important; + } + .pr-md-3, + .px-md-3 { + padding-right: 1rem !important; + } + .pb-md-3, + .py-md-3 { + padding-bottom: 1rem !important; + } + .pl-md-3, + .px-md-3 { + padding-left: 1rem !important; + } + .p-md-4 { + padding: 1.5rem !important; + } + .pt-md-4, + .py-md-4 { + padding-top: 1.5rem !important; + } + .pr-md-4, + .px-md-4 { + padding-right: 1.5rem !important; + } + .pb-md-4, + .py-md-4 { + padding-bottom: 1.5rem !important; + } + .pl-md-4, + .px-md-4 { + padding-left: 1.5rem !important; + } + .p-md-5 { + padding: 3rem !important; + } + .pt-md-5, + .py-md-5 { + padding-top: 3rem !important; + } + .pr-md-5, + .px-md-5 { + padding-right: 3rem !important; + } + .pb-md-5, + .py-md-5 { + padding-bottom: 3rem !important; + } + .pl-md-5, + .px-md-5 { + padding-left: 3rem !important; + } + .m-md-n1 { + margin: -0.25rem !important; + } + .mt-md-n1, + .my-md-n1 { + margin-top: -0.25rem !important; + } + .mr-md-n1, + .mx-md-n1 { + margin-right: -0.25rem !important; + } + .mb-md-n1, + .my-md-n1 { + margin-bottom: -0.25rem !important; + } + .ml-md-n1, + .mx-md-n1 { + margin-left: -0.25rem !important; + } + .m-md-n2 { + margin: -0.5rem !important; + } + .mt-md-n2, + .my-md-n2 { + margin-top: -0.5rem !important; + } + .mr-md-n2, + .mx-md-n2 { + margin-right: -0.5rem !important; + } + .mb-md-n2, + .my-md-n2 { + margin-bottom: -0.5rem !important; + } + .ml-md-n2, + .mx-md-n2 { + margin-left: -0.5rem !important; + } + .m-md-n3 { + margin: -1rem !important; + } + .mt-md-n3, + .my-md-n3 { + margin-top: -1rem !important; + } + .mr-md-n3, + .mx-md-n3 { + margin-right: -1rem !important; + } + .mb-md-n3, + .my-md-n3 { + margin-bottom: -1rem !important; + } + .ml-md-n3, + .mx-md-n3 { + margin-left: -1rem !important; + } + .m-md-n4 { + margin: -1.5rem !important; + } + .mt-md-n4, + .my-md-n4 { + margin-top: -1.5rem !important; + } + .mr-md-n4, + .mx-md-n4 { + margin-right: -1.5rem !important; + } + .mb-md-n4, + .my-md-n4 { + margin-bottom: -1.5rem !important; + } + .ml-md-n4, + .mx-md-n4 { + margin-left: -1.5rem !important; + } + .m-md-n5 { + margin: -3rem !important; + } + .mt-md-n5, + .my-md-n5 { + margin-top: -3rem !important; + } + .mr-md-n5, + .mx-md-n5 { + margin-right: -3rem !important; + } + .mb-md-n5, + .my-md-n5 { + margin-bottom: -3rem !important; + } + .ml-md-n5, + .mx-md-n5 { + margin-left: -3rem !important; + } + .m-md-auto { + margin: auto !important; + } + .mt-md-auto, + .my-md-auto { + margin-top: auto !important; + } + .mr-md-auto, + .mx-md-auto { + margin-right: auto !important; + } + .mb-md-auto, + .my-md-auto { + margin-bottom: auto !important; + } + .ml-md-auto, + .mx-md-auto { + margin-left: auto !important; + } +} + +@media (min-width: 992px) { + .m-lg-0 { + margin: 0 !important; + } + .mt-lg-0, + .my-lg-0 { + margin-top: 0 !important; + } + .mr-lg-0, + .mx-lg-0 { + margin-right: 0 !important; + } + .mb-lg-0, + .my-lg-0 { + margin-bottom: 0 !important; + } + .ml-lg-0, + .mx-lg-0 { + margin-left: 0 !important; + } + .m-lg-1 { + margin: 0.25rem !important; + } + .mt-lg-1, + .my-lg-1 { + margin-top: 0.25rem !important; + } + .mr-lg-1, + .mx-lg-1 { + margin-right: 0.25rem !important; + } + .mb-lg-1, + .my-lg-1 { + margin-bottom: 0.25rem !important; + } + .ml-lg-1, + .mx-lg-1 { + margin-left: 0.25rem !important; + } + .m-lg-2 { + margin: 0.5rem !important; + } + .mt-lg-2, + .my-lg-2 { + margin-top: 0.5rem !important; + } + .mr-lg-2, + .mx-lg-2 { + margin-right: 0.5rem !important; + } + .mb-lg-2, + .my-lg-2 { + margin-bottom: 0.5rem !important; + } + .ml-lg-2, + .mx-lg-2 { + margin-left: 0.5rem !important; + } + .m-lg-3 { + margin: 1rem !important; + } + .mt-lg-3, + .my-lg-3 { + margin-top: 1rem !important; + } + .mr-lg-3, + .mx-lg-3 { + margin-right: 1rem !important; + } + .mb-lg-3, + .my-lg-3 { + margin-bottom: 1rem !important; + } + .ml-lg-3, + .mx-lg-3 { + margin-left: 1rem !important; + } + .m-lg-4 { + margin: 1.5rem !important; + } + .mt-lg-4, + .my-lg-4 { + margin-top: 1.5rem !important; + } + .mr-lg-4, + .mx-lg-4 { + margin-right: 1.5rem !important; + } + .mb-lg-4, + .my-lg-4 { + margin-bottom: 1.5rem !important; + } + .ml-lg-4, + .mx-lg-4 { + margin-left: 1.5rem !important; + } + .m-lg-5 { + margin: 3rem !important; + } + .mt-lg-5, + .my-lg-5 { + margin-top: 3rem !important; + } + .mr-lg-5, + .mx-lg-5 { + margin-right: 3rem !important; + } + .mb-lg-5, + .my-lg-5 { + margin-bottom: 3rem !important; + } + .ml-lg-5, + .mx-lg-5 { + margin-left: 3rem !important; + } + .p-lg-0 { + padding: 0 !important; + } + .pt-lg-0, + .py-lg-0 { + padding-top: 0 !important; + } + .pr-lg-0, + .px-lg-0 { + padding-right: 0 !important; + } + .pb-lg-0, + .py-lg-0 { + padding-bottom: 0 !important; + } + .pl-lg-0, + .px-lg-0 { + padding-left: 0 !important; + } + .p-lg-1 { + padding: 0.25rem !important; + } + .pt-lg-1, + .py-lg-1 { + padding-top: 0.25rem !important; + } + .pr-lg-1, + .px-lg-1 { + padding-right: 0.25rem !important; + } + .pb-lg-1, + .py-lg-1 { + padding-bottom: 0.25rem !important; + } + .pl-lg-1, + .px-lg-1 { + padding-left: 0.25rem !important; + } + .p-lg-2 { + padding: 0.5rem !important; + } + .pt-lg-2, + .py-lg-2 { + padding-top: 0.5rem !important; + } + .pr-lg-2, + .px-lg-2 { + padding-right: 0.5rem !important; + } + .pb-lg-2, + .py-lg-2 { + padding-bottom: 0.5rem !important; + } + .pl-lg-2, + .px-lg-2 { + padding-left: 0.5rem !important; + } + .p-lg-3 { + padding: 1rem !important; + } + .pt-lg-3, + .py-lg-3 { + padding-top: 1rem !important; + } + .pr-lg-3, + .px-lg-3 { + padding-right: 1rem !important; + } + .pb-lg-3, + .py-lg-3 { + padding-bottom: 1rem !important; + } + .pl-lg-3, + .px-lg-3 { + padding-left: 1rem !important; + } + .p-lg-4 { + padding: 1.5rem !important; + } + .pt-lg-4, + .py-lg-4 { + padding-top: 1.5rem !important; + } + .pr-lg-4, + .px-lg-4 { + padding-right: 1.5rem !important; + } + .pb-lg-4, + .py-lg-4 { + padding-bottom: 1.5rem !important; + } + .pl-lg-4, + .px-lg-4 { + padding-left: 1.5rem !important; + } + .p-lg-5 { + padding: 3rem !important; + } + .pt-lg-5, + .py-lg-5 { + padding-top: 3rem !important; + } + .pr-lg-5, + .px-lg-5 { + padding-right: 3rem !important; + } + .pb-lg-5, + .py-lg-5 { + padding-bottom: 3rem !important; + } + .pl-lg-5, + .px-lg-5 { + padding-left: 3rem !important; + } + .m-lg-n1 { + margin: -0.25rem !important; + } + .mt-lg-n1, + .my-lg-n1 { + margin-top: -0.25rem !important; + } + .mr-lg-n1, + .mx-lg-n1 { + margin-right: -0.25rem !important; + } + .mb-lg-n1, + .my-lg-n1 { + margin-bottom: -0.25rem !important; + } + .ml-lg-n1, + .mx-lg-n1 { + margin-left: -0.25rem !important; + } + .m-lg-n2 { + margin: -0.5rem !important; + } + .mt-lg-n2, + .my-lg-n2 { + margin-top: -0.5rem !important; + } + .mr-lg-n2, + .mx-lg-n2 { + margin-right: -0.5rem !important; + } + .mb-lg-n2, + .my-lg-n2 { + margin-bottom: -0.5rem !important; + } + .ml-lg-n2, + .mx-lg-n2 { + margin-left: -0.5rem !important; + } + .m-lg-n3 { + margin: -1rem !important; + } + .mt-lg-n3, + .my-lg-n3 { + margin-top: -1rem !important; + } + .mr-lg-n3, + .mx-lg-n3 { + margin-right: -1rem !important; + } + .mb-lg-n3, + .my-lg-n3 { + margin-bottom: -1rem !important; + } + .ml-lg-n3, + .mx-lg-n3 { + margin-left: -1rem !important; + } + .m-lg-n4 { + margin: -1.5rem !important; + } + .mt-lg-n4, + .my-lg-n4 { + margin-top: -1.5rem !important; + } + .mr-lg-n4, + .mx-lg-n4 { + margin-right: -1.5rem !important; + } + .mb-lg-n4, + .my-lg-n4 { + margin-bottom: -1.5rem !important; + } + .ml-lg-n4, + .mx-lg-n4 { + margin-left: -1.5rem !important; + } + .m-lg-n5 { + margin: -3rem !important; + } + .mt-lg-n5, + .my-lg-n5 { + margin-top: -3rem !important; + } + .mr-lg-n5, + .mx-lg-n5 { + margin-right: -3rem !important; + } + .mb-lg-n5, + .my-lg-n5 { + margin-bottom: -3rem !important; + } + .ml-lg-n5, + .mx-lg-n5 { + margin-left: -3rem !important; + } + .m-lg-auto { + margin: auto !important; + } + .mt-lg-auto, + .my-lg-auto { + margin-top: auto !important; + } + .mr-lg-auto, + .mx-lg-auto { + margin-right: auto !important; + } + .mb-lg-auto, + .my-lg-auto { + margin-bottom: auto !important; + } + .ml-lg-auto, + .mx-lg-auto { + margin-left: auto !important; + } +} + +@media (min-width: 1200px) { + .m-xl-0 { + margin: 0 !important; + } + .mt-xl-0, + .my-xl-0 { + margin-top: 0 !important; + } + .mr-xl-0, + .mx-xl-0 { + margin-right: 0 !important; + } + .mb-xl-0, + .my-xl-0 { + margin-bottom: 0 !important; + } + .ml-xl-0, + .mx-xl-0 { + margin-left: 0 !important; + } + .m-xl-1 { + margin: 0.25rem !important; + } + .mt-xl-1, + .my-xl-1 { + margin-top: 0.25rem !important; + } + .mr-xl-1, + .mx-xl-1 { + margin-right: 0.25rem !important; + } + .mb-xl-1, + .my-xl-1 { + margin-bottom: 0.25rem !important; + } + .ml-xl-1, + .mx-xl-1 { + margin-left: 0.25rem !important; + } + .m-xl-2 { + margin: 0.5rem !important; + } + .mt-xl-2, + .my-xl-2 { + margin-top: 0.5rem !important; + } + .mr-xl-2, + .mx-xl-2 { + margin-right: 0.5rem !important; + } + .mb-xl-2, + .my-xl-2 { + margin-bottom: 0.5rem !important; + } + .ml-xl-2, + .mx-xl-2 { + margin-left: 0.5rem !important; + } + .m-xl-3 { + margin: 1rem !important; + } + .mt-xl-3, + .my-xl-3 { + margin-top: 1rem !important; + } + .mr-xl-3, + .mx-xl-3 { + margin-right: 1rem !important; + } + .mb-xl-3, + .my-xl-3 { + margin-bottom: 1rem !important; + } + .ml-xl-3, + .mx-xl-3 { + margin-left: 1rem !important; + } + .m-xl-4 { + margin: 1.5rem !important; + } + .mt-xl-4, + .my-xl-4 { + margin-top: 1.5rem !important; + } + .mr-xl-4, + .mx-xl-4 { + margin-right: 1.5rem !important; + } + .mb-xl-4, + .my-xl-4 { + margin-bottom: 1.5rem !important; + } + .ml-xl-4, + .mx-xl-4 { + margin-left: 1.5rem !important; + } + .m-xl-5 { + margin: 3rem !important; + } + .mt-xl-5, + .my-xl-5 { + margin-top: 3rem !important; + } + .mr-xl-5, + .mx-xl-5 { + margin-right: 3rem !important; + } + .mb-xl-5, + .my-xl-5 { + margin-bottom: 3rem !important; + } + .ml-xl-5, + .mx-xl-5 { + margin-left: 3rem !important; + } + .p-xl-0 { + padding: 0 !important; + } + .pt-xl-0, + .py-xl-0 { + padding-top: 0 !important; + } + .pr-xl-0, + .px-xl-0 { + padding-right: 0 !important; + } + .pb-xl-0, + .py-xl-0 { + padding-bottom: 0 !important; + } + .pl-xl-0, + .px-xl-0 { + padding-left: 0 !important; + } + .p-xl-1 { + padding: 0.25rem !important; + } + .pt-xl-1, + .py-xl-1 { + padding-top: 0.25rem !important; + } + .pr-xl-1, + .px-xl-1 { + padding-right: 0.25rem !important; + } + .pb-xl-1, + .py-xl-1 { + padding-bottom: 0.25rem !important; + } + .pl-xl-1, + .px-xl-1 { + padding-left: 0.25rem !important; + } + .p-xl-2 { + padding: 0.5rem !important; + } + .pt-xl-2, + .py-xl-2 { + padding-top: 0.5rem !important; + } + .pr-xl-2, + .px-xl-2 { + padding-right: 0.5rem !important; + } + .pb-xl-2, + .py-xl-2 { + padding-bottom: 0.5rem !important; + } + .pl-xl-2, + .px-xl-2 { + padding-left: 0.5rem !important; + } + .p-xl-3 { + padding: 1rem !important; + } + .pt-xl-3, + .py-xl-3 { + padding-top: 1rem !important; + } + .pr-xl-3, + .px-xl-3 { + padding-right: 1rem !important; + } + .pb-xl-3, + .py-xl-3 { + padding-bottom: 1rem !important; + } + .pl-xl-3, + .px-xl-3 { + padding-left: 1rem !important; + } + .p-xl-4 { + padding: 1.5rem !important; + } + .pt-xl-4, + .py-xl-4 { + padding-top: 1.5rem !important; + } + .pr-xl-4, + .px-xl-4 { + padding-right: 1.5rem !important; + } + .pb-xl-4, + .py-xl-4 { + padding-bottom: 1.5rem !important; + } + .pl-xl-4, + .px-xl-4 { + padding-left: 1.5rem !important; + } + .p-xl-5 { + padding: 3rem !important; + } + .pt-xl-5, + .py-xl-5 { + padding-top: 3rem !important; + } + .pr-xl-5, + .px-xl-5 { + padding-right: 3rem !important; + } + .pb-xl-5, + .py-xl-5 { + padding-bottom: 3rem !important; + } + .pl-xl-5, + .px-xl-5 { + padding-left: 3rem !important; + } + .m-xl-n1 { + margin: -0.25rem !important; + } + .mt-xl-n1, + .my-xl-n1 { + margin-top: -0.25rem !important; + } + .mr-xl-n1, + .mx-xl-n1 { + margin-right: -0.25rem !important; + } + .mb-xl-n1, + .my-xl-n1 { + margin-bottom: -0.25rem !important; + } + .ml-xl-n1, + .mx-xl-n1 { + margin-left: -0.25rem !important; + } + .m-xl-n2 { + margin: -0.5rem !important; + } + .mt-xl-n2, + .my-xl-n2 { + margin-top: -0.5rem !important; + } + .mr-xl-n2, + .mx-xl-n2 { + margin-right: -0.5rem !important; + } + .mb-xl-n2, + .my-xl-n2 { + margin-bottom: -0.5rem !important; + } + .ml-xl-n2, + .mx-xl-n2 { + margin-left: -0.5rem !important; + } + .m-xl-n3 { + margin: -1rem !important; + } + .mt-xl-n3, + .my-xl-n3 { + margin-top: -1rem !important; + } + .mr-xl-n3, + .mx-xl-n3 { + margin-right: -1rem !important; + } + .mb-xl-n3, + .my-xl-n3 { + margin-bottom: -1rem !important; + } + .ml-xl-n3, + .mx-xl-n3 { + margin-left: -1rem !important; + } + .m-xl-n4 { + margin: -1.5rem !important; + } + .mt-xl-n4, + .my-xl-n4 { + margin-top: -1.5rem !important; + } + .mr-xl-n4, + .mx-xl-n4 { + margin-right: -1.5rem !important; + } + .mb-xl-n4, + .my-xl-n4 { + margin-bottom: -1.5rem !important; + } + .ml-xl-n4, + .mx-xl-n4 { + margin-left: -1.5rem !important; + } + .m-xl-n5 { + margin: -3rem !important; + } + .mt-xl-n5, + .my-xl-n5 { + margin-top: -3rem !important; + } + .mr-xl-n5, + .mx-xl-n5 { + margin-right: -3rem !important; + } + .mb-xl-n5, + .my-xl-n5 { + margin-bottom: -3rem !important; + } + .ml-xl-n5, + .mx-xl-n5 { + margin-left: -3rem !important; + } + .m-xl-auto { + margin: auto !important; + } + .mt-xl-auto, + .my-xl-auto { + margin-top: auto !important; + } + .mr-xl-auto, + .mx-xl-auto { + margin-right: auto !important; + } + .mb-xl-auto, + .my-xl-auto { + margin-bottom: auto !important; + } + .ml-xl-auto, + .mx-xl-auto { + margin-left: auto !important; + } +} + +.stretched-link::after { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1; + pointer-events: auto; + content: ""; + background-color: rgba(0, 0, 0, 0); +} + +.text-monospace { + font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !important; +} + +.text-justify { + text-align: justify !important; +} + +.text-wrap { + white-space: normal !important; +} + +.text-nowrap { + white-space: nowrap !important; +} + +.text-truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.text-left { + text-align: left !important; +} + +.text-right { + text-align: right !important; +} + +.text-center { + text-align: center !important; +} + +@media (min-width: 576px) { + .text-sm-left { + text-align: left !important; + } + .text-sm-right { + text-align: right !important; + } + .text-sm-center { + text-align: center !important; + } +} + +@media (min-width: 768px) { + .text-md-left { + text-align: left !important; + } + .text-md-right { + text-align: right !important; + } + .text-md-center { + text-align: center !important; + } +} + +@media (min-width: 992px) { + .text-lg-left { + text-align: left !important; + } + .text-lg-right { + text-align: right !important; + } + .text-lg-center { + text-align: center !important; + } +} + +@media (min-width: 1200px) { + .text-xl-left { + text-align: left !important; + } + .text-xl-right { + text-align: right !important; + } + .text-xl-center { + text-align: center !important; + } +} + +.text-lowercase { + text-transform: lowercase !important; +} + +.text-uppercase { + text-transform: uppercase !important; +} + +.text-capitalize { + text-transform: capitalize !important; +} + +.font-weight-light { + font-weight: 300 !important; +} + +.font-weight-lighter { + font-weight: lighter !important; +} + +.font-weight-normal { + font-weight: 400 !important; +} + +.font-weight-bold { + font-weight: 700 !important; +} + +.font-weight-bolder { + font-weight: bolder !important; +} + +.font-italic { + font-style: italic !important; +} + +.text-white { + color: #fff !important; +} + +.text-primary { + color: #007bff !important; +} + +a.text-primary:hover, a.text-primary:focus { + color: #0056b3 !important; +} + +.text-secondary { + color: #6c757d !important; +} + +a.text-secondary:hover, a.text-secondary:focus { + color: #494f54 !important; +} + +.text-success { + color: #28a745 !important; +} + +a.text-success:hover, a.text-success:focus { + color: #19692c !important; +} + +.text-info { + color: #17a2b8 !important; +} + +a.text-info:hover, a.text-info:focus { + color: #0f6674 !important; +} + +.text-warning { + color: #ffc107 !important; +} + +a.text-warning:hover, a.text-warning:focus { + color: #ba8b00 !important; +} + +.text-danger { + color: #dc3545 !important; +} + +a.text-danger:hover, a.text-danger:focus { + color: #a71d2a !important; +} + +.text-light { + color: #f8f9fa !important; +} + +a.text-light:hover, a.text-light:focus { + color: #cbd3da !important; +} + +.text-dark { + color: #343a40 !important; +} + +a.text-dark:hover, a.text-dark:focus { + color: #121416 !important; +} + +.text-body { + color: #212529 !important; +} + +.text-muted { + color: #6c757d !important; +} + +.text-black-50 { + color: rgba(0, 0, 0, 0.5) !important; +} + +.text-white-50 { + color: rgba(255, 255, 255, 0.5) !important; +} + +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} + +.text-decoration-none { + text-decoration: none !important; +} + +.text-break { + word-wrap: break-word !important; +} + +.text-reset { + color: inherit !important; +} + +.visible { + visibility: visible !important; +} + +.invisible { + visibility: hidden !important; +} + +@media print { + *, + *::before, + *::after { + text-shadow: none !important; + box-shadow: none !important; + } + a:not(.btn) { + text-decoration: underline; + } + abbr[title]::after { + content: " (" attr(title) ")"; + } + pre { + white-space: pre-wrap !important; + } + pre, + blockquote { + border: 1px solid #adb5bd; + page-break-inside: avoid; + } + thead { + display: table-header-group; + } + tr, + img { + page-break-inside: avoid; + } + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + h2, + h3 { + page-break-after: avoid; + } + @page { + size: a3; + } + body { + min-width: 992px !important; + } + .container { + min-width: 992px !important; + } + .navbar { + display: none; + } + .badge { + border: 1px solid #000; + } + .table { + border-collapse: collapse !important; + } + .table td, + .table th { + background-color: #fff !important; + } + .table-bordered th, + .table-bordered td { + border: 1px solid #dee2e6 !important; + } + .table-dark { + color: inherit; + } + .table-dark th, + .table-dark td, + .table-dark thead th, + .table-dark tbody + tbody { + border-color: #dee2e6; + } + .table .thead-dark th { + color: inherit; + border-color: #dee2e6; + } +} +/*# sourceMappingURL=bootstrap.css.map */ \ No newline at end of file diff --git a/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap/js/bootstrap.bundle.js b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap/js/bootstrap.bundle.js new file mode 100644 index 0000000000..d9236dace8 --- /dev/null +++ b/modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/wwwroot/libs/bootstrap/js/bootstrap.bundle.js @@ -0,0 +1,7033 @@ +/*! + * Bootstrap v4.5.0 (https://getbootstrap.com/) + * Copyright 2011-2020 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('jquery')) : + typeof define === 'function' && define.amd ? define(['exports', 'jquery'], factory) : + (global = global || self, factory(global.bootstrap = {}, global.jQuery)); +}(this, (function (exports, $) { 'use strict'; + + $ = $ && Object.prototype.hasOwnProperty.call($, 'default') ? $['default'] : $; + + function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); + return Constructor; + } + + function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + + return obj; + } + + function ownKeys(object, enumerableOnly) { + var keys = Object.keys(object); + + if (Object.getOwnPropertySymbols) { + var symbols = Object.getOwnPropertySymbols(object); + if (enumerableOnly) symbols = symbols.filter(function (sym) { + return Object.getOwnPropertyDescriptor(object, sym).enumerable; + }); + keys.push.apply(keys, symbols); + } + + return keys; + } + + function _objectSpread2(target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i] != null ? arguments[i] : {}; + + if (i % 2) { + ownKeys(Object(source), true).forEach(function (key) { + _defineProperty(target, key, source[key]); + }); + } else if (Object.getOwnPropertyDescriptors) { + Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); + } else { + ownKeys(Object(source)).forEach(function (key) { + Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); + }); + } + } + + return target; + } + + function _inheritsLoose(subClass, superClass) { + subClass.prototype = Object.create(superClass.prototype); + subClass.prototype.constructor = subClass; + subClass.__proto__ = superClass; + } + + /** + * -------------------------------------------------------------------------- + * Bootstrap (v4.5.0): util.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * -------------------------------------------------------------------------- + */ + /** + * ------------------------------------------------------------------------ + * Private TransitionEnd Helpers + * ------------------------------------------------------------------------ + */ + + var TRANSITION_END = 'transitionend'; + var MAX_UID = 1000000; + var MILLISECONDS_MULTIPLIER = 1000; // Shoutout AngusCroll (https://goo.gl/pxwQGp) + + function toType(obj) { + if (obj === null || typeof obj === 'undefined') { + return "" + obj; + } + + return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase(); + } + + function getSpecialTransitionEndEvent() { + return { + bindType: TRANSITION_END, + delegateType: TRANSITION_END, + handle: function handle(event) { + if ($(event.target).is(this)) { + return event.handleObj.handler.apply(this, arguments); // eslint-disable-line prefer-rest-params + } + + return undefined; + } + }; + } + + function transitionEndEmulator(duration) { + var _this = this; + + var called = false; + $(this).one(Util.TRANSITION_END, function () { + called = true; + }); + setTimeout(function () { + if (!called) { + Util.triggerTransitionEnd(_this); + } + }, duration); + return this; + } + + function setTransitionEndSupport() { + $.fn.emulateTransitionEnd = transitionEndEmulator; + $.event.special[Util.TRANSITION_END] = getSpecialTransitionEndEvent(); + } + /** + * -------------------------------------------------------------------------- + * Public Util Api + * -------------------------------------------------------------------------- + */ + + + var Util = { + TRANSITION_END: 'bsTransitionEnd', + getUID: function getUID(prefix) { + do { + // eslint-disable-next-line no-bitwise + prefix += ~~(Math.random() * MAX_UID); // "~~" acts like a faster Math.floor() here + } while (document.getElementById(prefix)); + + return prefix; + }, + getSelectorFromElement: function getSelectorFromElement(element) { + var selector = element.getAttribute('data-target'); + + if (!selector || selector === '#') { + var hrefAttr = element.getAttribute('href'); + selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : ''; + } + + try { + return document.querySelector(selector) ? selector : null; + } catch (err) { + return null; + } + }, + getTransitionDurationFromElement: function getTransitionDurationFromElement(element) { + if (!element) { + return 0; + } // Get transition-duration of the element + + + var transitionDuration = $(element).css('transition-duration'); + var transitionDelay = $(element).css('transition-delay'); + var floatTransitionDuration = parseFloat(transitionDuration); + var floatTransitionDelay = parseFloat(transitionDelay); // Return 0 if element or transition duration is not found + + if (!floatTransitionDuration && !floatTransitionDelay) { + return 0; + } // If multiple durations are defined, take the first + + + transitionDuration = transitionDuration.split(',')[0]; + transitionDelay = transitionDelay.split(',')[0]; + return (parseFloat(transitionDuration) + parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER; + }, + reflow: function reflow(element) { + return element.offsetHeight; + }, + triggerTransitionEnd: function triggerTransitionEnd(element) { + $(element).trigger(TRANSITION_END); + }, + // TODO: Remove in v5 + supportsTransitionEnd: function supportsTransitionEnd() { + return Boolean(TRANSITION_END); + }, + isElement: function isElement(obj) { + return (obj[0] || obj).nodeType; + }, + typeCheckConfig: function typeCheckConfig(componentName, config, configTypes) { + for (var property in configTypes) { + if (Object.prototype.hasOwnProperty.call(configTypes, property)) { + var expectedTypes = configTypes[property]; + var value = config[property]; + var valueType = value && Util.isElement(value) ? 'element' : toType(value); + + if (!new RegExp(expectedTypes).test(valueType)) { + throw new Error(componentName.toUpperCase() + ": " + ("Option \"" + property + "\" provided type \"" + valueType + "\" ") + ("but expected type \"" + expectedTypes + "\".")); + } + } + } + }, + findShadowRoot: function findShadowRoot(element) { + if (!document.documentElement.attachShadow) { + return null; + } // Can find the shadow root otherwise it'll return the document + + + if (typeof element.getRootNode === 'function') { + var root = element.getRootNode(); + return root instanceof ShadowRoot ? root : null; + } + + if (element instanceof ShadowRoot) { + return element; + } // when we don't find a shadow root + + + if (!element.parentNode) { + return null; + } + + return Util.findShadowRoot(element.parentNode); + }, + jQueryDetection: function jQueryDetection() { + if (typeof $ === 'undefined') { + throw new TypeError('Bootstrap\'s JavaScript requires jQuery. jQuery must be included before Bootstrap\'s JavaScript.'); + } + + var version = $.fn.jquery.split(' ')[0].split('.'); + var minMajor = 1; + var ltMajor = 2; + var minMinor = 9; + var minPatch = 1; + var maxMajor = 4; + + if (version[0] < ltMajor && version[1] < minMinor || version[0] === minMajor && version[1] === minMinor && version[2] < minPatch || version[0] >= maxMajor) { + throw new Error('Bootstrap\'s JavaScript requires at least jQuery v1.9.1 but less than v4.0.0'); + } + } + }; + Util.jQueryDetection(); + setTransitionEndSupport(); + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + var NAME = 'alert'; + var VERSION = '4.5.0'; + var DATA_KEY = 'bs.alert'; + var EVENT_KEY = "." + DATA_KEY; + var DATA_API_KEY = '.data-api'; + var JQUERY_NO_CONFLICT = $.fn[NAME]; + var SELECTOR_DISMISS = '[data-dismiss="alert"]'; + var EVENT_CLOSE = "close" + EVENT_KEY; + var EVENT_CLOSED = "closed" + EVENT_KEY; + var EVENT_CLICK_DATA_API = "click" + EVENT_KEY + DATA_API_KEY; + var CLASS_NAME_ALERT = 'alert'; + var CLASS_NAME_FADE = 'fade'; + var CLASS_NAME_SHOW = 'show'; + /** + * ------------------------------------------------------------------------ + * Class Definition + * ------------------------------------------------------------------------ + */ + + var Alert = /*#__PURE__*/function () { + function Alert(element) { + this._element = element; + } // Getters + + + var _proto = Alert.prototype; + + // Public + _proto.close = function close(element) { + var rootElement = this._element; + + if (element) { + rootElement = this._getRootElement(element); + } + + var customEvent = this._triggerCloseEvent(rootElement); + + if (customEvent.isDefaultPrevented()) { + return; + } + + this._removeElement(rootElement); + }; + + _proto.dispose = function dispose() { + $.removeData(this._element, DATA_KEY); + this._element = null; + } // Private + ; + + _proto._getRootElement = function _getRootElement(element) { + var selector = Util.getSelectorFromElement(element); + var parent = false; + + if (selector) { + parent = document.querySelector(selector); + } + + if (!parent) { + parent = $(element).closest("." + CLASS_NAME_ALERT)[0]; + } + + return parent; + }; + + _proto._triggerCloseEvent = function _triggerCloseEvent(element) { + var closeEvent = $.Event(EVENT_CLOSE); + $(element).trigger(closeEvent); + return closeEvent; + }; + + _proto._removeElement = function _removeElement(element) { + var _this = this; + + $(element).removeClass(CLASS_NAME_SHOW); + + if (!$(element).hasClass(CLASS_NAME_FADE)) { + this._destroyElement(element); + + return; + } + + var transitionDuration = Util.getTransitionDurationFromElement(element); + $(element).one(Util.TRANSITION_END, function (event) { + return _this._destroyElement(element, event); + }).emulateTransitionEnd(transitionDuration); + }; + + _proto._destroyElement = function _destroyElement(element) { + $(element).detach().trigger(EVENT_CLOSED).remove(); + } // Static + ; + + Alert._jQueryInterface = function _jQueryInterface(config) { + return this.each(function () { + var $element = $(this); + var data = $element.data(DATA_KEY); + + if (!data) { + data = new Alert(this); + $element.data(DATA_KEY, data); + } + + if (config === 'close') { + data[config](this); + } + }); + }; + + Alert._handleDismiss = function _handleDismiss(alertInstance) { + return function (event) { + if (event) { + event.preventDefault(); + } + + alertInstance.close(this); + }; + }; + + _createClass(Alert, null, [{ + key: "VERSION", + get: function get() { + return VERSION; + } + }]); + + return Alert; + }(); + /** + * ------------------------------------------------------------------------ + * Data Api implementation + * ------------------------------------------------------------------------ + */ + + + $(document).on(EVENT_CLICK_DATA_API, SELECTOR_DISMISS, Alert._handleDismiss(new Alert())); + /** + * ------------------------------------------------------------------------ + * jQuery + * ------------------------------------------------------------------------ + */ + + $.fn[NAME] = Alert._jQueryInterface; + $.fn[NAME].Constructor = Alert; + + $.fn[NAME].noConflict = function () { + $.fn[NAME] = JQUERY_NO_CONFLICT; + return Alert._jQueryInterface; + }; + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + var NAME$1 = 'button'; + var VERSION$1 = '4.5.0'; + var DATA_KEY$1 = 'bs.button'; + var EVENT_KEY$1 = "." + DATA_KEY$1; + var DATA_API_KEY$1 = '.data-api'; + var JQUERY_NO_CONFLICT$1 = $.fn[NAME$1]; + var CLASS_NAME_ACTIVE = 'active'; + var CLASS_NAME_BUTTON = 'btn'; + var CLASS_NAME_FOCUS = 'focus'; + var SELECTOR_DATA_TOGGLE_CARROT = '[data-toggle^="button"]'; + var SELECTOR_DATA_TOGGLES = '[data-toggle="buttons"]'; + var SELECTOR_DATA_TOGGLE = '[data-toggle="button"]'; + var SELECTOR_DATA_TOGGLES_BUTTONS = '[data-toggle="buttons"] .btn'; + var SELECTOR_INPUT = 'input:not([type="hidden"])'; + var SELECTOR_ACTIVE = '.active'; + var SELECTOR_BUTTON = '.btn'; + var EVENT_CLICK_DATA_API$1 = "click" + EVENT_KEY$1 + DATA_API_KEY$1; + var EVENT_FOCUS_BLUR_DATA_API = "focus" + EVENT_KEY$1 + DATA_API_KEY$1 + " " + ("blur" + EVENT_KEY$1 + DATA_API_KEY$1); + var EVENT_LOAD_DATA_API = "load" + EVENT_KEY$1 + DATA_API_KEY$1; + /** + * ------------------------------------------------------------------------ + * Class Definition + * ------------------------------------------------------------------------ + */ + + var Button = /*#__PURE__*/function () { + function Button(element) { + this._element = element; + } // Getters + + + var _proto = Button.prototype; + + // Public + _proto.toggle = function toggle() { + var triggerChangeEvent = true; + var addAriaPressed = true; + var rootElement = $(this._element).closest(SELECTOR_DATA_TOGGLES)[0]; + + if (rootElement) { + var input = this._element.querySelector(SELECTOR_INPUT); + + if (input) { + if (input.type === 'radio') { + if (input.checked && this._element.classList.contains(CLASS_NAME_ACTIVE)) { + triggerChangeEvent = false; + } else { + var activeElement = rootElement.querySelector(SELECTOR_ACTIVE); + + if (activeElement) { + $(activeElement).removeClass(CLASS_NAME_ACTIVE); + } + } + } + + if (triggerChangeEvent) { + // if it's not a radio button or checkbox don't add a pointless/invalid checked property to the input + if (input.type === 'checkbox' || input.type === 'radio') { + input.checked = !this._element.classList.contains(CLASS_NAME_ACTIVE); + } + + $(input).trigger('change'); + } + + input.focus(); + addAriaPressed = false; + } + } + + if (!(this._element.hasAttribute('disabled') || this._element.classList.contains('disabled'))) { + if (addAriaPressed) { + this._element.setAttribute('aria-pressed', !this._element.classList.contains(CLASS_NAME_ACTIVE)); + } + + if (triggerChangeEvent) { + $(this._element).toggleClass(CLASS_NAME_ACTIVE); + } + } + }; + + _proto.dispose = function dispose() { + $.removeData(this._element, DATA_KEY$1); + this._element = null; + } // Static + ; + + Button._jQueryInterface = function _jQueryInterface(config) { + return this.each(function () { + var data = $(this).data(DATA_KEY$1); + + if (!data) { + data = new Button(this); + $(this).data(DATA_KEY$1, data); + } + + if (config === 'toggle') { + data[config](); + } + }); + }; + + _createClass(Button, null, [{ + key: "VERSION", + get: function get() { + return VERSION$1; + } + }]); + + return Button; + }(); + /** + * ------------------------------------------------------------------------ + * Data Api implementation + * ------------------------------------------------------------------------ + */ + + + $(document).on(EVENT_CLICK_DATA_API$1, SELECTOR_DATA_TOGGLE_CARROT, function (event) { + var button = event.target; + var initialButton = button; + + if (!$(button).hasClass(CLASS_NAME_BUTTON)) { + button = $(button).closest(SELECTOR_BUTTON)[0]; + } + + if (!button || button.hasAttribute('disabled') || button.classList.contains('disabled')) { + event.preventDefault(); // work around Firefox bug #1540995 + } else { + var inputBtn = button.querySelector(SELECTOR_INPUT); + + if (inputBtn && (inputBtn.hasAttribute('disabled') || inputBtn.classList.contains('disabled'))) { + event.preventDefault(); // work around Firefox bug #1540995 + + return; + } + + if (initialButton.tagName === 'LABEL' && inputBtn && inputBtn.type === 'checkbox') { + event.preventDefault(); // work around event sent to label and input + } + + Button._jQueryInterface.call($(button), 'toggle'); + } + }).on(EVENT_FOCUS_BLUR_DATA_API, SELECTOR_DATA_TOGGLE_CARROT, function (event) { + var button = $(event.target).closest(SELECTOR_BUTTON)[0]; + $(button).toggleClass(CLASS_NAME_FOCUS, /^focus(in)?$/.test(event.type)); + }); + $(window).on(EVENT_LOAD_DATA_API, function () { + // ensure correct active class is set to match the controls' actual values/states + // find all checkboxes/readio buttons inside data-toggle groups + var buttons = [].slice.call(document.querySelectorAll(SELECTOR_DATA_TOGGLES_BUTTONS)); + + for (var i = 0, len = buttons.length; i < len; i++) { + var button = buttons[i]; + var input = button.querySelector(SELECTOR_INPUT); + + if (input.checked || input.hasAttribute('checked')) { + button.classList.add(CLASS_NAME_ACTIVE); + } else { + button.classList.remove(CLASS_NAME_ACTIVE); + } + } // find all button toggles + + + buttons = [].slice.call(document.querySelectorAll(SELECTOR_DATA_TOGGLE)); + + for (var _i = 0, _len = buttons.length; _i < _len; _i++) { + var _button = buttons[_i]; + + if (_button.getAttribute('aria-pressed') === 'true') { + _button.classList.add(CLASS_NAME_ACTIVE); + } else { + _button.classList.remove(CLASS_NAME_ACTIVE); + } + } + }); + /** + * ------------------------------------------------------------------------ + * jQuery + * ------------------------------------------------------------------------ + */ + + $.fn[NAME$1] = Button._jQueryInterface; + $.fn[NAME$1].Constructor = Button; + + $.fn[NAME$1].noConflict = function () { + $.fn[NAME$1] = JQUERY_NO_CONFLICT$1; + return Button._jQueryInterface; + }; + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + var NAME$2 = 'carousel'; + var VERSION$2 = '4.5.0'; + var DATA_KEY$2 = 'bs.carousel'; + var EVENT_KEY$2 = "." + DATA_KEY$2; + var DATA_API_KEY$2 = '.data-api'; + var JQUERY_NO_CONFLICT$2 = $.fn[NAME$2]; + var ARROW_LEFT_KEYCODE = 37; // KeyboardEvent.which value for left arrow key + + var ARROW_RIGHT_KEYCODE = 39; // KeyboardEvent.which value for right arrow key + + var TOUCHEVENT_COMPAT_WAIT = 500; // Time for mouse compat events to fire after touch + + var SWIPE_THRESHOLD = 40; + var Default = { + interval: 5000, + keyboard: true, + slide: false, + pause: 'hover', + wrap: true, + touch: true + }; + var DefaultType = { + interval: '(number|boolean)', + keyboard: 'boolean', + slide: '(boolean|string)', + pause: '(string|boolean)', + wrap: 'boolean', + touch: 'boolean' + }; + var DIRECTION_NEXT = 'next'; + var DIRECTION_PREV = 'prev'; + var DIRECTION_LEFT = 'left'; + var DIRECTION_RIGHT = 'right'; + var EVENT_SLIDE = "slide" + EVENT_KEY$2; + var EVENT_SLID = "slid" + EVENT_KEY$2; + var EVENT_KEYDOWN = "keydown" + EVENT_KEY$2; + var EVENT_MOUSEENTER = "mouseenter" + EVENT_KEY$2; + var EVENT_MOUSELEAVE = "mouseleave" + EVENT_KEY$2; + var EVENT_TOUCHSTART = "touchstart" + EVENT_KEY$2; + var EVENT_TOUCHMOVE = "touchmove" + EVENT_KEY$2; + var EVENT_TOUCHEND = "touchend" + EVENT_KEY$2; + var EVENT_POINTERDOWN = "pointerdown" + EVENT_KEY$2; + var EVENT_POINTERUP = "pointerup" + EVENT_KEY$2; + var EVENT_DRAG_START = "dragstart" + EVENT_KEY$2; + var EVENT_LOAD_DATA_API$1 = "load" + EVENT_KEY$2 + DATA_API_KEY$2; + var EVENT_CLICK_DATA_API$2 = "click" + EVENT_KEY$2 + DATA_API_KEY$2; + var CLASS_NAME_CAROUSEL = 'carousel'; + var CLASS_NAME_ACTIVE$1 = 'active'; + var CLASS_NAME_SLIDE = 'slide'; + var CLASS_NAME_RIGHT = 'carousel-item-right'; + var CLASS_NAME_LEFT = 'carousel-item-left'; + var CLASS_NAME_NEXT = 'carousel-item-next'; + var CLASS_NAME_PREV = 'carousel-item-prev'; + var CLASS_NAME_POINTER_EVENT = 'pointer-event'; + var SELECTOR_ACTIVE$1 = '.active'; + var SELECTOR_ACTIVE_ITEM = '.active.carousel-item'; + var SELECTOR_ITEM = '.carousel-item'; + var SELECTOR_ITEM_IMG = '.carousel-item img'; + var SELECTOR_NEXT_PREV = '.carousel-item-next, .carousel-item-prev'; + var SELECTOR_INDICATORS = '.carousel-indicators'; + var SELECTOR_DATA_SLIDE = '[data-slide], [data-slide-to]'; + var SELECTOR_DATA_RIDE = '[data-ride="carousel"]'; + var PointerType = { + TOUCH: 'touch', + PEN: 'pen' + }; + /** + * ------------------------------------------------------------------------ + * Class Definition + * ------------------------------------------------------------------------ + */ + + var Carousel = /*#__PURE__*/function () { + function Carousel(element, config) { + this._items = null; + this._interval = null; + this._activeElement = null; + this._isPaused = false; + this._isSliding = false; + this.touchTimeout = null; + this.touchStartX = 0; + this.touchDeltaX = 0; + this._config = this._getConfig(config); + this._element = element; + this._indicatorsElement = this._element.querySelector(SELECTOR_INDICATORS); + this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0; + this._pointerEvent = Boolean(window.PointerEvent || window.MSPointerEvent); + + this._addEventListeners(); + } // Getters + + + var _proto = Carousel.prototype; + + // Public + _proto.next = function next() { + if (!this._isSliding) { + this._slide(DIRECTION_NEXT); + } + }; + + _proto.nextWhenVisible = function nextWhenVisible() { + // Don't call next when the page isn't visible + // or the carousel or its parent isn't visible + if (!document.hidden && $(this._element).is(':visible') && $(this._element).css('visibility') !== 'hidden') { + this.next(); + } + }; + + _proto.prev = function prev() { + if (!this._isSliding) { + this._slide(DIRECTION_PREV); + } + }; + + _proto.pause = function pause(event) { + if (!event) { + this._isPaused = true; + } + + if (this._element.querySelector(SELECTOR_NEXT_PREV)) { + Util.triggerTransitionEnd(this._element); + this.cycle(true); + } + + clearInterval(this._interval); + this._interval = null; + }; + + _proto.cycle = function cycle(event) { + if (!event) { + this._isPaused = false; + } + + if (this._interval) { + clearInterval(this._interval); + this._interval = null; + } + + if (this._config.interval && !this._isPaused) { + this._interval = setInterval((document.visibilityState ? this.nextWhenVisible : this.next).bind(this), this._config.interval); + } + }; + + _proto.to = function to(index) { + var _this = this; + + this._activeElement = this._element.querySelector(SELECTOR_ACTIVE_ITEM); + + var activeIndex = this._getItemIndex(this._activeElement); + + if (index > this._items.length - 1 || index < 0) { + return; + } + + if (this._isSliding) { + $(this._element).one(EVENT_SLID, function () { + return _this.to(index); + }); + return; + } + + if (activeIndex === index) { + this.pause(); + this.cycle(); + return; + } + + var direction = index > activeIndex ? DIRECTION_NEXT : DIRECTION_PREV; + + this._slide(direction, this._items[index]); + }; + + _proto.dispose = function dispose() { + $(this._element).off(EVENT_KEY$2); + $.removeData(this._element, DATA_KEY$2); + this._items = null; + this._config = null; + this._element = null; + this._interval = null; + this._isPaused = null; + this._isSliding = null; + this._activeElement = null; + this._indicatorsElement = null; + } // Private + ; + + _proto._getConfig = function _getConfig(config) { + config = _objectSpread2(_objectSpread2({}, Default), config); + Util.typeCheckConfig(NAME$2, config, DefaultType); + return config; + }; + + _proto._handleSwipe = function _handleSwipe() { + var absDeltax = Math.abs(this.touchDeltaX); + + if (absDeltax <= SWIPE_THRESHOLD) { + return; + } + + var direction = absDeltax / this.touchDeltaX; + this.touchDeltaX = 0; // swipe left + + if (direction > 0) { + this.prev(); + } // swipe right + + + if (direction < 0) { + this.next(); + } + }; + + _proto._addEventListeners = function _addEventListeners() { + var _this2 = this; + + if (this._config.keyboard) { + $(this._element).on(EVENT_KEYDOWN, function (event) { + return _this2._keydown(event); + }); + } + + if (this._config.pause === 'hover') { + $(this._element).on(EVENT_MOUSEENTER, function (event) { + return _this2.pause(event); + }).on(EVENT_MOUSELEAVE, function (event) { + return _this2.cycle(event); + }); + } + + if (this._config.touch) { + this._addTouchEventListeners(); + } + }; + + _proto._addTouchEventListeners = function _addTouchEventListeners() { + var _this3 = this; + + if (!this._touchSupported) { + return; + } + + var start = function start(event) { + if (_this3._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) { + _this3.touchStartX = event.originalEvent.clientX; + } else if (!_this3._pointerEvent) { + _this3.touchStartX = event.originalEvent.touches[0].clientX; + } + }; + + var move = function move(event) { + // ensure swiping with one touch and not pinching + if (event.originalEvent.touches && event.originalEvent.touches.length > 1) { + _this3.touchDeltaX = 0; + } else { + _this3.touchDeltaX = event.originalEvent.touches[0].clientX - _this3.touchStartX; + } + }; + + var end = function end(event) { + if (_this3._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) { + _this3.touchDeltaX = event.originalEvent.clientX - _this3.touchStartX; + } + + _this3._handleSwipe(); + + if (_this3._config.pause === 'hover') { + // If it's a touch-enabled device, mouseenter/leave are fired as + // part of the mouse compatibility events on first tap - the carousel + // would stop cycling until user tapped out of it; + // here, we listen for touchend, explicitly pause the carousel + // (as if it's the second time we tap on it, mouseenter compat event + // is NOT fired) and after a timeout (to allow for mouse compatibility + // events to fire) we explicitly restart cycling + _this3.pause(); + + if (_this3.touchTimeout) { + clearTimeout(_this3.touchTimeout); + } + + _this3.touchTimeout = setTimeout(function (event) { + return _this3.cycle(event); + }, TOUCHEVENT_COMPAT_WAIT + _this3._config.interval); + } + }; + + $(this._element.querySelectorAll(SELECTOR_ITEM_IMG)).on(EVENT_DRAG_START, function (e) { + return e.preventDefault(); + }); + + if (this._pointerEvent) { + $(this._element).on(EVENT_POINTERDOWN, function (event) { + return start(event); + }); + $(this._element).on(EVENT_POINTERUP, function (event) { + return end(event); + }); + + this._element.classList.add(CLASS_NAME_POINTER_EVENT); + } else { + $(this._element).on(EVENT_TOUCHSTART, function (event) { + return start(event); + }); + $(this._element).on(EVENT_TOUCHMOVE, function (event) { + return move(event); + }); + $(this._element).on(EVENT_TOUCHEND, function (event) { + return end(event); + }); + } + }; + + _proto._keydown = function _keydown(event) { + if (/input|textarea/i.test(event.target.tagName)) { + return; + } + + switch (event.which) { + case ARROW_LEFT_KEYCODE: + event.preventDefault(); + this.prev(); + break; + + case ARROW_RIGHT_KEYCODE: + event.preventDefault(); + this.next(); + break; + } + }; + + _proto._getItemIndex = function _getItemIndex(element) { + this._items = element && element.parentNode ? [].slice.call(element.parentNode.querySelectorAll(SELECTOR_ITEM)) : []; + return this._items.indexOf(element); + }; + + _proto._getItemByDirection = function _getItemByDirection(direction, activeElement) { + var isNextDirection = direction === DIRECTION_NEXT; + var isPrevDirection = direction === DIRECTION_PREV; + + var activeIndex = this._getItemIndex(activeElement); + + var lastItemIndex = this._items.length - 1; + var isGoingToWrap = isPrevDirection && activeIndex === 0 || isNextDirection && activeIndex === lastItemIndex; + + if (isGoingToWrap && !this._config.wrap) { + return activeElement; + } + + var delta = direction === DIRECTION_PREV ? -1 : 1; + var itemIndex = (activeIndex + delta) % this._items.length; + return itemIndex === -1 ? this._items[this._items.length - 1] : this._items[itemIndex]; + }; + + _proto._triggerSlideEvent = function _triggerSlideEvent(relatedTarget, eventDirectionName) { + var targetIndex = this._getItemIndex(relatedTarget); + + var fromIndex = this._getItemIndex(this._element.querySelector(SELECTOR_ACTIVE_ITEM)); + + var slideEvent = $.Event(EVENT_SLIDE, { + relatedTarget: relatedTarget, + direction: eventDirectionName, + from: fromIndex, + to: targetIndex + }); + $(this._element).trigger(slideEvent); + return slideEvent; + }; + + _proto._setActiveIndicatorElement = function _setActiveIndicatorElement(element) { + if (this._indicatorsElement) { + var indicators = [].slice.call(this._indicatorsElement.querySelectorAll(SELECTOR_ACTIVE$1)); + $(indicators).removeClass(CLASS_NAME_ACTIVE$1); + + var nextIndicator = this._indicatorsElement.children[this._getItemIndex(element)]; + + if (nextIndicator) { + $(nextIndicator).addClass(CLASS_NAME_ACTIVE$1); + } + } + }; + + _proto._slide = function _slide(direction, element) { + var _this4 = this; + + var activeElement = this._element.querySelector(SELECTOR_ACTIVE_ITEM); + + var activeElementIndex = this._getItemIndex(activeElement); + + var nextElement = element || activeElement && this._getItemByDirection(direction, activeElement); + + var nextElementIndex = this._getItemIndex(nextElement); + + var isCycling = Boolean(this._interval); + var directionalClassName; + var orderClassName; + var eventDirectionName; + + if (direction === DIRECTION_NEXT) { + directionalClassName = CLASS_NAME_LEFT; + orderClassName = CLASS_NAME_NEXT; + eventDirectionName = DIRECTION_LEFT; + } else { + directionalClassName = CLASS_NAME_RIGHT; + orderClassName = CLASS_NAME_PREV; + eventDirectionName = DIRECTION_RIGHT; + } + + if (nextElement && $(nextElement).hasClass(CLASS_NAME_ACTIVE$1)) { + this._isSliding = false; + return; + } + + var slideEvent = this._triggerSlideEvent(nextElement, eventDirectionName); + + if (slideEvent.isDefaultPrevented()) { + return; + } + + if (!activeElement || !nextElement) { + // Some weirdness is happening, so we bail + return; + } + + this._isSliding = true; + + if (isCycling) { + this.pause(); + } + + this._setActiveIndicatorElement(nextElement); + + var slidEvent = $.Event(EVENT_SLID, { + relatedTarget: nextElement, + direction: eventDirectionName, + from: activeElementIndex, + to: nextElementIndex + }); + + if ($(this._element).hasClass(CLASS_NAME_SLIDE)) { + $(nextElement).addClass(orderClassName); + Util.reflow(nextElement); + $(activeElement).addClass(directionalClassName); + $(nextElement).addClass(directionalClassName); + var nextElementInterval = parseInt(nextElement.getAttribute('data-interval'), 10); + + if (nextElementInterval) { + this._config.defaultInterval = this._config.defaultInterval || this._config.interval; + this._config.interval = nextElementInterval; + } else { + this._config.interval = this._config.defaultInterval || this._config.interval; + } + + var transitionDuration = Util.getTransitionDurationFromElement(activeElement); + $(activeElement).one(Util.TRANSITION_END, function () { + $(nextElement).removeClass(directionalClassName + " " + orderClassName).addClass(CLASS_NAME_ACTIVE$1); + $(activeElement).removeClass(CLASS_NAME_ACTIVE$1 + " " + orderClassName + " " + directionalClassName); + _this4._isSliding = false; + setTimeout(function () { + return $(_this4._element).trigger(slidEvent); + }, 0); + }).emulateTransitionEnd(transitionDuration); + } else { + $(activeElement).removeClass(CLASS_NAME_ACTIVE$1); + $(nextElement).addClass(CLASS_NAME_ACTIVE$1); + this._isSliding = false; + $(this._element).trigger(slidEvent); + } + + if (isCycling) { + this.cycle(); + } + } // Static + ; + + Carousel._jQueryInterface = function _jQueryInterface(config) { + return this.each(function () { + var data = $(this).data(DATA_KEY$2); + + var _config = _objectSpread2(_objectSpread2({}, Default), $(this).data()); + + if (typeof config === 'object') { + _config = _objectSpread2(_objectSpread2({}, _config), config); + } + + var action = typeof config === 'string' ? config : _config.slide; + + if (!data) { + data = new Carousel(this, _config); + $(this).data(DATA_KEY$2, data); + } + + if (typeof config === 'number') { + data.to(config); + } else if (typeof action === 'string') { + if (typeof data[action] === 'undefined') { + throw new TypeError("No method named \"" + action + "\""); + } + + data[action](); + } else if (_config.interval && _config.ride) { + data.pause(); + data.cycle(); + } + }); + }; + + Carousel._dataApiClickHandler = function _dataApiClickHandler(event) { + var selector = Util.getSelectorFromElement(this); + + if (!selector) { + return; + } + + var target = $(selector)[0]; + + if (!target || !$(target).hasClass(CLASS_NAME_CAROUSEL)) { + return; + } + + var config = _objectSpread2(_objectSpread2({}, $(target).data()), $(this).data()); + + var slideIndex = this.getAttribute('data-slide-to'); + + if (slideIndex) { + config.interval = false; + } + + Carousel._jQueryInterface.call($(target), config); + + if (slideIndex) { + $(target).data(DATA_KEY$2).to(slideIndex); + } + + event.preventDefault(); + }; + + _createClass(Carousel, null, [{ + key: "VERSION", + get: function get() { + return VERSION$2; + } + }, { + key: "Default", + get: function get() { + return Default; + } + }]); + + return Carousel; + }(); + /** + * ------------------------------------------------------------------------ + * Data Api implementation + * ------------------------------------------------------------------------ + */ + + + $(document).on(EVENT_CLICK_DATA_API$2, SELECTOR_DATA_SLIDE, Carousel._dataApiClickHandler); + $(window).on(EVENT_LOAD_DATA_API$1, function () { + var carousels = [].slice.call(document.querySelectorAll(SELECTOR_DATA_RIDE)); + + for (var i = 0, len = carousels.length; i < len; i++) { + var $carousel = $(carousels[i]); + + Carousel._jQueryInterface.call($carousel, $carousel.data()); + } + }); + /** + * ------------------------------------------------------------------------ + * jQuery + * ------------------------------------------------------------------------ + */ + + $.fn[NAME$2] = Carousel._jQueryInterface; + $.fn[NAME$2].Constructor = Carousel; + + $.fn[NAME$2].noConflict = function () { + $.fn[NAME$2] = JQUERY_NO_CONFLICT$2; + return Carousel._jQueryInterface; + }; + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + var NAME$3 = 'collapse'; + var VERSION$3 = '4.5.0'; + var DATA_KEY$3 = 'bs.collapse'; + var EVENT_KEY$3 = "." + DATA_KEY$3; + var DATA_API_KEY$3 = '.data-api'; + var JQUERY_NO_CONFLICT$3 = $.fn[NAME$3]; + var Default$1 = { + toggle: true, + parent: '' + }; + var DefaultType$1 = { + toggle: 'boolean', + parent: '(string|element)' + }; + var EVENT_SHOW = "show" + EVENT_KEY$3; + var EVENT_SHOWN = "shown" + EVENT_KEY$3; + var EVENT_HIDE = "hide" + EVENT_KEY$3; + var EVENT_HIDDEN = "hidden" + EVENT_KEY$3; + var EVENT_CLICK_DATA_API$3 = "click" + EVENT_KEY$3 + DATA_API_KEY$3; + var CLASS_NAME_SHOW$1 = 'show'; + var CLASS_NAME_COLLAPSE = 'collapse'; + var CLASS_NAME_COLLAPSING = 'collapsing'; + var CLASS_NAME_COLLAPSED = 'collapsed'; + var DIMENSION_WIDTH = 'width'; + var DIMENSION_HEIGHT = 'height'; + var SELECTOR_ACTIVES = '.show, .collapsing'; + var SELECTOR_DATA_TOGGLE$1 = '[data-toggle="collapse"]'; + /** + * ------------------------------------------------------------------------ + * Class Definition + * ------------------------------------------------------------------------ + */ + + var Collapse = /*#__PURE__*/function () { + function Collapse(element, config) { + this._isTransitioning = false; + this._element = element; + this._config = this._getConfig(config); + this._triggerArray = [].slice.call(document.querySelectorAll("[data-toggle=\"collapse\"][href=\"#" + element.id + "\"]," + ("[data-toggle=\"collapse\"][data-target=\"#" + element.id + "\"]"))); + var toggleList = [].slice.call(document.querySelectorAll(SELECTOR_DATA_TOGGLE$1)); + + for (var i = 0, len = toggleList.length; i < len; i++) { + var elem = toggleList[i]; + var selector = Util.getSelectorFromElement(elem); + var filterElement = [].slice.call(document.querySelectorAll(selector)).filter(function (foundElem) { + return foundElem === element; + }); + + if (selector !== null && filterElement.length > 0) { + this._selector = selector; + + this._triggerArray.push(elem); + } + } + + this._parent = this._config.parent ? this._getParent() : null; + + if (!this._config.parent) { + this._addAriaAndCollapsedClass(this._element, this._triggerArray); + } + + if (this._config.toggle) { + this.toggle(); + } + } // Getters + + + var _proto = Collapse.prototype; + + // Public + _proto.toggle = function toggle() { + if ($(this._element).hasClass(CLASS_NAME_SHOW$1)) { + this.hide(); + } else { + this.show(); + } + }; + + _proto.show = function show() { + var _this = this; + + if (this._isTransitioning || $(this._element).hasClass(CLASS_NAME_SHOW$1)) { + return; + } + + var actives; + var activesData; + + if (this._parent) { + actives = [].slice.call(this._parent.querySelectorAll(SELECTOR_ACTIVES)).filter(function (elem) { + if (typeof _this._config.parent === 'string') { + return elem.getAttribute('data-parent') === _this._config.parent; + } + + return elem.classList.contains(CLASS_NAME_COLLAPSE); + }); + + if (actives.length === 0) { + actives = null; + } + } + + if (actives) { + activesData = $(actives).not(this._selector).data(DATA_KEY$3); + + if (activesData && activesData._isTransitioning) { + return; + } + } + + var startEvent = $.Event(EVENT_SHOW); + $(this._element).trigger(startEvent); + + if (startEvent.isDefaultPrevented()) { + return; + } + + if (actives) { + Collapse._jQueryInterface.call($(actives).not(this._selector), 'hide'); + + if (!activesData) { + $(actives).data(DATA_KEY$3, null); + } + } + + var dimension = this._getDimension(); + + $(this._element).removeClass(CLASS_NAME_COLLAPSE).addClass(CLASS_NAME_COLLAPSING); + this._element.style[dimension] = 0; + + if (this._triggerArray.length) { + $(this._triggerArray).removeClass(CLASS_NAME_COLLAPSED).attr('aria-expanded', true); + } + + this.setTransitioning(true); + + var complete = function complete() { + $(_this._element).removeClass(CLASS_NAME_COLLAPSING).addClass(CLASS_NAME_COLLAPSE + " " + CLASS_NAME_SHOW$1); + _this._element.style[dimension] = ''; + + _this.setTransitioning(false); + + $(_this._element).trigger(EVENT_SHOWN); + }; + + var capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1); + var scrollSize = "scroll" + capitalizedDimension; + var transitionDuration = Util.getTransitionDurationFromElement(this._element); + $(this._element).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration); + this._element.style[dimension] = this._element[scrollSize] + "px"; + }; + + _proto.hide = function hide() { + var _this2 = this; + + if (this._isTransitioning || !$(this._element).hasClass(CLASS_NAME_SHOW$1)) { + return; + } + + var startEvent = $.Event(EVENT_HIDE); + $(this._element).trigger(startEvent); + + if (startEvent.isDefaultPrevented()) { + return; + } + + var dimension = this._getDimension(); + + this._element.style[dimension] = this._element.getBoundingClientRect()[dimension] + "px"; + Util.reflow(this._element); + $(this._element).addClass(CLASS_NAME_COLLAPSING).removeClass(CLASS_NAME_COLLAPSE + " " + CLASS_NAME_SHOW$1); + var triggerArrayLength = this._triggerArray.length; + + if (triggerArrayLength > 0) { + for (var i = 0; i < triggerArrayLength; i++) { + var trigger = this._triggerArray[i]; + var selector = Util.getSelectorFromElement(trigger); + + if (selector !== null) { + var $elem = $([].slice.call(document.querySelectorAll(selector))); + + if (!$elem.hasClass(CLASS_NAME_SHOW$1)) { + $(trigger).addClass(CLASS_NAME_COLLAPSED).attr('aria-expanded', false); + } + } + } + } + + this.setTransitioning(true); + + var complete = function complete() { + _this2.setTransitioning(false); + + $(_this2._element).removeClass(CLASS_NAME_COLLAPSING).addClass(CLASS_NAME_COLLAPSE).trigger(EVENT_HIDDEN); + }; + + this._element.style[dimension] = ''; + var transitionDuration = Util.getTransitionDurationFromElement(this._element); + $(this._element).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration); + }; + + _proto.setTransitioning = function setTransitioning(isTransitioning) { + this._isTransitioning = isTransitioning; + }; + + _proto.dispose = function dispose() { + $.removeData(this._element, DATA_KEY$3); + this._config = null; + this._parent = null; + this._element = null; + this._triggerArray = null; + this._isTransitioning = null; + } // Private + ; + + _proto._getConfig = function _getConfig(config) { + config = _objectSpread2(_objectSpread2({}, Default$1), config); + config.toggle = Boolean(config.toggle); // Coerce string values + + Util.typeCheckConfig(NAME$3, config, DefaultType$1); + return config; + }; + + _proto._getDimension = function _getDimension() { + var hasWidth = $(this._element).hasClass(DIMENSION_WIDTH); + return hasWidth ? DIMENSION_WIDTH : DIMENSION_HEIGHT; + }; + + _proto._getParent = function _getParent() { + var _this3 = this; + + var parent; + + if (Util.isElement(this._config.parent)) { + parent = this._config.parent; // It's a jQuery object + + if (typeof this._config.parent.jquery !== 'undefined') { + parent = this._config.parent[0]; + } + } else { + parent = document.querySelector(this._config.parent); + } + + var selector = "[data-toggle=\"collapse\"][data-parent=\"" + this._config.parent + "\"]"; + var children = [].slice.call(parent.querySelectorAll(selector)); + $(children).each(function (i, element) { + _this3._addAriaAndCollapsedClass(Collapse._getTargetFromElement(element), [element]); + }); + return parent; + }; + + _proto._addAriaAndCollapsedClass = function _addAriaAndCollapsedClass(element, triggerArray) { + var isOpen = $(element).hasClass(CLASS_NAME_SHOW$1); + + if (triggerArray.length) { + $(triggerArray).toggleClass(CLASS_NAME_COLLAPSED, !isOpen).attr('aria-expanded', isOpen); + } + } // Static + ; + + Collapse._getTargetFromElement = function _getTargetFromElement(element) { + var selector = Util.getSelectorFromElement(element); + return selector ? document.querySelector(selector) : null; + }; + + Collapse._jQueryInterface = function _jQueryInterface(config) { + return this.each(function () { + var $this = $(this); + var data = $this.data(DATA_KEY$3); + + var _config = _objectSpread2(_objectSpread2(_objectSpread2({}, Default$1), $this.data()), typeof config === 'object' && config ? config : {}); + + if (!data && _config.toggle && typeof config === 'string' && /show|hide/.test(config)) { + _config.toggle = false; + } + + if (!data) { + data = new Collapse(this, _config); + $this.data(DATA_KEY$3, data); + } + + if (typeof config === 'string') { + if (typeof data[config] === 'undefined') { + throw new TypeError("No method named \"" + config + "\""); + } + + data[config](); + } + }); + }; + + _createClass(Collapse, null, [{ + key: "VERSION", + get: function get() { + return VERSION$3; + } + }, { + key: "Default", + get: function get() { + return Default$1; + } + }]); + + return Collapse; + }(); + /** + * ------------------------------------------------------------------------ + * Data Api implementation + * ------------------------------------------------------------------------ + */ + + + $(document).on(EVENT_CLICK_DATA_API$3, SELECTOR_DATA_TOGGLE$1, function (event) { + // preventDefault only for elements (which change the URL) not inside the collapsible element + if (event.currentTarget.tagName === 'A') { + event.preventDefault(); + } + + var $trigger = $(this); + var selector = Util.getSelectorFromElement(this); + var selectors = [].slice.call(document.querySelectorAll(selector)); + $(selectors).each(function () { + var $target = $(this); + var data = $target.data(DATA_KEY$3); + var config = data ? 'toggle' : $trigger.data(); + + Collapse._jQueryInterface.call($target, config); + }); + }); + /** + * ------------------------------------------------------------------------ + * jQuery + * ------------------------------------------------------------------------ + */ + + $.fn[NAME$3] = Collapse._jQueryInterface; + $.fn[NAME$3].Constructor = Collapse; + + $.fn[NAME$3].noConflict = function () { + $.fn[NAME$3] = JQUERY_NO_CONFLICT$3; + return Collapse._jQueryInterface; + }; + + /**! + * @fileOverview Kickass library to create and place poppers near their reference elements. + * @version 1.16.0 + * @license + * Copyright (c) 2016 Federico Zivolo and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + var isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined' && typeof navigator !== 'undefined'; + + var timeoutDuration = function () { + var longerTimeoutBrowsers = ['Edge', 'Trident', 'Firefox']; + for (var i = 0; i < longerTimeoutBrowsers.length; i += 1) { + if (isBrowser && navigator.userAgent.indexOf(longerTimeoutBrowsers[i]) >= 0) { + return 1; + } + } + return 0; + }(); + + function microtaskDebounce(fn) { + var called = false; + return function () { + if (called) { + return; + } + called = true; + window.Promise.resolve().then(function () { + called = false; + fn(); + }); + }; + } + + function taskDebounce(fn) { + var scheduled = false; + return function () { + if (!scheduled) { + scheduled = true; + setTimeout(function () { + scheduled = false; + fn(); + }, timeoutDuration); + } + }; + } + + var supportsMicroTasks = isBrowser && window.Promise; + + /** + * Create a debounced version of a method, that's asynchronously deferred + * but called in the minimum time possible. + * + * @method + * @memberof Popper.Utils + * @argument {Function} fn + * @returns {Function} + */ + var debounce = supportsMicroTasks ? microtaskDebounce : taskDebounce; + + /** + * Check if the given variable is a function + * @method + * @memberof Popper.Utils + * @argument {Any} functionToCheck - variable to check + * @returns {Boolean} answer to: is a function? + */ + function isFunction(functionToCheck) { + var getType = {}; + return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]'; + } + + /** + * Get CSS computed property of the given element + * @method + * @memberof Popper.Utils + * @argument {Eement} element + * @argument {String} property + */ + function getStyleComputedProperty(element, property) { + if (element.nodeType !== 1) { + return []; + } + // NOTE: 1 DOM access here + var window = element.ownerDocument.defaultView; + var css = window.getComputedStyle(element, null); + return property ? css[property] : css; + } + + /** + * Returns the parentNode or the host of the element + * @method + * @memberof Popper.Utils + * @argument {Element} element + * @returns {Element} parent + */ + function getParentNode(element) { + if (element.nodeName === 'HTML') { + return element; + } + return element.parentNode || element.host; + } + + /** + * Returns the scrolling parent of the given element + * @method + * @memberof Popper.Utils + * @argument {Element} element + * @returns {Element} scroll parent + */ + function getScrollParent(element) { + // Return body, `getScroll` will take care to get the correct `scrollTop` from it + if (!element) { + return document.body; + } + + switch (element.nodeName) { + case 'HTML': + case 'BODY': + return element.ownerDocument.body; + case '#document': + return element.body; + } + + // Firefox want us to check `-x` and `-y` variations as well + + var _getStyleComputedProp = getStyleComputedProperty(element), + overflow = _getStyleComputedProp.overflow, + overflowX = _getStyleComputedProp.overflowX, + overflowY = _getStyleComputedProp.overflowY; + + if (/(auto|scroll|overlay)/.test(overflow + overflowY + overflowX)) { + return element; + } + + return getScrollParent(getParentNode(element)); + } + + /** + * Returns the reference node of the reference object, or the reference object itself. + * @method + * @memberof Popper.Utils + * @param {Element|Object} reference - the reference element (the popper will be relative to this) + * @returns {Element} parent + */ + function getReferenceNode(reference) { + return reference && reference.referenceNode ? reference.referenceNode : reference; + } + + var isIE11 = isBrowser && !!(window.MSInputMethodContext && document.documentMode); + var isIE10 = isBrowser && /MSIE 10/.test(navigator.userAgent); + + /** + * Determines if the browser is Internet Explorer + * @method + * @memberof Popper.Utils + * @param {Number} version to check + * @returns {Boolean} isIE + */ + function isIE(version) { + if (version === 11) { + return isIE11; + } + if (version === 10) { + return isIE10; + } + return isIE11 || isIE10; + } + + /** + * Returns the offset parent of the given element + * @method + * @memberof Popper.Utils + * @argument {Element} element + * @returns {Element} offset parent + */ + function getOffsetParent(element) { + if (!element) { + return document.documentElement; + } + + var noOffsetParent = isIE(10) ? document.body : null; + + // NOTE: 1 DOM access here + var offsetParent = element.offsetParent || null; + // Skip hidden elements which don't have an offsetParent + while (offsetParent === noOffsetParent && element.nextElementSibling) { + offsetParent = (element = element.nextElementSibling).offsetParent; + } + + var nodeName = offsetParent && offsetParent.nodeName; + + if (!nodeName || nodeName === 'BODY' || nodeName === 'HTML') { + return element ? element.ownerDocument.documentElement : document.documentElement; + } + + // .offsetParent will return the closest TH, TD or TABLE in case + // no offsetParent is present, I hate this job... + if (['TH', 'TD', 'TABLE'].indexOf(offsetParent.nodeName) !== -1 && getStyleComputedProperty(offsetParent, 'position') === 'static') { + return getOffsetParent(offsetParent); + } + + return offsetParent; + } + + function isOffsetContainer(element) { + var nodeName = element.nodeName; + + if (nodeName === 'BODY') { + return false; + } + return nodeName === 'HTML' || getOffsetParent(element.firstElementChild) === element; + } + + /** + * Finds the root node (document, shadowDOM root) of the given element + * @method + * @memberof Popper.Utils + * @argument {Element} node + * @returns {Element} root node + */ + function getRoot(node) { + if (node.parentNode !== null) { + return getRoot(node.parentNode); + } + + return node; + } + + /** + * Finds the offset parent common to the two provided nodes + * @method + * @memberof Popper.Utils + * @argument {Element} element1 + * @argument {Element} element2 + * @returns {Element} common offset parent + */ + function findCommonOffsetParent(element1, element2) { + // This check is needed to avoid errors in case one of the elements isn't defined for any reason + if (!element1 || !element1.nodeType || !element2 || !element2.nodeType) { + return document.documentElement; + } + + // Here we make sure to give as "start" the element that comes first in the DOM + var order = element1.compareDocumentPosition(element2) & Node.DOCUMENT_POSITION_FOLLOWING; + var start = order ? element1 : element2; + var end = order ? element2 : element1; + + // Get common ancestor container + var range = document.createRange(); + range.setStart(start, 0); + range.setEnd(end, 0); + var commonAncestorContainer = range.commonAncestorContainer; + + // Both nodes are inside #document + + if (element1 !== commonAncestorContainer && element2 !== commonAncestorContainer || start.contains(end)) { + if (isOffsetContainer(commonAncestorContainer)) { + return commonAncestorContainer; + } + + return getOffsetParent(commonAncestorContainer); + } + + // one of the nodes is inside shadowDOM, find which one + var element1root = getRoot(element1); + if (element1root.host) { + return findCommonOffsetParent(element1root.host, element2); + } else { + return findCommonOffsetParent(element1, getRoot(element2).host); + } + } + + /** + * Gets the scroll value of the given element in the given side (top and left) + * @method + * @memberof Popper.Utils + * @argument {Element} element + * @argument {String} side `top` or `left` + * @returns {number} amount of scrolled pixels + */ + function getScroll(element) { + var side = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'top'; + + var upperSide = side === 'top' ? 'scrollTop' : 'scrollLeft'; + var nodeName = element.nodeName; + + if (nodeName === 'BODY' || nodeName === 'HTML') { + var html = element.ownerDocument.documentElement; + var scrollingElement = element.ownerDocument.scrollingElement || html; + return scrollingElement[upperSide]; + } + + return element[upperSide]; + } + + /* + * Sum or subtract the element scroll values (left and top) from a given rect object + * @method + * @memberof Popper.Utils + * @param {Object} rect - Rect object you want to change + * @param {HTMLElement} element - The element from the function reads the scroll values + * @param {Boolean} subtract - set to true if you want to subtract the scroll values + * @return {Object} rect - The modifier rect object + */ + function includeScroll(rect, element) { + var subtract = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; + + var scrollTop = getScroll(element, 'top'); + var scrollLeft = getScroll(element, 'left'); + var modifier = subtract ? -1 : 1; + rect.top += scrollTop * modifier; + rect.bottom += scrollTop * modifier; + rect.left += scrollLeft * modifier; + rect.right += scrollLeft * modifier; + return rect; + } + + /* + * Helper to detect borders of a given element + * @method + * @memberof Popper.Utils + * @param {CSSStyleDeclaration} styles + * Result of `getStyleComputedProperty` on the given element + * @param {String} axis - `x` or `y` + * @return {number} borders - The borders size of the given axis + */ + + function getBordersSize(styles, axis) { + var sideA = axis === 'x' ? 'Left' : 'Top'; + var sideB = sideA === 'Left' ? 'Right' : 'Bottom'; + + return parseFloat(styles['border' + sideA + 'Width'], 10) + parseFloat(styles['border' + sideB + 'Width'], 10); + } + + function getSize(axis, body, html, computedStyle) { + return Math.max(body['offset' + axis], body['scroll' + axis], html['client' + axis], html['offset' + axis], html['scroll' + axis], isIE(10) ? parseInt(html['offset' + axis]) + parseInt(computedStyle['margin' + (axis === 'Height' ? 'Top' : 'Left')]) + parseInt(computedStyle['margin' + (axis === 'Height' ? 'Bottom' : 'Right')]) : 0); + } + + function getWindowSizes(document) { + var body = document.body; + var html = document.documentElement; + var computedStyle = isIE(10) && getComputedStyle(html); + + return { + height: getSize('Height', body, html, computedStyle), + width: getSize('Width', body, html, computedStyle) + }; + } + + var classCallCheck = function (instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + }; + + var createClass = function () { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + return function (Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; + }; + }(); + + + + + + var defineProperty = function (obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + + return obj; + }; + + var _extends = Object.assign || function (target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + + return target; + }; + + /** + * Given element offsets, generate an output similar to getBoundingClientRect + * @method + * @memberof Popper.Utils + * @argument {Object} offsets + * @returns {Object} ClientRect like output + */ + function getClientRect(offsets) { + return _extends({}, offsets, { + right: offsets.left + offsets.width, + bottom: offsets.top + offsets.height + }); + } + + /** + * Get bounding client rect of given element + * @method + * @memberof Popper.Utils + * @param {HTMLElement} element + * @return {Object} client rect + */ + function getBoundingClientRect(element) { + var rect = {}; + + // IE10 10 FIX: Please, don't ask, the element isn't + // considered in DOM in some circumstances... + // This isn't reproducible in IE10 compatibility mode of IE11 + try { + if (isIE(10)) { + rect = element.getBoundingClientRect(); + var scrollTop = getScroll(element, 'top'); + var scrollLeft = getScroll(element, 'left'); + rect.top += scrollTop; + rect.left += scrollLeft; + rect.bottom += scrollTop; + rect.right += scrollLeft; + } else { + rect = element.getBoundingClientRect(); + } + } catch (e) {} + + var result = { + left: rect.left, + top: rect.top, + width: rect.right - rect.left, + height: rect.bottom - rect.top + }; + + // subtract scrollbar size from sizes + var sizes = element.nodeName === 'HTML' ? getWindowSizes(element.ownerDocument) : {}; + var width = sizes.width || element.clientWidth || result.width; + var height = sizes.height || element.clientHeight || result.height; + + var horizScrollbar = element.offsetWidth - width; + var vertScrollbar = element.offsetHeight - height; + + // if an hypothetical scrollbar is detected, we must be sure it's not a `border` + // we make this check conditional for performance reasons + if (horizScrollbar || vertScrollbar) { + var styles = getStyleComputedProperty(element); + horizScrollbar -= getBordersSize(styles, 'x'); + vertScrollbar -= getBordersSize(styles, 'y'); + + result.width -= horizScrollbar; + result.height -= vertScrollbar; + } + + return getClientRect(result); + } + + function getOffsetRectRelativeToArbitraryNode(children, parent) { + var fixedPosition = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; + + var isIE10 = isIE(10); + var isHTML = parent.nodeName === 'HTML'; + var childrenRect = getBoundingClientRect(children); + var parentRect = getBoundingClientRect(parent); + var scrollParent = getScrollParent(children); + + var styles = getStyleComputedProperty(parent); + var borderTopWidth = parseFloat(styles.borderTopWidth, 10); + var borderLeftWidth = parseFloat(styles.borderLeftWidth, 10); + + // In cases where the parent is fixed, we must ignore negative scroll in offset calc + if (fixedPosition && isHTML) { + parentRect.top = Math.max(parentRect.top, 0); + parentRect.left = Math.max(parentRect.left, 0); + } + var offsets = getClientRect({ + top: childrenRect.top - parentRect.top - borderTopWidth, + left: childrenRect.left - parentRect.left - borderLeftWidth, + width: childrenRect.width, + height: childrenRect.height + }); + offsets.marginTop = 0; + offsets.marginLeft = 0; + + // Subtract margins of documentElement in case it's being used as parent + // we do this only on HTML because it's the only element that behaves + // differently when margins are applied to it. The margins are included in + // the box of the documentElement, in the other cases not. + if (!isIE10 && isHTML) { + var marginTop = parseFloat(styles.marginTop, 10); + var marginLeft = parseFloat(styles.marginLeft, 10); + + offsets.top -= borderTopWidth - marginTop; + offsets.bottom -= borderTopWidth - marginTop; + offsets.left -= borderLeftWidth - marginLeft; + offsets.right -= borderLeftWidth - marginLeft; + + // Attach marginTop and marginLeft because in some circumstances we may need them + offsets.marginTop = marginTop; + offsets.marginLeft = marginLeft; + } + + if (isIE10 && !fixedPosition ? parent.contains(scrollParent) : parent === scrollParent && scrollParent.nodeName !== 'BODY') { + offsets = includeScroll(offsets, parent); + } + + return offsets; + } + + function getViewportOffsetRectRelativeToArtbitraryNode(element) { + var excludeScroll = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + + var html = element.ownerDocument.documentElement; + var relativeOffset = getOffsetRectRelativeToArbitraryNode(element, html); + var width = Math.max(html.clientWidth, window.innerWidth || 0); + var height = Math.max(html.clientHeight, window.innerHeight || 0); + + var scrollTop = !excludeScroll ? getScroll(html) : 0; + var scrollLeft = !excludeScroll ? getScroll(html, 'left') : 0; + + var offset = { + top: scrollTop - relativeOffset.top + relativeOffset.marginTop, + left: scrollLeft - relativeOffset.left + relativeOffset.marginLeft, + width: width, + height: height + }; + + return getClientRect(offset); + } + + /** + * Check if the given element is fixed or is inside a fixed parent + * @method + * @memberof Popper.Utils + * @argument {Element} element + * @argument {Element} customContainer + * @returns {Boolean} answer to "isFixed?" + */ + function isFixed(element) { + var nodeName = element.nodeName; + if (nodeName === 'BODY' || nodeName === 'HTML') { + return false; + } + if (getStyleComputedProperty(element, 'position') === 'fixed') { + return true; + } + var parentNode = getParentNode(element); + if (!parentNode) { + return false; + } + return isFixed(parentNode); + } + + /** + * Finds the first parent of an element that has a transformed property defined + * @method + * @memberof Popper.Utils + * @argument {Element} element + * @returns {Element} first transformed parent or documentElement + */ + + function getFixedPositionOffsetParent(element) { + // This check is needed to avoid errors in case one of the elements isn't defined for any reason + if (!element || !element.parentElement || isIE()) { + return document.documentElement; + } + var el = element.parentElement; + while (el && getStyleComputedProperty(el, 'transform') === 'none') { + el = el.parentElement; + } + return el || document.documentElement; + } + + /** + * Computed the boundaries limits and return them + * @method + * @memberof Popper.Utils + * @param {HTMLElement} popper + * @param {HTMLElement} reference + * @param {number} padding + * @param {HTMLElement} boundariesElement - Element used to define the boundaries + * @param {Boolean} fixedPosition - Is in fixed position mode + * @returns {Object} Coordinates of the boundaries + */ + function getBoundaries(popper, reference, padding, boundariesElement) { + var fixedPosition = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; + + // NOTE: 1 DOM access here + + var boundaries = { top: 0, left: 0 }; + var offsetParent = fixedPosition ? getFixedPositionOffsetParent(popper) : findCommonOffsetParent(popper, getReferenceNode(reference)); + + // Handle viewport case + if (boundariesElement === 'viewport') { + boundaries = getViewportOffsetRectRelativeToArtbitraryNode(offsetParent, fixedPosition); + } else { + // Handle other cases based on DOM element used as boundaries + var boundariesNode = void 0; + if (boundariesElement === 'scrollParent') { + boundariesNode = getScrollParent(getParentNode(reference)); + if (boundariesNode.nodeName === 'BODY') { + boundariesNode = popper.ownerDocument.documentElement; + } + } else if (boundariesElement === 'window') { + boundariesNode = popper.ownerDocument.documentElement; + } else { + boundariesNode = boundariesElement; + } + + var offsets = getOffsetRectRelativeToArbitraryNode(boundariesNode, offsetParent, fixedPosition); + + // In case of HTML, we need a different computation + if (boundariesNode.nodeName === 'HTML' && !isFixed(offsetParent)) { + var _getWindowSizes = getWindowSizes(popper.ownerDocument), + height = _getWindowSizes.height, + width = _getWindowSizes.width; + + boundaries.top += offsets.top - offsets.marginTop; + boundaries.bottom = height + offsets.top; + boundaries.left += offsets.left - offsets.marginLeft; + boundaries.right = width + offsets.left; + } else { + // for all the other DOM elements, this one is good + boundaries = offsets; + } + } + + // Add paddings + padding = padding || 0; + var isPaddingNumber = typeof padding === 'number'; + boundaries.left += isPaddingNumber ? padding : padding.left || 0; + boundaries.top += isPaddingNumber ? padding : padding.top || 0; + boundaries.right -= isPaddingNumber ? padding : padding.right || 0; + boundaries.bottom -= isPaddingNumber ? padding : padding.bottom || 0; + + return boundaries; + } + + function getArea(_ref) { + var width = _ref.width, + height = _ref.height; + + return width * height; + } + + /** + * Utility used to transform the `auto` placement to the placement with more + * available space. + * @method + * @memberof Popper.Utils + * @argument {Object} data - The data object generated by update method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function computeAutoPlacement(placement, refRect, popper, reference, boundariesElement) { + var padding = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 0; + + if (placement.indexOf('auto') === -1) { + return placement; + } + + var boundaries = getBoundaries(popper, reference, padding, boundariesElement); + + var rects = { + top: { + width: boundaries.width, + height: refRect.top - boundaries.top + }, + right: { + width: boundaries.right - refRect.right, + height: boundaries.height + }, + bottom: { + width: boundaries.width, + height: boundaries.bottom - refRect.bottom + }, + left: { + width: refRect.left - boundaries.left, + height: boundaries.height + } + }; + + var sortedAreas = Object.keys(rects).map(function (key) { + return _extends({ + key: key + }, rects[key], { + area: getArea(rects[key]) + }); + }).sort(function (a, b) { + return b.area - a.area; + }); + + var filteredAreas = sortedAreas.filter(function (_ref2) { + var width = _ref2.width, + height = _ref2.height; + return width >= popper.clientWidth && height >= popper.clientHeight; + }); + + var computedPlacement = filteredAreas.length > 0 ? filteredAreas[0].key : sortedAreas[0].key; + + var variation = placement.split('-')[1]; + + return computedPlacement + (variation ? '-' + variation : ''); + } + + /** + * Get offsets to the reference element + * @method + * @memberof Popper.Utils + * @param {Object} state + * @param {Element} popper - the popper element + * @param {Element} reference - the reference element (the popper will be relative to this) + * @param {Element} fixedPosition - is in fixed position mode + * @returns {Object} An object containing the offsets which will be applied to the popper + */ + function getReferenceOffsets(state, popper, reference) { + var fixedPosition = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; + + var commonOffsetParent = fixedPosition ? getFixedPositionOffsetParent(popper) : findCommonOffsetParent(popper, getReferenceNode(reference)); + return getOffsetRectRelativeToArbitraryNode(reference, commonOffsetParent, fixedPosition); + } + + /** + * Get the outer sizes of the given element (offset size + margins) + * @method + * @memberof Popper.Utils + * @argument {Element} element + * @returns {Object} object containing width and height properties + */ + function getOuterSizes(element) { + var window = element.ownerDocument.defaultView; + var styles = window.getComputedStyle(element); + var x = parseFloat(styles.marginTop || 0) + parseFloat(styles.marginBottom || 0); + var y = parseFloat(styles.marginLeft || 0) + parseFloat(styles.marginRight || 0); + var result = { + width: element.offsetWidth + y, + height: element.offsetHeight + x + }; + return result; + } + + /** + * Get the opposite placement of the given one + * @method + * @memberof Popper.Utils + * @argument {String} placement + * @returns {String} flipped placement + */ + function getOppositePlacement(placement) { + var hash = { left: 'right', right: 'left', bottom: 'top', top: 'bottom' }; + return placement.replace(/left|right|bottom|top/g, function (matched) { + return hash[matched]; + }); + } + + /** + * Get offsets to the popper + * @method + * @memberof Popper.Utils + * @param {Object} position - CSS position the Popper will get applied + * @param {HTMLElement} popper - the popper element + * @param {Object} referenceOffsets - the reference offsets (the popper will be relative to this) + * @param {String} placement - one of the valid placement options + * @returns {Object} popperOffsets - An object containing the offsets which will be applied to the popper + */ + function getPopperOffsets(popper, referenceOffsets, placement) { + placement = placement.split('-')[0]; + + // Get popper node sizes + var popperRect = getOuterSizes(popper); + + // Add position, width and height to our offsets object + var popperOffsets = { + width: popperRect.width, + height: popperRect.height + }; + + // depending by the popper placement we have to compute its offsets slightly differently + var isHoriz = ['right', 'left'].indexOf(placement) !== -1; + var mainSide = isHoriz ? 'top' : 'left'; + var secondarySide = isHoriz ? 'left' : 'top'; + var measurement = isHoriz ? 'height' : 'width'; + var secondaryMeasurement = !isHoriz ? 'height' : 'width'; + + popperOffsets[mainSide] = referenceOffsets[mainSide] + referenceOffsets[measurement] / 2 - popperRect[measurement] / 2; + if (placement === secondarySide) { + popperOffsets[secondarySide] = referenceOffsets[secondarySide] - popperRect[secondaryMeasurement]; + } else { + popperOffsets[secondarySide] = referenceOffsets[getOppositePlacement(secondarySide)]; + } + + return popperOffsets; + } + + /** + * Mimics the `find` method of Array + * @method + * @memberof Popper.Utils + * @argument {Array} arr + * @argument prop + * @argument value + * @returns index or -1 + */ + function find(arr, check) { + // use native find if supported + if (Array.prototype.find) { + return arr.find(check); + } + + // use `filter` to obtain the same behavior of `find` + return arr.filter(check)[0]; + } + + /** + * Return the index of the matching object + * @method + * @memberof Popper.Utils + * @argument {Array} arr + * @argument prop + * @argument value + * @returns index or -1 + */ + function findIndex(arr, prop, value) { + // use native findIndex if supported + if (Array.prototype.findIndex) { + return arr.findIndex(function (cur) { + return cur[prop] === value; + }); + } + + // use `find` + `indexOf` if `findIndex` isn't supported + var match = find(arr, function (obj) { + return obj[prop] === value; + }); + return arr.indexOf(match); + } + + /** + * Loop trough the list of modifiers and run them in order, + * each of them will then edit the data object. + * @method + * @memberof Popper.Utils + * @param {dataObject} data + * @param {Array} modifiers + * @param {String} ends - Optional modifier name used as stopper + * @returns {dataObject} + */ + function runModifiers(modifiers, data, ends) { + var modifiersToRun = ends === undefined ? modifiers : modifiers.slice(0, findIndex(modifiers, 'name', ends)); + + modifiersToRun.forEach(function (modifier) { + if (modifier['function']) { + // eslint-disable-line dot-notation + console.warn('`modifier.function` is deprecated, use `modifier.fn`!'); + } + var fn = modifier['function'] || modifier.fn; // eslint-disable-line dot-notation + if (modifier.enabled && isFunction(fn)) { + // Add properties to offsets to make them a complete clientRect object + // we do this before each modifier to make sure the previous one doesn't + // mess with these values + data.offsets.popper = getClientRect(data.offsets.popper); + data.offsets.reference = getClientRect(data.offsets.reference); + + data = fn(data, modifier); + } + }); + + return data; + } + + /** + * Updates the position of the popper, computing the new offsets and applying + * the new style.
+ * Prefer `scheduleUpdate` over `update` because of performance reasons. + * @method + * @memberof Popper + */ + function update() { + // if popper is destroyed, don't perform any further update + if (this.state.isDestroyed) { + return; + } + + var data = { + instance: this, + styles: {}, + arrowStyles: {}, + attributes: {}, + flipped: false, + offsets: {} + }; + + // compute reference element offsets + data.offsets.reference = getReferenceOffsets(this.state, this.popper, this.reference, this.options.positionFixed); + + // compute auto placement, store placement inside the data object, + // modifiers will be able to edit `placement` if needed + // and refer to originalPlacement to know the original value + data.placement = computeAutoPlacement(this.options.placement, data.offsets.reference, this.popper, this.reference, this.options.modifiers.flip.boundariesElement, this.options.modifiers.flip.padding); + + // store the computed placement inside `originalPlacement` + data.originalPlacement = data.placement; + + data.positionFixed = this.options.positionFixed; + + // compute the popper offsets + data.offsets.popper = getPopperOffsets(this.popper, data.offsets.reference, data.placement); + + data.offsets.popper.position = this.options.positionFixed ? 'fixed' : 'absolute'; + + // run the modifiers + data = runModifiers(this.modifiers, data); + + // the first `update` will call `onCreate` callback + // the other ones will call `onUpdate` callback + if (!this.state.isCreated) { + this.state.isCreated = true; + this.options.onCreate(data); + } else { + this.options.onUpdate(data); + } + } + + /** + * Helper used to know if the given modifier is enabled. + * @method + * @memberof Popper.Utils + * @returns {Boolean} + */ + function isModifierEnabled(modifiers, modifierName) { + return modifiers.some(function (_ref) { + var name = _ref.name, + enabled = _ref.enabled; + return enabled && name === modifierName; + }); + } + + /** + * Get the prefixed supported property name + * @method + * @memberof Popper.Utils + * @argument {String} property (camelCase) + * @returns {String} prefixed property (camelCase or PascalCase, depending on the vendor prefix) + */ + function getSupportedPropertyName(property) { + var prefixes = [false, 'ms', 'Webkit', 'Moz', 'O']; + var upperProp = property.charAt(0).toUpperCase() + property.slice(1); + + for (var i = 0; i < prefixes.length; i++) { + var prefix = prefixes[i]; + var toCheck = prefix ? '' + prefix + upperProp : property; + if (typeof document.body.style[toCheck] !== 'undefined') { + return toCheck; + } + } + return null; + } + + /** + * Destroys the popper. + * @method + * @memberof Popper + */ + function destroy() { + this.state.isDestroyed = true; + + // touch DOM only if `applyStyle` modifier is enabled + if (isModifierEnabled(this.modifiers, 'applyStyle')) { + this.popper.removeAttribute('x-placement'); + this.popper.style.position = ''; + this.popper.style.top = ''; + this.popper.style.left = ''; + this.popper.style.right = ''; + this.popper.style.bottom = ''; + this.popper.style.willChange = ''; + this.popper.style[getSupportedPropertyName('transform')] = ''; + } + + this.disableEventListeners(); + + // remove the popper if user explicitly asked for the deletion on destroy + // do not use `remove` because IE11 doesn't support it + if (this.options.removeOnDestroy) { + this.popper.parentNode.removeChild(this.popper); + } + return this; + } + + /** + * Get the window associated with the element + * @argument {Element} element + * @returns {Window} + */ + function getWindow(element) { + var ownerDocument = element.ownerDocument; + return ownerDocument ? ownerDocument.defaultView : window; + } + + function attachToScrollParents(scrollParent, event, callback, scrollParents) { + var isBody = scrollParent.nodeName === 'BODY'; + var target = isBody ? scrollParent.ownerDocument.defaultView : scrollParent; + target.addEventListener(event, callback, { passive: true }); + + if (!isBody) { + attachToScrollParents(getScrollParent(target.parentNode), event, callback, scrollParents); + } + scrollParents.push(target); + } + + /** + * Setup needed event listeners used to update the popper position + * @method + * @memberof Popper.Utils + * @private + */ + function setupEventListeners(reference, options, state, updateBound) { + // Resize event listener on window + state.updateBound = updateBound; + getWindow(reference).addEventListener('resize', state.updateBound, { passive: true }); + + // Scroll event listener on scroll parents + var scrollElement = getScrollParent(reference); + attachToScrollParents(scrollElement, 'scroll', state.updateBound, state.scrollParents); + state.scrollElement = scrollElement; + state.eventsEnabled = true; + + return state; + } + + /** + * It will add resize/scroll events and start recalculating + * position of the popper element when they are triggered. + * @method + * @memberof Popper + */ + function enableEventListeners() { + if (!this.state.eventsEnabled) { + this.state = setupEventListeners(this.reference, this.options, this.state, this.scheduleUpdate); + } + } + + /** + * Remove event listeners used to update the popper position + * @method + * @memberof Popper.Utils + * @private + */ + function removeEventListeners(reference, state) { + // Remove resize event listener on window + getWindow(reference).removeEventListener('resize', state.updateBound); + + // Remove scroll event listener on scroll parents + state.scrollParents.forEach(function (target) { + target.removeEventListener('scroll', state.updateBound); + }); + + // Reset state + state.updateBound = null; + state.scrollParents = []; + state.scrollElement = null; + state.eventsEnabled = false; + return state; + } + + /** + * It will remove resize/scroll events and won't recalculate popper position + * when they are triggered. It also won't trigger `onUpdate` callback anymore, + * unless you call `update` method manually. + * @method + * @memberof Popper + */ + function disableEventListeners() { + if (this.state.eventsEnabled) { + cancelAnimationFrame(this.scheduleUpdate); + this.state = removeEventListeners(this.reference, this.state); + } + } + + /** + * Tells if a given input is a number + * @method + * @memberof Popper.Utils + * @param {*} input to check + * @return {Boolean} + */ + function isNumeric(n) { + return n !== '' && !isNaN(parseFloat(n)) && isFinite(n); + } + + /** + * Set the style to the given popper + * @method + * @memberof Popper.Utils + * @argument {Element} element - Element to apply the style to + * @argument {Object} styles + * Object with a list of properties and values which will be applied to the element + */ + function setStyles(element, styles) { + Object.keys(styles).forEach(function (prop) { + var unit = ''; + // add unit if the value is numeric and is one of the following + if (['width', 'height', 'top', 'right', 'bottom', 'left'].indexOf(prop) !== -1 && isNumeric(styles[prop])) { + unit = 'px'; + } + element.style[prop] = styles[prop] + unit; + }); + } + + /** + * Set the attributes to the given popper + * @method + * @memberof Popper.Utils + * @argument {Element} element - Element to apply the attributes to + * @argument {Object} styles + * Object with a list of properties and values which will be applied to the element + */ + function setAttributes(element, attributes) { + Object.keys(attributes).forEach(function (prop) { + var value = attributes[prop]; + if (value !== false) { + element.setAttribute(prop, attributes[prop]); + } else { + element.removeAttribute(prop); + } + }); + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by `update` method + * @argument {Object} data.styles - List of style properties - values to apply to popper element + * @argument {Object} data.attributes - List of attribute properties - values to apply to popper element + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The same data object + */ + function applyStyle(data) { + // any property present in `data.styles` will be applied to the popper, + // in this way we can make the 3rd party modifiers add custom styles to it + // Be aware, modifiers could override the properties defined in the previous + // lines of this modifier! + setStyles(data.instance.popper, data.styles); + + // any property present in `data.attributes` will be applied to the popper, + // they will be set as HTML attributes of the element + setAttributes(data.instance.popper, data.attributes); + + // if arrowElement is defined and arrowStyles has some properties + if (data.arrowElement && Object.keys(data.arrowStyles).length) { + setStyles(data.arrowElement, data.arrowStyles); + } + + return data; + } + + /** + * Set the x-placement attribute before everything else because it could be used + * to add margins to the popper margins needs to be calculated to get the + * correct popper offsets. + * @method + * @memberof Popper.modifiers + * @param {HTMLElement} reference - The reference element used to position the popper + * @param {HTMLElement} popper - The HTML element used as popper + * @param {Object} options - Popper.js options + */ + function applyStyleOnLoad(reference, popper, options, modifierOptions, state) { + // compute reference element offsets + var referenceOffsets = getReferenceOffsets(state, popper, reference, options.positionFixed); + + // compute auto placement, store placement inside the data object, + // modifiers will be able to edit `placement` if needed + // and refer to originalPlacement to know the original value + var placement = computeAutoPlacement(options.placement, referenceOffsets, popper, reference, options.modifiers.flip.boundariesElement, options.modifiers.flip.padding); + + popper.setAttribute('x-placement', placement); + + // Apply `position` to popper before anything else because + // without the position applied we can't guarantee correct computations + setStyles(popper, { position: options.positionFixed ? 'fixed' : 'absolute' }); + + return options; + } + + /** + * @function + * @memberof Popper.Utils + * @argument {Object} data - The data object generated by `update` method + * @argument {Boolean} shouldRound - If the offsets should be rounded at all + * @returns {Object} The popper's position offsets rounded + * + * The tale of pixel-perfect positioning. It's still not 100% perfect, but as + * good as it can be within reason. + * Discussion here: https://github.com/FezVrasta/popper.js/pull/715 + * + * Low DPI screens cause a popper to be blurry if not using full pixels (Safari + * as well on High DPI screens). + * + * Firefox prefers no rounding for positioning and does not have blurriness on + * high DPI screens. + * + * Only horizontal placement and left/right values need to be considered. + */ + function getRoundedOffsets(data, shouldRound) { + var _data$offsets = data.offsets, + popper = _data$offsets.popper, + reference = _data$offsets.reference; + var round = Math.round, + floor = Math.floor; + + var noRound = function noRound(v) { + return v; + }; + + var referenceWidth = round(reference.width); + var popperWidth = round(popper.width); + + var isVertical = ['left', 'right'].indexOf(data.placement) !== -1; + var isVariation = data.placement.indexOf('-') !== -1; + var sameWidthParity = referenceWidth % 2 === popperWidth % 2; + var bothOddWidth = referenceWidth % 2 === 1 && popperWidth % 2 === 1; + + var horizontalToInteger = !shouldRound ? noRound : isVertical || isVariation || sameWidthParity ? round : floor; + var verticalToInteger = !shouldRound ? noRound : round; + + return { + left: horizontalToInteger(bothOddWidth && !isVariation && shouldRound ? popper.left - 1 : popper.left), + top: verticalToInteger(popper.top), + bottom: verticalToInteger(popper.bottom), + right: horizontalToInteger(popper.right) + }; + } + + var isFirefox = isBrowser && /Firefox/i.test(navigator.userAgent); + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by `update` method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function computeStyle(data, options) { + var x = options.x, + y = options.y; + var popper = data.offsets.popper; + + // Remove this legacy support in Popper.js v2 + + var legacyGpuAccelerationOption = find(data.instance.modifiers, function (modifier) { + return modifier.name === 'applyStyle'; + }).gpuAcceleration; + if (legacyGpuAccelerationOption !== undefined) { + console.warn('WARNING: `gpuAcceleration` option moved to `computeStyle` modifier and will not be supported in future versions of Popper.js!'); + } + var gpuAcceleration = legacyGpuAccelerationOption !== undefined ? legacyGpuAccelerationOption : options.gpuAcceleration; + + var offsetParent = getOffsetParent(data.instance.popper); + var offsetParentRect = getBoundingClientRect(offsetParent); + + // Styles + var styles = { + position: popper.position + }; + + var offsets = getRoundedOffsets(data, window.devicePixelRatio < 2 || !isFirefox); + + var sideA = x === 'bottom' ? 'top' : 'bottom'; + var sideB = y === 'right' ? 'left' : 'right'; + + // if gpuAcceleration is set to `true` and transform is supported, + // we use `translate3d` to apply the position to the popper we + // automatically use the supported prefixed version if needed + var prefixedProperty = getSupportedPropertyName('transform'); + + // now, let's make a step back and look at this code closely (wtf?) + // If the content of the popper grows once it's been positioned, it + // may happen that the popper gets misplaced because of the new content + // overflowing its reference element + // To avoid this problem, we provide two options (x and y), which allow + // the consumer to define the offset origin. + // If we position a popper on top of a reference element, we can set + // `x` to `top` to make the popper grow towards its top instead of + // its bottom. + var left = void 0, + top = void 0; + if (sideA === 'bottom') { + // when offsetParent is the positioning is relative to the bottom of the screen (excluding the scrollbar) + // and not the bottom of the html element + if (offsetParent.nodeName === 'HTML') { + top = -offsetParent.clientHeight + offsets.bottom; + } else { + top = -offsetParentRect.height + offsets.bottom; + } + } else { + top = offsets.top; + } + if (sideB === 'right') { + if (offsetParent.nodeName === 'HTML') { + left = -offsetParent.clientWidth + offsets.right; + } else { + left = -offsetParentRect.width + offsets.right; + } + } else { + left = offsets.left; + } + if (gpuAcceleration && prefixedProperty) { + styles[prefixedProperty] = 'translate3d(' + left + 'px, ' + top + 'px, 0)'; + styles[sideA] = 0; + styles[sideB] = 0; + styles.willChange = 'transform'; + } else { + // othwerise, we use the standard `top`, `left`, `bottom` and `right` properties + var invertTop = sideA === 'bottom' ? -1 : 1; + var invertLeft = sideB === 'right' ? -1 : 1; + styles[sideA] = top * invertTop; + styles[sideB] = left * invertLeft; + styles.willChange = sideA + ', ' + sideB; + } + + // Attributes + var attributes = { + 'x-placement': data.placement + }; + + // Update `data` attributes, styles and arrowStyles + data.attributes = _extends({}, attributes, data.attributes); + data.styles = _extends({}, styles, data.styles); + data.arrowStyles = _extends({}, data.offsets.arrow, data.arrowStyles); + + return data; + } + + /** + * Helper used to know if the given modifier depends from another one.
+ * It checks if the needed modifier is listed and enabled. + * @method + * @memberof Popper.Utils + * @param {Array} modifiers - list of modifiers + * @param {String} requestingName - name of requesting modifier + * @param {String} requestedName - name of requested modifier + * @returns {Boolean} + */ + function isModifierRequired(modifiers, requestingName, requestedName) { + var requesting = find(modifiers, function (_ref) { + var name = _ref.name; + return name === requestingName; + }); + + var isRequired = !!requesting && modifiers.some(function (modifier) { + return modifier.name === requestedName && modifier.enabled && modifier.order < requesting.order; + }); + + if (!isRequired) { + var _requesting = '`' + requestingName + '`'; + var requested = '`' + requestedName + '`'; + console.warn(requested + ' modifier is required by ' + _requesting + ' modifier in order to work, be sure to include it before ' + _requesting + '!'); + } + return isRequired; + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by update method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function arrow(data, options) { + var _data$offsets$arrow; + + // arrow depends on keepTogether in order to work + if (!isModifierRequired(data.instance.modifiers, 'arrow', 'keepTogether')) { + return data; + } + + var arrowElement = options.element; + + // if arrowElement is a string, suppose it's a CSS selector + if (typeof arrowElement === 'string') { + arrowElement = data.instance.popper.querySelector(arrowElement); + + // if arrowElement is not found, don't run the modifier + if (!arrowElement) { + return data; + } + } else { + // if the arrowElement isn't a query selector we must check that the + // provided DOM node is child of its popper node + if (!data.instance.popper.contains(arrowElement)) { + console.warn('WARNING: `arrow.element` must be child of its popper element!'); + return data; + } + } + + var placement = data.placement.split('-')[0]; + var _data$offsets = data.offsets, + popper = _data$offsets.popper, + reference = _data$offsets.reference; + + var isVertical = ['left', 'right'].indexOf(placement) !== -1; + + var len = isVertical ? 'height' : 'width'; + var sideCapitalized = isVertical ? 'Top' : 'Left'; + var side = sideCapitalized.toLowerCase(); + var altSide = isVertical ? 'left' : 'top'; + var opSide = isVertical ? 'bottom' : 'right'; + var arrowElementSize = getOuterSizes(arrowElement)[len]; + + // + // extends keepTogether behavior making sure the popper and its + // reference have enough pixels in conjunction + // + + // top/left side + if (reference[opSide] - arrowElementSize < popper[side]) { + data.offsets.popper[side] -= popper[side] - (reference[opSide] - arrowElementSize); + } + // bottom/right side + if (reference[side] + arrowElementSize > popper[opSide]) { + data.offsets.popper[side] += reference[side] + arrowElementSize - popper[opSide]; + } + data.offsets.popper = getClientRect(data.offsets.popper); + + // compute center of the popper + var center = reference[side] + reference[len] / 2 - arrowElementSize / 2; + + // Compute the sideValue using the updated popper offsets + // take popper margin in account because we don't have this info available + var css = getStyleComputedProperty(data.instance.popper); + var popperMarginSide = parseFloat(css['margin' + sideCapitalized], 10); + var popperBorderSide = parseFloat(css['border' + sideCapitalized + 'Width'], 10); + var sideValue = center - data.offsets.popper[side] - popperMarginSide - popperBorderSide; + + // prevent arrowElement from being placed not contiguously to its popper + sideValue = Math.max(Math.min(popper[len] - arrowElementSize, sideValue), 0); + + data.arrowElement = arrowElement; + data.offsets.arrow = (_data$offsets$arrow = {}, defineProperty(_data$offsets$arrow, side, Math.round(sideValue)), defineProperty(_data$offsets$arrow, altSide, ''), _data$offsets$arrow); + + return data; + } + + /** + * Get the opposite placement variation of the given one + * @method + * @memberof Popper.Utils + * @argument {String} placement variation + * @returns {String} flipped placement variation + */ + function getOppositeVariation(variation) { + if (variation === 'end') { + return 'start'; + } else if (variation === 'start') { + return 'end'; + } + return variation; + } + + /** + * List of accepted placements to use as values of the `placement` option.
+ * Valid placements are: + * - `auto` + * - `top` + * - `right` + * - `bottom` + * - `left` + * + * Each placement can have a variation from this list: + * - `-start` + * - `-end` + * + * Variations are interpreted easily if you think of them as the left to right + * written languages. Horizontally (`top` and `bottom`), `start` is left and `end` + * is right.
+ * Vertically (`left` and `right`), `start` is top and `end` is bottom. + * + * Some valid examples are: + * - `top-end` (on top of reference, right aligned) + * - `right-start` (on right of reference, top aligned) + * - `bottom` (on bottom, centered) + * - `auto-end` (on the side with more space available, alignment depends by placement) + * + * @static + * @type {Array} + * @enum {String} + * @readonly + * @method placements + * @memberof Popper + */ + var placements = ['auto-start', 'auto', 'auto-end', 'top-start', 'top', 'top-end', 'right-start', 'right', 'right-end', 'bottom-end', 'bottom', 'bottom-start', 'left-end', 'left', 'left-start']; + + // Get rid of `auto` `auto-start` and `auto-end` + var validPlacements = placements.slice(3); + + /** + * Given an initial placement, returns all the subsequent placements + * clockwise (or counter-clockwise). + * + * @method + * @memberof Popper.Utils + * @argument {String} placement - A valid placement (it accepts variations) + * @argument {Boolean} counter - Set to true to walk the placements counterclockwise + * @returns {Array} placements including their variations + */ + function clockwise(placement) { + var counter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + + var index = validPlacements.indexOf(placement); + var arr = validPlacements.slice(index + 1).concat(validPlacements.slice(0, index)); + return counter ? arr.reverse() : arr; + } + + var BEHAVIORS = { + FLIP: 'flip', + CLOCKWISE: 'clockwise', + COUNTERCLOCKWISE: 'counterclockwise' + }; + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by update method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function flip(data, options) { + // if `inner` modifier is enabled, we can't use the `flip` modifier + if (isModifierEnabled(data.instance.modifiers, 'inner')) { + return data; + } + + if (data.flipped && data.placement === data.originalPlacement) { + // seems like flip is trying to loop, probably there's not enough space on any of the flippable sides + return data; + } + + var boundaries = getBoundaries(data.instance.popper, data.instance.reference, options.padding, options.boundariesElement, data.positionFixed); + + var placement = data.placement.split('-')[0]; + var placementOpposite = getOppositePlacement(placement); + var variation = data.placement.split('-')[1] || ''; + + var flipOrder = []; + + switch (options.behavior) { + case BEHAVIORS.FLIP: + flipOrder = [placement, placementOpposite]; + break; + case BEHAVIORS.CLOCKWISE: + flipOrder = clockwise(placement); + break; + case BEHAVIORS.COUNTERCLOCKWISE: + flipOrder = clockwise(placement, true); + break; + default: + flipOrder = options.behavior; + } + + flipOrder.forEach(function (step, index) { + if (placement !== step || flipOrder.length === index + 1) { + return data; + } + + placement = data.placement.split('-')[0]; + placementOpposite = getOppositePlacement(placement); + + var popperOffsets = data.offsets.popper; + var refOffsets = data.offsets.reference; + + // using floor because the reference offsets may contain decimals we are not going to consider here + var floor = Math.floor; + var overlapsRef = placement === 'left' && floor(popperOffsets.right) > floor(refOffsets.left) || placement === 'right' && floor(popperOffsets.left) < floor(refOffsets.right) || placement === 'top' && floor(popperOffsets.bottom) > floor(refOffsets.top) || placement === 'bottom' && floor(popperOffsets.top) < floor(refOffsets.bottom); + + var overflowsLeft = floor(popperOffsets.left) < floor(boundaries.left); + var overflowsRight = floor(popperOffsets.right) > floor(boundaries.right); + var overflowsTop = floor(popperOffsets.top) < floor(boundaries.top); + var overflowsBottom = floor(popperOffsets.bottom) > floor(boundaries.bottom); + + var overflowsBoundaries = placement === 'left' && overflowsLeft || placement === 'right' && overflowsRight || placement === 'top' && overflowsTop || placement === 'bottom' && overflowsBottom; + + // flip the variation if required + var isVertical = ['top', 'bottom'].indexOf(placement) !== -1; + + // flips variation if reference element overflows boundaries + var flippedVariationByRef = !!options.flipVariations && (isVertical && variation === 'start' && overflowsLeft || isVertical && variation === 'end' && overflowsRight || !isVertical && variation === 'start' && overflowsTop || !isVertical && variation === 'end' && overflowsBottom); + + // flips variation if popper content overflows boundaries + var flippedVariationByContent = !!options.flipVariationsByContent && (isVertical && variation === 'start' && overflowsRight || isVertical && variation === 'end' && overflowsLeft || !isVertical && variation === 'start' && overflowsBottom || !isVertical && variation === 'end' && overflowsTop); + + var flippedVariation = flippedVariationByRef || flippedVariationByContent; + + if (overlapsRef || overflowsBoundaries || flippedVariation) { + // this boolean to detect any flip loop + data.flipped = true; + + if (overlapsRef || overflowsBoundaries) { + placement = flipOrder[index + 1]; + } + + if (flippedVariation) { + variation = getOppositeVariation(variation); + } + + data.placement = placement + (variation ? '-' + variation : ''); + + // this object contains `position`, we want to preserve it along with + // any additional property we may add in the future + data.offsets.popper = _extends({}, data.offsets.popper, getPopperOffsets(data.instance.popper, data.offsets.reference, data.placement)); + + data = runModifiers(data.instance.modifiers, data, 'flip'); + } + }); + return data; + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by update method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function keepTogether(data) { + var _data$offsets = data.offsets, + popper = _data$offsets.popper, + reference = _data$offsets.reference; + + var placement = data.placement.split('-')[0]; + var floor = Math.floor; + var isVertical = ['top', 'bottom'].indexOf(placement) !== -1; + var side = isVertical ? 'right' : 'bottom'; + var opSide = isVertical ? 'left' : 'top'; + var measurement = isVertical ? 'width' : 'height'; + + if (popper[side] < floor(reference[opSide])) { + data.offsets.popper[opSide] = floor(reference[opSide]) - popper[measurement]; + } + if (popper[opSide] > floor(reference[side])) { + data.offsets.popper[opSide] = floor(reference[side]); + } + + return data; + } + + /** + * Converts a string containing value + unit into a px value number + * @function + * @memberof {modifiers~offset} + * @private + * @argument {String} str - Value + unit string + * @argument {String} measurement - `height` or `width` + * @argument {Object} popperOffsets + * @argument {Object} referenceOffsets + * @returns {Number|String} + * Value in pixels, or original string if no values were extracted + */ + function toValue(str, measurement, popperOffsets, referenceOffsets) { + // separate value from unit + var split = str.match(/((?:\-|\+)?\d*\.?\d*)(.*)/); + var value = +split[1]; + var unit = split[2]; + + // If it's not a number it's an operator, I guess + if (!value) { + return str; + } + + if (unit.indexOf('%') === 0) { + var element = void 0; + switch (unit) { + case '%p': + element = popperOffsets; + break; + case '%': + case '%r': + default: + element = referenceOffsets; + } + + var rect = getClientRect(element); + return rect[measurement] / 100 * value; + } else if (unit === 'vh' || unit === 'vw') { + // if is a vh or vw, we calculate the size based on the viewport + var size = void 0; + if (unit === 'vh') { + size = Math.max(document.documentElement.clientHeight, window.innerHeight || 0); + } else { + size = Math.max(document.documentElement.clientWidth, window.innerWidth || 0); + } + return size / 100 * value; + } else { + // if is an explicit pixel unit, we get rid of the unit and keep the value + // if is an implicit unit, it's px, and we return just the value + return value; + } + } + + /** + * Parse an `offset` string to extrapolate `x` and `y` numeric offsets. + * @function + * @memberof {modifiers~offset} + * @private + * @argument {String} offset + * @argument {Object} popperOffsets + * @argument {Object} referenceOffsets + * @argument {String} basePlacement + * @returns {Array} a two cells array with x and y offsets in numbers + */ + function parseOffset(offset, popperOffsets, referenceOffsets, basePlacement) { + var offsets = [0, 0]; + + // Use height if placement is left or right and index is 0 otherwise use width + // in this way the first offset will use an axis and the second one + // will use the other one + var useHeight = ['right', 'left'].indexOf(basePlacement) !== -1; + + // Split the offset string to obtain a list of values and operands + // The regex addresses values with the plus or minus sign in front (+10, -20, etc) + var fragments = offset.split(/(\+|\-)/).map(function (frag) { + return frag.trim(); + }); + + // Detect if the offset string contains a pair of values or a single one + // they could be separated by comma or space + var divider = fragments.indexOf(find(fragments, function (frag) { + return frag.search(/,|\s/) !== -1; + })); + + if (fragments[divider] && fragments[divider].indexOf(',') === -1) { + console.warn('Offsets separated by white space(s) are deprecated, use a comma (,) instead.'); + } + + // If divider is found, we divide the list of values and operands to divide + // them by ofset X and Y. + var splitRegex = /\s*,\s*|\s+/; + var ops = divider !== -1 ? [fragments.slice(0, divider).concat([fragments[divider].split(splitRegex)[0]]), [fragments[divider].split(splitRegex)[1]].concat(fragments.slice(divider + 1))] : [fragments]; + + // Convert the values with units to absolute pixels to allow our computations + ops = ops.map(function (op, index) { + // Most of the units rely on the orientation of the popper + var measurement = (index === 1 ? !useHeight : useHeight) ? 'height' : 'width'; + var mergeWithPrevious = false; + return op + // This aggregates any `+` or `-` sign that aren't considered operators + // e.g.: 10 + +5 => [10, +, +5] + .reduce(function (a, b) { + if (a[a.length - 1] === '' && ['+', '-'].indexOf(b) !== -1) { + a[a.length - 1] = b; + mergeWithPrevious = true; + return a; + } else if (mergeWithPrevious) { + a[a.length - 1] += b; + mergeWithPrevious = false; + return a; + } else { + return a.concat(b); + } + }, []) + // Here we convert the string values into number values (in px) + .map(function (str) { + return toValue(str, measurement, popperOffsets, referenceOffsets); + }); + }); + + // Loop trough the offsets arrays and execute the operations + ops.forEach(function (op, index) { + op.forEach(function (frag, index2) { + if (isNumeric(frag)) { + offsets[index] += frag * (op[index2 - 1] === '-' ? -1 : 1); + } + }); + }); + return offsets; + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by update method + * @argument {Object} options - Modifiers configuration and options + * @argument {Number|String} options.offset=0 + * The offset value as described in the modifier description + * @returns {Object} The data object, properly modified + */ + function offset(data, _ref) { + var offset = _ref.offset; + var placement = data.placement, + _data$offsets = data.offsets, + popper = _data$offsets.popper, + reference = _data$offsets.reference; + + var basePlacement = placement.split('-')[0]; + + var offsets = void 0; + if (isNumeric(+offset)) { + offsets = [+offset, 0]; + } else { + offsets = parseOffset(offset, popper, reference, basePlacement); + } + + if (basePlacement === 'left') { + popper.top += offsets[0]; + popper.left -= offsets[1]; + } else if (basePlacement === 'right') { + popper.top += offsets[0]; + popper.left += offsets[1]; + } else if (basePlacement === 'top') { + popper.left += offsets[0]; + popper.top -= offsets[1]; + } else if (basePlacement === 'bottom') { + popper.left += offsets[0]; + popper.top += offsets[1]; + } + + data.popper = popper; + return data; + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by `update` method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function preventOverflow(data, options) { + var boundariesElement = options.boundariesElement || getOffsetParent(data.instance.popper); + + // If offsetParent is the reference element, we really want to + // go one step up and use the next offsetParent as reference to + // avoid to make this modifier completely useless and look like broken + if (data.instance.reference === boundariesElement) { + boundariesElement = getOffsetParent(boundariesElement); + } + + // NOTE: DOM access here + // resets the popper's position so that the document size can be calculated excluding + // the size of the popper element itself + var transformProp = getSupportedPropertyName('transform'); + var popperStyles = data.instance.popper.style; // assignment to help minification + var top = popperStyles.top, + left = popperStyles.left, + transform = popperStyles[transformProp]; + + popperStyles.top = ''; + popperStyles.left = ''; + popperStyles[transformProp] = ''; + + var boundaries = getBoundaries(data.instance.popper, data.instance.reference, options.padding, boundariesElement, data.positionFixed); + + // NOTE: DOM access here + // restores the original style properties after the offsets have been computed + popperStyles.top = top; + popperStyles.left = left; + popperStyles[transformProp] = transform; + + options.boundaries = boundaries; + + var order = options.priority; + var popper = data.offsets.popper; + + var check = { + primary: function primary(placement) { + var value = popper[placement]; + if (popper[placement] < boundaries[placement] && !options.escapeWithReference) { + value = Math.max(popper[placement], boundaries[placement]); + } + return defineProperty({}, placement, value); + }, + secondary: function secondary(placement) { + var mainSide = placement === 'right' ? 'left' : 'top'; + var value = popper[mainSide]; + if (popper[placement] > boundaries[placement] && !options.escapeWithReference) { + value = Math.min(popper[mainSide], boundaries[placement] - (placement === 'right' ? popper.width : popper.height)); + } + return defineProperty({}, mainSide, value); + } + }; + + order.forEach(function (placement) { + var side = ['left', 'top'].indexOf(placement) !== -1 ? 'primary' : 'secondary'; + popper = _extends({}, popper, check[side](placement)); + }); + + data.offsets.popper = popper; + + return data; + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by `update` method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function shift(data) { + var placement = data.placement; + var basePlacement = placement.split('-')[0]; + var shiftvariation = placement.split('-')[1]; + + // if shift shiftvariation is specified, run the modifier + if (shiftvariation) { + var _data$offsets = data.offsets, + reference = _data$offsets.reference, + popper = _data$offsets.popper; + + var isVertical = ['bottom', 'top'].indexOf(basePlacement) !== -1; + var side = isVertical ? 'left' : 'top'; + var measurement = isVertical ? 'width' : 'height'; + + var shiftOffsets = { + start: defineProperty({}, side, reference[side]), + end: defineProperty({}, side, reference[side] + reference[measurement] - popper[measurement]) + }; + + data.offsets.popper = _extends({}, popper, shiftOffsets[shiftvariation]); + } + + return data; + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by update method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function hide(data) { + if (!isModifierRequired(data.instance.modifiers, 'hide', 'preventOverflow')) { + return data; + } + + var refRect = data.offsets.reference; + var bound = find(data.instance.modifiers, function (modifier) { + return modifier.name === 'preventOverflow'; + }).boundaries; + + if (refRect.bottom < bound.top || refRect.left > bound.right || refRect.top > bound.bottom || refRect.right < bound.left) { + // Avoid unnecessary DOM access if visibility hasn't changed + if (data.hide === true) { + return data; + } + + data.hide = true; + data.attributes['x-out-of-boundaries'] = ''; + } else { + // Avoid unnecessary DOM access if visibility hasn't changed + if (data.hide === false) { + return data; + } + + data.hide = false; + data.attributes['x-out-of-boundaries'] = false; + } + + return data; + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by `update` method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function inner(data) { + var placement = data.placement; + var basePlacement = placement.split('-')[0]; + var _data$offsets = data.offsets, + popper = _data$offsets.popper, + reference = _data$offsets.reference; + + var isHoriz = ['left', 'right'].indexOf(basePlacement) !== -1; + + var subtractLength = ['top', 'left'].indexOf(basePlacement) === -1; + + popper[isHoriz ? 'left' : 'top'] = reference[basePlacement] - (subtractLength ? popper[isHoriz ? 'width' : 'height'] : 0); + + data.placement = getOppositePlacement(placement); + data.offsets.popper = getClientRect(popper); + + return data; + } + + /** + * Modifier function, each modifier can have a function of this type assigned + * to its `fn` property.
+ * These functions will be called on each update, this means that you must + * make sure they are performant enough to avoid performance bottlenecks. + * + * @function ModifierFn + * @argument {dataObject} data - The data object generated by `update` method + * @argument {Object} options - Modifiers configuration and options + * @returns {dataObject} The data object, properly modified + */ + + /** + * Modifiers are plugins used to alter the behavior of your poppers.
+ * Popper.js uses a set of 9 modifiers to provide all the basic functionalities + * needed by the library. + * + * Usually you don't want to override the `order`, `fn` and `onLoad` props. + * All the other properties are configurations that could be tweaked. + * @namespace modifiers + */ + var modifiers = { + /** + * Modifier used to shift the popper on the start or end of its reference + * element.
+ * It will read the variation of the `placement` property.
+ * It can be one either `-end` or `-start`. + * @memberof modifiers + * @inner + */ + shift: { + /** @prop {number} order=100 - Index used to define the order of execution */ + order: 100, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: shift + }, + + /** + * The `offset` modifier can shift your popper on both its axis. + * + * It accepts the following units: + * - `px` or unit-less, interpreted as pixels + * - `%` or `%r`, percentage relative to the length of the reference element + * - `%p`, percentage relative to the length of the popper element + * - `vw`, CSS viewport width unit + * - `vh`, CSS viewport height unit + * + * For length is intended the main axis relative to the placement of the popper.
+ * This means that if the placement is `top` or `bottom`, the length will be the + * `width`. In case of `left` or `right`, it will be the `height`. + * + * You can provide a single value (as `Number` or `String`), or a pair of values + * as `String` divided by a comma or one (or more) white spaces.
+ * The latter is a deprecated method because it leads to confusion and will be + * removed in v2.
+ * Additionally, it accepts additions and subtractions between different units. + * Note that multiplications and divisions aren't supported. + * + * Valid examples are: + * ``` + * 10 + * '10%' + * '10, 10' + * '10%, 10' + * '10 + 10%' + * '10 - 5vh + 3%' + * '-10px + 5vh, 5px - 6%' + * ``` + * > **NB**: If you desire to apply offsets to your poppers in a way that may make them overlap + * > with their reference element, unfortunately, you will have to disable the `flip` modifier. + * > You can read more on this at this [issue](https://github.com/FezVrasta/popper.js/issues/373). + * + * @memberof modifiers + * @inner + */ + offset: { + /** @prop {number} order=200 - Index used to define the order of execution */ + order: 200, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: offset, + /** @prop {Number|String} offset=0 + * The offset value as described in the modifier description + */ + offset: 0 + }, + + /** + * Modifier used to prevent the popper from being positioned outside the boundary. + * + * A scenario exists where the reference itself is not within the boundaries.
+ * We can say it has "escaped the boundaries" — or just "escaped".
+ * In this case we need to decide whether the popper should either: + * + * - detach from the reference and remain "trapped" in the boundaries, or + * - if it should ignore the boundary and "escape with its reference" + * + * When `escapeWithReference` is set to`true` and reference is completely + * outside its boundaries, the popper will overflow (or completely leave) + * the boundaries in order to remain attached to the edge of the reference. + * + * @memberof modifiers + * @inner + */ + preventOverflow: { + /** @prop {number} order=300 - Index used to define the order of execution */ + order: 300, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: preventOverflow, + /** + * @prop {Array} [priority=['left','right','top','bottom']] + * Popper will try to prevent overflow following these priorities by default, + * then, it could overflow on the left and on top of the `boundariesElement` + */ + priority: ['left', 'right', 'top', 'bottom'], + /** + * @prop {number} padding=5 + * Amount of pixel used to define a minimum distance between the boundaries + * and the popper. This makes sure the popper always has a little padding + * between the edges of its container + */ + padding: 5, + /** + * @prop {String|HTMLElement} boundariesElement='scrollParent' + * Boundaries used by the modifier. Can be `scrollParent`, `window`, + * `viewport` or any DOM element. + */ + boundariesElement: 'scrollParent' + }, + + /** + * Modifier used to make sure the reference and its popper stay near each other + * without leaving any gap between the two. Especially useful when the arrow is + * enabled and you want to ensure that it points to its reference element. + * It cares only about the first axis. You can still have poppers with margin + * between the popper and its reference element. + * @memberof modifiers + * @inner + */ + keepTogether: { + /** @prop {number} order=400 - Index used to define the order of execution */ + order: 400, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: keepTogether + }, + + /** + * This modifier is used to move the `arrowElement` of the popper to make + * sure it is positioned between the reference element and its popper element. + * It will read the outer size of the `arrowElement` node to detect how many + * pixels of conjunction are needed. + * + * It has no effect if no `arrowElement` is provided. + * @memberof modifiers + * @inner + */ + arrow: { + /** @prop {number} order=500 - Index used to define the order of execution */ + order: 500, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: arrow, + /** @prop {String|HTMLElement} element='[x-arrow]' - Selector or node used as arrow */ + element: '[x-arrow]' + }, + + /** + * Modifier used to flip the popper's placement when it starts to overlap its + * reference element. + * + * Requires the `preventOverflow` modifier before it in order to work. + * + * **NOTE:** this modifier will interrupt the current update cycle and will + * restart it if it detects the need to flip the placement. + * @memberof modifiers + * @inner + */ + flip: { + /** @prop {number} order=600 - Index used to define the order of execution */ + order: 600, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: flip, + /** + * @prop {String|Array} behavior='flip' + * The behavior used to change the popper's placement. It can be one of + * `flip`, `clockwise`, `counterclockwise` or an array with a list of valid + * placements (with optional variations) + */ + behavior: 'flip', + /** + * @prop {number} padding=5 + * The popper will flip if it hits the edges of the `boundariesElement` + */ + padding: 5, + /** + * @prop {String|HTMLElement} boundariesElement='viewport' + * The element which will define the boundaries of the popper position. + * The popper will never be placed outside of the defined boundaries + * (except if `keepTogether` is enabled) + */ + boundariesElement: 'viewport', + /** + * @prop {Boolean} flipVariations=false + * The popper will switch placement variation between `-start` and `-end` when + * the reference element overlaps its boundaries. + * + * The original placement should have a set variation. + */ + flipVariations: false, + /** + * @prop {Boolean} flipVariationsByContent=false + * The popper will switch placement variation between `-start` and `-end` when + * the popper element overlaps its reference boundaries. + * + * The original placement should have a set variation. + */ + flipVariationsByContent: false + }, + + /** + * Modifier used to make the popper flow toward the inner of the reference element. + * By default, when this modifier is disabled, the popper will be placed outside + * the reference element. + * @memberof modifiers + * @inner + */ + inner: { + /** @prop {number} order=700 - Index used to define the order of execution */ + order: 700, + /** @prop {Boolean} enabled=false - Whether the modifier is enabled or not */ + enabled: false, + /** @prop {ModifierFn} */ + fn: inner + }, + + /** + * Modifier used to hide the popper when its reference element is outside of the + * popper boundaries. It will set a `x-out-of-boundaries` attribute which can + * be used to hide with a CSS selector the popper when its reference is + * out of boundaries. + * + * Requires the `preventOverflow` modifier before it in order to work. + * @memberof modifiers + * @inner + */ + hide: { + /** @prop {number} order=800 - Index used to define the order of execution */ + order: 800, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: hide + }, + + /** + * Computes the style that will be applied to the popper element to gets + * properly positioned. + * + * Note that this modifier will not touch the DOM, it just prepares the styles + * so that `applyStyle` modifier can apply it. This separation is useful + * in case you need to replace `applyStyle` with a custom implementation. + * + * This modifier has `850` as `order` value to maintain backward compatibility + * with previous versions of Popper.js. Expect the modifiers ordering method + * to change in future major versions of the library. + * + * @memberof modifiers + * @inner + */ + computeStyle: { + /** @prop {number} order=850 - Index used to define the order of execution */ + order: 850, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: computeStyle, + /** + * @prop {Boolean} gpuAcceleration=true + * If true, it uses the CSS 3D transformation to position the popper. + * Otherwise, it will use the `top` and `left` properties + */ + gpuAcceleration: true, + /** + * @prop {string} [x='bottom'] + * Where to anchor the X axis (`bottom` or `top`). AKA X offset origin. + * Change this if your popper should grow in a direction different from `bottom` + */ + x: 'bottom', + /** + * @prop {string} [x='left'] + * Where to anchor the Y axis (`left` or `right`). AKA Y offset origin. + * Change this if your popper should grow in a direction different from `right` + */ + y: 'right' + }, + + /** + * Applies the computed styles to the popper element. + * + * All the DOM manipulations are limited to this modifier. This is useful in case + * you want to integrate Popper.js inside a framework or view library and you + * want to delegate all the DOM manipulations to it. + * + * Note that if you disable this modifier, you must make sure the popper element + * has its position set to `absolute` before Popper.js can do its work! + * + * Just disable this modifier and define your own to achieve the desired effect. + * + * @memberof modifiers + * @inner + */ + applyStyle: { + /** @prop {number} order=900 - Index used to define the order of execution */ + order: 900, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: applyStyle, + /** @prop {Function} */ + onLoad: applyStyleOnLoad, + /** + * @deprecated since version 1.10.0, the property moved to `computeStyle` modifier + * @prop {Boolean} gpuAcceleration=true + * If true, it uses the CSS 3D transformation to position the popper. + * Otherwise, it will use the `top` and `left` properties + */ + gpuAcceleration: undefined + } + }; + + /** + * The `dataObject` is an object containing all the information used by Popper.js. + * This object is passed to modifiers and to the `onCreate` and `onUpdate` callbacks. + * @name dataObject + * @property {Object} data.instance The Popper.js instance + * @property {String} data.placement Placement applied to popper + * @property {String} data.originalPlacement Placement originally defined on init + * @property {Boolean} data.flipped True if popper has been flipped by flip modifier + * @property {Boolean} data.hide True if the reference element is out of boundaries, useful to know when to hide the popper + * @property {HTMLElement} data.arrowElement Node used as arrow by arrow modifier + * @property {Object} data.styles Any CSS property defined here will be applied to the popper. It expects the JavaScript nomenclature (eg. `marginBottom`) + * @property {Object} data.arrowStyles Any CSS property defined here will be applied to the popper arrow. It expects the JavaScript nomenclature (eg. `marginBottom`) + * @property {Object} data.boundaries Offsets of the popper boundaries + * @property {Object} data.offsets The measurements of popper, reference and arrow elements + * @property {Object} data.offsets.popper `top`, `left`, `width`, `height` values + * @property {Object} data.offsets.reference `top`, `left`, `width`, `height` values + * @property {Object} data.offsets.arrow] `top` and `left` offsets, only one of them will be different from 0 + */ + + /** + * Default options provided to Popper.js constructor.
+ * These can be overridden using the `options` argument of Popper.js.
+ * To override an option, simply pass an object with the same + * structure of the `options` object, as the 3rd argument. For example: + * ``` + * new Popper(ref, pop, { + * modifiers: { + * preventOverflow: { enabled: false } + * } + * }) + * ``` + * @type {Object} + * @static + * @memberof Popper + */ + var Defaults = { + /** + * Popper's placement. + * @prop {Popper.placements} placement='bottom' + */ + placement: 'bottom', + + /** + * Set this to true if you want popper to position it self in 'fixed' mode + * @prop {Boolean} positionFixed=false + */ + positionFixed: false, + + /** + * Whether events (resize, scroll) are initially enabled. + * @prop {Boolean} eventsEnabled=true + */ + eventsEnabled: true, + + /** + * Set to true if you want to automatically remove the popper when + * you call the `destroy` method. + * @prop {Boolean} removeOnDestroy=false + */ + removeOnDestroy: false, + + /** + * Callback called when the popper is created.
+ * By default, it is set to no-op.
+ * Access Popper.js instance with `data.instance`. + * @prop {onCreate} + */ + onCreate: function onCreate() {}, + + /** + * Callback called when the popper is updated. This callback is not called + * on the initialization/creation of the popper, but only on subsequent + * updates.
+ * By default, it is set to no-op.
+ * Access Popper.js instance with `data.instance`. + * @prop {onUpdate} + */ + onUpdate: function onUpdate() {}, + + /** + * List of modifiers used to modify the offsets before they are applied to the popper. + * They provide most of the functionalities of Popper.js. + * @prop {modifiers} + */ + modifiers: modifiers + }; + + /** + * @callback onCreate + * @param {dataObject} data + */ + + /** + * @callback onUpdate + * @param {dataObject} data + */ + + // Utils + // Methods + var Popper = function () { + /** + * Creates a new Popper.js instance. + * @class Popper + * @param {Element|referenceObject} reference - The reference element used to position the popper + * @param {Element} popper - The HTML / XML element used as the popper + * @param {Object} options - Your custom options to override the ones defined in [Defaults](#defaults) + * @return {Object} instance - The generated Popper.js instance + */ + function Popper(reference, popper) { + var _this = this; + + var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + classCallCheck(this, Popper); + + this.scheduleUpdate = function () { + return requestAnimationFrame(_this.update); + }; + + // make update() debounced, so that it only runs at most once-per-tick + this.update = debounce(this.update.bind(this)); + + // with {} we create a new object with the options inside it + this.options = _extends({}, Popper.Defaults, options); + + // init state + this.state = { + isDestroyed: false, + isCreated: false, + scrollParents: [] + }; + + // get reference and popper elements (allow jQuery wrappers) + this.reference = reference && reference.jquery ? reference[0] : reference; + this.popper = popper && popper.jquery ? popper[0] : popper; + + // Deep merge modifiers options + this.options.modifiers = {}; + Object.keys(_extends({}, Popper.Defaults.modifiers, options.modifiers)).forEach(function (name) { + _this.options.modifiers[name] = _extends({}, Popper.Defaults.modifiers[name] || {}, options.modifiers ? options.modifiers[name] : {}); + }); + + // Refactoring modifiers' list (Object => Array) + this.modifiers = Object.keys(this.options.modifiers).map(function (name) { + return _extends({ + name: name + }, _this.options.modifiers[name]); + }) + // sort the modifiers by order + .sort(function (a, b) { + return a.order - b.order; + }); + + // modifiers have the ability to execute arbitrary code when Popper.js get inited + // such code is executed in the same order of its modifier + // they could add new properties to their options configuration + // BE AWARE: don't add options to `options.modifiers.name` but to `modifierOptions`! + this.modifiers.forEach(function (modifierOptions) { + if (modifierOptions.enabled && isFunction(modifierOptions.onLoad)) { + modifierOptions.onLoad(_this.reference, _this.popper, _this.options, modifierOptions, _this.state); + } + }); + + // fire the first update to position the popper in the right place + this.update(); + + var eventsEnabled = this.options.eventsEnabled; + if (eventsEnabled) { + // setup event listeners, they will take care of update the position in specific situations + this.enableEventListeners(); + } + + this.state.eventsEnabled = eventsEnabled; + } + + // We can't use class properties because they don't get listed in the + // class prototype and break stuff like Sinon stubs + + + createClass(Popper, [{ + key: 'update', + value: function update$$1() { + return update.call(this); + } + }, { + key: 'destroy', + value: function destroy$$1() { + return destroy.call(this); + } + }, { + key: 'enableEventListeners', + value: function enableEventListeners$$1() { + return enableEventListeners.call(this); + } + }, { + key: 'disableEventListeners', + value: function disableEventListeners$$1() { + return disableEventListeners.call(this); + } + + /** + * Schedules an update. It will run on the next UI update available. + * @method scheduleUpdate + * @memberof Popper + */ + + + /** + * Collection of utilities useful when writing custom modifiers. + * Starting from version 1.7, this method is available only if you + * include `popper-utils.js` before `popper.js`. + * + * **DEPRECATION**: This way to access PopperUtils is deprecated + * and will be removed in v2! Use the PopperUtils module directly instead. + * Due to the high instability of the methods contained in Utils, we can't + * guarantee them to follow semver. Use them at your own risk! + * @static + * @private + * @type {Object} + * @deprecated since version 1.8 + * @member Utils + * @memberof Popper + */ + + }]); + return Popper; + }(); + + /** + * The `referenceObject` is an object that provides an interface compatible with Popper.js + * and lets you use it as replacement of a real DOM node.
+ * You can use this method to position a popper relatively to a set of coordinates + * in case you don't have a DOM node to use as reference. + * + * ``` + * new Popper(referenceObject, popperNode); + * ``` + * + * NB: This feature isn't supported in Internet Explorer 10. + * @name referenceObject + * @property {Function} data.getBoundingClientRect + * A function that returns a set of coordinates compatible with the native `getBoundingClientRect` method. + * @property {number} data.clientWidth + * An ES6 getter that will return the width of the virtual reference element. + * @property {number} data.clientHeight + * An ES6 getter that will return the height of the virtual reference element. + */ + + + Popper.Utils = (typeof window !== 'undefined' ? window : global).PopperUtils; + Popper.placements = placements; + Popper.Defaults = Defaults; + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + var NAME$4 = 'dropdown'; + var VERSION$4 = '4.5.0'; + var DATA_KEY$4 = 'bs.dropdown'; + var EVENT_KEY$4 = "." + DATA_KEY$4; + var DATA_API_KEY$4 = '.data-api'; + var JQUERY_NO_CONFLICT$4 = $.fn[NAME$4]; + var ESCAPE_KEYCODE = 27; // KeyboardEvent.which value for Escape (Esc) key + + var SPACE_KEYCODE = 32; // KeyboardEvent.which value for space key + + var TAB_KEYCODE = 9; // KeyboardEvent.which value for tab key + + var ARROW_UP_KEYCODE = 38; // KeyboardEvent.which value for up arrow key + + var ARROW_DOWN_KEYCODE = 40; // KeyboardEvent.which value for down arrow key + + var RIGHT_MOUSE_BUTTON_WHICH = 3; // MouseEvent.which value for the right button (assuming a right-handed mouse) + + var REGEXP_KEYDOWN = new RegExp(ARROW_UP_KEYCODE + "|" + ARROW_DOWN_KEYCODE + "|" + ESCAPE_KEYCODE); + var EVENT_HIDE$1 = "hide" + EVENT_KEY$4; + var EVENT_HIDDEN$1 = "hidden" + EVENT_KEY$4; + var EVENT_SHOW$1 = "show" + EVENT_KEY$4; + var EVENT_SHOWN$1 = "shown" + EVENT_KEY$4; + var EVENT_CLICK = "click" + EVENT_KEY$4; + var EVENT_CLICK_DATA_API$4 = "click" + EVENT_KEY$4 + DATA_API_KEY$4; + var EVENT_KEYDOWN_DATA_API = "keydown" + EVENT_KEY$4 + DATA_API_KEY$4; + var EVENT_KEYUP_DATA_API = "keyup" + EVENT_KEY$4 + DATA_API_KEY$4; + var CLASS_NAME_DISABLED = 'disabled'; + var CLASS_NAME_SHOW$2 = 'show'; + var CLASS_NAME_DROPUP = 'dropup'; + var CLASS_NAME_DROPRIGHT = 'dropright'; + var CLASS_NAME_DROPLEFT = 'dropleft'; + var CLASS_NAME_MENURIGHT = 'dropdown-menu-right'; + var CLASS_NAME_POSITION_STATIC = 'position-static'; + var SELECTOR_DATA_TOGGLE$2 = '[data-toggle="dropdown"]'; + var SELECTOR_FORM_CHILD = '.dropdown form'; + var SELECTOR_MENU = '.dropdown-menu'; + var SELECTOR_NAVBAR_NAV = '.navbar-nav'; + var SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'; + var PLACEMENT_TOP = 'top-start'; + var PLACEMENT_TOPEND = 'top-end'; + var PLACEMENT_BOTTOM = 'bottom-start'; + var PLACEMENT_BOTTOMEND = 'bottom-end'; + var PLACEMENT_RIGHT = 'right-start'; + var PLACEMENT_LEFT = 'left-start'; + var Default$2 = { + offset: 0, + flip: true, + boundary: 'scrollParent', + reference: 'toggle', + display: 'dynamic', + popperConfig: null + }; + var DefaultType$2 = { + offset: '(number|string|function)', + flip: 'boolean', + boundary: '(string|element)', + reference: '(string|element)', + display: 'string', + popperConfig: '(null|object)' + }; + /** + * ------------------------------------------------------------------------ + * Class Definition + * ------------------------------------------------------------------------ + */ + + var Dropdown = /*#__PURE__*/function () { + function Dropdown(element, config) { + this._element = element; + this._popper = null; + this._config = this._getConfig(config); + this._menu = this._getMenuElement(); + this._inNavbar = this._detectNavbar(); + + this._addEventListeners(); + } // Getters + + + var _proto = Dropdown.prototype; + + // Public + _proto.toggle = function toggle() { + if (this._element.disabled || $(this._element).hasClass(CLASS_NAME_DISABLED)) { + return; + } + + var isActive = $(this._menu).hasClass(CLASS_NAME_SHOW$2); + + Dropdown._clearMenus(); + + if (isActive) { + return; + } + + this.show(true); + }; + + _proto.show = function show(usePopper) { + if (usePopper === void 0) { + usePopper = false; + } + + if (this._element.disabled || $(this._element).hasClass(CLASS_NAME_DISABLED) || $(this._menu).hasClass(CLASS_NAME_SHOW$2)) { + return; + } + + var relatedTarget = { + relatedTarget: this._element + }; + var showEvent = $.Event(EVENT_SHOW$1, relatedTarget); + + var parent = Dropdown._getParentFromElement(this._element); + + $(parent).trigger(showEvent); + + if (showEvent.isDefaultPrevented()) { + return; + } // Disable totally Popper.js for Dropdown in Navbar + + + if (!this._inNavbar && usePopper) { + /** + * Check for Popper dependency + * Popper - https://popper.js.org + */ + if (typeof Popper === 'undefined') { + throw new TypeError('Bootstrap\'s dropdowns require Popper.js (https://popper.js.org/)'); + } + + var referenceElement = this._element; + + if (this._config.reference === 'parent') { + referenceElement = parent; + } else if (Util.isElement(this._config.reference)) { + referenceElement = this._config.reference; // Check if it's jQuery element + + if (typeof this._config.reference.jquery !== 'undefined') { + referenceElement = this._config.reference[0]; + } + } // If boundary is not `scrollParent`, then set position to `static` + // to allow the menu to "escape" the scroll parent's boundaries + // https://github.com/twbs/bootstrap/issues/24251 + + + if (this._config.boundary !== 'scrollParent') { + $(parent).addClass(CLASS_NAME_POSITION_STATIC); + } + + this._popper = new Popper(referenceElement, this._menu, this._getPopperConfig()); + } // If this is a touch-enabled device we add extra + // empty mouseover listeners to the body's immediate children; + // only needed because of broken event delegation on iOS + // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html + + + if ('ontouchstart' in document.documentElement && $(parent).closest(SELECTOR_NAVBAR_NAV).length === 0) { + $(document.body).children().on('mouseover', null, $.noop); + } + + this._element.focus(); + + this._element.setAttribute('aria-expanded', true); + + $(this._menu).toggleClass(CLASS_NAME_SHOW$2); + $(parent).toggleClass(CLASS_NAME_SHOW$2).trigger($.Event(EVENT_SHOWN$1, relatedTarget)); + }; + + _proto.hide = function hide() { + if (this._element.disabled || $(this._element).hasClass(CLASS_NAME_DISABLED) || !$(this._menu).hasClass(CLASS_NAME_SHOW$2)) { + return; + } + + var relatedTarget = { + relatedTarget: this._element + }; + var hideEvent = $.Event(EVENT_HIDE$1, relatedTarget); + + var parent = Dropdown._getParentFromElement(this._element); + + $(parent).trigger(hideEvent); + + if (hideEvent.isDefaultPrevented()) { + return; + } + + if (this._popper) { + this._popper.destroy(); + } + + $(this._menu).toggleClass(CLASS_NAME_SHOW$2); + $(parent).toggleClass(CLASS_NAME_SHOW$2).trigger($.Event(EVENT_HIDDEN$1, relatedTarget)); + }; + + _proto.dispose = function dispose() { + $.removeData(this._element, DATA_KEY$4); + $(this._element).off(EVENT_KEY$4); + this._element = null; + this._menu = null; + + if (this._popper !== null) { + this._popper.destroy(); + + this._popper = null; + } + }; + + _proto.update = function update() { + this._inNavbar = this._detectNavbar(); + + if (this._popper !== null) { + this._popper.scheduleUpdate(); + } + } // Private + ; + + _proto._addEventListeners = function _addEventListeners() { + var _this = this; + + $(this._element).on(EVENT_CLICK, function (event) { + event.preventDefault(); + event.stopPropagation(); + + _this.toggle(); + }); + }; + + _proto._getConfig = function _getConfig(config) { + config = _objectSpread2(_objectSpread2(_objectSpread2({}, this.constructor.Default), $(this._element).data()), config); + Util.typeCheckConfig(NAME$4, config, this.constructor.DefaultType); + return config; + }; + + _proto._getMenuElement = function _getMenuElement() { + if (!this._menu) { + var parent = Dropdown._getParentFromElement(this._element); + + if (parent) { + this._menu = parent.querySelector(SELECTOR_MENU); + } + } + + return this._menu; + }; + + _proto._getPlacement = function _getPlacement() { + var $parentDropdown = $(this._element.parentNode); + var placement = PLACEMENT_BOTTOM; // Handle dropup + + if ($parentDropdown.hasClass(CLASS_NAME_DROPUP)) { + placement = $(this._menu).hasClass(CLASS_NAME_MENURIGHT) ? PLACEMENT_TOPEND : PLACEMENT_TOP; + } else if ($parentDropdown.hasClass(CLASS_NAME_DROPRIGHT)) { + placement = PLACEMENT_RIGHT; + } else if ($parentDropdown.hasClass(CLASS_NAME_DROPLEFT)) { + placement = PLACEMENT_LEFT; + } else if ($(this._menu).hasClass(CLASS_NAME_MENURIGHT)) { + placement = PLACEMENT_BOTTOMEND; + } + + return placement; + }; + + _proto._detectNavbar = function _detectNavbar() { + return $(this._element).closest('.navbar').length > 0; + }; + + _proto._getOffset = function _getOffset() { + var _this2 = this; + + var offset = {}; + + if (typeof this._config.offset === 'function') { + offset.fn = function (data) { + data.offsets = _objectSpread2(_objectSpread2({}, data.offsets), _this2._config.offset(data.offsets, _this2._element) || {}); + return data; + }; + } else { + offset.offset = this._config.offset; + } + + return offset; + }; + + _proto._getPopperConfig = function _getPopperConfig() { + var popperConfig = { + placement: this._getPlacement(), + modifiers: { + offset: this._getOffset(), + flip: { + enabled: this._config.flip + }, + preventOverflow: { + boundariesElement: this._config.boundary + } + } + }; // Disable Popper.js if we have a static display + + if (this._config.display === 'static') { + popperConfig.modifiers.applyStyle = { + enabled: false + }; + } + + return _objectSpread2(_objectSpread2({}, popperConfig), this._config.popperConfig); + } // Static + ; + + Dropdown._jQueryInterface = function _jQueryInterface(config) { + return this.each(function () { + var data = $(this).data(DATA_KEY$4); + + var _config = typeof config === 'object' ? config : null; + + if (!data) { + data = new Dropdown(this, _config); + $(this).data(DATA_KEY$4, data); + } + + if (typeof config === 'string') { + if (typeof data[config] === 'undefined') { + throw new TypeError("No method named \"" + config + "\""); + } + + data[config](); + } + }); + }; + + Dropdown._clearMenus = function _clearMenus(event) { + if (event && (event.which === RIGHT_MOUSE_BUTTON_WHICH || event.type === 'keyup' && event.which !== TAB_KEYCODE)) { + return; + } + + var toggles = [].slice.call(document.querySelectorAll(SELECTOR_DATA_TOGGLE$2)); + + for (var i = 0, len = toggles.length; i < len; i++) { + var parent = Dropdown._getParentFromElement(toggles[i]); + + var context = $(toggles[i]).data(DATA_KEY$4); + var relatedTarget = { + relatedTarget: toggles[i] + }; + + if (event && event.type === 'click') { + relatedTarget.clickEvent = event; + } + + if (!context) { + continue; + } + + var dropdownMenu = context._menu; + + if (!$(parent).hasClass(CLASS_NAME_SHOW$2)) { + continue; + } + + if (event && (event.type === 'click' && /input|textarea/i.test(event.target.tagName) || event.type === 'keyup' && event.which === TAB_KEYCODE) && $.contains(parent, event.target)) { + continue; + } + + var hideEvent = $.Event(EVENT_HIDE$1, relatedTarget); + $(parent).trigger(hideEvent); + + if (hideEvent.isDefaultPrevented()) { + continue; + } // If this is a touch-enabled device we remove the extra + // empty mouseover listeners we added for iOS support + + + if ('ontouchstart' in document.documentElement) { + $(document.body).children().off('mouseover', null, $.noop); + } + + toggles[i].setAttribute('aria-expanded', 'false'); + + if (context._popper) { + context._popper.destroy(); + } + + $(dropdownMenu).removeClass(CLASS_NAME_SHOW$2); + $(parent).removeClass(CLASS_NAME_SHOW$2).trigger($.Event(EVENT_HIDDEN$1, relatedTarget)); + } + }; + + Dropdown._getParentFromElement = function _getParentFromElement(element) { + var parent; + var selector = Util.getSelectorFromElement(element); + + if (selector) { + parent = document.querySelector(selector); + } + + return parent || element.parentNode; + } // eslint-disable-next-line complexity + ; + + Dropdown._dataApiKeydownHandler = function _dataApiKeydownHandler(event) { + // If not input/textarea: + // - And not a key in REGEXP_KEYDOWN => not a dropdown command + // If input/textarea: + // - If space key => not a dropdown command + // - If key is other than escape + // - If key is not up or down => not a dropdown command + // - If trigger inside the menu => not a dropdown command + if (/input|textarea/i.test(event.target.tagName) ? event.which === SPACE_KEYCODE || event.which !== ESCAPE_KEYCODE && (event.which !== ARROW_DOWN_KEYCODE && event.which !== ARROW_UP_KEYCODE || $(event.target).closest(SELECTOR_MENU).length) : !REGEXP_KEYDOWN.test(event.which)) { + return; + } + + if (this.disabled || $(this).hasClass(CLASS_NAME_DISABLED)) { + return; + } + + var parent = Dropdown._getParentFromElement(this); + + var isActive = $(parent).hasClass(CLASS_NAME_SHOW$2); + + if (!isActive && event.which === ESCAPE_KEYCODE) { + return; + } + + event.preventDefault(); + event.stopPropagation(); + + if (!isActive || isActive && (event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE)) { + if (event.which === ESCAPE_KEYCODE) { + $(parent.querySelector(SELECTOR_DATA_TOGGLE$2)).trigger('focus'); + } + + $(this).trigger('click'); + return; + } + + var items = [].slice.call(parent.querySelectorAll(SELECTOR_VISIBLE_ITEMS)).filter(function (item) { + return $(item).is(':visible'); + }); + + if (items.length === 0) { + return; + } + + var index = items.indexOf(event.target); + + if (event.which === ARROW_UP_KEYCODE && index > 0) { + // Up + index--; + } + + if (event.which === ARROW_DOWN_KEYCODE && index < items.length - 1) { + // Down + index++; + } + + if (index < 0) { + index = 0; + } + + items[index].focus(); + }; + + _createClass(Dropdown, null, [{ + key: "VERSION", + get: function get() { + return VERSION$4; + } + }, { + key: "Default", + get: function get() { + return Default$2; + } + }, { + key: "DefaultType", + get: function get() { + return DefaultType$2; + } + }]); + + return Dropdown; + }(); + /** + * ------------------------------------------------------------------------ + * Data Api implementation + * ------------------------------------------------------------------------ + */ + + + $(document).on(EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE$2, Dropdown._dataApiKeydownHandler).on(EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown._dataApiKeydownHandler).on(EVENT_CLICK_DATA_API$4 + " " + EVENT_KEYUP_DATA_API, Dropdown._clearMenus).on(EVENT_CLICK_DATA_API$4, SELECTOR_DATA_TOGGLE$2, function (event) { + event.preventDefault(); + event.stopPropagation(); + + Dropdown._jQueryInterface.call($(this), 'toggle'); + }).on(EVENT_CLICK_DATA_API$4, SELECTOR_FORM_CHILD, function (e) { + e.stopPropagation(); + }); + /** + * ------------------------------------------------------------------------ + * jQuery + * ------------------------------------------------------------------------ + */ + + $.fn[NAME$4] = Dropdown._jQueryInterface; + $.fn[NAME$4].Constructor = Dropdown; + + $.fn[NAME$4].noConflict = function () { + $.fn[NAME$4] = JQUERY_NO_CONFLICT$4; + return Dropdown._jQueryInterface; + }; + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + var NAME$5 = 'modal'; + var VERSION$5 = '4.5.0'; + var DATA_KEY$5 = 'bs.modal'; + var EVENT_KEY$5 = "." + DATA_KEY$5; + var DATA_API_KEY$5 = '.data-api'; + var JQUERY_NO_CONFLICT$5 = $.fn[NAME$5]; + var ESCAPE_KEYCODE$1 = 27; // KeyboardEvent.which value for Escape (Esc) key + + var Default$3 = { + backdrop: true, + keyboard: true, + focus: true, + show: true + }; + var DefaultType$3 = { + backdrop: '(boolean|string)', + keyboard: 'boolean', + focus: 'boolean', + show: 'boolean' + }; + var EVENT_HIDE$2 = "hide" + EVENT_KEY$5; + var EVENT_HIDE_PREVENTED = "hidePrevented" + EVENT_KEY$5; + var EVENT_HIDDEN$2 = "hidden" + EVENT_KEY$5; + var EVENT_SHOW$2 = "show" + EVENT_KEY$5; + var EVENT_SHOWN$2 = "shown" + EVENT_KEY$5; + var EVENT_FOCUSIN = "focusin" + EVENT_KEY$5; + var EVENT_RESIZE = "resize" + EVENT_KEY$5; + var EVENT_CLICK_DISMISS = "click.dismiss" + EVENT_KEY$5; + var EVENT_KEYDOWN_DISMISS = "keydown.dismiss" + EVENT_KEY$5; + var EVENT_MOUSEUP_DISMISS = "mouseup.dismiss" + EVENT_KEY$5; + var EVENT_MOUSEDOWN_DISMISS = "mousedown.dismiss" + EVENT_KEY$5; + var EVENT_CLICK_DATA_API$5 = "click" + EVENT_KEY$5 + DATA_API_KEY$5; + var CLASS_NAME_SCROLLABLE = 'modal-dialog-scrollable'; + var CLASS_NAME_SCROLLBAR_MEASURER = 'modal-scrollbar-measure'; + var CLASS_NAME_BACKDROP = 'modal-backdrop'; + var CLASS_NAME_OPEN = 'modal-open'; + var CLASS_NAME_FADE$1 = 'fade'; + var CLASS_NAME_SHOW$3 = 'show'; + var CLASS_NAME_STATIC = 'modal-static'; + var SELECTOR_DIALOG = '.modal-dialog'; + var SELECTOR_MODAL_BODY = '.modal-body'; + var SELECTOR_DATA_TOGGLE$3 = '[data-toggle="modal"]'; + var SELECTOR_DATA_DISMISS = '[data-dismiss="modal"]'; + var SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'; + var SELECTOR_STICKY_CONTENT = '.sticky-top'; + /** + * ------------------------------------------------------------------------ + * Class Definition + * ------------------------------------------------------------------------ + */ + + var Modal = /*#__PURE__*/function () { + function Modal(element, config) { + this._config = this._getConfig(config); + this._element = element; + this._dialog = element.querySelector(SELECTOR_DIALOG); + this._backdrop = null; + this._isShown = false; + this._isBodyOverflowing = false; + this._ignoreBackdropClick = false; + this._isTransitioning = false; + this._scrollbarWidth = 0; + } // Getters + + + var _proto = Modal.prototype; + + // Public + _proto.toggle = function toggle(relatedTarget) { + return this._isShown ? this.hide() : this.show(relatedTarget); + }; + + _proto.show = function show(relatedTarget) { + var _this = this; + + if (this._isShown || this._isTransitioning) { + return; + } + + if ($(this._element).hasClass(CLASS_NAME_FADE$1)) { + this._isTransitioning = true; + } + + var showEvent = $.Event(EVENT_SHOW$2, { + relatedTarget: relatedTarget + }); + $(this._element).trigger(showEvent); + + if (this._isShown || showEvent.isDefaultPrevented()) { + return; + } + + this._isShown = true; + + this._checkScrollbar(); + + this._setScrollbar(); + + this._adjustDialog(); + + this._setEscapeEvent(); + + this._setResizeEvent(); + + $(this._element).on(EVENT_CLICK_DISMISS, SELECTOR_DATA_DISMISS, function (event) { + return _this.hide(event); + }); + $(this._dialog).on(EVENT_MOUSEDOWN_DISMISS, function () { + $(_this._element).one(EVENT_MOUSEUP_DISMISS, function (event) { + if ($(event.target).is(_this._element)) { + _this._ignoreBackdropClick = true; + } + }); + }); + + this._showBackdrop(function () { + return _this._showElement(relatedTarget); + }); + }; + + _proto.hide = function hide(event) { + var _this2 = this; + + if (event) { + event.preventDefault(); + } + + if (!this._isShown || this._isTransitioning) { + return; + } + + var hideEvent = $.Event(EVENT_HIDE$2); + $(this._element).trigger(hideEvent); + + if (!this._isShown || hideEvent.isDefaultPrevented()) { + return; + } + + this._isShown = false; + var transition = $(this._element).hasClass(CLASS_NAME_FADE$1); + + if (transition) { + this._isTransitioning = true; + } + + this._setEscapeEvent(); + + this._setResizeEvent(); + + $(document).off(EVENT_FOCUSIN); + $(this._element).removeClass(CLASS_NAME_SHOW$3); + $(this._element).off(EVENT_CLICK_DISMISS); + $(this._dialog).off(EVENT_MOUSEDOWN_DISMISS); + + if (transition) { + var transitionDuration = Util.getTransitionDurationFromElement(this._element); + $(this._element).one(Util.TRANSITION_END, function (event) { + return _this2._hideModal(event); + }).emulateTransitionEnd(transitionDuration); + } else { + this._hideModal(); + } + }; + + _proto.dispose = function dispose() { + [window, this._element, this._dialog].forEach(function (htmlElement) { + return $(htmlElement).off(EVENT_KEY$5); + }); + /** + * `document` has 2 events `EVENT_FOCUSIN` and `EVENT_CLICK_DATA_API` + * Do not move `document` in `htmlElements` array + * It will remove `EVENT_CLICK_DATA_API` event that should remain + */ + + $(document).off(EVENT_FOCUSIN); + $.removeData(this._element, DATA_KEY$5); + this._config = null; + this._element = null; + this._dialog = null; + this._backdrop = null; + this._isShown = null; + this._isBodyOverflowing = null; + this._ignoreBackdropClick = null; + this._isTransitioning = null; + this._scrollbarWidth = null; + }; + + _proto.handleUpdate = function handleUpdate() { + this._adjustDialog(); + } // Private + ; + + _proto._getConfig = function _getConfig(config) { + config = _objectSpread2(_objectSpread2({}, Default$3), config); + Util.typeCheckConfig(NAME$5, config, DefaultType$3); + return config; + }; + + _proto._triggerBackdropTransition = function _triggerBackdropTransition() { + var _this3 = this; + + if (this._config.backdrop === 'static') { + var hideEventPrevented = $.Event(EVENT_HIDE_PREVENTED); + $(this._element).trigger(hideEventPrevented); + + if (hideEventPrevented.defaultPrevented) { + return; + } + + this._element.classList.add(CLASS_NAME_STATIC); + + var modalTransitionDuration = Util.getTransitionDurationFromElement(this._element); + $(this._element).one(Util.TRANSITION_END, function () { + _this3._element.classList.remove(CLASS_NAME_STATIC); + }).emulateTransitionEnd(modalTransitionDuration); + + this._element.focus(); + } else { + this.hide(); + } + }; + + _proto._showElement = function _showElement(relatedTarget) { + var _this4 = this; + + var transition = $(this._element).hasClass(CLASS_NAME_FADE$1); + var modalBody = this._dialog ? this._dialog.querySelector(SELECTOR_MODAL_BODY) : null; + + if (!this._element.parentNode || this._element.parentNode.nodeType !== Node.ELEMENT_NODE) { + // Don't move modal's DOM position + document.body.appendChild(this._element); + } + + this._element.style.display = 'block'; + + this._element.removeAttribute('aria-hidden'); + + this._element.setAttribute('aria-modal', true); + + if ($(this._dialog).hasClass(CLASS_NAME_SCROLLABLE) && modalBody) { + modalBody.scrollTop = 0; + } else { + this._element.scrollTop = 0; + } + + if (transition) { + Util.reflow(this._element); + } + + $(this._element).addClass(CLASS_NAME_SHOW$3); + + if (this._config.focus) { + this._enforceFocus(); + } + + var shownEvent = $.Event(EVENT_SHOWN$2, { + relatedTarget: relatedTarget + }); + + var transitionComplete = function transitionComplete() { + if (_this4._config.focus) { + _this4._element.focus(); + } + + _this4._isTransitioning = false; + $(_this4._element).trigger(shownEvent); + }; + + if (transition) { + var transitionDuration = Util.getTransitionDurationFromElement(this._dialog); + $(this._dialog).one(Util.TRANSITION_END, transitionComplete).emulateTransitionEnd(transitionDuration); + } else { + transitionComplete(); + } + }; + + _proto._enforceFocus = function _enforceFocus() { + var _this5 = this; + + $(document).off(EVENT_FOCUSIN) // Guard against infinite focus loop + .on(EVENT_FOCUSIN, function (event) { + if (document !== event.target && _this5._element !== event.target && $(_this5._element).has(event.target).length === 0) { + _this5._element.focus(); + } + }); + }; + + _proto._setEscapeEvent = function _setEscapeEvent() { + var _this6 = this; + + if (this._isShown) { + $(this._element).on(EVENT_KEYDOWN_DISMISS, function (event) { + if (_this6._config.keyboard && event.which === ESCAPE_KEYCODE$1) { + event.preventDefault(); + + _this6.hide(); + } else if (!_this6._config.keyboard && event.which === ESCAPE_KEYCODE$1) { + _this6._triggerBackdropTransition(); + } + }); + } else if (!this._isShown) { + $(this._element).off(EVENT_KEYDOWN_DISMISS); + } + }; + + _proto._setResizeEvent = function _setResizeEvent() { + var _this7 = this; + + if (this._isShown) { + $(window).on(EVENT_RESIZE, function (event) { + return _this7.handleUpdate(event); + }); + } else { + $(window).off(EVENT_RESIZE); + } + }; + + _proto._hideModal = function _hideModal() { + var _this8 = this; + + this._element.style.display = 'none'; + + this._element.setAttribute('aria-hidden', true); + + this._element.removeAttribute('aria-modal'); + + this._isTransitioning = false; + + this._showBackdrop(function () { + $(document.body).removeClass(CLASS_NAME_OPEN); + + _this8._resetAdjustments(); + + _this8._resetScrollbar(); + + $(_this8._element).trigger(EVENT_HIDDEN$2); + }); + }; + + _proto._removeBackdrop = function _removeBackdrop() { + if (this._backdrop) { + $(this._backdrop).remove(); + this._backdrop = null; + } + }; + + _proto._showBackdrop = function _showBackdrop(callback) { + var _this9 = this; + + var animate = $(this._element).hasClass(CLASS_NAME_FADE$1) ? CLASS_NAME_FADE$1 : ''; + + if (this._isShown && this._config.backdrop) { + this._backdrop = document.createElement('div'); + this._backdrop.className = CLASS_NAME_BACKDROP; + + if (animate) { + this._backdrop.classList.add(animate); + } + + $(this._backdrop).appendTo(document.body); + $(this._element).on(EVENT_CLICK_DISMISS, function (event) { + if (_this9._ignoreBackdropClick) { + _this9._ignoreBackdropClick = false; + return; + } + + if (event.target !== event.currentTarget) { + return; + } + + _this9._triggerBackdropTransition(); + }); + + if (animate) { + Util.reflow(this._backdrop); + } + + $(this._backdrop).addClass(CLASS_NAME_SHOW$3); + + if (!callback) { + return; + } + + if (!animate) { + callback(); + return; + } + + var backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop); + $(this._backdrop).one(Util.TRANSITION_END, callback).emulateTransitionEnd(backdropTransitionDuration); + } else if (!this._isShown && this._backdrop) { + $(this._backdrop).removeClass(CLASS_NAME_SHOW$3); + + var callbackRemove = function callbackRemove() { + _this9._removeBackdrop(); + + if (callback) { + callback(); + } + }; + + if ($(this._element).hasClass(CLASS_NAME_FADE$1)) { + var _backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop); + + $(this._backdrop).one(Util.TRANSITION_END, callbackRemove).emulateTransitionEnd(_backdropTransitionDuration); + } else { + callbackRemove(); + } + } else if (callback) { + callback(); + } + } // ---------------------------------------------------------------------- + // the following methods are used to handle overflowing modals + // todo (fat): these should probably be refactored out of modal.js + // ---------------------------------------------------------------------- + ; + + _proto._adjustDialog = function _adjustDialog() { + var isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight; + + if (!this._isBodyOverflowing && isModalOverflowing) { + this._element.style.paddingLeft = this._scrollbarWidth + "px"; + } + + if (this._isBodyOverflowing && !isModalOverflowing) { + this._element.style.paddingRight = this._scrollbarWidth + "px"; + } + }; + + _proto._resetAdjustments = function _resetAdjustments() { + this._element.style.paddingLeft = ''; + this._element.style.paddingRight = ''; + }; + + _proto._checkScrollbar = function _checkScrollbar() { + var rect = document.body.getBoundingClientRect(); + this._isBodyOverflowing = Math.round(rect.left + rect.right) < window.innerWidth; + this._scrollbarWidth = this._getScrollbarWidth(); + }; + + _proto._setScrollbar = function _setScrollbar() { + var _this10 = this; + + if (this._isBodyOverflowing) { + // Note: DOMNode.style.paddingRight returns the actual value or '' if not set + // while $(DOMNode).css('padding-right') returns the calculated value or 0 if not set + var fixedContent = [].slice.call(document.querySelectorAll(SELECTOR_FIXED_CONTENT)); + var stickyContent = [].slice.call(document.querySelectorAll(SELECTOR_STICKY_CONTENT)); // Adjust fixed content padding + + $(fixedContent).each(function (index, element) { + var actualPadding = element.style.paddingRight; + var calculatedPadding = $(element).css('padding-right'); + $(element).data('padding-right', actualPadding).css('padding-right', parseFloat(calculatedPadding) + _this10._scrollbarWidth + "px"); + }); // Adjust sticky content margin + + $(stickyContent).each(function (index, element) { + var actualMargin = element.style.marginRight; + var calculatedMargin = $(element).css('margin-right'); + $(element).data('margin-right', actualMargin).css('margin-right', parseFloat(calculatedMargin) - _this10._scrollbarWidth + "px"); + }); // Adjust body padding + + var actualPadding = document.body.style.paddingRight; + var calculatedPadding = $(document.body).css('padding-right'); + $(document.body).data('padding-right', actualPadding).css('padding-right', parseFloat(calculatedPadding) + this._scrollbarWidth + "px"); + } + + $(document.body).addClass(CLASS_NAME_OPEN); + }; + + _proto._resetScrollbar = function _resetScrollbar() { + // Restore fixed content padding + var fixedContent = [].slice.call(document.querySelectorAll(SELECTOR_FIXED_CONTENT)); + $(fixedContent).each(function (index, element) { + var padding = $(element).data('padding-right'); + $(element).removeData('padding-right'); + element.style.paddingRight = padding ? padding : ''; + }); // Restore sticky content + + var elements = [].slice.call(document.querySelectorAll("" + SELECTOR_STICKY_CONTENT)); + $(elements).each(function (index, element) { + var margin = $(element).data('margin-right'); + + if (typeof margin !== 'undefined') { + $(element).css('margin-right', margin).removeData('margin-right'); + } + }); // Restore body padding + + var padding = $(document.body).data('padding-right'); + $(document.body).removeData('padding-right'); + document.body.style.paddingRight = padding ? padding : ''; + }; + + _proto._getScrollbarWidth = function _getScrollbarWidth() { + // thx d.walsh + var scrollDiv = document.createElement('div'); + scrollDiv.className = CLASS_NAME_SCROLLBAR_MEASURER; + document.body.appendChild(scrollDiv); + var scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth; + document.body.removeChild(scrollDiv); + return scrollbarWidth; + } // Static + ; + + Modal._jQueryInterface = function _jQueryInterface(config, relatedTarget) { + return this.each(function () { + var data = $(this).data(DATA_KEY$5); + + var _config = _objectSpread2(_objectSpread2(_objectSpread2({}, Default$3), $(this).data()), typeof config === 'object' && config ? config : {}); + + if (!data) { + data = new Modal(this, _config); + $(this).data(DATA_KEY$5, data); + } + + if (typeof config === 'string') { + if (typeof data[config] === 'undefined') { + throw new TypeError("No method named \"" + config + "\""); + } + + data[config](relatedTarget); + } else if (_config.show) { + data.show(relatedTarget); + } + }); + }; + + _createClass(Modal, null, [{ + key: "VERSION", + get: function get() { + return VERSION$5; + } + }, { + key: "Default", + get: function get() { + return Default$3; + } + }]); + + return Modal; + }(); + /** + * ------------------------------------------------------------------------ + * Data Api implementation + * ------------------------------------------------------------------------ + */ + + + $(document).on(EVENT_CLICK_DATA_API$5, SELECTOR_DATA_TOGGLE$3, function (event) { + var _this11 = this; + + var target; + var selector = Util.getSelectorFromElement(this); + + if (selector) { + target = document.querySelector(selector); + } + + var config = $(target).data(DATA_KEY$5) ? 'toggle' : _objectSpread2(_objectSpread2({}, $(target).data()), $(this).data()); + + if (this.tagName === 'A' || this.tagName === 'AREA') { + event.preventDefault(); + } + + var $target = $(target).one(EVENT_SHOW$2, function (showEvent) { + if (showEvent.isDefaultPrevented()) { + // Only register focus restorer if modal will actually get shown + return; + } + + $target.one(EVENT_HIDDEN$2, function () { + if ($(_this11).is(':visible')) { + _this11.focus(); + } + }); + }); + + Modal._jQueryInterface.call($(target), config, this); + }); + /** + * ------------------------------------------------------------------------ + * jQuery + * ------------------------------------------------------------------------ + */ + + $.fn[NAME$5] = Modal._jQueryInterface; + $.fn[NAME$5].Constructor = Modal; + + $.fn[NAME$5].noConflict = function () { + $.fn[NAME$5] = JQUERY_NO_CONFLICT$5; + return Modal._jQueryInterface; + }; + + /** + * -------------------------------------------------------------------------- + * Bootstrap (v4.5.0): tools/sanitizer.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * -------------------------------------------------------------------------- + */ + var uriAttrs = ['background', 'cite', 'href', 'itemtype', 'longdesc', 'poster', 'src', 'xlink:href']; + var ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i; + var DefaultWhitelist = { + // Global attributes allowed on any supplied element below. + '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN], + a: ['target', 'href', 'title', 'rel'], + area: [], + b: [], + br: [], + col: [], + code: [], + div: [], + em: [], + hr: [], + h1: [], + h2: [], + h3: [], + h4: [], + h5: [], + h6: [], + i: [], + img: ['src', 'srcset', 'alt', 'title', 'width', 'height'], + li: [], + ol: [], + p: [], + pre: [], + s: [], + small: [], + span: [], + sub: [], + sup: [], + strong: [], + u: [], + ul: [] + }; + /** + * A pattern that recognizes a commonly useful subset of URLs that are safe. + * + * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts + */ + + var SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file):|[^#&/:?]*(?:[#/?]|$))/gi; + /** + * A pattern that matches safe data URLs. Only matches image, video and audio types. + * + * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts + */ + + var DATA_URL_PATTERN = /^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[\d+/a-z]+=*$/i; + + function allowedAttribute(attr, allowedAttributeList) { + var attrName = attr.nodeName.toLowerCase(); + + if (allowedAttributeList.indexOf(attrName) !== -1) { + if (uriAttrs.indexOf(attrName) !== -1) { + return Boolean(attr.nodeValue.match(SAFE_URL_PATTERN) || attr.nodeValue.match(DATA_URL_PATTERN)); + } + + return true; + } + + var regExp = allowedAttributeList.filter(function (attrRegex) { + return attrRegex instanceof RegExp; + }); // Check if a regular expression validates the attribute. + + for (var i = 0, len = regExp.length; i < len; i++) { + if (attrName.match(regExp[i])) { + return true; + } + } + + return false; + } + + function sanitizeHtml(unsafeHtml, whiteList, sanitizeFn) { + if (unsafeHtml.length === 0) { + return unsafeHtml; + } + + if (sanitizeFn && typeof sanitizeFn === 'function') { + return sanitizeFn(unsafeHtml); + } + + var domParser = new window.DOMParser(); + var createdDocument = domParser.parseFromString(unsafeHtml, 'text/html'); + var whitelistKeys = Object.keys(whiteList); + var elements = [].slice.call(createdDocument.body.querySelectorAll('*')); + + var _loop = function _loop(i, len) { + var el = elements[i]; + var elName = el.nodeName.toLowerCase(); + + if (whitelistKeys.indexOf(el.nodeName.toLowerCase()) === -1) { + el.parentNode.removeChild(el); + return "continue"; + } + + var attributeList = [].slice.call(el.attributes); + var whitelistedAttributes = [].concat(whiteList['*'] || [], whiteList[elName] || []); + attributeList.forEach(function (attr) { + if (!allowedAttribute(attr, whitelistedAttributes)) { + el.removeAttribute(attr.nodeName); + } + }); + }; + + for (var i = 0, len = elements.length; i < len; i++) { + var _ret = _loop(i); + + if (_ret === "continue") continue; + } + + return createdDocument.body.innerHTML; + } + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + var NAME$6 = 'tooltip'; + var VERSION$6 = '4.5.0'; + var DATA_KEY$6 = 'bs.tooltip'; + var EVENT_KEY$6 = "." + DATA_KEY$6; + var JQUERY_NO_CONFLICT$6 = $.fn[NAME$6]; + var CLASS_PREFIX = 'bs-tooltip'; + var BSCLS_PREFIX_REGEX = new RegExp("(^|\\s)" + CLASS_PREFIX + "\\S+", 'g'); + var DISALLOWED_ATTRIBUTES = ['sanitize', 'whiteList', 'sanitizeFn']; + var DefaultType$4 = { + animation: 'boolean', + template: 'string', + title: '(string|element|function)', + trigger: 'string', + delay: '(number|object)', + html: 'boolean', + selector: '(string|boolean)', + placement: '(string|function)', + offset: '(number|string|function)', + container: '(string|element|boolean)', + fallbackPlacement: '(string|array)', + boundary: '(string|element)', + sanitize: 'boolean', + sanitizeFn: '(null|function)', + whiteList: 'object', + popperConfig: '(null|object)' + }; + var AttachmentMap = { + AUTO: 'auto', + TOP: 'top', + RIGHT: 'right', + BOTTOM: 'bottom', + LEFT: 'left' + }; + var Default$4 = { + animation: true, + template: '', + trigger: 'hover focus', + title: '', + delay: 0, + html: false, + selector: false, + placement: 'top', + offset: 0, + container: false, + fallbackPlacement: 'flip', + boundary: 'scrollParent', + sanitize: true, + sanitizeFn: null, + whiteList: DefaultWhitelist, + popperConfig: null + }; + var HOVER_STATE_SHOW = 'show'; + var HOVER_STATE_OUT = 'out'; + var Event = { + HIDE: "hide" + EVENT_KEY$6, + HIDDEN: "hidden" + EVENT_KEY$6, + SHOW: "show" + EVENT_KEY$6, + SHOWN: "shown" + EVENT_KEY$6, + INSERTED: "inserted" + EVENT_KEY$6, + CLICK: "click" + EVENT_KEY$6, + FOCUSIN: "focusin" + EVENT_KEY$6, + FOCUSOUT: "focusout" + EVENT_KEY$6, + MOUSEENTER: "mouseenter" + EVENT_KEY$6, + MOUSELEAVE: "mouseleave" + EVENT_KEY$6 + }; + var CLASS_NAME_FADE$2 = 'fade'; + var CLASS_NAME_SHOW$4 = 'show'; + var SELECTOR_TOOLTIP_INNER = '.tooltip-inner'; + var SELECTOR_ARROW = '.arrow'; + var TRIGGER_HOVER = 'hover'; + var TRIGGER_FOCUS = 'focus'; + var TRIGGER_CLICK = 'click'; + var TRIGGER_MANUAL = 'manual'; + /** + * ------------------------------------------------------------------------ + * Class Definition + * ------------------------------------------------------------------------ + */ + + var Tooltip = /*#__PURE__*/function () { + function Tooltip(element, config) { + if (typeof Popper === 'undefined') { + throw new TypeError('Bootstrap\'s tooltips require Popper.js (https://popper.js.org/)'); + } // private + + + this._isEnabled = true; + this._timeout = 0; + this._hoverState = ''; + this._activeTrigger = {}; + this._popper = null; // Protected + + this.element = element; + this.config = this._getConfig(config); + this.tip = null; + + this._setListeners(); + } // Getters + + + var _proto = Tooltip.prototype; + + // Public + _proto.enable = function enable() { + this._isEnabled = true; + }; + + _proto.disable = function disable() { + this._isEnabled = false; + }; + + _proto.toggleEnabled = function toggleEnabled() { + this._isEnabled = !this._isEnabled; + }; + + _proto.toggle = function toggle(event) { + if (!this._isEnabled) { + return; + } + + if (event) { + var dataKey = this.constructor.DATA_KEY; + var context = $(event.currentTarget).data(dataKey); + + if (!context) { + context = new this.constructor(event.currentTarget, this._getDelegateConfig()); + $(event.currentTarget).data(dataKey, context); + } + + context._activeTrigger.click = !context._activeTrigger.click; + + if (context._isWithActiveTrigger()) { + context._enter(null, context); + } else { + context._leave(null, context); + } + } else { + if ($(this.getTipElement()).hasClass(CLASS_NAME_SHOW$4)) { + this._leave(null, this); + + return; + } + + this._enter(null, this); + } + }; + + _proto.dispose = function dispose() { + clearTimeout(this._timeout); + $.removeData(this.element, this.constructor.DATA_KEY); + $(this.element).off(this.constructor.EVENT_KEY); + $(this.element).closest('.modal').off('hide.bs.modal', this._hideModalHandler); + + if (this.tip) { + $(this.tip).remove(); + } + + this._isEnabled = null; + this._timeout = null; + this._hoverState = null; + this._activeTrigger = null; + + if (this._popper) { + this._popper.destroy(); + } + + this._popper = null; + this.element = null; + this.config = null; + this.tip = null; + }; + + _proto.show = function show() { + var _this = this; + + if ($(this.element).css('display') === 'none') { + throw new Error('Please use show on visible elements'); + } + + var showEvent = $.Event(this.constructor.Event.SHOW); + + if (this.isWithContent() && this._isEnabled) { + $(this.element).trigger(showEvent); + var shadowRoot = Util.findShadowRoot(this.element); + var isInTheDom = $.contains(shadowRoot !== null ? shadowRoot : this.element.ownerDocument.documentElement, this.element); + + if (showEvent.isDefaultPrevented() || !isInTheDom) { + return; + } + + var tip = this.getTipElement(); + var tipId = Util.getUID(this.constructor.NAME); + tip.setAttribute('id', tipId); + this.element.setAttribute('aria-describedby', tipId); + this.setContent(); + + if (this.config.animation) { + $(tip).addClass(CLASS_NAME_FADE$2); + } + + var placement = typeof this.config.placement === 'function' ? this.config.placement.call(this, tip, this.element) : this.config.placement; + + var attachment = this._getAttachment(placement); + + this.addAttachmentClass(attachment); + + var container = this._getContainer(); + + $(tip).data(this.constructor.DATA_KEY, this); + + if (!$.contains(this.element.ownerDocument.documentElement, this.tip)) { + $(tip).appendTo(container); + } + + $(this.element).trigger(this.constructor.Event.INSERTED); + this._popper = new Popper(this.element, tip, this._getPopperConfig(attachment)); + $(tip).addClass(CLASS_NAME_SHOW$4); // If this is a touch-enabled device we add extra + // empty mouseover listeners to the body's immediate children; + // only needed because of broken event delegation on iOS + // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html + + if ('ontouchstart' in document.documentElement) { + $(document.body).children().on('mouseover', null, $.noop); + } + + var complete = function complete() { + if (_this.config.animation) { + _this._fixTransition(); + } + + var prevHoverState = _this._hoverState; + _this._hoverState = null; + $(_this.element).trigger(_this.constructor.Event.SHOWN); + + if (prevHoverState === HOVER_STATE_OUT) { + _this._leave(null, _this); + } + }; + + if ($(this.tip).hasClass(CLASS_NAME_FADE$2)) { + var transitionDuration = Util.getTransitionDurationFromElement(this.tip); + $(this.tip).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration); + } else { + complete(); + } + } + }; + + _proto.hide = function hide(callback) { + var _this2 = this; + + var tip = this.getTipElement(); + var hideEvent = $.Event(this.constructor.Event.HIDE); + + var complete = function complete() { + if (_this2._hoverState !== HOVER_STATE_SHOW && tip.parentNode) { + tip.parentNode.removeChild(tip); + } + + _this2._cleanTipClass(); + + _this2.element.removeAttribute('aria-describedby'); + + $(_this2.element).trigger(_this2.constructor.Event.HIDDEN); + + if (_this2._popper !== null) { + _this2._popper.destroy(); + } + + if (callback) { + callback(); + } + }; + + $(this.element).trigger(hideEvent); + + if (hideEvent.isDefaultPrevented()) { + return; + } + + $(tip).removeClass(CLASS_NAME_SHOW$4); // If this is a touch-enabled device we remove the extra + // empty mouseover listeners we added for iOS support + + if ('ontouchstart' in document.documentElement) { + $(document.body).children().off('mouseover', null, $.noop); + } + + this._activeTrigger[TRIGGER_CLICK] = false; + this._activeTrigger[TRIGGER_FOCUS] = false; + this._activeTrigger[TRIGGER_HOVER] = false; + + if ($(this.tip).hasClass(CLASS_NAME_FADE$2)) { + var transitionDuration = Util.getTransitionDurationFromElement(tip); + $(tip).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration); + } else { + complete(); + } + + this._hoverState = ''; + }; + + _proto.update = function update() { + if (this._popper !== null) { + this._popper.scheduleUpdate(); + } + } // Protected + ; + + _proto.isWithContent = function isWithContent() { + return Boolean(this.getTitle()); + }; + + _proto.addAttachmentClass = function addAttachmentClass(attachment) { + $(this.getTipElement()).addClass(CLASS_PREFIX + "-" + attachment); + }; + + _proto.getTipElement = function getTipElement() { + this.tip = this.tip || $(this.config.template)[0]; + return this.tip; + }; + + _proto.setContent = function setContent() { + var tip = this.getTipElement(); + this.setElementContent($(tip.querySelectorAll(SELECTOR_TOOLTIP_INNER)), this.getTitle()); + $(tip).removeClass(CLASS_NAME_FADE$2 + " " + CLASS_NAME_SHOW$4); + }; + + _proto.setElementContent = function setElementContent($element, content) { + if (typeof content === 'object' && (content.nodeType || content.jquery)) { + // Content is a DOM node or a jQuery + if (this.config.html) { + if (!$(content).parent().is($element)) { + $element.empty().append(content); + } + } else { + $element.text($(content).text()); + } + + return; + } + + if (this.config.html) { + if (this.config.sanitize) { + content = sanitizeHtml(content, this.config.whiteList, this.config.sanitizeFn); + } + + $element.html(content); + } else { + $element.text(content); + } + }; + + _proto.getTitle = function getTitle() { + var title = this.element.getAttribute('data-original-title'); + + if (!title) { + title = typeof this.config.title === 'function' ? this.config.title.call(this.element) : this.config.title; + } + + return title; + } // Private + ; + + _proto._getPopperConfig = function _getPopperConfig(attachment) { + var _this3 = this; + + var defaultBsConfig = { + placement: attachment, + modifiers: { + offset: this._getOffset(), + flip: { + behavior: this.config.fallbackPlacement + }, + arrow: { + element: SELECTOR_ARROW + }, + preventOverflow: { + boundariesElement: this.config.boundary + } + }, + onCreate: function onCreate(data) { + if (data.originalPlacement !== data.placement) { + _this3._handlePopperPlacementChange(data); + } + }, + onUpdate: function onUpdate(data) { + return _this3._handlePopperPlacementChange(data); + } + }; + return _objectSpread2(_objectSpread2({}, defaultBsConfig), this.config.popperConfig); + }; + + _proto._getOffset = function _getOffset() { + var _this4 = this; + + var offset = {}; + + if (typeof this.config.offset === 'function') { + offset.fn = function (data) { + data.offsets = _objectSpread2(_objectSpread2({}, data.offsets), _this4.config.offset(data.offsets, _this4.element) || {}); + return data; + }; + } else { + offset.offset = this.config.offset; + } + + return offset; + }; + + _proto._getContainer = function _getContainer() { + if (this.config.container === false) { + return document.body; + } + + if (Util.isElement(this.config.container)) { + return $(this.config.container); + } + + return $(document).find(this.config.container); + }; + + _proto._getAttachment = function _getAttachment(placement) { + return AttachmentMap[placement.toUpperCase()]; + }; + + _proto._setListeners = function _setListeners() { + var _this5 = this; + + var triggers = this.config.trigger.split(' '); + triggers.forEach(function (trigger) { + if (trigger === 'click') { + $(_this5.element).on(_this5.constructor.Event.CLICK, _this5.config.selector, function (event) { + return _this5.toggle(event); + }); + } else if (trigger !== TRIGGER_MANUAL) { + var eventIn = trigger === TRIGGER_HOVER ? _this5.constructor.Event.MOUSEENTER : _this5.constructor.Event.FOCUSIN; + var eventOut = trigger === TRIGGER_HOVER ? _this5.constructor.Event.MOUSELEAVE : _this5.constructor.Event.FOCUSOUT; + $(_this5.element).on(eventIn, _this5.config.selector, function (event) { + return _this5._enter(event); + }).on(eventOut, _this5.config.selector, function (event) { + return _this5._leave(event); + }); + } + }); + + this._hideModalHandler = function () { + if (_this5.element) { + _this5.hide(); + } + }; + + $(this.element).closest('.modal').on('hide.bs.modal', this._hideModalHandler); + + if (this.config.selector) { + this.config = _objectSpread2(_objectSpread2({}, this.config), {}, { + trigger: 'manual', + selector: '' + }); + } else { + this._fixTitle(); + } + }; + + _proto._fixTitle = function _fixTitle() { + var titleType = typeof this.element.getAttribute('data-original-title'); + + if (this.element.getAttribute('title') || titleType !== 'string') { + this.element.setAttribute('data-original-title', this.element.getAttribute('title') || ''); + this.element.setAttribute('title', ''); + } + }; + + _proto._enter = function _enter(event, context) { + var dataKey = this.constructor.DATA_KEY; + context = context || $(event.currentTarget).data(dataKey); + + if (!context) { + context = new this.constructor(event.currentTarget, this._getDelegateConfig()); + $(event.currentTarget).data(dataKey, context); + } + + if (event) { + context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true; + } + + if ($(context.getTipElement()).hasClass(CLASS_NAME_SHOW$4) || context._hoverState === HOVER_STATE_SHOW) { + context._hoverState = HOVER_STATE_SHOW; + return; + } + + clearTimeout(context._timeout); + context._hoverState = HOVER_STATE_SHOW; + + if (!context.config.delay || !context.config.delay.show) { + context.show(); + return; + } + + context._timeout = setTimeout(function () { + if (context._hoverState === HOVER_STATE_SHOW) { + context.show(); + } + }, context.config.delay.show); + }; + + _proto._leave = function _leave(event, context) { + var dataKey = this.constructor.DATA_KEY; + context = context || $(event.currentTarget).data(dataKey); + + if (!context) { + context = new this.constructor(event.currentTarget, this._getDelegateConfig()); + $(event.currentTarget).data(dataKey, context); + } + + if (event) { + context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] = false; + } + + if (context._isWithActiveTrigger()) { + return; + } + + clearTimeout(context._timeout); + context._hoverState = HOVER_STATE_OUT; + + if (!context.config.delay || !context.config.delay.hide) { + context.hide(); + return; + } + + context._timeout = setTimeout(function () { + if (context._hoverState === HOVER_STATE_OUT) { + context.hide(); + } + }, context.config.delay.hide); + }; + + _proto._isWithActiveTrigger = function _isWithActiveTrigger() { + for (var trigger in this._activeTrigger) { + if (this._activeTrigger[trigger]) { + return true; + } + } + + return false; + }; + + _proto._getConfig = function _getConfig(config) { + var dataAttributes = $(this.element).data(); + Object.keys(dataAttributes).forEach(function (dataAttr) { + if (DISALLOWED_ATTRIBUTES.indexOf(dataAttr) !== -1) { + delete dataAttributes[dataAttr]; + } + }); + config = _objectSpread2(_objectSpread2(_objectSpread2({}, this.constructor.Default), dataAttributes), typeof config === 'object' && config ? config : {}); + + if (typeof config.delay === 'number') { + config.delay = { + show: config.delay, + hide: config.delay + }; + } + + if (typeof config.title === 'number') { + config.title = config.title.toString(); + } + + if (typeof config.content === 'number') { + config.content = config.content.toString(); + } + + Util.typeCheckConfig(NAME$6, config, this.constructor.DefaultType); + + if (config.sanitize) { + config.template = sanitizeHtml(config.template, config.whiteList, config.sanitizeFn); + } + + return config; + }; + + _proto._getDelegateConfig = function _getDelegateConfig() { + var config = {}; + + if (this.config) { + for (var key in this.config) { + if (this.constructor.Default[key] !== this.config[key]) { + config[key] = this.config[key]; + } + } + } + + return config; + }; + + _proto._cleanTipClass = function _cleanTipClass() { + var $tip = $(this.getTipElement()); + var tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX); + + if (tabClass !== null && tabClass.length) { + $tip.removeClass(tabClass.join('')); + } + }; + + _proto._handlePopperPlacementChange = function _handlePopperPlacementChange(popperData) { + this.tip = popperData.instance.popper; + + this._cleanTipClass(); + + this.addAttachmentClass(this._getAttachment(popperData.placement)); + }; + + _proto._fixTransition = function _fixTransition() { + var tip = this.getTipElement(); + var initConfigAnimation = this.config.animation; + + if (tip.getAttribute('x-placement') !== null) { + return; + } + + $(tip).removeClass(CLASS_NAME_FADE$2); + this.config.animation = false; + this.hide(); + this.show(); + this.config.animation = initConfigAnimation; + } // Static + ; + + Tooltip._jQueryInterface = function _jQueryInterface(config) { + return this.each(function () { + var data = $(this).data(DATA_KEY$6); + + var _config = typeof config === 'object' && config; + + if (!data && /dispose|hide/.test(config)) { + return; + } + + if (!data) { + data = new Tooltip(this, _config); + $(this).data(DATA_KEY$6, data); + } + + if (typeof config === 'string') { + if (typeof data[config] === 'undefined') { + throw new TypeError("No method named \"" + config + "\""); + } + + data[config](); + } + }); + }; + + _createClass(Tooltip, null, [{ + key: "VERSION", + get: function get() { + return VERSION$6; + } + }, { + key: "Default", + get: function get() { + return Default$4; + } + }, { + key: "NAME", + get: function get() { + return NAME$6; + } + }, { + key: "DATA_KEY", + get: function get() { + return DATA_KEY$6; + } + }, { + key: "Event", + get: function get() { + return Event; + } + }, { + key: "EVENT_KEY", + get: function get() { + return EVENT_KEY$6; + } + }, { + key: "DefaultType", + get: function get() { + return DefaultType$4; + } + }]); + + return Tooltip; + }(); + /** + * ------------------------------------------------------------------------ + * jQuery + * ------------------------------------------------------------------------ + */ + + + $.fn[NAME$6] = Tooltip._jQueryInterface; + $.fn[NAME$6].Constructor = Tooltip; + + $.fn[NAME$6].noConflict = function () { + $.fn[NAME$6] = JQUERY_NO_CONFLICT$6; + return Tooltip._jQueryInterface; + }; + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + var NAME$7 = 'popover'; + var VERSION$7 = '4.5.0'; + var DATA_KEY$7 = 'bs.popover'; + var EVENT_KEY$7 = "." + DATA_KEY$7; + var JQUERY_NO_CONFLICT$7 = $.fn[NAME$7]; + var CLASS_PREFIX$1 = 'bs-popover'; + var BSCLS_PREFIX_REGEX$1 = new RegExp("(^|\\s)" + CLASS_PREFIX$1 + "\\S+", 'g'); + + var Default$5 = _objectSpread2(_objectSpread2({}, Tooltip.Default), {}, { + placement: 'right', + trigger: 'click', + content: '', + template: '' + }); + + var DefaultType$5 = _objectSpread2(_objectSpread2({}, Tooltip.DefaultType), {}, { + content: '(string|element|function)' + }); + + var CLASS_NAME_FADE$3 = 'fade'; + var CLASS_NAME_SHOW$5 = 'show'; + var SELECTOR_TITLE = '.popover-header'; + var SELECTOR_CONTENT = '.popover-body'; + var Event$1 = { + HIDE: "hide" + EVENT_KEY$7, + HIDDEN: "hidden" + EVENT_KEY$7, + SHOW: "show" + EVENT_KEY$7, + SHOWN: "shown" + EVENT_KEY$7, + INSERTED: "inserted" + EVENT_KEY$7, + CLICK: "click" + EVENT_KEY$7, + FOCUSIN: "focusin" + EVENT_KEY$7, + FOCUSOUT: "focusout" + EVENT_KEY$7, + MOUSEENTER: "mouseenter" + EVENT_KEY$7, + MOUSELEAVE: "mouseleave" + EVENT_KEY$7 + }; + /** + * ------------------------------------------------------------------------ + * Class Definition + * ------------------------------------------------------------------------ + */ + + var Popover = /*#__PURE__*/function (_Tooltip) { + _inheritsLoose(Popover, _Tooltip); + + function Popover() { + return _Tooltip.apply(this, arguments) || this; + } + + var _proto = Popover.prototype; + + // Overrides + _proto.isWithContent = function isWithContent() { + return this.getTitle() || this._getContent(); + }; + + _proto.addAttachmentClass = function addAttachmentClass(attachment) { + $(this.getTipElement()).addClass(CLASS_PREFIX$1 + "-" + attachment); + }; + + _proto.getTipElement = function getTipElement() { + this.tip = this.tip || $(this.config.template)[0]; + return this.tip; + }; + + _proto.setContent = function setContent() { + var $tip = $(this.getTipElement()); // We use append for html objects to maintain js events + + this.setElementContent($tip.find(SELECTOR_TITLE), this.getTitle()); + + var content = this._getContent(); + + if (typeof content === 'function') { + content = content.call(this.element); + } + + this.setElementContent($tip.find(SELECTOR_CONTENT), content); + $tip.removeClass(CLASS_NAME_FADE$3 + " " + CLASS_NAME_SHOW$5); + } // Private + ; + + _proto._getContent = function _getContent() { + return this.element.getAttribute('data-content') || this.config.content; + }; + + _proto._cleanTipClass = function _cleanTipClass() { + var $tip = $(this.getTipElement()); + var tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX$1); + + if (tabClass !== null && tabClass.length > 0) { + $tip.removeClass(tabClass.join('')); + } + } // Static + ; + + Popover._jQueryInterface = function _jQueryInterface(config) { + return this.each(function () { + var data = $(this).data(DATA_KEY$7); + + var _config = typeof config === 'object' ? config : null; + + if (!data && /dispose|hide/.test(config)) { + return; + } + + if (!data) { + data = new Popover(this, _config); + $(this).data(DATA_KEY$7, data); + } + + if (typeof config === 'string') { + if (typeof data[config] === 'undefined') { + throw new TypeError("No method named \"" + config + "\""); + } + + data[config](); + } + }); + }; + + _createClass(Popover, null, [{ + key: "VERSION", + // Getters + get: function get() { + return VERSION$7; + } + }, { + key: "Default", + get: function get() { + return Default$5; + } + }, { + key: "NAME", + get: function get() { + return NAME$7; + } + }, { + key: "DATA_KEY", + get: function get() { + return DATA_KEY$7; + } + }, { + key: "Event", + get: function get() { + return Event$1; + } + }, { + key: "EVENT_KEY", + get: function get() { + return EVENT_KEY$7; + } + }, { + key: "DefaultType", + get: function get() { + return DefaultType$5; + } + }]); + + return Popover; + }(Tooltip); + /** + * ------------------------------------------------------------------------ + * jQuery + * ------------------------------------------------------------------------ + */ + + + $.fn[NAME$7] = Popover._jQueryInterface; + $.fn[NAME$7].Constructor = Popover; + + $.fn[NAME$7].noConflict = function () { + $.fn[NAME$7] = JQUERY_NO_CONFLICT$7; + return Popover._jQueryInterface; + }; + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + var NAME$8 = 'scrollspy'; + var VERSION$8 = '4.5.0'; + var DATA_KEY$8 = 'bs.scrollspy'; + var EVENT_KEY$8 = "." + DATA_KEY$8; + var DATA_API_KEY$6 = '.data-api'; + var JQUERY_NO_CONFLICT$8 = $.fn[NAME$8]; + var Default$6 = { + offset: 10, + method: 'auto', + target: '' + }; + var DefaultType$6 = { + offset: 'number', + method: 'string', + target: '(string|element)' + }; + var EVENT_ACTIVATE = "activate" + EVENT_KEY$8; + var EVENT_SCROLL = "scroll" + EVENT_KEY$8; + var EVENT_LOAD_DATA_API$2 = "load" + EVENT_KEY$8 + DATA_API_KEY$6; + var CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item'; + var CLASS_NAME_ACTIVE$2 = 'active'; + var SELECTOR_DATA_SPY = '[data-spy="scroll"]'; + var SELECTOR_NAV_LIST_GROUP = '.nav, .list-group'; + var SELECTOR_NAV_LINKS = '.nav-link'; + var SELECTOR_NAV_ITEMS = '.nav-item'; + var SELECTOR_LIST_ITEMS = '.list-group-item'; + var SELECTOR_DROPDOWN = '.dropdown'; + var SELECTOR_DROPDOWN_ITEMS = '.dropdown-item'; + var SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle'; + var METHOD_OFFSET = 'offset'; + var METHOD_POSITION = 'position'; + /** + * ------------------------------------------------------------------------ + * Class Definition + * ------------------------------------------------------------------------ + */ + + var ScrollSpy = /*#__PURE__*/function () { + function ScrollSpy(element, config) { + var _this = this; + + this._element = element; + this._scrollElement = element.tagName === 'BODY' ? window : element; + this._config = this._getConfig(config); + this._selector = this._config.target + " " + SELECTOR_NAV_LINKS + "," + (this._config.target + " " + SELECTOR_LIST_ITEMS + ",") + (this._config.target + " " + SELECTOR_DROPDOWN_ITEMS); + this._offsets = []; + this._targets = []; + this._activeTarget = null; + this._scrollHeight = 0; + $(this._scrollElement).on(EVENT_SCROLL, function (event) { + return _this._process(event); + }); + this.refresh(); + + this._process(); + } // Getters + + + var _proto = ScrollSpy.prototype; + + // Public + _proto.refresh = function refresh() { + var _this2 = this; + + var autoMethod = this._scrollElement === this._scrollElement.window ? METHOD_OFFSET : METHOD_POSITION; + var offsetMethod = this._config.method === 'auto' ? autoMethod : this._config.method; + var offsetBase = offsetMethod === METHOD_POSITION ? this._getScrollTop() : 0; + this._offsets = []; + this._targets = []; + this._scrollHeight = this._getScrollHeight(); + var targets = [].slice.call(document.querySelectorAll(this._selector)); + targets.map(function (element) { + var target; + var targetSelector = Util.getSelectorFromElement(element); + + if (targetSelector) { + target = document.querySelector(targetSelector); + } + + if (target) { + var targetBCR = target.getBoundingClientRect(); + + if (targetBCR.width || targetBCR.height) { + // TODO (fat): remove sketch reliance on jQuery position/offset + return [$(target)[offsetMethod]().top + offsetBase, targetSelector]; + } + } + + return null; + }).filter(function (item) { + return item; + }).sort(function (a, b) { + return a[0] - b[0]; + }).forEach(function (item) { + _this2._offsets.push(item[0]); + + _this2._targets.push(item[1]); + }); + }; + + _proto.dispose = function dispose() { + $.removeData(this._element, DATA_KEY$8); + $(this._scrollElement).off(EVENT_KEY$8); + this._element = null; + this._scrollElement = null; + this._config = null; + this._selector = null; + this._offsets = null; + this._targets = null; + this._activeTarget = null; + this._scrollHeight = null; + } // Private + ; + + _proto._getConfig = function _getConfig(config) { + config = _objectSpread2(_objectSpread2({}, Default$6), typeof config === 'object' && config ? config : {}); + + if (typeof config.target !== 'string' && Util.isElement(config.target)) { + var id = $(config.target).attr('id'); + + if (!id) { + id = Util.getUID(NAME$8); + $(config.target).attr('id', id); + } + + config.target = "#" + id; + } + + Util.typeCheckConfig(NAME$8, config, DefaultType$6); + return config; + }; + + _proto._getScrollTop = function _getScrollTop() { + return this._scrollElement === window ? this._scrollElement.pageYOffset : this._scrollElement.scrollTop; + }; + + _proto._getScrollHeight = function _getScrollHeight() { + return this._scrollElement.scrollHeight || Math.max(document.body.scrollHeight, document.documentElement.scrollHeight); + }; + + _proto._getOffsetHeight = function _getOffsetHeight() { + return this._scrollElement === window ? window.innerHeight : this._scrollElement.getBoundingClientRect().height; + }; + + _proto._process = function _process() { + var scrollTop = this._getScrollTop() + this._config.offset; + + var scrollHeight = this._getScrollHeight(); + + var maxScroll = this._config.offset + scrollHeight - this._getOffsetHeight(); + + if (this._scrollHeight !== scrollHeight) { + this.refresh(); + } + + if (scrollTop >= maxScroll) { + var target = this._targets[this._targets.length - 1]; + + if (this._activeTarget !== target) { + this._activate(target); + } + + return; + } + + if (this._activeTarget && scrollTop < this._offsets[0] && this._offsets[0] > 0) { + this._activeTarget = null; + + this._clear(); + + return; + } + + for (var i = this._offsets.length; i--;) { + var isActiveTarget = this._activeTarget !== this._targets[i] && scrollTop >= this._offsets[i] && (typeof this._offsets[i + 1] === 'undefined' || scrollTop < this._offsets[i + 1]); + + if (isActiveTarget) { + this._activate(this._targets[i]); + } + } + }; + + _proto._activate = function _activate(target) { + this._activeTarget = target; + + this._clear(); + + var queries = this._selector.split(',').map(function (selector) { + return selector + "[data-target=\"" + target + "\"]," + selector + "[href=\"" + target + "\"]"; + }); + + var $link = $([].slice.call(document.querySelectorAll(queries.join(',')))); + + if ($link.hasClass(CLASS_NAME_DROPDOWN_ITEM)) { + $link.closest(SELECTOR_DROPDOWN).find(SELECTOR_DROPDOWN_TOGGLE).addClass(CLASS_NAME_ACTIVE$2); + $link.addClass(CLASS_NAME_ACTIVE$2); + } else { + // Set triggered link as active + $link.addClass(CLASS_NAME_ACTIVE$2); // Set triggered links parents as active + // With both